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