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