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