LiteralNode.java revision 971:c93b6091b11e
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;
32import jdk.nashorn.internal.codegen.CompileUnit;
33import jdk.nashorn.internal.codegen.types.ArrayType;
34import jdk.nashorn.internal.codegen.types.Type;
35import jdk.nashorn.internal.ir.annotations.Immutable;
36import jdk.nashorn.internal.ir.visitor.NodeVisitor;
37import jdk.nashorn.internal.objects.NativeArray;
38import jdk.nashorn.internal.parser.Lexer.LexerToken;
39import jdk.nashorn.internal.parser.Token;
40import jdk.nashorn.internal.parser.TokenType;
41import jdk.nashorn.internal.runtime.JSType;
42import jdk.nashorn.internal.runtime.ScriptRuntime;
43import jdk.nashorn.internal.runtime.Undefined;
44
45/**
46 * Literal nodes represent JavaScript values.
47 *
48 * @param <T> the literal type
49 */
50@Immutable
51public abstract class LiteralNode<T> extends Expression implements PropertyKey {
52    /** Literal value */
53    protected final T value;
54
55    /** Marker for values that must be computed at runtime */
56    public static final Object POSTSET_MARKER = new Object();
57
58    /**
59     * Constructor
60     *
61     * @param token   token
62     * @param finish  finish
63     * @param value   the value of the literal
64     */
65    protected LiteralNode(final long token, final int finish, final T value) {
66        super(token, finish);
67        this.value = value;
68    }
69
70    /**
71     * Copy constructor
72     *
73     * @param literalNode source node
74     */
75    protected LiteralNode(final LiteralNode<T> literalNode) {
76        this(literalNode, literalNode.value);
77    }
78
79    /**
80     * A copy constructor with value change.
81     * @param literalNode the original literal node
82     * @param newValue new value for this node
83     */
84    protected LiteralNode(final LiteralNode<T> literalNode, final T newValue) {
85        super(literalNode);
86        this.value = newValue;
87    }
88
89    /**
90     * Initialization setter, if required for immutable state. This is used for
91     * things like ArrayLiteralNodes that need to carry state for the splitter.
92     * Default implementation is just a nop.
93     * @param lc lexical context
94     * @return new literal node with initialized state, or same if nothing changed
95     */
96    public LiteralNode<?> initialize(final LexicalContext lc) {
97        return this;
98    }
99
100    /**
101     * Check if the literal value is null
102     * @return true if literal value is null
103     */
104    public boolean isNull() {
105        return value == null;
106    }
107
108    @Override
109    public Type getType(final Function<Symbol, Type> localVariableTypes) {
110        return Type.typeFor(value.getClass());
111    }
112
113    @Override
114    public String getPropertyName() {
115        return JSType.toString(getObject());
116    }
117
118    /**
119     * Fetch boolean value of node.
120     *
121     * @return boolean value of node.
122     */
123    public boolean getBoolean() {
124        return JSType.toBoolean(value);
125    }
126
127    /**
128     * Fetch int32 value of node.
129     *
130     * @return Int32 value of node.
131     */
132    public int getInt32() {
133        return JSType.toInt32(value);
134    }
135
136    /**
137     * Fetch uint32 value of node.
138     *
139     * @return uint32 value of node.
140     */
141    public long getUint32() {
142        return JSType.toUint32(value);
143    }
144
145    /**
146     * Fetch long value of node
147     *
148     * @return long value of node
149     */
150    public long getLong() {
151        return JSType.toLong(value);
152    }
153
154    /**
155     * Fetch double value of node.
156     *
157     * @return double value of node.
158     */
159    public double getNumber() {
160        return JSType.toNumber(value);
161    }
162
163    /**
164     * 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 {
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            public CompileUnit getCompileUnit() {
646                return compileUnit;
647            }
648        }
649
650        private static final class ArrayLiteralInitializer {
651
652            static ArrayLiteralNode initialize(final ArrayLiteralNode node) {
653                final Type elementType = computeElementType(node.value, node.elementType);
654                final int[] postsets = computePostsets(node.value);
655                final Object presets = computePresets(node.value, elementType, postsets);
656                return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units);
657            }
658
659            private static Type computeElementType(final Expression[] value, final Type elementType) {
660                Type widestElementType = Type.INT;
661
662                for (final Expression elem : value) {
663                    if (elem == null) {
664                        widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
665                        break;
666                    }
667
668                    final Type type = elem.getType().isUnknown() ? Type.OBJECT : elem.getType();
669                    if (type.isBoolean()) {
670                        //TODO fix this with explicit boolean types
671                        widestElementType = widestElementType.widest(Type.OBJECT);
672                        break;
673                    }
674
675                    widestElementType = widestElementType.widest(type);
676                    if (widestElementType.isObject()) {
677                        break;
678                    }
679                }
680                return widestElementType;
681            }
682
683            private static int[] computePostsets(final Expression[] value) {
684                final int[] computed = new int[value.length];
685                int nComputed = 0;
686
687                for (int i = 0; i < value.length; i++) {
688                    final Expression element = value[i];
689                    if (element == null || objectAsConstant(element) == POSTSET_MARKER) {
690                        computed[nComputed++] = i;
691                    }
692                }
693                return Arrays.copyOf(computed, nComputed);
694            }
695
696            private static boolean setArrayElement(final int[] array, final int i, final Object n) {
697                if (n instanceof Number) {
698                    array[i] = ((Number)n).intValue();
699                    return true;
700                }
701                return false;
702            }
703
704            private static boolean setArrayElement(final long[] array, final int i, final Object n) {
705                if (n instanceof Number) {
706                    array[i] = ((Number)n).longValue();
707                    return true;
708                }
709                return false;
710            }
711
712            private static boolean setArrayElement(final double[] array, final int i, final Object n) {
713                if (n instanceof Number) {
714                    array[i] = ((Number)n).doubleValue();
715                    return true;
716                }
717                return false;
718            }
719
720            private static int[] presetIntArray(final Expression[] value, final int[] postsets) {
721                final int[] array = new int[value.length];
722                int nComputed = 0;
723                for (int i = 0; i < value.length; i++) {
724                    if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
725                        assert postsets[nComputed++] == i;
726                    }
727                }
728                assert postsets.length == nComputed;
729                return array;
730            }
731
732            private static long[] presetLongArray(final Expression[] value, final int[] postsets) {
733                final long[] array = new long[value.length];
734                int nComputed = 0;
735                for (int i = 0; i < value.length; i++) {
736                    if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
737                        assert postsets[nComputed++] == i;
738                    }
739                }
740                assert postsets.length == nComputed;
741                return array;
742            }
743
744            private static double[] presetDoubleArray(final Expression[] value, final int[] postsets) {
745                final double[] array = new double[value.length];
746                int nComputed = 0;
747                for (int i = 0; i < value.length; i++) {
748                    if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
749                        assert postsets[nComputed++] == i;
750                    }
751                }
752                assert postsets.length == nComputed;
753                return array;
754            }
755
756            private static Object[] presetObjectArray(final Expression[] value, final int[] postsets) {
757                final Object[] array = new Object[value.length];
758                int nComputed = 0;
759
760                for (int i = 0; i < value.length; i++) {
761                    final Node node = value[i];
762
763                    if (node == null) {
764                        assert postsets[nComputed++] == i;
765                        continue;
766                    }
767                    final Object element = objectAsConstant(node);
768
769                    if (element != POSTSET_MARKER) {
770                        array[i] = element;
771                    } else {
772                        assert postsets[nComputed++] == i;
773                    }
774                }
775
776                assert postsets.length == nComputed;
777                return array;
778            }
779
780            static Object computePresets(final Expression[] value, final Type elementType, final int[] postsets) {
781                assert !elementType.isUnknown();
782                if (elementType.isInteger()) {
783                    return presetIntArray(value, postsets);
784                } else if (elementType.isLong()) {
785                    return presetLongArray(value, postsets);
786                } else if (elementType.isNumeric()) {
787                    return presetDoubleArray(value, postsets);
788                } else {
789                    return presetObjectArray(value, postsets);
790                }
791            }
792        }
793
794        /**
795         * Constructor
796         *
797         * @param token   token
798         * @param finish  finish
799         * @param value   array literal value, a Node array
800         */
801        protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) {
802            super(Token.recast(token, TokenType.ARRAY), finish, value);
803            this.elementType = Type.UNKNOWN;
804            this.presets     = null;
805            this.postsets    = null;
806            this.units       = null;
807        }
808
809        /**
810         * Copy constructor
811         * @param node source array literal node
812         */
813        private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<ArrayUnit> units) {
814            super(node, value);
815            this.elementType = elementType;
816            this.postsets    = postsets;
817            this.presets     = presets;
818            this.units       = units;
819        }
820
821        @Override
822        public Node[] getArray() {
823            return value;
824        }
825
826        /**
827         * Setter that initializes all code generation meta data for an
828         * ArrayLiteralNode. This acts a setter, so the return value may
829         * return a new node and must be handled
830         *
831         * @param lc lexical context
832         * @return new array literal node with postsets, presets and element types initialized
833         */
834        @Override
835        public ArrayLiteralNode initialize(final LexicalContext lc) {
836            return Node.replaceInLexicalContext(lc, this, ArrayLiteralInitializer.initialize(this));
837        }
838
839        /**
840         * Get the array element type as Java format, e.g. [I
841         * @return array element type
842         */
843        public ArrayType getArrayType() {
844            return getArrayType(getElementType());
845        }
846
847        private static ArrayType getArrayType(final Type elementType) {
848            if (elementType.isInteger()) {
849                return Type.INT_ARRAY;
850            } else if (elementType.isLong()) {
851                return Type.LONG_ARRAY;
852            } else if (elementType.isNumeric()) {
853                return Type.NUMBER_ARRAY;
854            } else {
855                return Type.OBJECT_ARRAY;
856            }
857        }
858
859        @Override
860        public Type getType(final Function<Symbol, Type> localVariableTypes) {
861            return Type.typeFor(NativeArray.class);
862        }
863
864        /**
865         * Get the element type of this array literal
866         * @return element type
867         */
868        public Type getElementType() {
869            assert !elementType.isUnknown() : this + " has elementType=unknown";
870            return elementType;
871        }
872
873        /**
874         * Get indices of arrays containing computed post sets. post sets
875         * are things like non literals e.g. "x+y" instead of i or 17
876         * @return post set indices
877         */
878        public int[] getPostsets() {
879            assert postsets != null : this + " elementType=" + elementType + " has no postsets";
880            return postsets;
881        }
882
883        private boolean presetsMatchElementType() {
884            if (elementType == Type.INT) {
885                return presets instanceof int[];
886            } else if (elementType == Type.LONG) {
887                return presets instanceof long[];
888            } else if (elementType == Type.NUMBER) {
889                return presets instanceof double[];
890            } else {
891                return presets instanceof Object[];
892            }
893        }
894
895        /**
896         * Get presets constant array
897         * @return presets array, always returns an array type
898         */
899        public Object getPresets() {
900            assert presets != null && presetsMatchElementType() : this + " doesn't have presets, or invalid preset type: " + presets;
901            return presets;
902        }
903
904        /**
905         * Get the array units that make up this ArrayLiteral
906         * @see ArrayUnit
907         * @return list of array units
908         */
909        public List<ArrayUnit> getUnits() {
910            return units == null ? null : Collections.unmodifiableList(units);
911        }
912
913        /**
914         * Set the ArrayUnits that make up this ArrayLiteral
915         * @param lc lexical context
916         * @see ArrayUnit
917         * @param units list of array units
918         * @return new or changed arrayliteralnode
919         */
920        public ArrayLiteralNode setUnits(final LexicalContext lc, final List<ArrayUnit> units) {
921            if (this.units == units) {
922                return this;
923            }
924            return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
925        }
926
927        @Override
928        public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
929            return Acceptor.accept(this, visitor);
930        }
931
932        @Override
933        public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
934            if (visitor.enterLiteralNode(this)) {
935                final List<Expression> oldValue = Arrays.asList(value);
936                final List<Expression> newValue = Node.accept(visitor, oldValue);
937                return visitor.leaveLiteralNode(oldValue != newValue ? setValue(lc, newValue) : this);
938            }
939            return this;
940        }
941
942        private ArrayLiteralNode setValue(final LexicalContext lc, final Expression[] value) {
943            if (this.value == value) {
944                return this;
945            }
946            return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
947        }
948
949        private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
950            return setValue(lc, value.toArray(new Expression[value.size()]));
951        }
952
953        @Override
954        public void toString(final StringBuilder sb, final boolean printType) {
955            sb.append('[');
956            boolean first = true;
957            for (final Node node : value) {
958                if (!first) {
959                    sb.append(',');
960                    sb.append(' ');
961                }
962                if (node == null) {
963                    sb.append("undefined");
964                } else {
965                    node.toString(sb, printType);
966                }
967                first = false;
968            }
969            sb.append(']');
970        }
971    }
972
973    /**
974     * Create a new array literal of Nodes from a list of Node values
975     *
976     * @param token   token
977     * @param finish  finish
978     * @param value   literal value list
979     *
980     * @return the new literal node
981     */
982    public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value) {
983        return new ArrayLiteralNode(token, finish, value.toArray(new Expression[value.size()]));
984    }
985
986
987    /**
988     * Create a new array literal based on a parent node (source, token, finish)
989     *
990     * @param parent parent node
991     * @param value  literal value list
992     *
993     * @return the new literal node
994     */
995    public static LiteralNode<?> newInstance(final Node parent, final List<Expression> value) {
996        return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), value.toArray(new Expression[value.size()]));
997    }
998
999    /**
1000     * Create a new array literal of Nodes
1001     *
1002     * @param token   token
1003     * @param finish  finish
1004     * @param value   literal value array
1005     *
1006     * @return the new literal node
1007     */
1008    public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final Expression[] value) {
1009        return new ArrayLiteralNode(token, finish, value);
1010    }
1011}
1012