LiteralNode.java revision 953:221a84ef44c0
1/* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.internal.ir; 27 28import java.util.Arrays; 29import java.util.Collections; 30import java.util.List; 31import java.util.function.Function; 32 33import jdk.nashorn.internal.codegen.CompileUnit; 34import jdk.nashorn.internal.codegen.types.ArrayType; 35import jdk.nashorn.internal.codegen.types.Type; 36import jdk.nashorn.internal.ir.annotations.Immutable; 37import jdk.nashorn.internal.ir.visitor.NodeVisitor; 38import jdk.nashorn.internal.objects.NativeArray; 39import jdk.nashorn.internal.parser.Lexer.LexerToken; 40import jdk.nashorn.internal.parser.Token; 41import jdk.nashorn.internal.parser.TokenType; 42import jdk.nashorn.internal.runtime.JSType; 43import jdk.nashorn.internal.runtime.ScriptRuntime; 44import jdk.nashorn.internal.runtime.Undefined; 45 46/** 47 * Literal nodes represent JavaScript values. 48 * 49 * @param <T> the literal type 50 */ 51@Immutable 52public abstract class LiteralNode<T> extends Expression implements PropertyKey { 53 /** Literal value */ 54 protected final T value; 55 56 /** Marker for values that must be computed at runtime */ 57 public static final Object POSTSET_MARKER = new Object(); 58 59 /** 60 * Constructor 61 * 62 * @param token token 63 * @param finish finish 64 * @param value the value of the literal 65 */ 66 protected LiteralNode(final long token, final int finish, final T value) { 67 super(token, finish); 68 this.value = value; 69 } 70 71 /** 72 * Copy constructor 73 * 74 * @param literalNode source node 75 */ 76 protected LiteralNode(final LiteralNode<T> literalNode) { 77 this(literalNode, literalNode.value); 78 } 79 80 /** 81 * A copy constructor with value change. 82 * @param literalNode the original literal node 83 * @param newValue new value for this node 84 */ 85 protected LiteralNode(final LiteralNode<T> literalNode, final T newValue) { 86 super(literalNode); 87 this.value = newValue; 88 } 89 90 /** 91 * Initialization setter, if required for immutable state. This is used for 92 * things like ArrayLiteralNodes that need to carry state for the splitter. 93 * Default implementation is just a nop. 94 * @param lc lexical context 95 * @return new literal node with initialized state, or same if nothing changed 96 */ 97 public LiteralNode<?> initialize(final LexicalContext lc) { 98 return this; 99 } 100 101 /** 102 * Check if the literal value is null 103 * @return true if literal value is null 104 */ 105 public boolean isNull() { 106 return value == null; 107 } 108 109 @Override 110 public Type getType(final Function<Symbol, Type> localVariableTypes) { 111 return Type.typeFor(value.getClass()); 112 } 113 114 @Override 115 public String getPropertyName() { 116 return JSType.toString(getObject()); 117 } 118 119 /** 120 * Fetch boolean value of node. 121 * 122 * @return boolean value of node. 123 */ 124 public boolean getBoolean() { 125 return JSType.toBoolean(value); 126 } 127 128 /** 129 * Fetch int32 value of node. 130 * 131 * @return Int32 value of node. 132 */ 133 public int getInt32() { 134 return JSType.toInt32(value); 135 } 136 137 /** 138 * Fetch uint32 value of node. 139 * 140 * @return uint32 value of node. 141 */ 142 public long getUint32() { 143 return JSType.toUint32(value); 144 } 145 146 /** 147 * Fetch long value of node 148 * 149 * @return long value of node 150 */ 151 public long getLong() { 152 return JSType.toLong(value); 153 } 154 155 /** 156 * Fetch double value of node. 157 * 158 * @return double value of node. 159 */ 160 public double getNumber() { 161 return JSType.toNumber(value); 162 } 163 164 /** 165 * Get the array value of the node 166 * 167 * @return the array value 168 */ 169 public Node[] getArray() { 170 assert false : "not an array node"; 171 return null; 172 } 173 174 /** 175 * Fetch String value of node. 176 * 177 * @return String value of node. 178 */ 179 public String getString() { 180 return JSType.toString(value); 181 } 182 183 /** 184 * Fetch Object value of node. 185 * 186 * @return Object value of node. 187 */ 188 public Object getObject() { 189 return value; 190 } 191 192 /** 193 * Test if the value is a string. 194 * 195 * @return True if value is a string. 196 */ 197 public boolean isString() { 198 return value instanceof String; 199 } 200 201 /** 202 * Test if tha value is a number 203 * 204 * @return True if value is a number 205 */ 206 public boolean isNumeric() { 207 return value instanceof Number; 208 } 209 210 /** 211 * Assist in IR navigation. 212 * 213 * @param visitor IR navigating visitor. 214 */ 215 @Override 216 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 217 if (visitor.enterLiteralNode(this)) { 218 return visitor.leaveLiteralNode(this); 219 } 220 221 return this; 222 } 223 224 @Override 225 public void toString(final StringBuilder sb, final boolean printType) { 226 if (value == null) { 227 sb.append("null"); 228 } else { 229 sb.append(value.toString()); 230 } 231 } 232 233 /** 234 * Get the literal node value 235 * @return the value 236 */ 237 public final T getValue() { 238 return value; 239 } 240 241 /** 242 * Create a new null literal 243 * 244 * @param token token 245 * @param finish finish 246 * 247 * @return the new literal node 248 */ 249 public static LiteralNode<Object> newInstance(final long token, final int finish) { 250 return new NullLiteralNode(token, finish); 251 } 252 253 /** 254 * Create a new null literal based on a parent node (source, token, finish) 255 * 256 * @param parent parent node 257 * 258 * @return the new literal node 259 */ 260 public static LiteralNode<Object> newInstance(final Node parent) { 261 return new NullLiteralNode(parent.getToken(), parent.getFinish()); 262 } 263 264 /** 265 * Super class for primitive (side-effect free) literals. 266 * 267 * @param <T> the literal type 268 */ 269 public static class PrimitiveLiteralNode<T> extends LiteralNode<T> { 270 private PrimitiveLiteralNode(final long token, final int finish, final T value) { 271 super(token, finish, value); 272 } 273 274 private PrimitiveLiteralNode(final PrimitiveLiteralNode<T> literalNode) { 275 super(literalNode); 276 } 277 278 /** 279 * Check if the literal value is boolean true 280 * @return true if literal value is boolean true 281 */ 282 public boolean isTrue() { 283 return JSType.toBoolean(value); 284 } 285 286 @Override 287 public boolean isLocal() { 288 return true; 289 } 290 291 @Override 292 public boolean isAlwaysFalse() { 293 return !isTrue(); 294 } 295 296 @Override 297 public boolean isAlwaysTrue() { 298 return isTrue(); 299 } 300 } 301 302 @Immutable 303 private static final class BooleanLiteralNode extends PrimitiveLiteralNode<Boolean> { 304 305 private BooleanLiteralNode(final long token, final int finish, final boolean value) { 306 super(Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value); 307 } 308 309 private BooleanLiteralNode(final BooleanLiteralNode literalNode) { 310 super(literalNode); 311 } 312 313 @Override 314 public boolean isTrue() { 315 return value; 316 } 317 318 @Override 319 public Type getType(final Function<Symbol, Type> localVariableTypes) { 320 return Type.BOOLEAN; 321 } 322 323 @Override 324 public Type getWidestOperationType() { 325 return Type.BOOLEAN; 326 } 327 } 328 329 /** 330 * Create a new boolean literal 331 * 332 * @param token token 333 * @param finish finish 334 * @param value true or false 335 * 336 * @return the new literal node 337 */ 338 public static LiteralNode<Boolean> newInstance(final long token, final int finish, final boolean value) { 339 return new BooleanLiteralNode(token, finish, value); 340 } 341 342 /** 343 * Create a new boolean literal based on a parent node (source, token, finish) 344 * 345 * @param parent parent node 346 * @param value true or false 347 * 348 * @return the new literal node 349 */ 350 public static LiteralNode<?> newInstance(final Node parent, final boolean value) { 351 return new BooleanLiteralNode(parent.getToken(), parent.getFinish(), value); 352 } 353 354 @Immutable 355 private static final class NumberLiteralNode extends PrimitiveLiteralNode<Number> { 356 357 private final Type type = numberGetType(value); 358 359 private NumberLiteralNode(final long token, final int finish, final Number value) { 360 super(Token.recast(token, TokenType.DECIMAL), finish, value); 361 } 362 363 private NumberLiteralNode(final NumberLiteralNode literalNode) { 364 super(literalNode); 365 } 366 367 private static Type numberGetType(final Number number) { 368 if (number instanceof Integer) { 369 return Type.INT; 370 } else if (number instanceof Long) { 371 return Type.LONG; 372 } else if (number instanceof Double) { 373 return Type.NUMBER; 374 } else { 375 assert false; 376 } 377 378 return null; 379 } 380 381 @Override 382 public Type getType(final Function<Symbol, Type> localVariableTypes) { 383 return type; 384 } 385 386 @Override 387 public Type getWidestOperationType() { 388 return getType(); 389 } 390 391 } 392 /** 393 * Create a new number literal 394 * 395 * @param token token 396 * @param finish finish 397 * @param value literal value 398 * 399 * @return the new literal node 400 */ 401 public static LiteralNode<Number> newInstance(final long token, final int finish, final Number value) { 402 return new NumberLiteralNode(token, finish, value); 403 } 404 405 /** 406 * Create a new number literal based on a parent node (source, token, finish) 407 * 408 * @param parent parent node 409 * @param value literal value 410 * 411 * @return the new literal node 412 */ 413 public static LiteralNode<?> newInstance(final Node parent, final Number value) { 414 return new NumberLiteralNode(parent.getToken(), parent.getFinish(), value); 415 } 416 417 private static class UndefinedLiteralNode extends PrimitiveLiteralNode<Undefined> { 418 private UndefinedLiteralNode(final long token, final int finish) { 419 super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED); 420 } 421 422 private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) { 423 super(literalNode); 424 } 425 } 426 427 /** 428 * Create a new undefined literal 429 * 430 * @param token token 431 * @param finish finish 432 * @param value undefined value, passed only for polymorphisism discrimination 433 * 434 * @return the new literal node 435 */ 436 public static LiteralNode<Undefined> newInstance(final long token, final int finish, final Undefined value) { 437 return new UndefinedLiteralNode(token, finish); 438 } 439 440 /** 441 * Create a new null literal based on a parent node (source, token, finish) 442 * 443 * @param parent parent node 444 * @param value undefined value 445 * 446 * @return the new literal node 447 */ 448 public static LiteralNode<?> newInstance(final Node parent, final Undefined value) { 449 return new UndefinedLiteralNode(parent.getToken(), parent.getFinish()); 450 } 451 452 @Immutable 453 private static class StringLiteralNode extends PrimitiveLiteralNode<String> { 454 private StringLiteralNode(final long token, final int finish, final String value) { 455 super(Token.recast(token, TokenType.STRING), finish, value); 456 } 457 458 private StringLiteralNode(final StringLiteralNode literalNode) { 459 super(literalNode); 460 } 461 462 @Override 463 public void toString(final StringBuilder sb, final boolean printType) { 464 sb.append('\"'); 465 sb.append(value); 466 sb.append('\"'); 467 } 468 } 469 470 /** 471 * Create a new string literal 472 * 473 * @param token token 474 * @param finish finish 475 * @param value string value 476 * 477 * @return the new literal node 478 */ 479 public static LiteralNode<String> newInstance(final long token, final int finish, final String value) { 480 return new StringLiteralNode(token, finish, value); 481 } 482 483 /** 484 * Create a new String literal based on a parent node (source, token, finish) 485 * 486 * @param parent parent node 487 * @param value string value 488 * 489 * @return the new literal node 490 */ 491 public static LiteralNode<?> newInstance(final Node parent, final String value) { 492 return new StringLiteralNode(parent.getToken(), parent.getFinish(), value); 493 } 494 495 @Immutable 496 private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> { 497 private LexerTokenLiteralNode(final long token, final int finish, final LexerToken value) { 498 super(Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here? 499 } 500 501 private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) { 502 super(literalNode); 503 } 504 505 @Override 506 public Type getType(final Function<Symbol, Type> localVariableTypes) { 507 return Type.OBJECT; 508 } 509 510 @Override 511 public void toString(final StringBuilder sb, final boolean printType) { 512 sb.append(value.toString()); 513 } 514 } 515 516 /** 517 * Create a new literal node for a lexer token 518 * 519 * @param token token 520 * @param finish finish 521 * @param value lexer token value 522 * 523 * @return the new literal node 524 */ 525 public static LiteralNode<LexerToken> newInstance(final long token, final int finish, final LexerToken value) { 526 return new LexerTokenLiteralNode(token, finish, value); 527 } 528 529 /** 530 * Create a new lexer token literal based on a parent node (source, token, finish) 531 * 532 * @param parent parent node 533 * @param value lexer token 534 * 535 * @return the new literal node 536 */ 537 public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) { 538 return new LexerTokenLiteralNode(parent.getToken(), parent.getFinish(), value); 539 } 540 541 /** 542 * Get the constant value for an object, or {@link #POSTSET_MARKER} if the value can't be statically computed. 543 * 544 * @param object a node or value object 545 * @return the constant value or {@code POSTSET_MARKER} 546 */ 547 public static Object objectAsConstant(final Object object) { 548 if (object == null) { 549 return null; 550 } else if (object instanceof Number || object instanceof String || object instanceof Boolean) { 551 return object; 552 } else if (object instanceof LiteralNode) { 553 return objectAsConstant(((LiteralNode<?>)object).getValue()); 554 } 555 556 return POSTSET_MARKER; 557 } 558 559 private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> { 560 561 private NullLiteralNode(final long token, final int finish) { 562 super(Token.recast(token, TokenType.OBJECT), finish, null); 563 } 564 565 @Override 566 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 567 if (visitor.enterLiteralNode(this)) { 568 return visitor.leaveLiteralNode(this); 569 } 570 571 return this; 572 } 573 574 @Override 575 public Type getType(final Function<Symbol, Type> localVariableTypes) { 576 return Type.OBJECT; 577 } 578 579 @Override 580 public Type getWidestOperationType() { 581 return Type.OBJECT; 582 } 583 } 584 585 /** 586 * Array literal node class. 587 */ 588 @Immutable 589 public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode { 590 591 /** Array element type. */ 592 private final Type elementType; 593 594 /** Preset constant array. */ 595 private final Object presets; 596 597 /** Indices of array elements requiring computed post sets. */ 598 private final int[] postsets; 599 600 /** Sub units with indexes ranges, in which to split up code generation, for large literals */ 601 private final List<ArrayUnit> units; 602 603 /** 604 * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can 605 * be split if they are too large, for bytecode generation reasons 606 */ 607 public static final class ArrayUnit { 608 /** Compile unit associated with the postsets range. */ 609 private final CompileUnit compileUnit; 610 611 /** postsets range associated with the unit (hi not inclusive). */ 612 private final int lo, hi; 613 614 /** 615 * Constructor 616 * @param compileUnit compile unit 617 * @param lo lowest array index in unit 618 * @param hi highest array index in unit + 1 619 */ 620 public ArrayUnit(final CompileUnit compileUnit, final int lo, final int hi) { 621 this.compileUnit = compileUnit; 622 this.lo = lo; 623 this.hi = hi; 624 } 625 626 /** 627 * Get the high index position of the ArrayUnit (non inclusive) 628 * @return high index position 629 */ 630 public int getHi() { 631 return hi; 632 } 633 634 /** 635 * Get the low index position of the ArrayUnit (inclusive) 636 * @return low index position 637 */ 638 public int getLo() { 639 return lo; 640 } 641 642 /** 643 * The array compile unit 644 * @return array compile unit 645 */ 646 public CompileUnit getCompileUnit() { 647 return compileUnit; 648 } 649 } 650 651 private static final class ArrayLiteralInitializer { 652 653 static ArrayLiteralNode initialize(final ArrayLiteralNode node) { 654 final Type elementType = computeElementType(node.value, node.elementType); 655 final int[] postsets = computePostsets(node.value); 656 final Object presets = computePresets(node.value, elementType, postsets); 657 return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units); 658 } 659 660 private static Type computeElementType(final Expression[] value, final Type elementType) { 661 Type widestElementType = Type.INT; 662 663 for (final Expression elem : value) { 664 if (elem == null) { 665 widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number 666 break; 667 } 668 669 final Type type = elem.getType().isUnknown() ? Type.OBJECT : elem.getType(); 670 if (type.isBoolean()) { 671 //TODO fix this with explicit boolean types 672 widestElementType = widestElementType.widest(Type.OBJECT); 673 break; 674 } 675 676 widestElementType = widestElementType.widest(type); 677 if (widestElementType.isObject()) { 678 break; 679 } 680 } 681 return widestElementType; 682 } 683 684 private static int[] computePostsets(final Expression[] value) { 685 final int[] computed = new int[value.length]; 686 int nComputed = 0; 687 688 for (int i = 0; i < value.length; i++) { 689 final Expression element = value[i]; 690 if (element == null || objectAsConstant(element) == POSTSET_MARKER) { 691 computed[nComputed++] = i; 692 } 693 } 694 return Arrays.copyOf(computed, nComputed); 695 } 696 697 private static boolean setArrayElement(final int[] array, final int i, final Object n) { 698 if (n instanceof Number) { 699 array[i] = ((Number)n).intValue(); 700 return true; 701 } 702 return false; 703 } 704 705 private static boolean setArrayElement(final long[] array, final int i, final Object n) { 706 if (n instanceof Number) { 707 array[i] = ((Number)n).longValue(); 708 return true; 709 } 710 return false; 711 } 712 713 private static boolean setArrayElement(final double[] array, final int i, final Object n) { 714 if (n instanceof Number) { 715 array[i] = ((Number)n).doubleValue(); 716 return true; 717 } 718 return false; 719 } 720 721 private static int[] presetIntArray(final Expression[] value, final int[] postsets) { 722 final int[] array = new int[value.length]; 723 int nComputed = 0; 724 for (int i = 0; i < value.length; i++) { 725 if (!setArrayElement(array, i, objectAsConstant(value[i]))) { 726 assert postsets[nComputed++] == i; 727 } 728 } 729 assert postsets.length == nComputed; 730 return array; 731 } 732 733 private static long[] presetLongArray(final Expression[] value, final int[] postsets) { 734 final long[] array = new long[value.length]; 735 int nComputed = 0; 736 for (int i = 0; i < value.length; i++) { 737 if (!setArrayElement(array, i, objectAsConstant(value[i]))) { 738 assert postsets[nComputed++] == i; 739 } 740 } 741 assert postsets.length == nComputed; 742 return array; 743 } 744 745 private static double[] presetDoubleArray(final Expression[] value, final int[] postsets) { 746 final double[] array = new double[value.length]; 747 int nComputed = 0; 748 for (int i = 0; i < value.length; i++) { 749 if (!setArrayElement(array, i, objectAsConstant(value[i]))) { 750 assert postsets[nComputed++] == i; 751 } 752 } 753 assert postsets.length == nComputed; 754 return array; 755 } 756 757 private static Object[] presetObjectArray(final Expression[] value, final int[] postsets) { 758 final Object[] array = new Object[value.length]; 759 int nComputed = 0; 760 761 for (int i = 0; i < value.length; i++) { 762 final Node node = value[i]; 763 764 if (node == null) { 765 assert postsets[nComputed++] == i; 766 continue; 767 } 768 final Object element = objectAsConstant(node); 769 770 if (element != POSTSET_MARKER) { 771 array[i] = element; 772 } else { 773 assert postsets[nComputed++] == i; 774 } 775 } 776 777 assert postsets.length == nComputed; 778 return array; 779 } 780 781 static Object computePresets(final Expression[] value, final Type elementType, final int[] postsets) { 782 assert !elementType.isUnknown(); 783 if (elementType.isInteger()) { 784 return presetIntArray(value, postsets); 785 } else if (elementType.isLong()) { 786 return presetLongArray(value, postsets); 787 } else if (elementType.isNumeric()) { 788 return presetDoubleArray(value, postsets); 789 } else { 790 return presetObjectArray(value, postsets); 791 } 792 } 793 } 794 795 /** 796 * Constructor 797 * 798 * @param token token 799 * @param finish finish 800 * @param value array literal value, a Node array 801 */ 802 protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) { 803 super(Token.recast(token, TokenType.ARRAY), finish, value); 804 this.elementType = Type.UNKNOWN; 805 this.presets = null; 806 this.postsets = null; 807 this.units = null; 808 } 809 810 /** 811 * Copy constructor 812 * @param node source array literal node 813 */ 814 private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<ArrayUnit> units) { 815 super(node, value); 816 this.elementType = elementType; 817 this.postsets = postsets; 818 this.presets = presets; 819 this.units = units; 820 } 821 822 @Override 823 public Node[] getArray() { 824 return value; 825 } 826 827 /** 828 * Setter that initializes all code generation meta data for an 829 * ArrayLiteralNode. This acts a setter, so the return value may 830 * return a new node and must be handled 831 * 832 * @param lc lexical context 833 * @return new array literal node with postsets, presets and element types initialized 834 */ 835 @Override 836 public ArrayLiteralNode initialize(final LexicalContext lc) { 837 return Node.replaceInLexicalContext(lc, this, ArrayLiteralInitializer.initialize(this)); 838 } 839 840 /** 841 * Get the array element type as Java format, e.g. [I 842 * @return array element type 843 */ 844 public ArrayType getArrayType() { 845 return getArrayType(getElementType()); 846 } 847 848 private static ArrayType getArrayType(final Type elementType) { 849 if (elementType.isInteger()) { 850 return Type.INT_ARRAY; 851 } else if (elementType.isLong()) { 852 return Type.LONG_ARRAY; 853 } else if (elementType.isNumeric()) { 854 return Type.NUMBER_ARRAY; 855 } else { 856 return Type.OBJECT_ARRAY; 857 } 858 } 859 860 @Override 861 public Type getType(final Function<Symbol, Type> localVariableTypes) { 862 return Type.typeFor(NativeArray.class); 863 } 864 865 /** 866 * Get the element type of this array literal 867 * @return element type 868 */ 869 public Type getElementType() { 870 assert !elementType.isUnknown() : this + " has elementType=unknown"; 871 return elementType; 872 } 873 874 /** 875 * Get indices of arrays containing computed post sets. post sets 876 * are things like non literals e.g. "x+y" instead of i or 17 877 * @return post set indices 878 */ 879 public int[] getPostsets() { 880 assert postsets != null : this + " elementType=" + elementType + " has no postsets"; 881 return postsets; 882 } 883 884 private boolean presetsMatchElementType() { 885 if (elementType == Type.INT) { 886 return presets instanceof int[]; 887 } else if (elementType == Type.LONG) { 888 return presets instanceof long[]; 889 } else if (elementType == Type.NUMBER) { 890 return presets instanceof double[]; 891 } else { 892 return presets instanceof Object[]; 893 } 894 } 895 896 /** 897 * Get presets constant array 898 * @return presets array, always returns an array type 899 */ 900 public Object getPresets() { 901 assert presets != null && presetsMatchElementType() : this + " doesn't have presets, or invalid preset type: " + presets; 902 return presets; 903 } 904 905 /** 906 * Get the array units that make up this ArrayLiteral 907 * @see ArrayUnit 908 * @return list of array units 909 */ 910 public List<ArrayUnit> getUnits() { 911 return units == null ? null : Collections.unmodifiableList(units); 912 } 913 914 /** 915 * Set the ArrayUnits that make up this ArrayLiteral 916 * @param lc lexical context 917 * @see ArrayUnit 918 * @param units list of array units 919 * @return new or changed arrayliteralnode 920 */ 921 public ArrayLiteralNode setUnits(final LexicalContext lc, final List<ArrayUnit> units) { 922 if (this.units == units) { 923 return this; 924 } 925 return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units)); 926 } 927 928 @Override 929 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 930 return Acceptor.accept(this, visitor); 931 } 932 933 @Override 934 public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { 935 if (visitor.enterLiteralNode(this)) { 936 final List<Expression> oldValue = Arrays.asList(value); 937 final List<Expression> newValue = Node.accept(visitor, oldValue); 938 return visitor.leaveLiteralNode(oldValue != newValue ? setValue(lc, newValue) : this); 939 } 940 return this; 941 } 942 943 private ArrayLiteralNode setValue(final LexicalContext lc, final Expression[] value) { 944 if (this.value == value) { 945 return this; 946 } 947 return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units)); 948 } 949 950 private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) { 951 return setValue(lc, value.toArray(new Expression[value.size()])); 952 } 953 954 @Override 955 public void toString(final StringBuilder sb, final boolean printType) { 956 sb.append('['); 957 boolean first = true; 958 for (final Node node : value) { 959 if (!first) { 960 sb.append(','); 961 sb.append(' '); 962 } 963 if (node == null) { 964 sb.append("undefined"); 965 } else { 966 node.toString(sb, printType); 967 } 968 first = false; 969 } 970 sb.append(']'); 971 } 972 } 973 974 /** 975 * Create a new array literal of Nodes from a list of Node values 976 * 977 * @param token token 978 * @param finish finish 979 * @param value literal value list 980 * 981 * @return the new literal node 982 */ 983 public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value) { 984 return new ArrayLiteralNode(token, finish, value.toArray(new Expression[value.size()])); 985 } 986 987 988 /** 989 * Create a new array literal based on a parent node (source, token, finish) 990 * 991 * @param parent parent node 992 * @param value literal value list 993 * 994 * @return the new literal node 995 */ 996 public static LiteralNode<?> newInstance(final Node parent, final List<Expression> value) { 997 return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), value.toArray(new Expression[value.size()])); 998 } 999 1000 /** 1001 * Create a new array literal of Nodes 1002 * 1003 * @param token token 1004 * @param finish finish 1005 * @param value literal value array 1006 * 1007 * @return the new literal node 1008 */ 1009 public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final Expression[] value) { 1010 return new ArrayLiteralNode(token, finish, value); 1011 } 1012} 1013