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 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[value.size()]);
 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 Long) {
 390                 return Type.LONG;
 391             } else if (number instanceof Double) {
 392                 return Type.NUMBER;
 393             } else {
 394                 assert false;
 395             }
 396 
 397             return null;
 398         }
 399 
 400         @Override
 401         public Type getType() {
 402             return type;
 403         }
 404 
 405         @Override
 406         public Type getWidestOperationType() {
 407             return getType();
 408         }
 409 
 410     }
 411     /**
 412      * Create a new number literal
 413      *
 414      * @param token   token
 415      * @param finish  finish
 416      * @param value   literal value
 417      *
 418      * @return the new literal node
 419      */
 420     public static LiteralNode<Number> newInstance(final long token, final int finish, final Number value) {
 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         private final List<Splittable.SplitRange> splitRanges;
 638 
 639         @Override
 640         public boolean isArray() {
 641             return true;
 642         }
 643 
 644 
 645         private static final class ArrayLiteralInitializer {
 646 
 647             static ArrayLiteralNode initialize(final ArrayLiteralNode node) {
 648                 final Type elementType = computeElementType(node.value);
 649                 final int[] postsets = computePostsets(node.value);
 650                 final Object presets = computePresets(node.value, elementType, postsets);
 651                 return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.splitRanges);
 652             }
 653 
 654             private static Type computeElementType(final Expression[] value) {
 655                 Type widestElementType = Type.INT;
 656 
 657                 for (final Expression elem : value) {
 658                     if (elem == null) {
 659                         widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
 660                         break;
 661                     }
 662 
 663                     final Type type = elem.getType().isUnknown() ? Type.OBJECT : elem.getType();
 664                     if (type.isBoolean()) {
 665                         //TODO fix this with explicit boolean types
 666                         widestElementType = widestElementType.widest(Type.OBJECT);
 667                         break;
 668                     }
 669 
 670                     widestElementType = widestElementType.widest(type);
 671                     if (widestElementType.isObject()) {
 672                         break;
 673                     }
 674                 }
 675                 return widestElementType;
 676             }
 677 
 678             private static int[] computePostsets(final Expression[] value) {
 679                 final int[] computed = new int[value.length];
 680                 int nComputed = 0;
 681 
 682                 for (int i = 0; i < value.length; i++) {
 683                     final Expression element = value[i];
 684                     if (element == null || !isConstant(element)) {
 685                         computed[nComputed++] = i;
 686                     }
 687                 }
 688                 return Arrays.copyOf(computed, nComputed);
 689             }
 690 
 691             private static boolean setArrayElement(final int[] array, final int i, final Object n) {
 692                 if (n instanceof Number) {
 693                     array[i] = ((Number)n).intValue();
 694                     return true;
 695                 }
 696                 return false;
 697             }
 698 
 699             private static boolean setArrayElement(final long[] array, final int i, final Object n) {
 700                 if (n instanceof Number) {
 701                     array[i] = ((Number)n).longValue();
 702                     return true;
 703                 }
 704                 return false;
 705             }
 706 
 707             private static boolean setArrayElement(final double[] array, final int i, final Object n) {
 708                 if (n instanceof Number) {
 709                     array[i] = ((Number)n).doubleValue();
 710                     return true;
 711                 }
 712                 return false;
 713             }
 714 
 715             private static int[] presetIntArray(final Expression[] value, final int[] postsets) {
 716                 final int[] array = new int[value.length];
 717                 int nComputed = 0;
 718                 for (int i = 0; i < value.length; i++) {
 719                     if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
 720                         assert postsets[nComputed++] == i;
 721                     }
 722                 }
 723                 assert postsets.length == nComputed;
 724                 return array;
 725             }
 726 
 727             private static long[] presetLongArray(final Expression[] value, final int[] postsets) {
 728                 final long[] array = new long[value.length];
 729                 int nComputed = 0;
 730                 for (int i = 0; i < value.length; i++) {
 731                     if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
 732                         assert postsets[nComputed++] == i;
 733                     }
 734                 }
 735                 assert postsets.length == nComputed;
 736                 return array;
 737             }
 738 
 739             private static double[] presetDoubleArray(final Expression[] value, final int[] postsets) {
 740                 final double[] array = new double[value.length];
 741                 int nComputed = 0;
 742                 for (int i = 0; i < value.length; i++) {
 743                     if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
 744                         assert postsets[nComputed++] == i;
 745                     }
 746                 }
 747                 assert postsets.length == nComputed;
 748                 return array;
 749             }
 750 
 751             private static Object[] presetObjectArray(final Expression[] value, final int[] postsets) {
 752                 final Object[] array = new Object[value.length];
 753                 int nComputed = 0;
 754 
 755                 for (int i = 0; i < value.length; i++) {
 756                     final Node node = value[i];
 757 
 758                     if (node == null) {
 759                         assert postsets[nComputed++] == i;
 760                         continue;
 761                     }
 762                     final Object element = objectAsConstant(node);
 763 
 764                     if (element != POSTSET_MARKER) {
 765                         array[i] = element;
 766                     } else {
 767                         assert postsets[nComputed++] == i;
 768                     }
 769                 }
 770 
 771                 assert postsets.length == nComputed;
 772                 return array;
 773             }
 774 
 775             static Object computePresets(final Expression[] value, final Type elementType, final int[] postsets) {
 776                 assert !elementType.isUnknown();
 777                 if (elementType.isInteger()) {
 778                     return presetIntArray(value, postsets);
 779                 } else if (elementType.isLong()) {
 780                     return presetLongArray(value, postsets);
 781                 } else if (elementType.isNumeric()) {
 782                     return presetDoubleArray(value, postsets);
 783                 } else {
 784                     return presetObjectArray(value, postsets);
 785                 }
 786             }
 787         }
 788 
 789         /**
 790          * Constructor
 791          *
 792          * @param token   token
 793          * @param finish  finish
 794          * @param value   array literal value, a Node array
 795          */
 796         protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) {
 797             super(Token.recast(token, TokenType.ARRAY), finish, value);
 798             this.elementType = Type.UNKNOWN;
 799             this.presets     = null;
 800             this.postsets    = null;
 801             this.splitRanges = null;
 802         }
 803 
 804         /**
 805          * Copy constructor
 806          * @param node source array literal node
 807          */
 808         private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<Splittable.SplitRange> splitRanges) {
 809             super(node, value);
 810             this.elementType = elementType;
 811             this.postsets    = postsets;
 812             this.presets     = presets;
 813             this.splitRanges = splitRanges;
 814         }
 815 
 816         /**
 817          * Returns a list of array element expressions. Note that empty array elements manifest themselves as
 818          * null.
 819          * @return a list of array element expressions.
 820          */
 821         @Override
 822         public List<Expression> getElementExpressions() {
 823             return Collections.unmodifiableList(Arrays.asList(value));
 824         }
 825 
 826         /**
 827          * Setter that initializes all code generation meta data for an
 828          * ArrayLiteralNode. This acts a setter, so the return value may
 829          * return a new node and must be handled
 830          *
 831          * @param lc lexical context
 832          * @return new array literal node with postsets, presets and element types initialized
 833          */
 834         @Override
 835         public ArrayLiteralNode initialize(final LexicalContext lc) {
 836             return Node.replaceInLexicalContext(lc, this, ArrayLiteralInitializer.initialize(this));
 837         }
 838 
 839         /**
 840          * Get the array element type as Java format, e.g. [I
 841          * @return array element type
 842          */
 843         public ArrayType getArrayType() {
 844             return getArrayType(getElementType());
 845         }
 846 
 847         private static ArrayType getArrayType(final Type elementType) {
 848             if (elementType.isInteger()) {
 849                 return Type.INT_ARRAY;
 850             } else if (elementType.isLong()) {
 851                 return Type.LONG_ARRAY;
 852             } else if (elementType.isNumeric()) {
 853                 return Type.NUMBER_ARRAY;
 854             } else {
 855                 return Type.OBJECT_ARRAY;
 856             }
 857         }
 858 
 859         @Override
 860         public Type getType() {
 861             return Type.typeFor(NativeArray.class);
 862         }
 863 
 864         /**
 865          * Get the element type of this array literal
 866          * @return element type
 867          */
 868         public Type getElementType() {
 869             assert !elementType.isUnknown() : this + " has elementType=unknown";
 870             return elementType;
 871         }
 872 
 873         /**
 874          * Get indices of arrays containing computed post sets. post sets
 875          * are things like non literals e.g. "x+y" instead of i or 17
 876          * @return post set indices
 877          */
 878         public int[] getPostsets() {
 879             assert postsets != null : this + " elementType=" + elementType + " has no postsets";
 880             return postsets;
 881         }
 882 
 883         private boolean presetsMatchElementType() {
 884             if (elementType == Type.INT) {
 885                 return presets instanceof int[];
 886             } else if (elementType == Type.LONG) {
 887                 return presets instanceof long[];
 888             } else if (elementType == Type.NUMBER) {
 889                 return presets instanceof double[];
 890             } else {
 891                 return presets instanceof Object[];
 892             }
 893         }
 894 
 895         /**
 896          * Get presets constant array
 897          * @return presets array, always returns an array type
 898          */
 899         public Object getPresets() {
 900             assert presets != null && presetsMatchElementType() : this + " doesn't have presets, or invalid preset type: " + presets;
 901             return presets;
 902         }
 903 
 904         /**
 905          * Get the split ranges for this ArrayLiteral, or null if this array does not have to be split.
 906          * @see Splittable.SplitRange
 907          * @return list of split ranges
 908          */
 909         @Override
 910         public List<Splittable.SplitRange> getSplitRanges() {
 911             return splitRanges == null ? null : Collections.unmodifiableList(splitRanges);
 912         }
 913 
 914         /**
 915          * Set the SplitRanges that make up this ArrayLiteral
 916          * @param lc lexical context
 917          * @see Splittable.SplitRange
 918          * @param splitRanges list of split ranges
 919          * @return new or changed node
 920          */
 921         public ArrayLiteralNode setSplitRanges(final LexicalContext lc, final List<Splittable.SplitRange> splitRanges) {
 922             if (this.splitRanges == splitRanges) {
 923                 return this;
 924             }
 925             return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, splitRanges));
 926         }
 927 
 928         @Override
 929         public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 930             return Acceptor.accept(this, visitor);
 931         }
 932 
 933         @Override
 934         public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
 935             if (visitor.enterLiteralNode(this)) {
 936                 final List<Expression> oldValue = Arrays.asList(value);
 937                 final List<Expression> newValue = Node.accept(visitor, oldValue);
 938                 return visitor.leaveLiteralNode(oldValue != newValue ? setValue(lc, newValue) : this);
 939             }
 940             return this;
 941         }
 942 
 943         private ArrayLiteralNode setValue(final LexicalContext lc, final Expression[] value) {
 944             if (this.value == value) {
 945                 return this;
 946             }
 947             return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, splitRanges));
 948         }
 949 
 950         private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
 951             return setValue(lc, value.toArray(new Expression[value.size()]));
 952         }
 953 
 954         @Override
 955         public void toString(final StringBuilder sb, final boolean printType) {
 956             sb.append('[');
 957             boolean first = true;
 958             for (final Node node : value) {
 959                 if (!first) {
 960                     sb.append(',');
 961                     sb.append(' ');
 962                 }
 963                 if (node == null) {
 964                     sb.append("undefined");
 965                 } else {
 966                     node.toString(sb, printType);
 967                 }
 968                 first = false;
 969             }
 970             sb.append(']');
 971         }
 972     }
 973 
 974     /**
 975      * Create a new array literal of Nodes from a list of Node values
 976      *
 977      * @param token   token
 978      * @param finish  finish
 979      * @param value   literal value list
 980      *
 981      * @return the new literal node
 982      */
 983     public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value) {
 984         return new ArrayLiteralNode(token, finish, valueToArray(value));
 985     }
 986 
 987     /**
 988      * Create a new array literal based on a parent node (source, token, finish)
 989      *
 990      * @param parent parent node
 991      * @param value  literal value list
 992      *
 993      * @return the new literal node
 994      */
 995     public static LiteralNode<?> newInstance(final Node parent, final List<Expression> value) {
 996         return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), valueToArray(value));
 997     }
 998 
 999     /**
1000      * Create a new array literal of Nodes
1001      *
1002      * @param token   token
1003      * @param finish  finish
1004      * @param value   literal value array
1005      *
1006      * @return the new literal node
1007      */
1008     public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final Expression[] value) {
1009         return new ArrayLiteralNode(token, finish, value);
1010     }
1011 }