LiteralNode.java revision 953:221a84ef44c0
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.util.Arrays;
29import java.util.Collections;
30import java.util.List;
31import java.util.function.Function;
32
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    /** Literal value */
54    protected final T value;
55
56    /** Marker for values that must be computed at runtime */
57    public static final Object POSTSET_MARKER = new Object();
58
59    /**
60     * Constructor
61     *
62     * @param token   token
63     * @param finish  finish
64     * @param value   the value of the literal
65     */
66    protected LiteralNode(final long token, final int finish, final T value) {
67        super(token, finish);
68        this.value = value;
69    }
70
71    /**
72     * Copy constructor
73     *
74     * @param literalNode source node
75     */
76    protected LiteralNode(final LiteralNode<T> literalNode) {
77        this(literalNode, literalNode.value);
78    }
79
80    /**
81     * A copy constructor with value change.
82     * @param literalNode the original literal node
83     * @param newValue new value for this node
84     */
85    protected LiteralNode(final LiteralNode<T> literalNode, final T newValue) {
86        super(literalNode);
87        this.value = newValue;
88    }
89
90    /**
91     * Initialization setter, if required for immutable state. This is used for
92     * things like ArrayLiteralNodes that need to carry state for the splitter.
93     * Default implementation is just a nop.
94     * @param lc lexical context
95     * @return new literal node with initialized state, or same if nothing changed
96     */
97    public LiteralNode<?> initialize(final LexicalContext lc) {
98        return this;
99    }
100
101    /**
102     * Check if the literal value is null
103     * @return true if literal value is null
104     */
105    public boolean isNull() {
106        return value == null;
107    }
108
109    @Override
110    public Type getType(final Function<Symbol, Type> localVariableTypes) {
111        return Type.typeFor(value.getClass());
112    }
113
114    @Override
115    public String getPropertyName() {
116        return JSType.toString(getObject());
117    }
118
119    /**
120     * Fetch boolean value of node.
121     *
122     * @return boolean value of node.
123     */
124    public boolean getBoolean() {
125        return JSType.toBoolean(value);
126    }
127
128    /**
129     * Fetch int32 value of node.
130     *
131     * @return Int32 value of node.
132     */
133    public int getInt32() {
134        return JSType.toInt32(value);
135    }
136
137    /**
138     * Fetch uint32 value of node.
139     *
140     * @return uint32 value of node.
141     */
142    public long getUint32() {
143        return JSType.toUint32(value);
144    }
145
146    /**
147     * Fetch long value of node
148     *
149     * @return long value of node
150     */
151    public long getLong() {
152        return JSType.toLong(value);
153    }
154
155    /**
156     * Fetch double value of node.
157     *
158     * @return double value of node.
159     */
160    public double getNumber() {
161        return JSType.toNumber(value);
162    }
163
164    /**
165     * Get the array value of the node
166     *
167     * @return the array value
168     */
169    public Node[] getArray() {
170        assert false : "not an array node";
171        return null;
172    }
173
174    /**
175     * Fetch String value of node.
176     *
177     * @return String value of node.
178     */
179    public String getString() {
180        return JSType.toString(value);
181    }
182
183    /**
184     * Fetch Object value of node.
185     *
186     * @return Object value of node.
187     */
188    public Object getObject() {
189        return value;
190    }
191
192    /**
193     * Test if the value is a string.
194     *
195     * @return True if value is a string.
196     */
197    public boolean isString() {
198        return value instanceof String;
199    }
200
201    /**
202     * Test if tha value is a number
203     *
204     * @return True if value is a number
205     */
206    public boolean isNumeric() {
207        return value instanceof Number;
208    }
209
210    /**
211     * Assist in IR navigation.
212     *
213     * @param visitor IR navigating visitor.
214     */
215    @Override
216    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
217        if (visitor.enterLiteralNode(this)) {
218            return visitor.leaveLiteralNode(this);
219        }
220
221        return this;
222    }
223
224    @Override
225    public void toString(final StringBuilder sb, final boolean printType) {
226        if (value == null) {
227            sb.append("null");
228        } else {
229            sb.append(value.toString());
230        }
231    }
232
233    /**
234     * Get the literal node value
235     * @return the value
236     */
237    public final T getValue() {
238        return value;
239    }
240
241    /**
242     * Create a new null literal
243     *
244     * @param token   token
245     * @param finish  finish
246     *
247     * @return the new literal node
248     */
249    public static LiteralNode<Object> newInstance(final long token, final int finish) {
250        return new NullLiteralNode(token, finish);
251    }
252
253    /**
254     * Create a new null literal based on a parent node (source, token, finish)
255     *
256     * @param parent parent node
257     *
258     * @return the new literal node
259     */
260    public static LiteralNode<Object> newInstance(final Node parent) {
261        return new NullLiteralNode(parent.getToken(), parent.getFinish());
262    }
263
264    /**
265     * Super class for primitive (side-effect free) literals.
266     *
267     * @param <T> the literal type
268     */
269    public static class PrimitiveLiteralNode<T> extends LiteralNode<T> {
270        private PrimitiveLiteralNode(final long token, final int finish, final T value) {
271            super(token, finish, value);
272        }
273
274        private PrimitiveLiteralNode(final PrimitiveLiteralNode<T> literalNode) {
275            super(literalNode);
276        }
277
278        /**
279         * Check if the literal value is boolean true
280         * @return true if literal value is boolean true
281         */
282        public boolean isTrue() {
283            return JSType.toBoolean(value);
284        }
285
286        @Override
287        public boolean isLocal() {
288            return true;
289        }
290
291        @Override
292        public boolean isAlwaysFalse() {
293            return !isTrue();
294        }
295
296        @Override
297        public boolean isAlwaysTrue() {
298            return isTrue();
299        }
300    }
301
302    @Immutable
303    private static final class BooleanLiteralNode extends PrimitiveLiteralNode<Boolean> {
304
305        private BooleanLiteralNode(final long token, final int finish, final boolean value) {
306            super(Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
307        }
308
309        private BooleanLiteralNode(final BooleanLiteralNode literalNode) {
310            super(literalNode);
311        }
312
313        @Override
314        public boolean isTrue() {
315            return value;
316        }
317
318        @Override
319        public Type getType(final Function<Symbol, Type> localVariableTypes) {
320            return Type.BOOLEAN;
321        }
322
323        @Override
324        public Type getWidestOperationType() {
325            return Type.BOOLEAN;
326        }
327    }
328
329    /**
330     * Create a new boolean literal
331     *
332     * @param token   token
333     * @param finish  finish
334     * @param value   true or false
335     *
336     * @return the new literal node
337     */
338    public static LiteralNode<Boolean> newInstance(final long token, final int finish, final boolean value) {
339        return new BooleanLiteralNode(token, finish, value);
340    }
341
342    /**
343     * Create a new boolean literal based on a parent node (source, token, finish)
344     *
345     * @param parent parent node
346     * @param value  true or false
347     *
348     * @return the new literal node
349     */
350    public static LiteralNode<?> newInstance(final Node parent, final boolean value) {
351        return new BooleanLiteralNode(parent.getToken(), parent.getFinish(), value);
352    }
353
354    @Immutable
355    private static final class NumberLiteralNode extends PrimitiveLiteralNode<Number> {
356
357        private final Type type = numberGetType(value);
358
359        private NumberLiteralNode(final long token, final int finish, final Number value) {
360            super(Token.recast(token, TokenType.DECIMAL), finish, value);
361        }
362
363        private NumberLiteralNode(final NumberLiteralNode literalNode) {
364            super(literalNode);
365        }
366
367        private static Type numberGetType(final Number number) {
368            if (number instanceof Integer) {
369                return Type.INT;
370            } else if (number instanceof Long) {
371                return Type.LONG;
372            } else if (number instanceof Double) {
373                return Type.NUMBER;
374            } else {
375                assert false;
376            }
377
378            return null;
379        }
380
381        @Override
382        public Type getType(final Function<Symbol, Type> localVariableTypes) {
383            return type;
384        }
385
386        @Override
387        public Type getWidestOperationType() {
388            return getType();
389        }
390
391    }
392    /**
393     * Create a new number literal
394     *
395     * @param token   token
396     * @param finish  finish
397     * @param value   literal value
398     *
399     * @return the new literal node
400     */
401    public static LiteralNode<Number> newInstance(final long token, final int finish, final Number value) {
402        return new NumberLiteralNode(token, finish, value);
403    }
404
405    /**
406     * Create a new number literal based on a parent node (source, token, finish)
407     *
408     * @param parent parent node
409     * @param value  literal value
410     *
411     * @return the new literal node
412     */
413    public static LiteralNode<?> newInstance(final Node parent, final Number value) {
414        return new NumberLiteralNode(parent.getToken(), parent.getFinish(), value);
415    }
416
417    private static class UndefinedLiteralNode extends PrimitiveLiteralNode<Undefined> {
418        private UndefinedLiteralNode(final long token, final int finish) {
419            super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED);
420        }
421
422        private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
423            super(literalNode);
424        }
425    }
426
427    /**
428     * Create a new undefined literal
429     *
430     * @param token   token
431     * @param finish  finish
432     * @param value   undefined value, passed only for polymorphisism discrimination
433     *
434     * @return the new literal node
435     */
436    public static LiteralNode<Undefined> newInstance(final long token, final int finish, final Undefined value) {
437        return new UndefinedLiteralNode(token, finish);
438    }
439
440    /**
441     * Create a new null literal based on a parent node (source, token, finish)
442     *
443     * @param parent parent node
444     * @param value  undefined value
445     *
446     * @return the new literal node
447     */
448    public static LiteralNode<?> newInstance(final Node parent, final Undefined value) {
449        return new UndefinedLiteralNode(parent.getToken(), parent.getFinish());
450    }
451
452    @Immutable
453    private static class StringLiteralNode extends PrimitiveLiteralNode<String> {
454        private StringLiteralNode(final long token, final int finish, final String value) {
455            super(Token.recast(token, TokenType.STRING), finish, value);
456        }
457
458        private StringLiteralNode(final StringLiteralNode literalNode) {
459            super(literalNode);
460        }
461
462        @Override
463        public void toString(final StringBuilder sb, final boolean printType) {
464            sb.append('\"');
465            sb.append(value);
466            sb.append('\"');
467        }
468    }
469
470    /**
471     * Create a new string literal
472     *
473     * @param token   token
474     * @param finish  finish
475     * @param value   string value
476     *
477     * @return the new literal node
478     */
479    public static LiteralNode<String> newInstance(final long token, final int finish, final String value) {
480        return new StringLiteralNode(token, finish, value);
481    }
482
483    /**
484     * Create a new String literal based on a parent node (source, token, finish)
485     *
486     * @param parent parent node
487     * @param value  string value
488     *
489     * @return the new literal node
490     */
491    public static LiteralNode<?> newInstance(final Node parent, final String value) {
492        return new StringLiteralNode(parent.getToken(), parent.getFinish(), value);
493    }
494
495    @Immutable
496    private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
497        private LexerTokenLiteralNode(final long token, final int finish, final LexerToken value) {
498            super(Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
499        }
500
501        private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) {
502            super(literalNode);
503        }
504
505        @Override
506        public Type getType(final Function<Symbol, Type> localVariableTypes) {
507            return Type.OBJECT;
508        }
509
510        @Override
511        public void toString(final StringBuilder sb, final boolean printType) {
512            sb.append(value.toString());
513        }
514    }
515
516    /**
517     * Create a new literal node for a lexer token
518     *
519     * @param token   token
520     * @param finish  finish
521     * @param value   lexer token value
522     *
523     * @return the new literal node
524     */
525    public static LiteralNode<LexerToken> newInstance(final long token, final int finish, final LexerToken value) {
526        return new LexerTokenLiteralNode(token, finish, value);
527    }
528
529    /**
530     * Create a new lexer token literal based on a parent node (source, token, finish)
531     *
532     * @param parent parent node
533     * @param value  lexer token
534     *
535     * @return the new literal node
536     */
537    public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) {
538        return new LexerTokenLiteralNode(parent.getToken(), parent.getFinish(), value);
539    }
540
541    /**
542     * Get the constant value for an object, or {@link #POSTSET_MARKER} if the value can't be statically computed.
543     *
544     * @param object a node or value object
545     * @return the constant value or {@code POSTSET_MARKER}
546     */
547    public static Object objectAsConstant(final Object object) {
548        if (object == null) {
549            return null;
550        } else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
551            return object;
552        } else if (object instanceof LiteralNode) {
553            return objectAsConstant(((LiteralNode<?>)object).getValue());
554        }
555
556        return POSTSET_MARKER;
557    }
558
559    private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> {
560
561        private NullLiteralNode(final long token, final int finish) {
562            super(Token.recast(token, TokenType.OBJECT), finish, null);
563        }
564
565        @Override
566        public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
567            if (visitor.enterLiteralNode(this)) {
568                return visitor.leaveLiteralNode(this);
569            }
570
571            return this;
572        }
573
574        @Override
575        public Type getType(final Function<Symbol, Type> localVariableTypes) {
576            return Type.OBJECT;
577        }
578
579        @Override
580        public Type getWidestOperationType() {
581            return Type.OBJECT;
582        }
583    }
584
585    /**
586     * Array literal node class.
587     */
588    @Immutable
589    public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode {
590
591        /** Array element type. */
592        private final Type elementType;
593
594        /** Preset constant array. */
595        private final Object presets;
596
597        /** Indices of array elements requiring computed post sets. */
598        private final int[] postsets;
599
600        /** Sub units with indexes ranges, in which to split up code generation, for large literals */
601        private final List<ArrayUnit> units;
602
603        /**
604         * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
605         * be split if they are too large, for bytecode generation reasons
606         */
607        public static final class ArrayUnit {
608            /** Compile unit associated with the postsets range. */
609            private final CompileUnit compileUnit;
610
611            /** postsets range associated with the unit (hi not inclusive). */
612            private final int lo, hi;
613
614            /**
615             * Constructor
616             * @param compileUnit compile unit
617             * @param lo lowest array index in unit
618             * @param hi highest array index in unit + 1
619             */
620            public ArrayUnit(final CompileUnit compileUnit, final int lo, final int hi) {
621                this.compileUnit = compileUnit;
622                this.lo   = lo;
623                this.hi   = hi;
624            }
625
626            /**
627             * Get the high index position of the ArrayUnit (non inclusive)
628             * @return high index position
629             */
630            public int getHi() {
631                return hi;
632            }
633
634            /**
635             * Get the low index position of the ArrayUnit (inclusive)
636             * @return low index position
637             */
638            public int getLo() {
639                return lo;
640            }
641
642            /**
643             * The array compile unit
644             * @return array compile unit
645             */
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