src/jdk/nashorn/internal/ir/LiteralNode.java

Wed, 30 Jan 2013 12:26:45 +0100

author
lagergren
date
Wed, 30 Jan 2013 12:26:45 +0100
changeset 57
59970b70ebb5
parent 7
5a1b0714df0e
child 71
f05d4dae30f7
permissions
-rw-r--r--

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 }

mercurial