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