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