LiteralNode.java revision 1007:4258ccc2eb8a
1219089Spjd/*
2209962Smm * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3209962Smm * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4209962Smm *
5209962Smm * This code is free software; you can redistribute it and/or modify it
6209962Smm * under the terms of the GNU General Public License version 2 only, as
7209962Smm * published by the Free Software Foundation.  Oracle designates this
8209962Smm * particular file as subject to the "Classpath" exception as provided
9209962Smm * by Oracle in the LICENSE file that accompanied this code.
10209962Smm *
11209962Smm * This code is distributed in the hope that it will be useful, but WITHOUT
12209962Smm * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13209962Smm * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14209962Smm * version 2 for more details (a copy is included in the LICENSE file that
15209962Smm * accompanied this code).
16209962Smm *
17209962Smm * You should have received a copy of the GNU General Public License version
18209962Smm * 2 along with this work; if not, write to the Free Software Foundation,
19209962Smm * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20209962Smm *
21209962Smm * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22219089Spjd * or visit www.oracle.com if you need additional information or have any
23209962Smm * questions.
24209962Smm */
25209962Smm
26209962Smmpackage jdk.nashorn.internal.ir;
27209962Smm
28209962Smmimport java.util.Arrays;
29209962Smmimport java.util.Collections;
30209962Smmimport java.util.List;
31219089Spjdimport java.util.function.Function;
32209962Smmimport jdk.nashorn.internal.codegen.CompileUnit;
33209962Smmimport jdk.nashorn.internal.codegen.types.ArrayType;
34209962Smmimport jdk.nashorn.internal.codegen.types.Type;
35209962Smmimport jdk.nashorn.internal.ir.annotations.Immutable;
36209962Smmimport jdk.nashorn.internal.ir.visitor.NodeVisitor;
37209962Smmimport jdk.nashorn.internal.objects.NativeArray;
38209962Smmimport jdk.nashorn.internal.parser.Lexer.LexerToken;
39209962Smmimport jdk.nashorn.internal.parser.Token;
40219089Spjdimport jdk.nashorn.internal.parser.TokenType;
41219089Spjdimport jdk.nashorn.internal.runtime.JSType;
42219089Spjdimport jdk.nashorn.internal.runtime.ScriptRuntime;
43219089Spjdimport jdk.nashorn.internal.runtime.Undefined;
44219089Spjd
45209962Smm/**
46209962Smm * Literal nodes represent JavaScript values.
47209962Smm *
48209962Smm * @param <T> the literal type
49209962Smm */
50209962Smm@Immutable
51209962Smmpublic abstract class LiteralNode<T> extends Expression implements PropertyKey {
52209962Smm    /** Literal value */
53209962Smm    protected final T value;
54209962Smm
55209962Smm    /** Marker for values that must be computed at runtime */
56209962Smm    public static final Object POSTSET_MARKER = new Object();
57209962Smm
58209962Smm    /**
59209962Smm     * Constructor
60209962Smm     *
61209962Smm     * @param token   token
62209962Smm     * @param finish  finish
63209962Smm     * @param value   the value of the literal
64209962Smm     */
65209962Smm    protected LiteralNode(final long token, final int finish, final T value) {
66209962Smm        super(token, finish);
67209962Smm        this.value = value;
68209962Smm    }
69209962Smm
70209962Smm    /**
71209962Smm     * Copy constructor
72209962Smm     *
73209962Smm     * @param literalNode source node
74209962Smm     */
75209962Smm    protected LiteralNode(final LiteralNode<T> literalNode) {
76209962Smm        this(literalNode, literalNode.value);
77209962Smm    }
78209962Smm
79209962Smm    /**
80209962Smm     * A copy constructor with value change.
81209962Smm     * @param literalNode the original literal node
82209962Smm     * @param newValue new value for this node
83209962Smm     */
84209962Smm    protected LiteralNode(final LiteralNode<T> literalNode, final T newValue) {
85209962Smm        super(literalNode);
86209962Smm        this.value = newValue;
87209962Smm    }
88209962Smm
89209962Smm    /**
90209962Smm     * Initialization setter, if required for immutable state. This is used for
91209962Smm     * things like ArrayLiteralNodes that need to carry state for the splitter.
92209962Smm     * Default implementation is just a nop.
93209962Smm     * @param lc lexical context
94209962Smm     * @return new literal node with initialized state, or same if nothing changed
95209962Smm     */
96209962Smm    public LiteralNode<?> initialize(final LexicalContext lc) {
97209962Smm        return this;
98209962Smm    }
99209962Smm
100209962Smm    /**
101209962Smm     * Check if the literal value is null
102209962Smm     * @return true if literal value is null
103209962Smm     */
104209962Smm    public boolean isNull() {
105209962Smm        return value == null;
106209962Smm    }
107209962Smm
108209962Smm    @Override
109209962Smm    public Type getType(final Function<Symbol, Type> localVariableTypes) {
110209962Smm        return Type.typeFor(value.getClass());
111209962Smm    }
112209962Smm
113209962Smm    @Override
114209962Smm    public String getPropertyName() {
115209962Smm        return JSType.toString(getObject());
116209962Smm    }
117209962Smm
118209962Smm    /**
119209962Smm     * Fetch boolean value of node.
120209962Smm     *
121209962Smm     * @return boolean value of node.
122209962Smm     */
123209962Smm    public boolean getBoolean() {
124209962Smm        return JSType.toBoolean(value);
125209962Smm    }
126209962Smm
127209962Smm    /**
128209962Smm     * Fetch int32 value of node.
129209962Smm     *
130209962Smm     * @return Int32 value of node.
131209962Smm     */
132209962Smm    public int getInt32() {
133209962Smm        return JSType.toInt32(value);
134209962Smm    }
135209962Smm
136209962Smm    /**
137209962Smm     * Fetch uint32 value of node.
138209962Smm     *
139209962Smm     * @return uint32 value of node.
140209962Smm     */
141209962Smm    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     * Get the array value of the node
165     *
166     * @return the array value
167     */
168    public Node[] getArray() {
169        assert false : "not an array node";
170        return null;
171    }
172
173    /**
174     * Fetch String value of node.
175     *
176     * @return String value of node.
177     */
178    public String getString() {
179        return JSType.toString(value);
180    }
181
182    /**
183     * Fetch Object value of node.
184     *
185     * @return Object value of node.
186     */
187    public Object getObject() {
188        return value;
189    }
190
191    /**
192     * Test if the value is a string.
193     *
194     * @return True if value is a string.
195     */
196    public boolean isString() {
197        return value instanceof String;
198    }
199
200    /**
201     * Test if tha value is a number
202     *
203     * @return True if value is a number
204     */
205    public boolean isNumeric() {
206        return value instanceof Number;
207    }
208
209    /**
210     * Assist in IR navigation.
211     *
212     * @param visitor IR navigating visitor.
213     */
214    @Override
215    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
216        if (visitor.enterLiteralNode(this)) {
217            return visitor.leaveLiteralNode(this);
218        }
219
220        return this;
221    }
222
223    @Override
224    public void toString(final StringBuilder sb, final boolean printType) {
225        if (value == null) {
226            sb.append("null");
227        } else {
228            sb.append(value.toString());
229        }
230    }
231
232    /**
233     * Get the literal node value
234     * @return the value
235     */
236    public final T getValue() {
237        return value;
238    }
239
240    /**
241     * Create a new null literal
242     *
243     * @param token   token
244     * @param finish  finish
245     *
246     * @return the new literal node
247     */
248    public static LiteralNode<Object> newInstance(final long token, final int finish) {
249        return new NullLiteralNode(token, finish);
250    }
251
252    /**
253     * Create a new null literal based on a parent node (source, token, finish)
254     *
255     * @param parent parent node
256     *
257     * @return the new literal node
258     */
259    public static LiteralNode<Object> newInstance(final Node parent) {
260        return new NullLiteralNode(parent.getToken(), parent.getFinish());
261    }
262
263    /**
264     * Super class for primitive (side-effect free) literals.
265     *
266     * @param <T> the literal type
267     */
268    public static class PrimitiveLiteralNode<T> extends LiteralNode<T> {
269        private PrimitiveLiteralNode(final long token, final int finish, final T value) {
270            super(token, finish, value);
271        }
272
273        private PrimitiveLiteralNode(final PrimitiveLiteralNode<T> literalNode) {
274            super(literalNode);
275        }
276
277        /**
278         * Check if the literal value is boolean true
279         * @return true if literal value is boolean true
280         */
281        public boolean isTrue() {
282            return JSType.toBoolean(value);
283        }
284
285        @Override
286        public boolean isLocal() {
287            return true;
288        }
289
290        @Override
291        public boolean isAlwaysFalse() {
292            return !isTrue();
293        }
294
295        @Override
296        public boolean isAlwaysTrue() {
297            return isTrue();
298        }
299    }
300
301    @Immutable
302    private static final class BooleanLiteralNode extends PrimitiveLiteralNode<Boolean> {
303
304        private BooleanLiteralNode(final long token, final int finish, final boolean value) {
305            super(Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
306        }
307
308        private BooleanLiteralNode(final BooleanLiteralNode literalNode) {
309            super(literalNode);
310        }
311
312        @Override
313        public boolean isTrue() {
314            return value;
315        }
316
317        @Override
318        public Type getType(final Function<Symbol, Type> localVariableTypes) {
319            return Type.BOOLEAN;
320        }
321
322        @Override
323        public Type getWidestOperationType() {
324            return Type.BOOLEAN;
325        }
326    }
327
328    /**
329     * Create a new boolean literal
330     *
331     * @param token   token
332     * @param finish  finish
333     * @param value   true or false
334     *
335     * @return the new literal node
336     */
337    public static LiteralNode<Boolean> newInstance(final long token, final int finish, final boolean value) {
338        return new BooleanLiteralNode(token, finish, value);
339    }
340
341    /**
342     * Create a new boolean literal based on a parent node (source, token, finish)
343     *
344     * @param parent parent node
345     * @param value  true or false
346     *
347     * @return the new literal node
348     */
349    public static LiteralNode<?> newInstance(final Node parent, final boolean value) {
350        return new BooleanLiteralNode(parent.getToken(), parent.getFinish(), value);
351    }
352
353    @Immutable
354    private static final class NumberLiteralNode extends PrimitiveLiteralNode<Number> {
355
356        private final Type type = numberGetType(value);
357
358        private NumberLiteralNode(final long token, final int finish, final Number value) {
359            super(Token.recast(token, TokenType.DECIMAL), finish, value);
360        }
361
362        private NumberLiteralNode(final NumberLiteralNode literalNode) {
363            super(literalNode);
364        }
365
366        private static Type numberGetType(final Number number) {
367            if (number instanceof Integer) {
368                return Type.INT;
369            } else if (number instanceof Long) {
370                return Type.LONG;
371            } else if (number instanceof Double) {
372                return Type.NUMBER;
373            } else {
374                assert false;
375            }
376
377            return null;
378        }
379
380        @Override
381        public Type getType(final Function<Symbol, Type> localVariableTypes) {
382            return type;
383        }
384
385        @Override
386        public Type getWidestOperationType() {
387            return getType();
388        }
389
390    }
391    /**
392     * Create a new number literal
393     *
394     * @param token   token
395     * @param finish  finish
396     * @param value   literal value
397     *
398     * @return the new literal node
399     */
400    public static LiteralNode<Number> newInstance(final long token, final int finish, final Number value) {
401        return new NumberLiteralNode(token, finish, value);
402    }
403
404    /**
405     * Create a new number literal based on a parent node (source, token, finish)
406     *
407     * @param parent parent node
408     * @param value  literal value
409     *
410     * @return the new literal node
411     */
412    public static LiteralNode<?> newInstance(final Node parent, final Number value) {
413        return new NumberLiteralNode(parent.getToken(), parent.getFinish(), value);
414    }
415
416    private static class UndefinedLiteralNode extends PrimitiveLiteralNode<Undefined> {
417        private UndefinedLiteralNode(final long token, final int finish) {
418            super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED);
419        }
420
421        private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
422            super(literalNode);
423        }
424    }
425
426    /**
427     * Create a new undefined literal
428     *
429     * @param token   token
430     * @param finish  finish
431     * @param value   undefined value, passed only for polymorphisism discrimination
432     *
433     * @return the new literal node
434     */
435    public static LiteralNode<Undefined> newInstance(final long token, final int finish, final Undefined value) {
436        return new UndefinedLiteralNode(token, finish);
437    }
438
439    /**
440     * Create a new null literal based on a parent node (source, token, finish)
441     *
442     * @param parent parent node
443     * @param value  undefined value
444     *
445     * @return the new literal node
446     */
447    public static LiteralNode<?> newInstance(final Node parent, final Undefined value) {
448        return new UndefinedLiteralNode(parent.getToken(), parent.getFinish());
449    }
450
451    @Immutable
452    private static class StringLiteralNode extends PrimitiveLiteralNode<String> {
453        private StringLiteralNode(final long token, final int finish, final String value) {
454            super(Token.recast(token, TokenType.STRING), finish, value);
455        }
456
457        private StringLiteralNode(final StringLiteralNode literalNode) {
458            super(literalNode);
459        }
460
461        @Override
462        public void toString(final StringBuilder sb, final boolean printType) {
463            sb.append('\"');
464            sb.append(value);
465            sb.append('\"');
466        }
467    }
468
469    /**
470     * Create a new string literal
471     *
472     * @param token   token
473     * @param finish  finish
474     * @param value   string value
475     *
476     * @return the new literal node
477     */
478    public static LiteralNode<String> newInstance(final long token, final int finish, final String value) {
479        return new StringLiteralNode(token, finish, value);
480    }
481
482    /**
483     * Create a new String literal based on a parent node (source, token, finish)
484     *
485     * @param parent parent node
486     * @param value  string value
487     *
488     * @return the new literal node
489     */
490    public static LiteralNode<?> newInstance(final Node parent, final String value) {
491        return new StringLiteralNode(parent.getToken(), parent.getFinish(), value);
492    }
493
494    @Immutable
495    private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
496        private LexerTokenLiteralNode(final long token, final int finish, final LexerToken value) {
497            super(Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
498        }
499
500        private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) {
501            super(literalNode);
502        }
503
504        @Override
505        public Type getType(final Function<Symbol, Type> localVariableTypes) {
506            return Type.OBJECT;
507        }
508
509        @Override
510        public void toString(final StringBuilder sb, final boolean printType) {
511            sb.append(value.toString());
512        }
513    }
514
515    /**
516     * Create a new literal node for a lexer token
517     *
518     * @param token   token
519     * @param finish  finish
520     * @param value   lexer token value
521     *
522     * @return the new literal node
523     */
524    public static LiteralNode<LexerToken> newInstance(final long token, final int finish, final LexerToken value) {
525        return new LexerTokenLiteralNode(token, finish, value);
526    }
527
528    /**
529     * Create a new lexer token literal based on a parent node (source, token, finish)
530     *
531     * @param parent parent node
532     * @param value  lexer token
533     *
534     * @return the new literal node
535     */
536    public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) {
537        return new LexerTokenLiteralNode(parent.getToken(), parent.getFinish(), value);
538    }
539
540    /**
541     * Get the constant value for an object, or {@link #POSTSET_MARKER} if the value can't be statically computed.
542     *
543     * @param object a node or value object
544     * @return the constant value or {@code POSTSET_MARKER}
545     */
546    public static Object objectAsConstant(final Object object) {
547        if (object == null) {
548            return null;
549        } else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
550            return object;
551        } else if (object instanceof LiteralNode) {
552            return objectAsConstant(((LiteralNode<?>)object).getValue());
553        }
554
555        return POSTSET_MARKER;
556    }
557
558    private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> {
559
560        private NullLiteralNode(final long token, final int finish) {
561            super(Token.recast(token, TokenType.OBJECT), finish, null);
562        }
563
564        @Override
565        public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
566            if (visitor.enterLiteralNode(this)) {
567                return visitor.leaveLiteralNode(this);
568            }
569
570            return this;
571        }
572
573        @Override
574        public Type getType(final Function<Symbol, Type> localVariableTypes) {
575            return Type.OBJECT;
576        }
577
578        @Override
579        public Type getWidestOperationType() {
580            return Type.OBJECT;
581        }
582    }
583
584    /**
585     * Array literal node class.
586     */
587    @Immutable
588    public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode {
589
590        /** Array element type. */
591        private final Type elementType;
592
593        /** Preset constant array. */
594        private final Object presets;
595
596        /** Indices of array elements requiring computed post sets. */
597        private final int[] postsets;
598
599        /** Sub units with indexes ranges, in which to split up code generation, for large literals */
600        private final List<ArrayUnit> units;
601
602        /**
603         * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
604         * be split if they are too large, for bytecode generation reasons
605         */
606        public static final class ArrayUnit implements CompileUnitHolder {
607            /** Compile unit associated with the postsets range. */
608            private final CompileUnit compileUnit;
609
610            /** postsets range associated with the unit (hi not inclusive). */
611            private final int lo, hi;
612
613            /**
614             * Constructor
615             * @param compileUnit compile unit
616             * @param lo lowest array index in unit
617             * @param hi highest array index in unit + 1
618             */
619            public ArrayUnit(final CompileUnit compileUnit, final int lo, final int hi) {
620                this.compileUnit = compileUnit;
621                this.lo   = lo;
622                this.hi   = hi;
623            }
624
625            /**
626             * Get the high index position of the ArrayUnit (non inclusive)
627             * @return high index position
628             */
629            public int getHi() {
630                return hi;
631            }
632
633            /**
634             * Get the low index position of the ArrayUnit (inclusive)
635             * @return low index position
636             */
637            public int getLo() {
638                return lo;
639            }
640
641            /**
642             * The array compile unit
643             * @return array compile unit
644             */
645            @Override
646            public CompileUnit getCompileUnit() {
647                return compileUnit;
648            }
649        }
650
651        private static final class ArrayLiteralInitializer {
652
653            static ArrayLiteralNode initialize(final ArrayLiteralNode node) {
654                final Type elementType = computeElementType(node.value, node.elementType);
655                final int[] postsets = computePostsets(node.value);
656                final Object presets = computePresets(node.value, elementType, postsets);
657                return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units);
658            }
659
660            private static Type computeElementType(final Expression[] value, final Type elementType) {
661                Type widestElementType = Type.INT;
662
663                for (final Expression elem : value) {
664                    if (elem == null) {
665                        widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
666                        break;
667                    }
668
669                    final Type type = elem.getType().isUnknown() ? Type.OBJECT : elem.getType();
670                    if (type.isBoolean()) {
671                        //TODO fix this with explicit boolean types
672                        widestElementType = widestElementType.widest(Type.OBJECT);
673                        break;
674                    }
675
676                    widestElementType = widestElementType.widest(type);
677                    if (widestElementType.isObject()) {
678                        break;
679                    }
680                }
681                return widestElementType;
682            }
683
684            private static int[] computePostsets(final Expression[] value) {
685                final int[] computed = new int[value.length];
686                int nComputed = 0;
687
688                for (int i = 0; i < value.length; i++) {
689                    final Expression element = value[i];
690                    if (element == null || objectAsConstant(element) == POSTSET_MARKER) {
691                        computed[nComputed++] = i;
692                    }
693                }
694                return Arrays.copyOf(computed, nComputed);
695            }
696
697            private static boolean setArrayElement(final int[] array, final int i, final Object n) {
698                if (n instanceof Number) {
699                    array[i] = ((Number)n).intValue();
700                    return true;
701                }
702                return false;
703            }
704
705            private static boolean setArrayElement(final long[] array, final int i, final Object n) {
706                if (n instanceof Number) {
707                    array[i] = ((Number)n).longValue();
708                    return true;
709                }
710                return false;
711            }
712
713            private static boolean setArrayElement(final double[] array, final int i, final Object n) {
714                if (n instanceof Number) {
715                    array[i] = ((Number)n).doubleValue();
716                    return true;
717                }
718                return false;
719            }
720
721            private static int[] presetIntArray(final Expression[] value, final int[] postsets) {
722                final int[] array = new int[value.length];
723                int nComputed = 0;
724                for (int i = 0; i < value.length; i++) {
725                    if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
726                        assert postsets[nComputed++] == i;
727                    }
728                }
729                assert postsets.length == nComputed;
730                return array;
731            }
732
733            private static long[] presetLongArray(final Expression[] value, final int[] postsets) {
734                final long[] array = new long[value.length];
735                int nComputed = 0;
736                for (int i = 0; i < value.length; i++) {
737                    if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
738                        assert postsets[nComputed++] == i;
739                    }
740                }
741                assert postsets.length == nComputed;
742                return array;
743            }
744
745            private static double[] presetDoubleArray(final Expression[] value, final int[] postsets) {
746                final double[] array = new double[value.length];
747                int nComputed = 0;
748                for (int i = 0; i < value.length; i++) {
749                    if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
750                        assert postsets[nComputed++] == i;
751                    }
752                }
753                assert postsets.length == nComputed;
754                return array;
755            }
756
757            private static Object[] presetObjectArray(final Expression[] value, final int[] postsets) {
758                final Object[] array = new Object[value.length];
759                int nComputed = 0;
760
761                for (int i = 0; i < value.length; i++) {
762                    final Node node = value[i];
763
764                    if (node == null) {
765                        assert postsets[nComputed++] == i;
766                        continue;
767                    }
768                    final Object element = objectAsConstant(node);
769
770                    if (element != POSTSET_MARKER) {
771                        array[i] = element;
772                    } else {
773                        assert postsets[nComputed++] == i;
774                    }
775                }
776
777                assert postsets.length == nComputed;
778                return array;
779            }
780
781            static Object computePresets(final Expression[] value, final Type elementType, final int[] postsets) {
782                assert !elementType.isUnknown();
783                if (elementType.isInteger()) {
784                    return presetIntArray(value, postsets);
785                } else if (elementType.isLong()) {
786                    return presetLongArray(value, postsets);
787                } else if (elementType.isNumeric()) {
788                    return presetDoubleArray(value, postsets);
789                } else {
790                    return presetObjectArray(value, postsets);
791                }
792            }
793        }
794
795        /**
796         * Constructor
797         *
798         * @param token   token
799         * @param finish  finish
800         * @param value   array literal value, a Node array
801         */
802        protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) {
803            super(Token.recast(token, TokenType.ARRAY), finish, value);
804            this.elementType = Type.UNKNOWN;
805            this.presets     = null;
806            this.postsets    = null;
807            this.units       = null;
808        }
809
810        /**
811         * Copy constructor
812         * @param node source array literal node
813         */
814        private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<ArrayUnit> units) {
815            super(node, value);
816            this.elementType = elementType;
817            this.postsets    = postsets;
818            this.presets     = presets;
819            this.units       = units;
820        }
821
822        @Override
823        public Node[] getArray() {
824            return value;
825        }
826
827        /**
828         * Setter that initializes all code generation meta data for an
829         * ArrayLiteralNode. This acts a setter, so the return value may
830         * return a new node and must be handled
831         *
832         * @param lc lexical context
833         * @return new array literal node with postsets, presets and element types initialized
834         */
835        @Override
836        public ArrayLiteralNode initialize(final LexicalContext lc) {
837            return Node.replaceInLexicalContext(lc, this, ArrayLiteralInitializer.initialize(this));
838        }
839
840        /**
841         * Get the array element type as Java format, e.g. [I
842         * @return array element type
843         */
844        public ArrayType getArrayType() {
845            return getArrayType(getElementType());
846        }
847
848        private static ArrayType getArrayType(final Type elementType) {
849            if (elementType.isInteger()) {
850                return Type.INT_ARRAY;
851            } else if (elementType.isLong()) {
852                return Type.LONG_ARRAY;
853            } else if (elementType.isNumeric()) {
854                return Type.NUMBER_ARRAY;
855            } else {
856                return Type.OBJECT_ARRAY;
857            }
858        }
859
860        @Override
861        public Type getType(final Function<Symbol, Type> localVariableTypes) {
862            return Type.typeFor(NativeArray.class);
863        }
864
865        /**
866         * Get the element type of this array literal
867         * @return element type
868         */
869        public Type getElementType() {
870            assert !elementType.isUnknown() : this + " has elementType=unknown";
871            return elementType;
872        }
873
874        /**
875         * Get indices of arrays containing computed post sets. post sets
876         * are things like non literals e.g. "x+y" instead of i or 17
877         * @return post set indices
878         */
879        public int[] getPostsets() {
880            assert postsets != null : this + " elementType=" + elementType + " has no postsets";
881            return postsets;
882        }
883
884        private boolean presetsMatchElementType() {
885            if (elementType == Type.INT) {
886                return presets instanceof int[];
887            } else if (elementType == Type.LONG) {
888                return presets instanceof long[];
889            } else if (elementType == Type.NUMBER) {
890                return presets instanceof double[];
891            } else {
892                return presets instanceof Object[];
893            }
894        }
895
896        /**
897         * Get presets constant array
898         * @return presets array, always returns an array type
899         */
900        public Object getPresets() {
901            assert presets != null && presetsMatchElementType() : this + " doesn't have presets, or invalid preset type: " + presets;
902            return presets;
903        }
904
905        /**
906         * Get the array units that make up this ArrayLiteral
907         * @see ArrayUnit
908         * @return list of array units
909         */
910        public List<ArrayUnit> getUnits() {
911            return units == null ? null : Collections.unmodifiableList(units);
912        }
913
914        /**
915         * Set the ArrayUnits that make up this ArrayLiteral
916         * @param lc lexical context
917         * @see ArrayUnit
918         * @param units list of array units
919         * @return new or changed arrayliteralnode
920         */
921        public ArrayLiteralNode setUnits(final LexicalContext lc, final List<ArrayUnit> units) {
922            if (this.units == units) {
923                return this;
924            }
925            return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
926        }
927
928        @Override
929        public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
930            return Acceptor.accept(this, visitor);
931        }
932
933        @Override
934        public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
935            if (visitor.enterLiteralNode(this)) {
936                final List<Expression> oldValue = Arrays.asList(value);
937                final List<Expression> newValue = Node.accept(visitor, oldValue);
938                return visitor.leaveLiteralNode(oldValue != newValue ? setValue(lc, newValue) : this);
939            }
940            return this;
941        }
942
943        private ArrayLiteralNode setValue(final LexicalContext lc, final Expression[] value) {
944            if (this.value == value) {
945                return this;
946            }
947            return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
948        }
949
950        private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
951            return setValue(lc, value.toArray(new Expression[value.size()]));
952        }
953
954        @Override
955        public void toString(final StringBuilder sb, final boolean printType) {
956            sb.append('[');
957            boolean first = true;
958            for (final Node node : value) {
959                if (!first) {
960                    sb.append(',');
961                    sb.append(' ');
962                }
963                if (node == null) {
964                    sb.append("undefined");
965                } else {
966                    node.toString(sb, printType);
967                }
968                first = false;
969            }
970            sb.append(']');
971        }
972    }
973
974    /**
975     * Create a new array literal of Nodes from a list of Node values
976     *
977     * @param token   token
978     * @param finish  finish
979     * @param value   literal value list
980     *
981     * @return the new literal node
982     */
983    public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value) {
984        return new ArrayLiteralNode(token, finish, value.toArray(new Expression[value.size()]));
985    }
986
987
988    /**
989     * Create a new array literal based on a parent node (source, token, finish)
990     *
991     * @param parent parent node
992     * @param value  literal value list
993     *
994     * @return the new literal node
995     */
996    public static LiteralNode<?> newInstance(final Node parent, final List<Expression> value) {
997        return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), value.toArray(new Expression[value.size()]));
998    }
999
1000    /**
1001     * Create a new array literal of Nodes
1002     *
1003     * @param token   token
1004     * @param finish  finish
1005     * @param value   literal value array
1006     *
1007     * @return the new literal node
1008     */
1009    public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final Expression[] value) {
1010        return new ArrayLiteralNode(token, finish, value);
1011    }
1012}
1013