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 }