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