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
26 package jdk.nashorn.internal.ir;
27
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.List;
31 import jdk.nashorn.internal.codegen.types.ArrayType;
32 import jdk.nashorn.internal.codegen.types.Type;
33 import jdk.nashorn.internal.ir.annotations.Immutable;
34 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
35 import jdk.nashorn.internal.objects.NativeArray;
36 import jdk.nashorn.internal.parser.Lexer.LexerToken;
37 import jdk.nashorn.internal.parser.Token;
38 import jdk.nashorn.internal.parser.TokenType;
39 import jdk.nashorn.internal.runtime.JSType;
40 import jdk.nashorn.internal.runtime.ScriptRuntime;
41 import jdk.nashorn.internal.runtime.Undefined;
42
43 /**
44 * Literal nodes represent JavaScript values.
45 *
46 * @param <T> the literal type
47 */
48 @Immutable
49 public 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[value.size()]);
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[value.size()]));
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 }
--- EOF ---