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.io.Serializable;
  29 import java.util.Arrays;
  30 import java.util.Collections;
  31 import java.util.List;
  32 import jdk.nashorn.internal.codegen.CompileUnit;
  33 import jdk.nashorn.internal.codegen.types.ArrayType;
  34 import jdk.nashorn.internal.codegen.types.Type;
  35 import jdk.nashorn.internal.ir.annotations.Immutable;
  36 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
  37 import jdk.nashorn.internal.objects.NativeArray;
  38 import jdk.nashorn.internal.parser.Lexer.LexerToken;
  39 import jdk.nashorn.internal.parser.Token;
  40 import jdk.nashorn.internal.parser.TokenType;
  41 import jdk.nashorn.internal.runtime.JSType;
  42 import jdk.nashorn.internal.runtime.ScriptRuntime;
  43 import jdk.nashorn.internal.runtime.Undefined;
  44 
  45 /**
  46  * Literal nodes represent JavaScript values.
  47  *
  48  * @param <T> the literal type
  49  */
  50 @Immutable
  51 public abstract class LiteralNode<T> extends Expression implements PropertyKey {
  52     private static final long serialVersionUID = 1L;
  53 
  54     /** Literal value */
  55     protected final T value;
  56 
  57     /** Marker for values that must be computed at runtime */
  58     public static final Object POSTSET_MARKER = new Object();
  59 
  60     /**
  61      * Constructor
  62      *
  63      * @param token   token
  64      * @param finish  finish
  65      * @param value   the value of the literal
  66      */
  67     protected LiteralNode(final long token, final int finish, final T value) {
  68         super(token, finish);
  69         this.value = value;
  70     }
  71 
  72     /**
  73      * Copy constructor
  74      *
  75      * @param literalNode source node
  76      */
  77     protected LiteralNode(final LiteralNode<T> literalNode) {
  78         this(literalNode, literalNode.value);
  79     }
  80 
  81     /**
  82      * A copy constructor with value change.
  83      * @param literalNode the original literal node
  84      * @param newValue new value for this node
  85      */
  86     protected LiteralNode(final LiteralNode<T> literalNode, final T newValue) {
  87         super(literalNode);
  88         this.value = newValue;
  89     }
  90 
  91     /**
  92      * Initialization setter, if required for immutable state. This is used for
  93      * things like ArrayLiteralNodes that need to carry state for the splitter.
  94      * Default implementation is just a nop.
  95      * @param lc lexical context
  96      * @return new literal node with initialized state, or same if nothing changed
  97      */
  98     public LiteralNode<?> initialize(final LexicalContext lc) {
  99         return this;
 100     }
 101 
 102     /**
 103      * Check if the literal value is null
 104      * @return true if literal value is null
 105      */
 106     public boolean isNull() {
 107         return value == null;
 108     }
 109 
 110     @Override
 111     public Type getType() {
 112         return Type.typeFor(value.getClass());
 113     }
 114 
 115     @Override
 116     public String getPropertyName() {
 117         return JSType.toString(getObject());
 118     }
 119 
 120     /**
 121      * Fetch boolean value of node.
 122      *
 123      * @return boolean value of node.
 124      */
 125     public boolean getBoolean() {
 126         return JSType.toBoolean(value);
 127     }
 128 
 129     /**
 130      * Fetch int32 value of node.
 131      *
 132      * @return Int32 value of node.
 133      */
 134     public int getInt32() {
 135         return JSType.toInt32(value);
 136     }
 137 
 138     /**
 139      * Fetch uint32 value of node.
 140      *
 141      * @return uint32 value of node.
 142      */
 143     public long getUint32() {
 144         return JSType.toUint32(value);
 145     }
 146 
 147     /**
 148      * Fetch long value of node
 149      *
 150      * @return long value of node
 151      */
 152     public long getLong() {
 153         return JSType.toLong(value);
 154     }
 155 
 156     /**
 157      * Fetch double value of node.
 158      *
 159      * @return double value of node.
 160      */
 161     public double getNumber() {
 162         return JSType.toNumber(value);
 163     }
 164 
 165     /**
 166      * Fetch String value of node.
 167      *
 168      * @return String value of node.
 169      */
 170     public String getString() {
 171         return JSType.toString(value);
 172     }
 173 
 174     /**
 175      * Fetch Object value of node.
 176      *
 177      * @return Object value of node.
 178      */
 179     public Object getObject() {
 180         return value;
 181     }
 182 
 183     /**
 184      * Test if the value is an array
 185      *
 186      * @return True if value is an array
 187      */
 188     public boolean isArray() {
 189         return false;
 190     }
 191 
 192     public List<Expression> getElementExpressions() {
 193         return null;
 194     }
 195 
 196     /**
 197      * Test if the value is a boolean.
 198      *
 199      * @return True if value is a boolean.
 200      */
 201     public boolean isBoolean() {
 202         return value instanceof Boolean;
 203     }
 204 
 205     /**
 206      * Test if the value is a string.
 207      *
 208      * @return True if value is a string.
 209      */
 210     public boolean isString() {
 211         return value instanceof String;
 212     }
 213 
 214     /**
 215      * Test if tha value is a number
 216      *
 217      * @return True if value is a number
 218      */
 219     public boolean isNumeric() {
 220         return value instanceof Number;
 221     }
 222 
 223     /**
 224      * Assist in IR navigation.
 225      *
 226      * @param visitor IR navigating visitor.
 227      */
 228     @Override
 229     public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 230         if (visitor.enterLiteralNode(this)) {
 231             return visitor.leaveLiteralNode(this);
 232         }
 233 
 234         return this;
 235     }
 236 
 237     @Override
 238     public void toString(final StringBuilder sb, final boolean printType) {
 239         if (value == null) {
 240             sb.append("null");
 241         } else {
 242             sb.append(value.toString());
 243         }
 244     }
 245 
 246     /**
 247      * Get the literal node value
 248      * @return the value
 249      */
 250     public final T getValue() {
 251         return value;
 252     }
 253 
 254     private static Expression[] valueToArray(final List<Expression> value) {
 255         return value.toArray(new Expression[value.size()]);
 256     }
 257 
 258     /**
 259      * Create a new null literal
 260      *
 261      * @param token   token
 262      * @param finish  finish
 263      *
 264      * @return the new literal node
 265      */
 266     public static LiteralNode<Object> newInstance(final long token, final int finish) {
 267         return new NullLiteralNode(token, finish);
 268     }
 269 
 270     /**
 271      * Create a new null literal based on a parent node (source, token, finish)
 272      *
 273      * @param parent parent node
 274      *
 275      * @return the new literal node
 276      */
 277     public static LiteralNode<Object> newInstance(final Node parent) {
 278         return new NullLiteralNode(parent.getToken(), parent.getFinish());
 279     }
 280 
 281     /**
 282      * Super class for primitive (side-effect free) literals.
 283      *
 284      * @param <T> the literal type
 285      */
 286     public static class PrimitiveLiteralNode<T> extends LiteralNode<T> {
 287         private static final long serialVersionUID = 1L;
 288 
 289         private PrimitiveLiteralNode(final long token, final int finish, final T value) {
 290             super(token, finish, value);
 291         }
 292 
 293         private PrimitiveLiteralNode(final PrimitiveLiteralNode<T> literalNode) {
 294             super(literalNode);
 295         }
 296 
 297         /**
 298          * Check if the literal value is boolean true
 299          * @return true if literal value is boolean true
 300          */
 301         public boolean isTrue() {
 302             return JSType.toBoolean(value);
 303         }
 304 
 305         @Override
 306         public boolean isLocal() {
 307             return true;
 308         }
 309 
 310         @Override
 311         public boolean isAlwaysFalse() {
 312             return !isTrue();
 313         }
 314 
 315         @Override
 316         public boolean isAlwaysTrue() {
 317             return isTrue();
 318         }
 319     }
 320 
 321     @Immutable
 322     private static final class BooleanLiteralNode extends PrimitiveLiteralNode<Boolean> {
 323         private static final long serialVersionUID = 1L;
 324 
 325         private BooleanLiteralNode(final long token, final int finish, final boolean value) {
 326             super(Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
 327         }
 328 
 329         private BooleanLiteralNode(final BooleanLiteralNode literalNode) {
 330             super(literalNode);
 331         }
 332 
 333         @Override
 334         public boolean isTrue() {
 335             return value;
 336         }
 337 
 338         @Override
 339         public Type getType() {
 340             return Type.BOOLEAN;
 341         }
 342 
 343         @Override
 344         public Type getWidestOperationType() {
 345             return Type.BOOLEAN;
 346         }
 347     }
 348 
 349     /**
 350      * Create a new boolean literal
 351      *
 352      * @param token   token
 353      * @param finish  finish
 354      * @param value   true or false
 355      *
 356      * @return the new literal node
 357      */
 358     public static LiteralNode<Boolean> newInstance(final long token, final int finish, final boolean value) {
 359         return new BooleanLiteralNode(token, finish, value);
 360     }
 361 
 362     /**
 363      * Create a new boolean literal based on a parent node (source, token, finish)
 364      *
 365      * @param parent parent node
 366      * @param value  true or false
 367      *
 368      * @return the new literal node
 369      */
 370     public static LiteralNode<?> newInstance(final Node parent, final boolean value) {
 371         return new BooleanLiteralNode(parent.getToken(), parent.getFinish(), value);
 372     }
 373 
 374     @Immutable
 375     private static final class NumberLiteralNode extends PrimitiveLiteralNode<Number> {
 376         private static final long serialVersionUID = 1L;
 377 
 378         private final Type type = numberGetType(value);
 379 
 380         private NumberLiteralNode(final long token, final int finish, final Number value) {
 381             super(Token.recast(token, TokenType.DECIMAL), finish, value);
 382         }
 383 
 384         private NumberLiteralNode(final NumberLiteralNode literalNode) {
 385             super(literalNode);
 386         }
 387 
 388         private static Type numberGetType(final Number number) {
 389             if (number instanceof Integer) {
 390                 return Type.INT;
 391             } else if (number instanceof Long) {
 392                 return Type.LONG;
 393             } else if (number instanceof Double) {
 394                 return Type.NUMBER;
 395             } else {
 396                 assert false;
 397             }
 398 
 399             return null;
 400         }
 401 
 402         @Override
 403         public Type getType() {
 404             return type;
 405         }
 406 
 407         @Override
 408         public Type getWidestOperationType() {
 409             return getType();
 410         }
 411 
 412     }
 413     /**
 414      * Create a new number literal
 415      *
 416      * @param token   token
 417      * @param finish  finish
 418      * @param value   literal value
 419      *
 420      * @return the new literal node
 421      */
 422     public static LiteralNode<Number> newInstance(final long token, final int finish, final Number value) {
 423         return new NumberLiteralNode(token, finish, value);
 424     }
 425 
 426     /**
 427      * Create a new number literal based on a parent node (source, token, finish)
 428      *
 429      * @param parent parent node
 430      * @param value  literal value
 431      *
 432      * @return the new literal node
 433      */
 434     public static LiteralNode<?> newInstance(final Node parent, final Number value) {
 435         return new NumberLiteralNode(parent.getToken(), parent.getFinish(), value);
 436     }
 437 
 438     private static class UndefinedLiteralNode extends PrimitiveLiteralNode<Undefined> {
 439         private static final long serialVersionUID = 1L;
 440 
 441         private UndefinedLiteralNode(final long token, final int finish) {
 442             super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED);
 443         }
 444 
 445         private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
 446             super(literalNode);
 447         }
 448     }
 449 
 450     /**
 451      * Create a new undefined literal
 452      *
 453      * @param token   token
 454      * @param finish  finish
 455      * @param value   undefined value, passed only for polymorphisism discrimination
 456      *
 457      * @return the new literal node
 458      */
 459     public static LiteralNode<Undefined> newInstance(final long token, final int finish, final Undefined value) {
 460         return new UndefinedLiteralNode(token, finish);
 461     }
 462 
 463     /**
 464      * Create a new null literal based on a parent node (source, token, finish)
 465      *
 466      * @param parent parent node
 467      * @param value  undefined value
 468      *
 469      * @return the new literal node
 470      */
 471     public static LiteralNode<?> newInstance(final Node parent, final Undefined value) {
 472         return new UndefinedLiteralNode(parent.getToken(), parent.getFinish());
 473     }
 474 
 475     @Immutable
 476     private static class StringLiteralNode extends PrimitiveLiteralNode<String> {
 477         private static final long serialVersionUID = 1L;
 478 
 479         private StringLiteralNode(final long token, final int finish, final String value) {
 480             super(Token.recast(token, TokenType.STRING), finish, value);
 481         }
 482 
 483         private StringLiteralNode(final StringLiteralNode literalNode) {
 484             super(literalNode);
 485         }
 486 
 487         @Override
 488         public void toString(final StringBuilder sb, final boolean printType) {
 489             sb.append('\"');
 490             sb.append(value);
 491             sb.append('\"');
 492         }
 493     }
 494 
 495     /**
 496      * Create a new string literal
 497      *
 498      * @param token   token
 499      * @param finish  finish
 500      * @param value   string value
 501      *
 502      * @return the new literal node
 503      */
 504     public static LiteralNode<String> newInstance(final long token, final int finish, final String value) {
 505         return new StringLiteralNode(token, finish, value);
 506     }
 507 
 508     /**
 509      * Create a new String literal based on a parent node (source, token, finish)
 510      *
 511      * @param parent parent node
 512      * @param value  string value
 513      *
 514      * @return the new literal node
 515      */
 516     public static LiteralNode<?> newInstance(final Node parent, final String value) {
 517         return new StringLiteralNode(parent.getToken(), parent.getFinish(), value);
 518     }
 519 
 520     @Immutable
 521     private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
 522         private static final long serialVersionUID = 1L;
 523 
 524         private LexerTokenLiteralNode(final long token, final int finish, final LexerToken value) {
 525             super(Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
 526         }
 527 
 528         private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) {
 529             super(literalNode);
 530         }
 531 
 532         @Override
 533         public Type getType() {
 534             return Type.OBJECT;
 535         }
 536 
 537         @Override
 538         public void toString(final StringBuilder sb, final boolean printType) {
 539             sb.append(value.toString());
 540         }
 541     }
 542 
 543     /**
 544      * Create a new literal node for a lexer token
 545      *
 546      * @param token   token
 547      * @param finish  finish
 548      * @param value   lexer token value
 549      *
 550      * @return the new literal node
 551      */
 552     public static LiteralNode<LexerToken> newInstance(final long token, final int finish, final LexerToken value) {
 553         return new LexerTokenLiteralNode(token, finish, value);
 554     }
 555 
 556     /**
 557      * Create a new lexer token literal based on a parent node (source, token, finish)
 558      *
 559      * @param parent parent node
 560      * @param value  lexer token
 561      *
 562      * @return the new literal node
 563      */
 564     public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) {
 565         return new LexerTokenLiteralNode(parent.getToken(), parent.getFinish(), value);
 566     }
 567 
 568     /**
 569      * Get the constant value for an object, or {@link #POSTSET_MARKER} if the value can't be statically computed.
 570      *
 571      * @param object a node or value object
 572      * @return the constant value or {@code POSTSET_MARKER}
 573      */
 574     public static Object objectAsConstant(final Object object) {
 575         if (object == null) {
 576             return null;
 577         } else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
 578             return object;
 579         } else if (object instanceof LiteralNode) {
 580             return objectAsConstant(((LiteralNode<?>)object).getValue());
 581         }
 582 
 583         return POSTSET_MARKER;
 584     }
 585 
 586     private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> {
 587         private static final long serialVersionUID = 1L;
 588 
 589         private NullLiteralNode(final long token, final int finish) {
 590             super(Token.recast(token, TokenType.OBJECT), finish, null);
 591         }
 592 
 593         @Override
 594         public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 595             if (visitor.enterLiteralNode(this)) {
 596                 return visitor.leaveLiteralNode(this);
 597             }
 598 
 599             return this;
 600         }
 601 
 602         @Override
 603         public Type getType() {
 604             return Type.OBJECT;
 605         }
 606 
 607         @Override
 608         public Type getWidestOperationType() {
 609             return Type.OBJECT;
 610         }
 611     }
 612 
 613     /**
 614      * Array literal node class.
 615      */
 616     @Immutable
 617     public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode {
 618         private static final long serialVersionUID = 1L;
 619 
 620         /** Array element type. */
 621         private final Type elementType;
 622 
 623         /** Preset constant array. */
 624         private final Object presets;
 625 
 626         /** Indices of array elements requiring computed post sets. */
 627         private final int[] postsets;
 628 
 629         /** Sub units with indexes ranges, in which to split up code generation, for large literals */
 630         private final List<ArrayUnit> units;
 631 
 632         @Override
 633         public boolean isArray() {
 634             return true;
 635         }
 636 
 637 
 638         /**
 639          * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
 640          * be split if they are too large, for bytecode generation reasons
 641          */
 642         public static final class ArrayUnit implements CompileUnitHolder, Serializable {
 643             private static final long serialVersionUID = 1L;
 644 
 645             /** Compile unit associated with the postsets range. */
 646             private final CompileUnit compileUnit;
 647 
 648             /** postsets range associated with the unit (hi not inclusive). */
 649             private final int lo, hi;
 650 
 651             /**
 652              * Constructor
 653              * @param compileUnit compile unit
 654              * @param lo lowest array index in unit
 655              * @param hi highest array index in unit + 1
 656              */
 657             public ArrayUnit(final CompileUnit compileUnit, final int lo, final int hi) {
 658                 this.compileUnit = compileUnit;
 659                 this.lo   = lo;
 660                 this.hi   = hi;
 661             }
 662 
 663             /**
 664              * Get the high index position of the ArrayUnit (non inclusive)
 665              * @return high index position
 666              */
 667             public int getHi() {
 668                 return hi;
 669             }
 670 
 671             /**
 672              * Get the low index position of the ArrayUnit (inclusive)
 673              * @return low index position
 674              */
 675             public int getLo() {
 676                 return lo;
 677             }
 678 
 679             /**
 680              * The array compile unit
 681              * @return array compile unit
 682              */
 683             @Override
 684             public CompileUnit getCompileUnit() {
 685                 return compileUnit;
 686             }
 687         }
 688 
 689         private static final class ArrayLiteralInitializer {
 690 
 691             static ArrayLiteralNode initialize(final ArrayLiteralNode node) {
 692                 final Type elementType = computeElementType(node.value);
 693                 final int[] postsets = computePostsets(node.value);
 694                 final Object presets = computePresets(node.value, elementType, postsets);
 695                 return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units);
 696             }
 697 
 698             private static Type computeElementType(final Expression[] value) {
 699                 Type widestElementType = Type.INT;
 700 
 701                 for (final Expression elem : value) {
 702                     if (elem == null) {
 703                         widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
 704                         break;
 705                     }
 706 
 707                     final Type type = elem.getType().isUnknown() ? Type.OBJECT : elem.getType();
 708                     if (type.isBoolean()) {
 709                         //TODO fix this with explicit boolean types
 710                         widestElementType = widestElementType.widest(Type.OBJECT);
 711                         break;
 712                     }
 713 
 714                     widestElementType = widestElementType.widest(type);
 715                     if (widestElementType.isObject()) {
 716                         break;
 717                     }
 718                 }
 719                 return widestElementType;
 720             }
 721 
 722             private static int[] computePostsets(final Expression[] value) {
 723                 final int[] computed = new int[value.length];
 724                 int nComputed = 0;
 725 
 726                 for (int i = 0; i < value.length; i++) {
 727                     final Expression element = value[i];
 728                     if (element == null || objectAsConstant(element) == POSTSET_MARKER) {
 729                         computed[nComputed++] = i;
 730                     }
 731                 }
 732                 return Arrays.copyOf(computed, nComputed);
 733             }
 734 
 735             private static boolean setArrayElement(final int[] array, final int i, final Object n) {
 736                 if (n instanceof Number) {
 737                     array[i] = ((Number)n).intValue();
 738                     return true;
 739                 }
 740                 return false;
 741             }
 742 
 743             private static boolean setArrayElement(final long[] array, final int i, final Object n) {
 744                 if (n instanceof Number) {
 745                     array[i] = ((Number)n).longValue();
 746                     return true;
 747                 }
 748                 return false;
 749             }
 750 
 751             private static boolean setArrayElement(final double[] array, final int i, final Object n) {
 752                 if (n instanceof Number) {
 753                     array[i] = ((Number)n).doubleValue();
 754                     return true;
 755                 }
 756                 return false;
 757             }
 758 
 759             private static int[] presetIntArray(final Expression[] value, final int[] postsets) {
 760                 final int[] array = new int[value.length];
 761                 int nComputed = 0;
 762                 for (int i = 0; i < value.length; i++) {
 763                     if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
 764                         assert postsets[nComputed++] == i;
 765                     }
 766                 }
 767                 assert postsets.length == nComputed;
 768                 return array;
 769             }
 770 
 771             private static long[] presetLongArray(final Expression[] value, final int[] postsets) {
 772                 final long[] array = new long[value.length];
 773                 int nComputed = 0;
 774                 for (int i = 0; i < value.length; i++) {
 775                     if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
 776                         assert postsets[nComputed++] == i;
 777                     }
 778                 }
 779                 assert postsets.length == nComputed;
 780                 return array;
 781             }
 782 
 783             private static double[] presetDoubleArray(final Expression[] value, final int[] postsets) {
 784                 final double[] array = new double[value.length];
 785                 int nComputed = 0;
 786                 for (int i = 0; i < value.length; i++) {
 787                     if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
 788                         assert postsets[nComputed++] == i;
 789                     }
 790                 }
 791                 assert postsets.length == nComputed;
 792                 return array;
 793             }
 794 
 795             private static Object[] presetObjectArray(final Expression[] value, final int[] postsets) {
 796                 final Object[] array = new Object[value.length];
 797                 int nComputed = 0;
 798 
 799                 for (int i = 0; i < value.length; i++) {
 800                     final Node node = value[i];
 801 
 802                     if (node == null) {
 803                         assert postsets[nComputed++] == i;
 804                         continue;
 805                     }
 806                     final Object element = objectAsConstant(node);
 807 
 808                     if (element != POSTSET_MARKER) {
 809                         array[i] = element;
 810                     } else {
 811                         assert postsets[nComputed++] == i;
 812                     }
 813                 }
 814 
 815                 assert postsets.length == nComputed;
 816                 return array;
 817             }
 818 
 819             static Object computePresets(final Expression[] value, final Type elementType, final int[] postsets) {
 820                 assert !elementType.isUnknown();
 821                 if (elementType.isInteger()) {
 822                     return presetIntArray(value, postsets);
 823                 } else if (elementType.isLong()) {
 824                     return presetLongArray(value, postsets);
 825                 } else if (elementType.isNumeric()) {
 826                     return presetDoubleArray(value, postsets);
 827                 } else {
 828                     return presetObjectArray(value, postsets);
 829                 }
 830             }
 831         }
 832 
 833         /**
 834          * Constructor
 835          *
 836          * @param token   token
 837          * @param finish  finish
 838          * @param value   array literal value, a Node array
 839          */
 840         protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) {
 841             super(Token.recast(token, TokenType.ARRAY), finish, value);
 842             this.elementType = Type.UNKNOWN;
 843             this.presets     = null;
 844             this.postsets    = null;
 845             this.units       = null;
 846         }
 847 
 848         /**
 849          * Copy constructor
 850          * @param node source array literal node
 851          */
 852         private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<ArrayUnit> units) {
 853             super(node, value);
 854             this.elementType = elementType;
 855             this.postsets    = postsets;
 856             this.presets     = presets;
 857             this.units       = units;
 858         }
 859 
 860         /**
 861          * Returns a list of array element expressions. Note that empty array elements manifest themselves as
 862          * null.
 863          * @return a list of array element expressions.
 864          */
 865         @Override
 866         public List<Expression> getElementExpressions() {
 867             return Collections.unmodifiableList(Arrays.asList(value));
 868         }
 869 
 870         /**
 871          * Setter that initializes all code generation meta data for an
 872          * ArrayLiteralNode. This acts a setter, so the return value may
 873          * return a new node and must be handled
 874          *
 875          * @param lc lexical context
 876          * @return new array literal node with postsets, presets and element types initialized
 877          */
 878         @Override
 879         public ArrayLiteralNode initialize(final LexicalContext lc) {
 880             return Node.replaceInLexicalContext(lc, this, ArrayLiteralInitializer.initialize(this));
 881         }
 882 
 883         /**
 884          * Get the array element type as Java format, e.g. [I
 885          * @return array element type
 886          */
 887         public ArrayType getArrayType() {
 888             return getArrayType(getElementType());
 889         }
 890 
 891         private static ArrayType getArrayType(final Type elementType) {
 892             if (elementType.isInteger()) {
 893                 return Type.INT_ARRAY;
 894             } else if (elementType.isLong()) {
 895                 return Type.LONG_ARRAY;
 896             } else if (elementType.isNumeric()) {
 897                 return Type.NUMBER_ARRAY;
 898             } else {
 899                 return Type.OBJECT_ARRAY;
 900             }
 901         }
 902 
 903         @Override
 904         public Type getType() {
 905             return Type.typeFor(NativeArray.class);
 906         }
 907 
 908         /**
 909          * Get the element type of this array literal
 910          * @return element type
 911          */
 912         public Type getElementType() {
 913             assert !elementType.isUnknown() : this + " has elementType=unknown";
 914             return elementType;
 915         }
 916 
 917         /**
 918          * Get indices of arrays containing computed post sets. post sets
 919          * are things like non literals e.g. "x+y" instead of i or 17
 920          * @return post set indices
 921          */
 922         public int[] getPostsets() {
 923             assert postsets != null : this + " elementType=" + elementType + " has no postsets";
 924             return postsets;
 925         }
 926 
 927         private boolean presetsMatchElementType() {
 928             if (elementType == Type.INT) {
 929                 return presets instanceof int[];
 930             } else if (elementType == Type.LONG) {
 931                 return presets instanceof long[];
 932             } else if (elementType == Type.NUMBER) {
 933                 return presets instanceof double[];
 934             } else {
 935                 return presets instanceof Object[];
 936             }
 937         }
 938 
 939         /**
 940          * Get presets constant array
 941          * @return presets array, always returns an array type
 942          */
 943         public Object getPresets() {
 944             assert presets != null && presetsMatchElementType() : this + " doesn't have presets, or invalid preset type: " + presets;
 945             return presets;
 946         }
 947 
 948         /**
 949          * Get the array units that make up this ArrayLiteral
 950          * @see ArrayUnit
 951          * @return list of array units
 952          */
 953         public List<ArrayUnit> getUnits() {
 954             return units == null ? null : Collections.unmodifiableList(units);
 955         }
 956 
 957         /**
 958          * Set the ArrayUnits that make up this ArrayLiteral
 959          * @param lc lexical context
 960          * @see ArrayUnit
 961          * @param units list of array units
 962          * @return new or changed arrayliteralnode
 963          */
 964         public ArrayLiteralNode setUnits(final LexicalContext lc, final List<ArrayUnit> units) {
 965             if (this.units == units) {
 966                 return this;
 967             }
 968             return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
 969         }
 970 
 971         @Override
 972         public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 973             return Acceptor.accept(this, visitor);
 974         }
 975 
 976         @Override
 977         public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
 978             if (visitor.enterLiteralNode(this)) {
 979                 final List<Expression> oldValue = Arrays.asList(value);
 980                 final List<Expression> newValue = Node.accept(visitor, oldValue);
 981                 return visitor.leaveLiteralNode(oldValue != newValue ? setValue(lc, newValue) : this);
 982             }
 983             return this;
 984         }
 985 
 986         private ArrayLiteralNode setValue(final LexicalContext lc, final Expression[] value) {
 987             if (this.value == value) {
 988                 return this;
 989             }
 990             return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
 991         }
 992 
 993         private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
 994             return setValue(lc, value.toArray(new Expression[value.size()]));
 995         }
 996 
 997         @Override
 998         public void toString(final StringBuilder sb, final boolean printType) {
 999             sb.append('[');
1000             boolean first = true;
1001             for (final Node node : value) {
1002                 if (!first) {
1003                     sb.append(',');
1004                     sb.append(' ');
1005                 }
1006                 if (node == null) {
1007                     sb.append("undefined");
1008                 } else {
1009                     node.toString(sb, printType);
1010                 }
1011                 first = false;
1012             }
1013             sb.append(']');
1014         }
1015     }
1016 
1017     /**
1018      * Create a new array literal of Nodes from a list of Node values
1019      *
1020      * @param token   token
1021      * @param finish  finish
1022      * @param value   literal value list
1023      *
1024      * @return the new literal node
1025      */
1026     public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value) {
1027         return new ArrayLiteralNode(token, finish, valueToArray(value));
1028     }
1029 
1030     /**
1031      * Create a new array literal based on a parent node (source, token, finish)
1032      *
1033      * @param parent parent node
1034      * @param value  literal value list
1035      *
1036      * @return the new literal node
1037      */
1038     public static LiteralNode<?> newInstance(final Node parent, final List<Expression> value) {
1039         return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), valueToArray(value));
1040     }
1041 
1042     /**
1043      * Create a new array literal of Nodes
1044      *
1045      * @param token   token
1046      * @param finish  finish
1047      * @param value   literal value array
1048      *
1049      * @return the new literal node
1050      */
1051     public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final Expression[] value) {
1052         return new ArrayLiteralNode(token, finish, value);
1053     }
1054 }