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