Wed, 30 Jan 2013 12:26:45 +0100
8007062: Split Lower up into Lower/Attr/FinalizeTypes. Integrate AccessSpecalizer into FinalizeTypes.
Summary: Lower suffered from being a "God class" trying to do everything at once. As Nashorn code generation has grown, so has Lower. It does several post processing passes, tries to do several things at once even though all type information isn't in place, adjusting state afterwards and so on. It also performs control flow analysis, type attribution and constant folding, and everything else code generation related before byte code emission. I have now separated the compilation process into Lower (create low level nodes from high level ones, copy code such as finally block inlining etc), Attr (assign types and symbols to all nodes - freeze slot and scope information) and FinalizeTypes (insert explicit casts, specialize invoke dynamic types for scope accesses). I've removed the kludgy AccessSpecializer, as this now integrates naturally with typing. Everything is now much easier to read and each module performs only one thing. I have added separate loggers for the separate tiers. In the process I have also fixed: (1) problems with type coercion (see test/script/basic/typecoercion.js, basically our coercion was too late and our symbol inference was erroneous. This only manifested itself in very rare occasions where toNumber coercion has side effects, such as for example when valueOf is overridden) (2) copying literal nodes (literal copy did not use the superclass copy, which made all the Node specific fields not to be copied (3) erroneous literal tokenization (literals shouldn't always just inherit token information from whatever node that creates them) (4) splitter weighnodes - unary nodes were considered weightless (4) removed the hateful and kludgy "VarNode.shouldAppend", which really isn't needed when we have an attribution phase that determines self reference symbols (the only thing it was used for) (5) duplicate line number issues in the parser (6) convert bug in CodeGenerator for intermediate results of scope accesses (see test/script/basic/access-specializer.js) ... Several of these things just stopped being problems with the new architecture "can't happen anymore" and are not bug fixes per se. All tests run. No performance regressions exist that I've been able to measure. Some increases in performance were measured, but in the statistical margin of error (which is very wide as HotSpot currently has warmup issues with LambdaForms/invoke dynamic). Compile speed has not measurably increased.
Reviewed-by: jlaskey, attila
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 */
26 package jdk.nashorn.internal.ir;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.List;
31 import jdk.nashorn.internal.codegen.CompileUnit;
32 import jdk.nashorn.internal.codegen.types.Type;
33 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
34 import jdk.nashorn.internal.parser.Lexer.LexerToken;
35 import jdk.nashorn.internal.parser.Token;
36 import jdk.nashorn.internal.parser.TokenType;
37 import jdk.nashorn.internal.runtime.JSType;
38 import jdk.nashorn.internal.runtime.ScriptRuntime;
39 import jdk.nashorn.internal.runtime.Source;
40 import jdk.nashorn.internal.runtime.Undefined;
42 /**
43 * Literal nodes represent JavaScript values.
44 *
45 * @param <T> the literal type
46 */
47 public abstract class LiteralNode<T> extends Node implements PropertyKey {
48 /** Literal value */
49 protected T value;
51 /**
52 * Constructor
53 *
54 * @param source the source
55 * @param token token
56 * @param finish finish
57 * @param value the value of the literal
58 */
59 protected LiteralNode(final Source source, final long token, final int finish, final T value) {
60 super(source, token, finish);
61 this.value = value;
62 }
64 /**
65 * Copy constructor
66 *
67 * @param literalNode source node
68 */
69 protected LiteralNode(final LiteralNode<T> literalNode) {
70 super(literalNode);
71 this.value = literalNode.value;
72 }
74 @Override
75 public boolean isAtom() {
76 return true;
77 }
79 /**
80 * Check if the literal value is null
81 * @return true if literal value is null
82 */
83 public boolean isNull() {
84 return value == null;
85 }
87 @Override
88 public int hashCode() {
89 return value == null ? 0 : value.hashCode();
90 }
92 @Override
93 public boolean equals(final Object other) {
94 if (!(other instanceof LiteralNode<?>)) {
95 return false;
96 }
97 final LiteralNode<?> otherNode = (LiteralNode<?>)other;
98 if (otherNode.isNull()) {
99 return isNull();
100 }
101 return ((LiteralNode<?>)other).getValue().equals(value);
102 }
104 /**
105 * Check if the literal value is boolean true
106 * @return true if literal value is boolean true
107 */
108 public boolean isTrue() {
109 return JSType.toBoolean(value);
110 }
112 @Override
113 public Type getType() {
114 return Type.typeFor(value.getClass());
115 }
117 @Override
118 public String getPropertyName() {
119 return JSType.toString(getObject());
120 }
122 /**
123 * Fetch boolean value of node.
124 *
125 * @return boolean value of node.
126 */
127 public boolean getBoolean() {
128 return JSType.toBoolean(value);
129 }
131 /**
132 * Fetch int32 value of node.
133 *
134 * @return Int32 value of node.
135 */
136 public int getInt32() {
137 return JSType.toInt32(value);
138 }
140 /**
141 * Fetch uint32 value of node.
142 *
143 * @return uint32 value of node.
144 */
145 public long getUint32() {
146 return JSType.toUint32(value);
147 }
149 /**
150 * Fetch long value of node
151 *
152 * @return long value of node
153 */
154 public long getLong() {
155 return JSType.toLong(value);
156 }
158 /**
159 * Fetch double value of node.
160 *
161 * @return double value of node.
162 */
163 public double getNumber() {
164 return JSType.toNumber(value);
165 }
167 /**
168 * Get the array value of the node
169 *
170 * @return the array value
171 */
172 public Node[] getArray() {
173 assert false : "not an array node";
174 return null;
175 }
177 /**
178 * Fetch String value of node.
179 *
180 * @return String value of node.
181 */
182 public String getString() {
183 return JSType.toString(value);
184 }
186 /**
187 * Fetch Object value of node.
188 *
189 * @return Object value of node.
190 */
191 public Object getObject() {
192 return value;
193 }
195 /**
196 * Test if the value is a string.
197 *
198 * @return True if value is a string.
199 */
200 public boolean isString() {
201 return value instanceof String;
202 }
204 /**
205 * Test if tha value is a number
206 *
207 * @return True if value is a number
208 */
209 public boolean isNumeric() {
210 return value instanceof Number;
211 }
213 /**
214 * Assist in IR navigation.
215 *
216 * @param visitor IR navigating visitor.
217 */
218 @Override
219 public Node accept(final NodeVisitor visitor) {
220 if (visitor.enter(this) != null) {
221 return visitor.leave(this);
222 }
224 return this;
225 }
227 @Override
228 public void toString(final StringBuilder sb) {
229 if (value == null) {
230 sb.append("null");
231 } else {
232 sb.append(value.toString());
233 }
234 }
236 /**
237 * Get the literal node value
238 * @return the value
239 */
240 public T getValue() {
241 return value;
242 }
244 /**
245 * Create a new null literal
246 *
247 * @param source the source
248 * @param token token
249 * @param finish finish
250 *
251 * @return the new literal node
252 */
253 public static LiteralNode<Node> newInstance(final Source source, final long token, final int finish) {
254 return new NodeLiteralNode(source, token, finish);
255 }
257 /**
258 * Create a new null literal based on a parent node (source, token, finish)
259 *
260 * @param parent parent node
261 *
262 * @return the new literal node
263 */
264 public static LiteralNode<?> newInstance(final Node parent) {
265 return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
266 }
268 private static class BooleanLiteralNode extends LiteralNode<Boolean> {
270 private BooleanLiteralNode(final Source source, final long token, final int finish, final boolean value) {
271 super(source, Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
272 }
274 private BooleanLiteralNode(final BooleanLiteralNode literalNode) {
275 super(literalNode);
276 }
278 @Override
279 protected Node copy(final CopyState cs) {
280 return new BooleanLiteralNode(this);
281 }
283 @Override
284 public boolean isTrue() {
285 return value;
286 }
288 @Override
289 public Type getType() {
290 return Type.BOOLEAN;
291 }
293 @Override
294 public Type getWidestOperationType() {
295 return Type.BOOLEAN;
296 }
297 }
299 /**
300 * Create a new boolean literal
301 *
302 * @param source the source
303 * @param token token
304 * @param finish finish
305 * @param value true or false
306 *
307 * @return the new literal node
308 */
309 public static LiteralNode<Boolean> newInstance(final Source source, final long token, final int finish, final boolean value) {
310 return new BooleanLiteralNode(source, token, finish, value);
311 }
313 /**
314 * Create a new boolean literal based on a parent node (source, token, finish)
315 *
316 * @param parent parent node
317 * @param value true or false
318 *
319 * @return the new literal node
320 */
321 public static LiteralNode<?> newInstance(final Node parent, final boolean value) {
322 return new BooleanLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
323 }
325 private static class NumberLiteralNode extends LiteralNode<Number> {
327 private final Type type = numberGetType(value);
329 private NumberLiteralNode(final Source source, final long token, final int finish, final Number value) {
330 super(source, Token.recast(token, TokenType.DECIMAL), finish, value);
331 }
333 private NumberLiteralNode(final NumberLiteralNode literalNode) {
334 super(literalNode);
335 }
337 private static Type numberGetType(final Number number) {
338 if (number instanceof Integer) {
339 return Type.INT;
340 } else if (number instanceof Long) {
341 return Type.LONG;
342 } else if (number instanceof Double) {
343 return Type.NUMBER;
344 } else {
345 assert false;
346 }
348 return null;
349 }
351 @Override
352 protected Node copy(final CopyState cs) {
353 return new NumberLiteralNode(this);
354 }
356 @Override
357 public Type getType() {
358 return type;
359 }
361 @Override
362 public Type getWidestOperationType() {
363 return getType();
364 }
366 }
367 /**
368 * Create a new number literal
369 *
370 * @param source the source
371 * @param token token
372 * @param finish finish
373 * @param value literal value
374 *
375 * @return the new literal node
376 */
377 public static LiteralNode<Number> newInstance(final Source source, final long token, final int finish, final Number value) {
378 return new NumberLiteralNode(source, token, finish, value);
379 }
381 /**
382 * Create a new number literal based on a parent node (source, token, finish)
383 *
384 * @param parent parent node
385 * @param value literal value
386 *
387 * @return the new literal node
388 */
389 public static LiteralNode<?> newInstance(final Node parent, final Number value) {
390 return new NumberLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
391 }
393 private static class UndefinedLiteralNode extends LiteralNode<Undefined> {
394 private UndefinedLiteralNode(final Source source, final long token, final int finish) {
395 super(source, Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED);
396 }
398 private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
399 super(literalNode);
400 }
402 @Override
403 protected Node copy(final CopyState cs) {
404 return new UndefinedLiteralNode(this);
405 }
406 }
408 /**
409 * Create a new undefined literal
410 *
411 * @param source the source
412 * @param token token
413 * @param finish finish
414 * @param value undefined value, passed only for polymorphisism discrimination
415 *
416 * @return the new literal node
417 */
418 public static LiteralNode<Undefined> newInstance(final Source source, final long token, final int finish, final Undefined value) {
419 return new UndefinedLiteralNode(source, token, finish);
420 }
422 /**
423 * Create a new null literal based on a parent node (source, token, finish)
424 *
425 * @param parent parent node
426 * @param value undefined value
427 *
428 * @return the new literal node
429 */
430 public static LiteralNode<?> newInstance(final Node parent, final Undefined value) {
431 return new UndefinedLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
432 }
434 private static class StringLiteralNode extends LiteralNode<String> {
435 private StringLiteralNode(final Source source, final long token, final int finish, final String value) {
436 super(source, Token.recast(token, TokenType.STRING), finish, value);
437 }
439 private StringLiteralNode(final StringLiteralNode literalNode) {
440 super(literalNode);
441 }
443 @Override
444 protected Node copy(final CopyState cs) {
445 return new StringLiteralNode(this);
446 }
448 @Override
449 public void toString(final StringBuilder sb) {
450 sb.append('\"');
451 sb.append(value);
452 sb.append('\"');
453 }
454 }
456 /**
457 * Create a new string literal
458 *
459 * @param source the source
460 * @param token token
461 * @param finish finish
462 * @param value string value
463 *
464 * @return the new literal node
465 */
466 public static LiteralNode<String> newInstance(final Source source, final long token, final int finish, final String value) {
467 return new StringLiteralNode(source, token, finish, value);
468 }
470 /**
471 * Create a new String literal based on a parent node (source, token, finish)
472 *
473 * @param parent parent node
474 * @param value string value
475 *
476 * @return the new literal node
477 */
478 public static LiteralNode<?> newInstance(final Node parent, final String value) {
479 return new StringLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
480 }
482 private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
483 private LexerTokenLiteralNode(final Source source, final long token, final int finish, final LexerToken value) {
484 super(source, Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
485 }
487 private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) {
488 super(literalNode);
489 }
491 @Override
492 protected Node copy(final CopyState cs) {
493 return new LexerTokenLiteralNode(this);
494 }
496 @Override
497 public void toString(final StringBuilder sb) {
498 sb.append(value.toString());
499 }
500 }
502 /**
503 * Create a new literal node for a lexer token
504 *
505 * @param source the source
506 * @param token token
507 * @param finish finish
508 * @param value lexer token value
509 *
510 * @return the new literal node
511 */
512 public static LiteralNode<LexerToken> newInstance(final Source source, final long token, final int finish, final LexerToken value) {
513 return new LexerTokenLiteralNode(source, token, finish, value);
514 }
516 /**
517 * Create a new lexer token literal based on a parent node (source, token, finish)
518 *
519 * @param parent parent node
520 * @param value lexer token
521 *
522 * @return the new literal node
523 */
524 public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) {
525 return new LexerTokenLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
526 }
528 private static class NodeLiteralNode extends LiteralNode<Node> {
530 private NodeLiteralNode(final Source source, final long token, final int finish) {
531 this(source, token, finish, null);
532 }
534 private NodeLiteralNode(final Source source, final long token, final int finish, final Node value) {
535 super(source, Token.recast(token, TokenType.OBJECT), finish, value);
536 }
538 private NodeLiteralNode(final LiteralNode<Node> literalNode) {
539 super(literalNode);
540 }
542 @Override
543 protected Node copy(final CopyState cs) {
544 return new NodeLiteralNode(this);
545 }
547 @Override
548 public Node accept(final NodeVisitor visitor) {
549 if (visitor.enter(this) != null) {
550 if (value != null) {
551 value = value.accept(visitor);
552 }
553 return visitor.leave(this);
554 }
556 return this;
557 }
559 @Override
560 public Type getType() {
561 return value == null ? Type.OBJECT : super.getType();
562 }
564 @Override
565 public Type getWidestOperationType() {
566 return value == null ? Type.OBJECT : value.getWidestOperationType();
567 }
569 }
570 /**
571 * Create a new node literal for an arbitrary node
572 *
573 * @param source the source
574 * @param token token
575 * @param finish finish
576 * @param value the literal value node
577 *
578 * @return the new literal node
579 */
580 public static LiteralNode<Node> newInstance(final Source source, final long token, final int finish, final Node value) {
581 return new NodeLiteralNode(source, token, finish, value);
582 }
584 /**
585 * Create a new node literal based on a parent node (source, token, finish)
586 *
587 * @param parent parent node
588 * @param value node value
589 *
590 * @return the new literal node
591 */
592 public static LiteralNode<?> newInstance(final Node parent, final Node value) {
593 return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
594 }
596 /**
597 * Array literal node class.
598 */
599 public static class ArrayLiteralNode extends LiteralNode<Node[]> {
600 private static class PostsetMarker {
601 //empty
602 }
604 private static PostsetMarker POSTSET_MARKER = new PostsetMarker();
606 /** Array element type. */
607 private Type elementType;
609 /** Preset constant array. */
610 private Object presets;
612 /** Indices of array elements requiring computed post sets. */
613 private int[] postsets;
615 private List<ArrayUnit> units;
617 /**
618 * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
619 * be split if they are too large, for bytecode generation reasons
620 */
621 public static class ArrayUnit {
622 /** Compile unit associated with the postsets range. */
623 private final CompileUnit compileUnit;
625 /** postsets range associated with the unit (hi not inclusive). */
626 private final int lo, hi;
628 /**
629 * Constructor
630 * @param compileUnit compile unit
631 * @param lo lowest array index in unit
632 * @param hi highest array index in unit + 1
633 */
634 public ArrayUnit(final CompileUnit compileUnit, final int lo, final int hi) {
635 this.compileUnit = compileUnit;
636 this.lo = lo;
637 this.hi = hi;
638 }
640 /**
641 * Get the high index position of the ArrayUnit (non inclusive)
642 * @return high index position
643 */
644 public int getHi() {
645 return hi;
646 }
648 /**
649 * Get the low index position of the ArrayUnit (inclusive)
650 * @return low index position
651 */
652 public int getLo() {
653 return lo;
654 }
656 /**
657 * The array compile unit
658 * @return array compile unit
659 */
660 public CompileUnit getCompileUnit() {
661 return compileUnit;
662 }
663 }
665 /**
666 * Constructor
667 *
668 * @param source the source
669 * @param token token
670 * @param finish finish
671 * @param value array literal value, a Node array
672 */
673 protected ArrayLiteralNode(final Source source, final long token, final int finish, final Node[] value) {
674 super(source, Token.recast(token, TokenType.ARRAY), finish, value);
675 this.elementType = Type.UNKNOWN;
676 }
678 /**
679 * Copy constructor
680 * @param node source array literal node
681 */
682 protected ArrayLiteralNode(final ArrayLiteralNode node) {
683 super(node);
684 this.elementType = node.elementType;
685 }
687 @Override
688 protected Node copy(final CopyState cs) {
689 return new ArrayLiteralNode(this);
690 }
692 /**
693 * Compute things like widest element type needed. Internal use from compiler only
694 */
695 public void analyze() {
696 elementType = Type.INT;
697 analyzeElements();
699 if (elementType == Type.INT) {
700 presetIntArray();
701 } else if (elementType.isNumeric()) {
702 presetNumberArray();
703 } else {
704 presetObjectArray();
705 }
706 }
708 private void presetIntArray() {
709 final int[] array = new int[value.length];
710 final int[] computed = new int[value.length];
711 int nComputed = 0;
713 for (int i = 0; i < value.length; i++) {
714 final Object element = objectAsConstant(value[i]);
716 if (element instanceof Number) {
717 array[i] = ((Number)element).intValue();
718 } else {
719 computed[nComputed++] = i;
720 }
721 }
723 presets = array;
724 postsets = Arrays.copyOf(computed, nComputed);
725 }
727 private void presetNumberArray() {
728 final double[] array = new double[value.length];
729 final int[] computed = new int[value.length];
730 int nComputed = 0;
732 for (int i = 0; i < value.length; i++) {
733 final Object element = objectAsConstant(value[i]);
735 if (element instanceof Number) {
736 array[i] = ((Number)element).doubleValue();
737 } else {
738 computed[nComputed++] = i;
739 }
740 }
742 presets = array;
743 postsets = Arrays.copyOf(computed, nComputed);
744 }
746 private void presetObjectArray() {
747 final Object[] array = new Object[value.length];
748 final int[] computed = new int[value.length];
749 int nComputed = 0;
751 for (int i = 0; i < value.length; i++) {
752 final Node node = value[i];
754 if (node == null) {
755 computed[nComputed++] = i;
756 } else {
757 final Object element = objectAsConstant(node);
759 if (element != POSTSET_MARKER) {
760 array[i] = element;
761 } else {
762 computed[nComputed++] = i;
763 }
764 }
765 }
767 presets = array;
768 postsets = Arrays.copyOf(computed, nComputed);
769 }
771 private void analyzeElements() {
772 for (final Node node : value) {
773 if (node == null) {
774 elementType = elementType.widest(Type.OBJECT); //no way to represent undefined as number
775 break;
776 }
778 final Symbol symbol = node.getSymbol();
779 assert symbol != null; //don't run this on unresolved nodes or you are in trouble
780 Type symbolType = symbol.getSymbolType();
781 if (symbolType.isUnknown()) {
782 symbolType = Type.OBJECT;
783 }
785 if (symbolType.isBoolean()) {
786 elementType = elementType.widest(Type.OBJECT);
787 break;
788 }
790 elementType = elementType.widest(symbolType);
792 if (elementType.isObject()) {
793 break;
794 }
795 }
796 }
798 private Object objectAsConstant(final Object object) {
799 if (object == null) {
800 return null;
801 } else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
802 return object;
803 } else if (object instanceof LiteralNode) {
804 return objectAsConstant(((LiteralNode<?>)object).getValue());
805 } else if (object instanceof UnaryNode) {
806 final UnaryNode unaryNode = (UnaryNode)object;
808 if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) {
809 return objectAsConstant(unaryNode.rhs());
810 }
811 }
813 return POSTSET_MARKER;
814 }
816 @Override
817 public Node[] getArray() {
818 return value;
819 }
821 @Override
822 public Type getType() {
823 if (elementType.isInteger()) {
824 return Type.INT_ARRAY;
825 } else if (elementType.isNumeric()) {
826 return Type.NUMBER_ARRAY;
827 } else {
828 return Type.OBJECT_ARRAY;
829 }
830 }
832 /**
833 * Get the element type of this array literal
834 * @return element type
835 */
836 public Type getElementType() {
837 return elementType;
838 }
840 /**
841 * Get indices of arrays containing computed post sets
842 * @return post set indices
843 */
844 public int[] getPostsets() {
845 return postsets;
846 }
848 /**
849 * Get presets constant array
850 * @return presets array, always returns an array type
851 */
852 public Object getPresets() {
853 return presets;
854 }
856 /**
857 * Get the array units that make up this ArrayLiteral
858 * @see ArrayUnit
859 * @return list of array units
860 */
861 public List<ArrayUnit> getUnits() {
862 return units == null ? null : Collections.unmodifiableList(units);
863 }
865 /**
866 * Set the ArrayUnits that make up this ArrayLiteral
867 * @see ArrayUnit
868 * @param units list of array units
869 */
870 public void setUnits(final List<ArrayUnit> units) {
871 this.units = units;
872 }
874 @Override
875 public Node accept(final NodeVisitor visitor) {
876 if (visitor.enter(this) != null) {
877 for (int i = 0; i < value.length; i++) {
878 final Node element = value[i];
879 if (element != null) {
880 value[i] = element.accept(visitor);
881 }
882 }
883 return visitor.leave(this);
884 }
885 return this;
886 }
888 @Override
889 public void toString(final StringBuilder sb) {
890 sb.append('[');
891 boolean first = true;
892 for (final Node node : value) {
893 if (!first) {
894 sb.append(',');
895 sb.append(' ');
896 }
897 if (node == null) {
898 sb.append("undefined");
899 } else {
900 node.toString(sb);
901 }
902 first = false;
903 }
904 sb.append(']');
905 }
906 }
908 /**
909 * Create a new array literal of Nodes from a list of Node values
910 *
911 * @param source the source
912 * @param token token
913 * @param finish finish
914 * @param value literal value list
915 *
916 * @return the new literal node
917 */
918 public static LiteralNode<Node[]> newInstance(final Source source, final long token, final int finish, final List<Node> value) {
919 return new ArrayLiteralNode(source, token, finish, value.toArray(new Node[value.size()]));
920 }
923 /**
924 * Create a new array literal based on a parent node (source, token, finish)
925 *
926 * @param parent parent node
927 * @param value literal value list
928 *
929 * @return the new literal node
930 */
931 public static LiteralNode<?> newInstance(final Node parent, final List<Node> value) {
932 return new ArrayLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value.toArray(new Node[value.size()]));
933 }
935 /**
936 * Create a new array literal of Nodes
937 *
938 * @param source the source
939 * @param token token
940 * @param finish finish
941 * @param value literal value array
942 *
943 * @return the new literal node
944 */
945 public static LiteralNode<Node[]> newInstance(final Source source, final long token, final int finish, final Node[] value) {
946 return new ArrayLiteralNode(source, token, finish, value);
947 }
948 }