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 Double) {
 390                 return Type.NUMBER;
 391             } else {
 392                 assert false;
 393             }
 394 
 395             return null;
 396         }
 397 
 398         @Override
 399         public Type getType() {
 400             return type;
 401         }
 402 
 403         @Override
 404         public Type getWidestOperationType() {
 405             return getType();
 406         }
 407 
 408     }
 409     /**
 410      * Create a new number literal
 411      *
 412      * @param token   token
 413      * @param finish  finish
 414      * @param value   literal value
 415      *
 416      * @return the new literal node
 417      */
 418     public static LiteralNode<Number> newInstance(final long token, final int finish, final Number value) {
 419         assert !(value instanceof Long);
 420         return new NumberLiteralNode(token, finish, value);
 421     }
 422 
 423     /**
 424      * Create a new number literal based on a parent node (source, token, finish)
 425      *
 426      * @param parent parent node
 427      * @param value  literal value
 428      *
 429      * @return the new literal node
 430      */
 431     public static LiteralNode<?> newInstance(final Node parent, final Number value) {
 432         return new NumberLiteralNode(parent.getToken(), parent.getFinish(), value);
 433     }
 434 
 435     private static class UndefinedLiteralNode extends PrimitiveLiteralNode<Undefined> {
 436         private static final long serialVersionUID = 1L;
 437 
 438         private UndefinedLiteralNode(final long token, final int finish) {
 439             super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED);
 440         }
 441 
 442         private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
 443             super(literalNode);
 444         }
 445     }
 446 
 447     /**
 448      * Create a new undefined literal
 449      *
 450      * @param token   token
 451      * @param finish  finish
 452      * @param value   undefined value, passed only for polymorphism discrimination
 453      *
 454      * @return the new literal node
 455      */
 456     public static LiteralNode<Undefined> newInstance(final long token, final int finish, final Undefined value) {
 457         return new UndefinedLiteralNode(token, finish);
 458     }
 459 
 460     /**
 461      * Create a new null literal based on a parent node (source, token, finish)
 462      *
 463      * @param parent parent node
 464      * @param value  undefined value
 465      *
 466      * @return the new literal node
 467      */
 468     public static LiteralNode<?> newInstance(final Node parent, final Undefined value) {
 469         return new UndefinedLiteralNode(parent.getToken(), parent.getFinish());
 470     }
 471 
 472     @Immutable
 473     private static class StringLiteralNode extends PrimitiveLiteralNode<String> {
 474         private static final long serialVersionUID = 1L;
 475 
 476         private StringLiteralNode(final long token, final int finish, final String value) {
 477             super(Token.recast(token, TokenType.STRING), finish, value);
 478         }
 479 
 480         private StringLiteralNode(final StringLiteralNode literalNode) {
 481             super(literalNode);
 482         }
 483 
 484         @Override
 485         public void toString(final StringBuilder sb, final boolean printType) {
 486             sb.append('\"');
 487             sb.append(value);
 488             sb.append('\"');
 489         }
 490     }
 491 
 492     /**
 493      * Create a new string literal
 494      *
 495      * @param token   token
 496      * @param finish  finish
 497      * @param value   string value
 498      *
 499      * @return the new literal node
 500      */
 501     public static LiteralNode<String> newInstance(final long token, final int finish, final String value) {
 502         return new StringLiteralNode(token, finish, value);
 503     }
 504 
 505     /**
 506      * Create a new String literal based on a parent node (source, token, finish)
 507      *
 508      * @param parent parent node
 509      * @param value  string value
 510      *
 511      * @return the new literal node
 512      */
 513     public static LiteralNode<?> newInstance(final Node parent, final String value) {
 514         return new StringLiteralNode(parent.getToken(), parent.getFinish(), value);
 515     }
 516 
 517     @Immutable
 518     private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
 519         private static final long serialVersionUID = 1L;
 520 
 521         private LexerTokenLiteralNode(final long token, final int finish, final LexerToken value) {
 522             super(Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
 523         }
 524 
 525         private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) {
 526             super(literalNode);
 527         }
 528 
 529         @Override
 530         public Type getType() {
 531             return Type.OBJECT;
 532         }
 533 
 534         @Override
 535         public void toString(final StringBuilder sb, final boolean printType) {
 536             sb.append(value.toString());
 537         }
 538     }
 539 
 540     /**
 541      * Create a new literal node for a lexer token
 542      *
 543      * @param token   token
 544      * @param finish  finish
 545      * @param value   lexer token value
 546      *
 547      * @return the new literal node
 548      */
 549     public static LiteralNode<LexerToken> newInstance(final long token, final int finish, final LexerToken value) {
 550         return new LexerTokenLiteralNode(token, finish, value);
 551     }
 552 
 553     /**
 554      * Create a new lexer token literal based on a parent node (source, token, finish)
 555      *
 556      * @param parent parent node
 557      * @param value  lexer token
 558      *
 559      * @return the new literal node
 560      */
 561     public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) {
 562         return new LexerTokenLiteralNode(parent.getToken(), parent.getFinish(), value);
 563     }
 564 
 565     /**
 566      * Get the constant value for an object, or {@link #POSTSET_MARKER} if the value can't be statically computed.
 567      *
 568      * @param object a node or value object
 569      * @return the constant value or {@code POSTSET_MARKER}
 570      */
 571     public static Object objectAsConstant(final Object object) {
 572         if (object == null) {
 573             return null;
 574         } else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
 575             return object;
 576         } else if (object instanceof LiteralNode) {
 577             return objectAsConstant(((LiteralNode<?>)object).getValue());
 578         }
 579 
 580         return POSTSET_MARKER;
 581     }
 582 
 583     /**
 584      * Test whether {@code object} represents a constant value.
 585      * @param object a node or value object
 586      * @return true if object is a constant value
 587      */
 588     public static boolean isConstant(final Object object) {
 589         return objectAsConstant(object) != POSTSET_MARKER;
 590     }
 591 
 592     private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> {
 593         private static final long serialVersionUID = 1L;
 594 
 595         private NullLiteralNode(final long token, final int finish) {
 596             super(Token.recast(token, TokenType.OBJECT), finish, null);
 597         }
 598 
 599         @Override
 600         public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 601             if (visitor.enterLiteralNode(this)) {
 602                 return visitor.leaveLiteralNode(this);
 603             }
 604 
 605             return this;
 606         }
 607 
 608         @Override
 609         public Type getType() {
 610             return Type.OBJECT;
 611         }
 612 
 613         @Override
 614         public Type getWidestOperationType() {
 615             return Type.OBJECT;
 616         }
 617     }
 618 
 619     /**
 620      * Array literal node class.
 621      */
 622     @Immutable
 623     public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode, Splittable {
 624         private static final long serialVersionUID = 1L;
 625 
 626         /** Array element type. */
 627         private final Type elementType;
 628 
 629         /** Preset constant array. */
 630         private final Object presets;
 631 
 632         /** Indices of array elements requiring computed post sets. */
 633         private final int[] postsets;
 634 
 635         /** Ranges for splitting up large literals in code generation */
 636         private final List<Splittable.SplitRange> splitRanges;
 637 
 638         @Override
 639         public boolean isArray() {
 640             return true;
 641         }
 642 
 643 
 644         private static final class ArrayLiteralInitializer {
 645 
 646             static ArrayLiteralNode initialize(final ArrayLiteralNode node) {
 647                 final Type elementType = computeElementType(node.value);
 648                 final int[] postsets = computePostsets(node.value);
 649                 final Object presets = computePresets(node.value, elementType, postsets);
 650                 return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.splitRanges);
 651             }
 652 
 653             private static Type computeElementType(final Expression[] value) {
 654                 Type widestElementType = Type.INT;
 655 
 656                 for (final Expression elem : value) {
 657                     if (elem == null) {
 658                         widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
 659                         break;
 660                     }
 661 
 662                     final Type type = elem.getType().isUnknown() ? Type.OBJECT : elem.getType();
 663                     if (type.isBoolean()) {
 664                         //TODO fix this with explicit boolean types
 665                         widestElementType = widestElementType.widest(Type.OBJECT);
 666                         break;
 667                     }
 668 
 669                     widestElementType = widestElementType.widest(type);
 670                     if (widestElementType.isObject()) {
 671                         break;
 672                     }
 673                 }
 674                 return widestElementType;
 675             }
 676 
 677             private static int[] computePostsets(final Expression[] value) {
 678                 final int[] computed = new int[value.length];
 679                 int nComputed = 0;
 680 
 681                 for (int i = 0; i < value.length; i++) {
 682                     final Expression element = value[i];
 683                     if (element == null || !isConstant(element)) {
 684                         computed[nComputed++] = i;
 685                     }
 686                 }
 687                 return Arrays.copyOf(computed, nComputed);
 688             }
 689 
 690             private static boolean setArrayElement(final int[] array, final int i, final Object n) {
 691                 if (n instanceof Number) {
 692                     array[i] = ((Number)n).intValue();
 693                     return true;
 694                 }
 695                 return false;
 696             }
 697 
 698             private static boolean setArrayElement(final long[] array, final int i, final Object n) {
 699                 if (n instanceof Number) {
 700                     array[i] = ((Number)n).longValue();
 701                     return true;
 702                 }
 703                 return false;
 704             }
 705 
 706             private static boolean setArrayElement(final double[] array, final int i, final Object n) {
 707                 if (n instanceof Number) {
 708                     array[i] = ((Number)n).doubleValue();
 709                     return true;
 710                 }
 711                 return false;
 712             }
 713 
 714             private static int[] presetIntArray(final Expression[] value, final int[] postsets) {
 715                 final int[] array = new int[value.length];
 716                 int nComputed = 0;
 717                 for (int i = 0; i < value.length; i++) {
 718                     if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
 719                         assert postsets[nComputed++] == i;
 720                     }
 721                 }
 722                 assert postsets.length == nComputed;
 723                 return array;
 724             }
 725 
 726             private static long[] presetLongArray(final Expression[] value, final int[] postsets) {
 727                 final long[] array = new long[value.length];
 728                 int nComputed = 0;
 729                 for (int i = 0; i < value.length; i++) {
 730                     if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
 731                         assert postsets[nComputed++] == i;
 732                     }
 733                 }
 734                 assert postsets.length == nComputed;
 735                 return array;
 736             }
 737 
 738             private static double[] presetDoubleArray(final Expression[] value, final int[] postsets) {
 739                 final double[] array = new double[value.length];
 740                 int nComputed = 0;
 741                 for (int i = 0; i < value.length; i++) {
 742                     if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
 743                         assert postsets[nComputed++] == i;
 744                     }
 745                 }
 746                 assert postsets.length == nComputed;
 747                 return array;
 748             }
 749 
 750             private static Object[] presetObjectArray(final Expression[] value, final int[] postsets) {
 751                 final Object[] array = new Object[value.length];
 752                 int nComputed = 0;
 753 
 754                 for (int i = 0; i < value.length; i++) {
 755                     final Node node = value[i];
 756 
 757                     if (node == null) {
 758                         assert postsets[nComputed++] == i;
 759                         continue;
 760                     }
 761                     final Object element = objectAsConstant(node);
 762 
 763                     if (element != POSTSET_MARKER) {
 764                         array[i] = element;
 765                     } else {
 766                         assert postsets[nComputed++] == i;
 767                     }
 768                 }
 769 
 770                 assert postsets.length == nComputed;
 771                 return array;
 772             }
 773 
 774             static Object computePresets(final Expression[] value, final Type elementType, final int[] postsets) {
 775                 assert !elementType.isUnknown();
 776                 if (elementType.isInteger()) {
 777                     return presetIntArray(value, postsets);
 778                 } else if (elementType.isNumeric()) {
 779                     return presetDoubleArray(value, postsets);
 780                 } else {
 781                     return presetObjectArray(value, postsets);
 782                 }
 783             }
 784         }
 785 
 786         /**
 787          * Constructor
 788          *
 789          * @param token   token
 790          * @param finish  finish
 791          * @param value   array literal value, a Node array
 792          */
 793         protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) {
 794             super(Token.recast(token, TokenType.ARRAY), finish, value);
 795             this.elementType = Type.UNKNOWN;
 796             this.presets     = null;
 797             this.postsets    = null;
 798             this.splitRanges = null;
 799         }
 800 
 801         /**
 802          * Copy constructor
 803          * @param node source array literal node
 804          */
 805         private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<Splittable.SplitRange> splitRanges) {
 806             super(node, value);
 807             this.elementType = elementType;
 808             this.postsets    = postsets;
 809             this.presets     = presets;
 810             this.splitRanges = splitRanges;
 811         }
 812 
 813         /**
 814          * Returns a list of array element expressions. Note that empty array elements manifest themselves as
 815          * null.
 816          * @return a list of array element expressions.
 817          */
 818         @Override
 819         public List<Expression> getElementExpressions() {
 820             return Collections.unmodifiableList(Arrays.asList(value));
 821         }
 822 
 823         /**
 824          * Setter that initializes all code generation meta data for an
 825          * ArrayLiteralNode. This acts a setter, so the return value may
 826          * return a new node and must be handled
 827          *
 828          * @param lc lexical context
 829          * @return new array literal node with postsets, presets and element types initialized
 830          */
 831         @Override
 832         public ArrayLiteralNode initialize(final LexicalContext lc) {
 833             return Node.replaceInLexicalContext(lc, this, ArrayLiteralInitializer.initialize(this));
 834         }
 835 
 836         /**
 837          * Get the array element type as Java format, e.g. [I
 838          * @return array element type
 839          */
 840         public ArrayType getArrayType() {
 841             return getArrayType(getElementType());
 842         }
 843 
 844         private static ArrayType getArrayType(final Type elementType) {
 845             if (elementType.isInteger()) {
 846                 return Type.INT_ARRAY;
 847             } else if (elementType.isNumeric()) {
 848                 return Type.NUMBER_ARRAY;
 849             } else {
 850                 return Type.OBJECT_ARRAY;
 851             }
 852         }
 853 
 854         @Override
 855         public Type getType() {
 856             return Type.typeFor(NativeArray.class);
 857         }
 858 
 859         /**
 860          * Get the element type of this array literal
 861          * @return element type
 862          */
 863         public Type getElementType() {
 864             assert !elementType.isUnknown() : this + " has elementType=unknown";
 865             return elementType;
 866         }
 867 
 868         /**
 869          * Get indices of arrays containing computed post sets. post sets
 870          * are things like non literals e.g. "x+y" instead of i or 17
 871          * @return post set indices
 872          */
 873         public int[] getPostsets() {
 874             assert postsets != null : this + " elementType=" + elementType + " has no postsets";
 875             return postsets;
 876         }
 877 
 878         private boolean presetsMatchElementType() {
 879             if (elementType == Type.INT) {
 880                 return presets instanceof int[];
 881             } else if (elementType == Type.NUMBER) {
 882                 return presets instanceof double[];
 883             } else {
 884                 return presets instanceof Object[];
 885             }
 886         }
 887 
 888         /**
 889          * Get presets constant array
 890          * @return presets array, always returns an array type
 891          */
 892         public Object getPresets() {
 893             assert presets != null && presetsMatchElementType() : this + " doesn't have presets, or invalid preset type: " + presets;
 894             return presets;
 895         }
 896 
 897         /**
 898          * Get the split ranges for this ArrayLiteral, or null if this array does not have to be split.
 899          * @see Splittable.SplitRange
 900          * @return list of split ranges
 901          */
 902         @Override
 903         public List<Splittable.SplitRange> getSplitRanges() {
 904             return splitRanges == null ? null : Collections.unmodifiableList(splitRanges);
 905         }
 906 
 907         /**
 908          * Set the SplitRanges that make up this ArrayLiteral
 909          * @param lc lexical context
 910          * @see Splittable.SplitRange
 911          * @param splitRanges list of split ranges
 912          * @return new or changed node
 913          */
 914         public ArrayLiteralNode setSplitRanges(final LexicalContext lc, final List<Splittable.SplitRange> splitRanges) {
 915             if (this.splitRanges == splitRanges) {
 916                 return this;
 917             }
 918             return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, splitRanges));
 919         }
 920 
 921         @Override
 922         public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 923             return Acceptor.accept(this, visitor);
 924         }
 925 
 926         @Override
 927         public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
 928             if (visitor.enterLiteralNode(this)) {
 929                 final List<Expression> oldValue = Arrays.asList(value);
 930                 final List<Expression> newValue = Node.accept(visitor, oldValue);
 931                 return visitor.leaveLiteralNode(oldValue != newValue ? setValue(lc, newValue) : this);
 932             }
 933             return this;
 934         }
 935 
 936         private ArrayLiteralNode setValue(final LexicalContext lc, final Expression[] value) {
 937             if (this.value == value) {
 938                 return this;
 939             }
 940             return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, splitRanges));
 941         }
 942 
 943         private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
 944             return setValue(lc, value.toArray(new Expression[value.size()]));
 945         }
 946 
 947         @Override
 948         public void toString(final StringBuilder sb, final boolean printType) {
 949             sb.append('[');
 950             boolean first = true;
 951             for (final Node node : value) {
 952                 if (!first) {
 953                     sb.append(',');
 954                     sb.append(' ');
 955                 }
 956                 if (node == null) {
 957                     sb.append("undefined");
 958                 } else {
 959                     node.toString(sb, printType);
 960                 }
 961                 first = false;
 962             }
 963             sb.append(']');
 964         }
 965     }
 966 
 967     /**
 968      * Create a new array literal of Nodes from a list of Node values
 969      *
 970      * @param token   token
 971      * @param finish  finish
 972      * @param value   literal value list
 973      *
 974      * @return the new literal node
 975      */
 976     public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value) {
 977         return new ArrayLiteralNode(token, finish, valueToArray(value));
 978     }
 979 
 980     /**
 981      * Create a new array literal based on a parent node (source, token, finish)
 982      *
 983      * @param parent parent node
 984      * @param value  literal value list
 985      *
 986      * @return the new literal node
 987      */
 988     public static LiteralNode<?> newInstance(final Node parent, final List<Expression> value) {
 989         return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), valueToArray(value));
 990     }
 991 
 992     /**
 993      * Create a new array literal of Nodes
 994      *
 995      * @param token   token
 996      * @param finish  finish
 997      * @param value   literal value array
 998      *
 999      * @return the new literal node
1000      */
1001     public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final Expression[] value) {
1002         return new ArrayLiteralNode(token, finish, value);
1003     }
1004 }