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.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;
  41 
  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;
  50 
  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     }
  63 
  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     }
  73 
  74     @Override
  75     public boolean isAtom() {
  76         return true;
  77     }
  78 
  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     }
  86 
  87     @Override
  88     public int hashCode() {
  89         return value == null ? 0 : value.hashCode();
  90     }
  91 
  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     }
 103 
 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     }
 111 
 112     @Override
 113     public Type getType() {
 114         return Type.typeFor(value.getClass());
 115     }
 116 
 117     @Override
 118     public String getPropertyName() {
 119         return JSType.toString(getObject());
 120     }
 121 
 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     }
 130 
 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     }
 139 
 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     }
 148 
 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     }
 157 
 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     }
 166 
 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     }
 176 
 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     }
 185 
 186     /**
 187      * Fetch Object value of node.
 188      *
 189      * @return Object value of node.
 190      */
 191     public Object getObject() {
 192         return value;
 193     }
 194 
 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     }
 203 
 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     }
 212 
 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         }
 223 
 224         return this;
 225     }
 226 
 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     }
 235 
 236     /**
 237      * Get the literal node value
 238      * @return the value
 239      */
 240     public T getValue() {
 241         return value;
 242     }
 243 
 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     }
 256 
 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     }
 267 
 268     private static class BooleanLiteralNode extends LiteralNode<Boolean> {
 269 
 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         }
 273 
 274         private BooleanLiteralNode(final BooleanLiteralNode literalNode) {
 275             super(literalNode);
 276         }
 277 
 278         @Override
 279         protected Node copy(final CopyState cs) {
 280             return new BooleanLiteralNode(this);
 281         }
 282 
 283         @Override
 284         public boolean isTrue() {
 285             return value;
 286         }
 287 
 288         @Override
 289         public Type getType() {
 290             return Type.BOOLEAN;
 291         }
 292 
 293         @Override
 294         public Type getWidestOperationType() {
 295             return Type.BOOLEAN;
 296         }
 297     }
 298 
 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     }
 312 
 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     }
 324 
 325     private static class NumberLiteralNode extends LiteralNode<Number> {
 326 
 327         private final Type type = numberGetType(value);
 328 
 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         }
 332 
 333         private NumberLiteralNode(final NumberLiteralNode literalNode) {
 334             super(literalNode);
 335         }
 336 
 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             }
 347 
 348             return null;
 349         }
 350 
 351         @Override
 352         protected Node copy(final CopyState cs) {
 353             return new NumberLiteralNode(this);
 354         }
 355 
 356         @Override
 357         public Type getType() {
 358             return type;
 359         }
 360 
 361         @Override
 362         public Type getWidestOperationType() {
 363             return getType();
 364         }
 365 
 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     }
 380 
 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     }
 392 
 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         }
 397 
 398         private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
 399             super(literalNode);
 400         }
 401 
 402         @Override
 403         protected Node copy(final CopyState cs) {
 404             return new UndefinedLiteralNode(this);
 405         }
 406     }
 407 
 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     }
 421 
 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     }
 433 
 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         }
 438 
 439         private StringLiteralNode(final StringLiteralNode literalNode) {
 440             super(literalNode);
 441         }
 442 
 443         @Override
 444         protected Node copy(final CopyState cs) {
 445             return new StringLiteralNode(this);
 446         }
 447 
 448         @Override
 449         public void toString(final StringBuilder sb) {
 450             sb.append('\"');
 451             sb.append(value);
 452             sb.append('\"');
 453         }
 454     }
 455 
 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     }
 469 
 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     }
 481 
 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         }
 486 
 487         private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) {
 488             super(literalNode);
 489         }
 490 
 491         @Override
 492         protected Node copy(final CopyState cs) {
 493             return new LexerTokenLiteralNode(this);
 494         }
 495 
 496         @Override





 497         public void toString(final StringBuilder sb) {
 498             sb.append(value.toString());
 499         }
 500     }
 501 
 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     }
 515 
 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     }
 527 
 528     private static class NodeLiteralNode extends LiteralNode<Node> {
 529 
 530         private NodeLiteralNode(final Source source, final long token, final int finish) {
 531             this(source, token, finish, null);
 532         }
 533 
 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         }
 537 
 538         private NodeLiteralNode(final LiteralNode<Node> literalNode) {
 539             super(literalNode);
 540         }
 541 
 542         @Override
 543         protected Node copy(final CopyState cs) {
 544             return new NodeLiteralNode(this);
 545         }
 546 
 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             }
 555 
 556             return this;
 557         }
 558 
 559         @Override
 560         public Type getType() {
 561             return value == null ? Type.OBJECT : super.getType();
 562         }
 563 
 564         @Override
 565         public Type getWidestOperationType() {
 566             return value == null ? Type.OBJECT : value.getWidestOperationType();
 567         }
 568 
 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     }
 583 
 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     }
 595 
 596     /**
 597      * Array literal node class.
 598      */
 599     public static class ArrayLiteralNode extends LiteralNode<Node[]> {
 600         private static class PostsetMarker {
 601             //empty
 602         }
 603 
 604         private static PostsetMarker POSTSET_MARKER = new PostsetMarker();
 605 
 606         /** Array element type. */
 607         private Type elementType;
 608 
 609         /** Preset constant array. */
 610         private Object presets;
 611 
 612         /** Indices of array elements requiring computed post sets. */
 613         private int[] postsets;
 614 
 615         private List<ArrayUnit> units;
 616 
 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;
 624 
 625             /** postsets range associated with the unit (hi not inclusive). */
 626             private final int lo, hi;
 627 
 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             }
 639 
 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             }
 647 
 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             }
 655 
 656             /**
 657              * The array compile unit
 658              * @return array compile unit
 659              */
 660             public CompileUnit getCompileUnit() {
 661                 return compileUnit;
 662             }
 663         }
 664 
 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         }
 677 
 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         }
 686 
 687         @Override
 688         protected Node copy(final CopyState cs) {
 689             return new ArrayLiteralNode(this);
 690         }
 691 
 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();
 698 
 699             if (elementType == Type.INT) {
 700                 presetIntArray();
 701             } else if (elementType.isNumeric()) {
 702                 presetNumberArray();
 703             } else {
 704                 presetObjectArray();
 705             }
 706         }
 707 
 708         private void presetIntArray() {
 709             final int[] array = new int[value.length];
 710             final int[] computed = new int[value.length];
 711             int nComputed = 0;
 712 
 713             for (int i = 0; i < value.length; i++) {
 714                 final Object element = objectAsConstant(value[i]);
 715 
 716                 if (element instanceof Number) {
 717                     array[i] = ((Number)element).intValue();
 718                 } else {
 719                     computed[nComputed++] = i;
 720                 }
 721             }
 722 
 723             presets = array;
 724             postsets = Arrays.copyOf(computed, nComputed);
 725         }
 726 
 727         private void presetNumberArray() {
 728             final double[] array = new double[value.length];
 729             final int[] computed = new int[value.length];
 730             int nComputed = 0;
 731 
 732             for (int i = 0; i < value.length; i++) {
 733                 final Object element = objectAsConstant(value[i]);
 734 
 735                 if (element instanceof Number) {
 736                     array[i] = ((Number)element).doubleValue();
 737                 } else {
 738                     computed[nComputed++] = i;
 739                 }
 740             }
 741 
 742             presets = array;
 743             postsets = Arrays.copyOf(computed, nComputed);
 744         }
 745 
 746         private void presetObjectArray() {
 747             final Object[] array = new Object[value.length];
 748             final int[] computed = new int[value.length];
 749             int nComputed = 0;
 750 
 751             for (int i = 0; i < value.length; i++) {
 752                 final Node node = value[i];
 753 
 754                 if (node == null) {
 755                     computed[nComputed++] = i;
 756                 } else {
 757                     final Object element = objectAsConstant(node);
 758 
 759                     if (element != POSTSET_MARKER) {
 760                         array[i] = element;
 761                     } else {
 762                         computed[nComputed++] = i;
 763                     }
 764                 }
 765             }
 766 
 767             presets = array;
 768             postsets = Arrays.copyOf(computed, nComputed);
 769         }
 770 
 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                 }
 777 
 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                 }
 784 
 785                 if (symbolType.isBoolean()) {
 786                     elementType = elementType.widest(Type.OBJECT);
 787                     break;
 788                 }
 789 
 790                 elementType = elementType.widest(symbolType);
 791 
 792                 if (elementType.isObject()) {
 793                     break;
 794                 }
 795             }
 796         }
 797 
 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;
 807 
 808                 if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) {
 809                     return objectAsConstant(unaryNode.rhs());
 810                 }
 811             }
 812 
 813             return POSTSET_MARKER;
 814         }
 815 
 816         @Override
 817         public Node[] getArray() {
 818             return value;
 819         }
 820 
 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         }
 831 
 832         /**
 833          * Get the element type of this array literal
 834          * @return element type
 835          */
 836         public Type getElementType() {
 837             return elementType;
 838         }
 839 
 840         /**
 841          * Get indices of arrays containing computed post sets
 842          * @return post set indices
 843          */
 844         public int[] getPostsets() {
 845             return postsets;
 846         }
 847 
 848         /**
 849          * Get presets constant array
 850          * @return presets array, always returns an array type
 851          */
 852         public Object getPresets() {
 853             return presets;
 854         }
 855 
 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         }
 864 
 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         }
 873 
 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         }
 887 
 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     }
 907 
 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     }
 921 
 922 
 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     }
 934 
 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 }
--- EOF ---