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 Type getType() {
 498             return Type.OBJECT;
 499         }
 500 
 501         @Override
 502         public void toString(final StringBuilder sb) {
 503             sb.append(value.toString());
 504         }
 505     }
 506 
 507     /**
 508      * Create a new literal node for a lexer token
 509      *
 510      * @param source  the source
 511      * @param token   token
 512      * @param finish  finish
 513      * @param value   lexer token value
 514      *
 515      * @return the new literal node
 516      */
 517     public static LiteralNode<LexerToken> newInstance(final Source source, final long token, final int finish, final LexerToken value) {
 518         return new LexerTokenLiteralNode(source, token, finish, value);
 519     }
 520 
 521     /**
 522      * Create a new lexer token literal based on a parent node (source, token, finish)
 523      *
 524      * @param parent parent node
 525      * @param value  lexer token
 526      *
 527      * @return the new literal node
 528      */
 529     public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) {
 530         return new LexerTokenLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
 531     }
 532 
 533     private static class NodeLiteralNode extends LiteralNode<Node> {
 534 
 535         private NodeLiteralNode(final Source source, final long token, final int finish) {
 536             this(source, token, finish, null);
 537         }
 538 
 539         private NodeLiteralNode(final Source source, final long token, final int finish, final Node value) {
 540             super(source, Token.recast(token, TokenType.OBJECT), finish, value);
 541         }
 542 
 543         private NodeLiteralNode(final LiteralNode<Node> literalNode) {
 544             super(literalNode);
 545         }
 546 
 547         @Override
 548         protected Node copy(final CopyState cs) {
 549             return new NodeLiteralNode(this);
 550         }
 551 
 552         @Override
 553         public Node accept(final NodeVisitor visitor) {
 554             if (visitor.enter(this) != null) {
 555                 if (value != null) {
 556                     value = value.accept(visitor);
 557                 }
 558                 return visitor.leave(this);
 559             }
 560 
 561             return this;
 562         }
 563 
 564         @Override
 565         public Type getType() {
 566             return value == null ? Type.OBJECT : super.getType();
 567         }
 568 
 569         @Override
 570         public Type getWidestOperationType() {
 571             return value == null ? Type.OBJECT : value.getWidestOperationType();
 572         }
 573 
 574     }
 575     /**
 576      * Create a new node literal for an arbitrary node
 577      *
 578      * @param source  the source
 579      * @param token   token
 580      * @param finish  finish
 581      * @param value   the literal value node
 582      *
 583      * @return the new literal node
 584      */
 585     public static LiteralNode<Node> newInstance(final Source source, final long token, final int finish, final Node value) {
 586         return new NodeLiteralNode(source, token, finish, value);
 587     }
 588 
 589     /**
 590      * Create a new node literal based on a parent node (source, token, finish)
 591      *
 592      * @param parent parent node
 593      * @param value  node value
 594      *
 595      * @return the new literal node
 596      */
 597     public static LiteralNode<?> newInstance(final Node parent, final Node value) {
 598         return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
 599     }
 600 
 601     /**
 602      * Array literal node class.
 603      */
 604     public static class ArrayLiteralNode extends LiteralNode<Node[]> {
 605         private static class PostsetMarker {
 606             //empty
 607         }
 608 
 609         private static PostsetMarker POSTSET_MARKER = new PostsetMarker();
 610 
 611         /** Array element type. */
 612         private Type elementType;
 613 
 614         /** Preset constant array. */
 615         private Object presets;
 616 
 617         /** Indices of array elements requiring computed post sets. */
 618         private int[] postsets;
 619 
 620         private List<ArrayUnit> units;
 621 
 622         /**
 623          * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
 624          * be split if they are too large, for bytecode generation reasons
 625          */
 626         public static class ArrayUnit {
 627             /** Compile unit associated with the postsets range. */
 628             private final CompileUnit compileUnit;
 629 
 630             /** postsets range associated with the unit (hi not inclusive). */
 631             private final int lo, hi;
 632 
 633             /**
 634              * Constructor
 635              * @param compileUnit compile unit
 636              * @param lo lowest array index in unit
 637              * @param hi highest array index in unit + 1
 638              */
 639             public ArrayUnit(final CompileUnit compileUnit, final int lo, final int hi) {
 640                 this.compileUnit = compileUnit;
 641                 this.lo   = lo;
 642                 this.hi   = hi;
 643             }
 644 
 645             /**
 646              * Get the high index position of the ArrayUnit (non inclusive)
 647              * @return high index position
 648              */
 649             public int getHi() {
 650                 return hi;
 651             }
 652 
 653             /**
 654              * Get the low index position of the ArrayUnit (inclusive)
 655              * @return low index position
 656              */
 657             public int getLo() {
 658                 return lo;
 659             }
 660 
 661             /**
 662              * The array compile unit
 663              * @return array compile unit
 664              */
 665             public CompileUnit getCompileUnit() {
 666                 return compileUnit;
 667             }
 668         }
 669 
 670         /**
 671          * Constructor
 672          *
 673          * @param source  the source
 674          * @param token   token
 675          * @param finish  finish
 676          * @param value   array literal value, a Node array
 677          */
 678         protected ArrayLiteralNode(final Source source, final long token, final int finish, final Node[] value) {
 679             super(source, Token.recast(token, TokenType.ARRAY), finish, value);
 680             this.elementType = Type.UNKNOWN;
 681         }
 682 
 683         /**
 684          * Copy constructor
 685          * @param node source array literal node
 686          */
 687         protected ArrayLiteralNode(final ArrayLiteralNode node) {
 688             super(node);
 689             this.elementType = node.elementType;
 690         }
 691 
 692         @Override
 693         protected Node copy(final CopyState cs) {
 694             return new ArrayLiteralNode(this);
 695         }
 696 
 697         /**
 698          * Compute things like widest element type needed. Internal use from compiler only
 699          */
 700         public void analyze() {
 701             elementType = Type.INT;
 702             analyzeElements();
 703 
 704             if (elementType == Type.INT) {
 705                 presetIntArray();
 706             } else if (elementType.isNumeric()) {
 707                 presetNumberArray();
 708             } else {
 709                 presetObjectArray();
 710             }
 711         }
 712 
 713         private void presetIntArray() {
 714             final int[] array = new int[value.length];
 715             final int[] computed = new int[value.length];
 716             int nComputed = 0;
 717 
 718             for (int i = 0; i < value.length; i++) {
 719                 final Object element = objectAsConstant(value[i]);
 720 
 721                 if (element instanceof Number) {
 722                     array[i] = ((Number)element).intValue();
 723                 } else {
 724                     computed[nComputed++] = i;
 725                 }
 726             }
 727 
 728             presets = array;
 729             postsets = Arrays.copyOf(computed, nComputed);
 730         }
 731 
 732         private void presetNumberArray() {
 733             final double[] array = new double[value.length];
 734             final int[] computed = new int[value.length];
 735             int nComputed = 0;
 736 
 737             for (int i = 0; i < value.length; i++) {
 738                 final Object element = objectAsConstant(value[i]);
 739 
 740                 if (element instanceof Number) {
 741                     array[i] = ((Number)element).doubleValue();
 742                 } else {
 743                     computed[nComputed++] = i;
 744                 }
 745             }
 746 
 747             presets = array;
 748             postsets = Arrays.copyOf(computed, nComputed);
 749         }
 750 
 751         private void presetObjectArray() {
 752             final Object[] array = new Object[value.length];
 753             final int[] computed = new int[value.length];
 754             int nComputed = 0;
 755 
 756             for (int i = 0; i < value.length; i++) {
 757                 final Node node = value[i];
 758 
 759                 if (node == null) {
 760                     computed[nComputed++] = i;
 761                 } else {
 762                     final Object element = objectAsConstant(node);
 763 
 764                     if (element != POSTSET_MARKER) {
 765                         array[i] = element;
 766                     } else {
 767                         computed[nComputed++] = i;
 768                     }
 769                 }
 770             }
 771 
 772             presets = array;
 773             postsets = Arrays.copyOf(computed, nComputed);
 774         }
 775 
 776         private void analyzeElements() {
 777             for (final Node node : value) {
 778                 if (node == null) {
 779                     elementType = elementType.widest(Type.OBJECT); //no way to represent undefined as number
 780                     break;
 781                 }
 782 
 783                 final Symbol symbol = node.getSymbol();
 784                 assert symbol != null; //don't run this on unresolved nodes or you are in trouble
 785                 Type symbolType = symbol.getSymbolType();
 786                 if (symbolType.isUnknown()) {
 787                     symbolType = Type.OBJECT;
 788                 }
 789 
 790                 if (symbolType.isBoolean()) {
 791                     elementType = elementType.widest(Type.OBJECT);
 792                     break;
 793                 }
 794 
 795                 elementType = elementType.widest(symbolType);
 796 
 797                 if (elementType.isObject()) {
 798                     break;
 799                 }
 800             }
 801         }
 802 
 803         private Object objectAsConstant(final Object object) {
 804             if (object == null) {
 805                 return null;
 806             } else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
 807                 return object;
 808             } else if (object instanceof LiteralNode) {
 809                 return objectAsConstant(((LiteralNode<?>)object).getValue());
 810             } else if (object instanceof UnaryNode) {
 811                 final UnaryNode unaryNode = (UnaryNode)object;
 812 
 813                 if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) {
 814                     return objectAsConstant(unaryNode.rhs());
 815                 }
 816             }
 817 
 818             return POSTSET_MARKER;
 819         }
 820 
 821         @Override
 822         public Node[] getArray() {
 823             return value;
 824         }
 825 
 826         @Override
 827         public Type getType() {
 828             if (elementType.isInteger()) {
 829                 return Type.INT_ARRAY;
 830             } else if (elementType.isNumeric()) {
 831                 return Type.NUMBER_ARRAY;
 832             } else {
 833                 return Type.OBJECT_ARRAY;
 834             }
 835         }
 836 
 837         /**
 838          * Get the element type of this array literal
 839          * @return element type
 840          */
 841         public Type getElementType() {
 842             return elementType;
 843         }
 844 
 845         /**
 846          * Get indices of arrays containing computed post sets
 847          * @return post set indices
 848          */
 849         public int[] getPostsets() {
 850             return postsets;
 851         }
 852 
 853         /**
 854          * Get presets constant array
 855          * @return presets array, always returns an array type
 856          */
 857         public Object getPresets() {
 858             return presets;
 859         }
 860 
 861         /**
 862          * Get the array units that make up this ArrayLiteral
 863          * @see ArrayUnit
 864          * @return list of array units
 865          */
 866         public List<ArrayUnit> getUnits() {
 867             return units == null ? null : Collections.unmodifiableList(units);
 868         }
 869 
 870         /**
 871          * Set the ArrayUnits that make up this ArrayLiteral
 872          * @see ArrayUnit
 873          * @param units list of array units
 874          */
 875         public void setUnits(final List<ArrayUnit> units) {
 876             this.units = units;
 877         }
 878 
 879         @Override
 880         public Node accept(final NodeVisitor visitor) {
 881             if (visitor.enter(this) != null) {
 882                 for (int i = 0; i < value.length; i++) {
 883                     final Node element = value[i];
 884                     if (element != null) {
 885                         value[i] = element.accept(visitor);
 886                     }
 887                 }
 888                 return visitor.leave(this);
 889             }
 890             return this;
 891         }
 892 
 893         @Override
 894         public void toString(final StringBuilder sb) {
 895             sb.append('[');
 896             boolean first = true;
 897             for (final Node node : value) {
 898                 if (!first) {
 899                     sb.append(',');
 900                     sb.append(' ');
 901                 }
 902                 if (node == null) {
 903                     sb.append("undefined");
 904                 } else {
 905                     node.toString(sb);
 906                 }
 907                 first = false;
 908             }
 909             sb.append(']');
 910         }
 911     }
 912 
 913     /**
 914      * Create a new array literal of Nodes from a list of Node values
 915      *
 916      * @param source  the source
 917      * @param token   token
 918      * @param finish  finish
 919      * @param value   literal value list
 920      *
 921      * @return the new literal node
 922      */
 923     public static LiteralNode<Node[]> newInstance(final Source source, final long token, final int finish, final List<Node> value) {
 924         return new ArrayLiteralNode(source, token, finish, value.toArray(new Node[value.size()]));
 925     }
 926 
 927 
 928     /**
 929      * Create a new array literal based on a parent node (source, token, finish)
 930      *
 931      * @param parent parent node
 932      * @param value  literal value list
 933      *
 934      * @return the new literal node
 935      */
 936     public static LiteralNode<?> newInstance(final Node parent, final List<Node> value) {
 937         return new ArrayLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value.toArray(new Node[value.size()]));
 938     }
 939 
 940     /**
 941      * Create a new array literal of Nodes
 942      *
 943      * @param source  the source
 944      * @param token   token
 945      * @param finish  finish
 946      * @param value   literal value array
 947      *
 948      * @return the new literal node
 949      */
 950     public static LiteralNode<Node[]> newInstance(final Source source, final long token, final int finish, final Node[] value) {
 951         return new ArrayLiteralNode(source, token, finish, value);
 952     }
 953 }
--- EOF ---