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