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