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