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      */
LiteralNode(final long token, final int finish, final T value)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      */
LiteralNode(final LiteralNode<T> literalNode)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      */
LiteralNode(final LiteralNode<T> literalNode, final T newValue)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      */
initialize(final LexicalContext lc)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      */
isNull()104     public boolean isNull() {
105         return value == null;
106     }
107 
108     @Override
getType()109     public Type getType() {
110         return Type.typeFor(value.getClass());
111     }
112 
113     @Override
getPropertyName()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      */
getBoolean()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      */
getInt32()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      */
getUint32()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      */
getLong()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      */
getNumber()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      */
getString()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      */
getObject()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      */
isString()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      */
isNumeric()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
accept(final NodeVisitor<? extends LexicalContext> visitor)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
toString(final StringBuilder sb, final boolean printType)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      */
getValue()226     public final T getValue() {
227         return value;
228     }
229 
valueToArray(final List<Expression> value)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      */
newInstance(final long token, final int finish)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      */
newInstance(final Node parent)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 
PrimitiveLiteralNode(final long token, final int finish, final T value)265         private PrimitiveLiteralNode(final long token, final int finish, final T value) {
266             super(token, finish, value);
267         }
268 
PrimitiveLiteralNode(final PrimitiveLiteralNode<T> literalNode)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          */
isTrue()277         public boolean isTrue() {
278             return JSType.toBoolean(value);
279         }
280 
281         @Override
isLocal()282         public boolean isLocal() {
283             return true;
284         }
285 
286         @Override
isAlwaysFalse()287         public boolean isAlwaysFalse() {
288             return !isTrue();
289         }
290 
291         @Override
isAlwaysTrue()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 
BooleanLiteralNode(final long token, final int finish, final boolean value)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 
BooleanLiteralNode(final BooleanLiteralNode literalNode)305         private BooleanLiteralNode(final BooleanLiteralNode literalNode) {
306             super(literalNode);
307         }
308 
309         @Override
isTrue()310         public boolean isTrue() {
311             return value;
312         }
313 
314         @Override
getType()315         public Type getType() {
316             return Type.BOOLEAN;
317         }
318 
319         @Override
getWidestOperationType()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      */
newInstance(final long token, final int finish, final boolean value)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      */
newInstance(final Node parent, final boolean value)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 
NumberLiteralNode(final long token, final int finish, final Number value)356         private NumberLiteralNode(final long token, final int finish, final Number value) {
357             super(Token.recast(token, TokenType.DECIMAL), finish, value);
358         }
359 
NumberLiteralNode(final NumberLiteralNode literalNode)360         private NumberLiteralNode(final NumberLiteralNode literalNode) {
361             super(literalNode);
362         }
363 
numberGetType(final Number number)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
getType()377         public Type getType() {
378             return type;
379         }
380 
381         @Override
getWidestOperationType()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      */
newInstance(final long token, final int finish, final Number value)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      */
newInstance(final Node parent, final Number value)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 
UndefinedLiteralNode(final long token, final int finish)416         private UndefinedLiteralNode(final long token, final int finish) {
417             super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED);
418         }
419 
UndefinedLiteralNode(final UndefinedLiteralNode literalNode)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      */
newInstance(final long token, final int finish, final Undefined value)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      */
newInstance(final Node parent, final Undefined value)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 
StringLiteralNode(final long token, final int finish, final String value)454         private StringLiteralNode(final long token, final int finish, final String value) {
455             super(Token.recast(token, TokenType.STRING), finish, value);
456         }
457 
StringLiteralNode(final StringLiteralNode literalNode)458         private StringLiteralNode(final StringLiteralNode literalNode) {
459             super(literalNode);
460         }
461 
462         @Override
toString(final StringBuilder sb, final boolean printType)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      */
newInstance(final long token, final int finish, final String value)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      */
newInstance(final Node parent, final String value)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 
LexerTokenLiteralNode(final long token, final int finish, final LexerToken value)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 
LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode)503         private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) {
504             super(literalNode);
505         }
506 
507         @Override
getType()508         public Type getType() {
509             return Type.OBJECT;
510         }
511 
512         @Override
toString(final StringBuilder sb, final boolean printType)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      */
newInstance(final long token, final int finish, final LexerToken value)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      */
newInstance(final Node parent, final LexerToken value)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      */
objectAsConstant(final Object object)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      */
isConstant(final Object object)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 
NullLiteralNode(final long token, final int finish)573         private NullLiteralNode(final long token, final int finish) {
574             super(Token.recast(token, TokenType.OBJECT), finish, null);
575         }
576 
577         @Override
accept(final NodeVisitor<? extends LexicalContext> visitor)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
getType()587         public Type getType() {
588             return Type.OBJECT;
589         }
590 
591         @Override
getWidestOperationType()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 
initialize(final ArrayLiteralNode node)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 
computeElementType(final Expression[] value)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 
computePostsets(final Expression[] value)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 
setArrayElement(final int[] array, final int i, final Object n)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 
setArrayElement(final long[] array, final int i, final Object n)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 
setArrayElement(final double[] array, final int i, final Object n)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 
presetIntArray(final Expression[] value, final int[] postsets)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 
presetLongArray(final Expression[] value, final int[] postsets)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 
presetDoubleArray(final Expression[] value, final int[] postsets)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 
presetObjectArray(final Expression[] value, final int[] postsets)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 
computePresets(final Expression[] value, final Type elementType, final int[] postsets)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          */
ArrayLiteralNode(final long token, final int finish, final Expression[] value)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          */
ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<Splittable.SplitRange> splitRanges)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          */
getElementExpressions()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
initialize(final LexicalContext lc)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          */
getArrayType()811         public ArrayType getArrayType() {
812             return getArrayType(getElementType());
813         }
814 
getArrayType(final Type elementType)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
getType()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          */
getElementType()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          */
getPostsets()844         public int[] getPostsets() {
845             assert postsets != null : this + " elementType=" + elementType + " has no postsets";
846             return postsets;
847         }
848 
presetsMatchElementType()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          */
getPresets()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
getSplitRanges()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          */
setSplitRanges(final LexicalContext lc, final List<Splittable.SplitRange> splitRanges)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
accept(final NodeVisitor<? extends LexicalContext> visitor)893         public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
894             return Acceptor.accept(this, visitor);
895         }
896 
897         @Override
accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor)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 
setValue(final LexicalContext lc, final Expression[] value)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 
setValue(final LexicalContext lc, final List<Expression> value)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
toString(final StringBuilder sb, final boolean printType)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      */
newInstance(final long token, final int finish, final List<Expression> value)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      */
newInstance(final Node parent, final List<Expression> value)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      */
newInstance(final long token, final int finish, final Expression[] value)972     public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final Expression[] value) {
973         return new ArrayLiteralNode(token, finish, value);
974     }
975 }
976