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

Sat, 23 Mar 2013 00:58:39 +0100

author
attila
date
Sat, 23 Mar 2013 00:58:39 +0100
changeset 144
4be452026847
parent 139
390d44ba90cf
child 211
3a209cbd1d8f
permissions
-rw-r--r--

8010652: Eliminate non-child references in Block/FunctionNode, and make few node types immutable
Reviewed-by: jlaskey, lagergren

     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 static jdk.nashorn.internal.codegen.CompilerConstants.LITERAL_PREFIX;
    29 import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
    30 import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
    31 import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
    33 import java.util.ArrayList;
    34 import java.util.Collections;
    35 import java.util.EnumSet;
    36 import java.util.HashMap;
    37 import java.util.Iterator;
    38 import java.util.List;
    39 import java.util.Map;
    40 import java.util.Stack;
    41 import jdk.nashorn.internal.codegen.CompileUnit;
    42 import jdk.nashorn.internal.codegen.Compiler;
    43 import jdk.nashorn.internal.codegen.Frame;
    44 import jdk.nashorn.internal.codegen.MethodEmitter;
    45 import jdk.nashorn.internal.codegen.Namespace;
    46 import jdk.nashorn.internal.codegen.types.Type;
    47 import jdk.nashorn.internal.ir.annotations.Ignore;
    48 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    49 import jdk.nashorn.internal.parser.Parser;
    50 import jdk.nashorn.internal.runtime.ScriptFunction;
    51 import jdk.nashorn.internal.runtime.Source;
    52 import jdk.nashorn.internal.runtime.UserAccessorProperty;
    53 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
    55 /**
    56  * IR representation for function (or script.)
    57  */
    58 public class FunctionNode extends Block {
    60     private static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
    62     /** Function kinds */
    63     public enum Kind {
    64         /** a normal function - nothing special */
    65         NORMAL,
    66         /** a script function */
    67         SCRIPT,
    68         /** a getter, @see {@link UserAccessorProperty} */
    69         GETTER,
    70         /** a setter, @see {@link UserAccessorProperty} */
    71         SETTER
    72     }
    74     /** Compilation states available */
    75     public enum CompilationState {
    76         /** compiler is ready */
    77         INITIALIZED,
    78         /** method has been parsed */
    79         PARSED,
    80         /** method has been parsed */
    81         PARSE_ERROR,
    82         /** constant folding pass */
    83         CONSTANT_FOLDED,
    84         /** method has been lowered */
    85         LOWERED,
    86         /** method hass been attributed */
    87         ATTR,
    88         /** method has been split */
    89         SPLIT,
    90         /** method has had its types finalized */
    91         FINALIZED,
    92         /** method has been emitted to bytecode */
    93         EMITTED,
    94         /** code installed in a class loader */
    95         INSTALLED
    96     }
    98     /** External function identifier. */
    99     @Ignore
   100     private IdentNode ident;
   102     /** Internal function name. */
   103     private String name;
   105     /** Compilation unit. */
   106     private CompileUnit compileUnit;
   108     /** Method emitter for current method. */
   109     private MethodEmitter method;
   111     /** Function kind. */
   112     private Kind kind;
   114     /** List of parameters. */
   115     private List<IdentNode> parameters;
   117     /** First token of function. **/
   118     private long firstToken;
   120     /** Last token of function. **/
   121     private long lastToken;
   123     /** Variable frames. */
   124     private Frame frames;
   126     /** Method's namespace. */
   127     private final Namespace namespace;
   129     /** Node representing current this. */
   130     @Ignore
   131     private IdentNode thisNode;
   133     /** Node representing current scope. */
   134     @Ignore
   135     private IdentNode scopeNode;
   137     /** Node representing return value. */
   138     @Ignore
   139     private IdentNode resultNode;
   141     /** Node representing current arguments. */
   142     @Ignore
   143     private IdentNode argumentsNode;
   145     /** Node representing callee */
   146     @Ignore
   147     private IdentNode calleeNode;
   149     /** Node representing varargs */
   150     @Ignore
   151     private IdentNode varArgsNode;
   153     /** Pending label list. */
   154     private final Stack<LabelNode> labelStack;
   156     /** Pending control list. */
   157     private final Stack<Node> controlStack;
   159     /** VarNode for this function statement */
   160     @Ignore //this is explicit code anyway and should not be traversed after lower
   161     private VarNode funcVarNode;
   163     /** Line number for function declaration */
   164     @Ignore
   165     private LineNumberNode funcVarLineNumberNode;
   167     /** Initializer var func = __callee__, where applicable */
   168     @Ignore
   169     private Node selfSymbolInit;
   171     /** Current compilation state */
   172     @Ignore
   173     private final EnumSet<CompilationState> compilationState;
   175     /** Type hints, e.g based on parameters at call site */
   176     private final Map<IdentNode, Type> specializedTypes;
   178     /** Function flags. */
   179     private int flags;
   181     /** Is anonymous function flag. */
   182     private static final int IS_ANONYMOUS                = 1 << 0;
   183     /** Is the function created in a function declaration (as opposed to a function expression) */
   184     private static final int IS_DECLARED                 = 1 << 1;
   185     /** is this a strict mode function? */
   186     private static final int IS_STRICT_MODE              = 1 << 2;
   187     /** Does the function use the "arguments" identifier ? */
   188     private static final int USES_ARGUMENTS              = 1 << 3;
   189     /** Are we lowered ? */
   190     private static final int IS_LOWERED                  = 1 << 4;
   191     /** Has this node been split because it was too large? */
   192     private static final int IS_SPLIT                    = 1 << 5;
   193     /** Does the function call eval? */
   194     private static final int HAS_EVAL                    = 1 << 6;
   195     /** Does the function contain a with block ? */
   196     private static final int HAS_WITH                    = 1 << 7;
   197     /** Does a descendant function contain a with or eval? */
   198     private static final int HAS_DESCENDANT_WITH_OR_EVAL = 1 << 8;
   199     /** Does the function define "arguments" identifier as a parameter of nested function name? */
   200     private static final int DEFINES_ARGUMENTS           = 1 << 9;
   201     /** Does the function need a self symbol? */
   202     private static final int NEEDS_SELF_SYMBOL           = 1 << 10;
   203     /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
   204     private static final int USES_ANCESTOR_SCOPE         = 1 << 11;
   205     /** Is this function lazily compiled? */
   206     private static final int IS_LAZY                     = 1 << 12;
   207     /** Does this function have lazy, yet uncompiled children */
   208     private static final int HAS_LAZY_CHILDREN           = 1 << 13;
   209     /** Does this function have lazy, yet uncompiled children */
   210     private static final int IS_PROGRAM                   = 1 << 14;
   212     /** Does this function or any nested functions contain a with or an eval? */
   213     private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL;
   214     /** Does this function need to store all its variables in scope? */
   215     private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
   216     /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
   217     private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
   218     /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval.
   219      *  We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
   220     private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL | HAS_LAZY_CHILDREN;
   222     /** What is the return type of this function? */
   223     private Type returnType = Type.UNKNOWN;
   225     /**
   226      * Constructor
   227      *
   228      * @param source    the source
   229      * @param token     token
   230      * @param finish    finish
   231      * @param namespace the namespace
   232      * @param ident     the identifier
   233      * @param name      the name of the function
   234      */
   235     public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final IdentNode ident, final String name) {
   236         super(source, token, finish);
   238         this.ident             = ident;
   239         this.name              = name;
   240         this.kind              = Kind.NORMAL;
   241         this.parameters        = new ArrayList<>();
   242         this.firstToken        = token;
   243         this.lastToken         = token;
   244         this.namespace         = namespace;
   245         this.labelStack        = new Stack<>();
   246         this.controlStack      = new Stack<>();
   247         this.compilationState  = EnumSet.of(CompilationState.INITIALIZED);
   248         this.specializedTypes  = new HashMap<>();
   249     }
   251     private FunctionNode(final FunctionNode functionNode, final CopyState cs) {
   252         super(functionNode, cs);
   254         this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident);
   255         this.name  = functionNode.name;
   256         this.kind  = functionNode.kind;
   258         this.parameters = new ArrayList<>();
   259         for (final IdentNode param : functionNode.getParameters()) {
   260             this.parameters.add((IdentNode)cs.existingOrCopy(param));
   261         }
   263         this.firstToken        = functionNode.firstToken;
   264         this.lastToken         = functionNode.lastToken;
   265         this.namespace         = functionNode.getNamespace();
   266         this.thisNode          = (IdentNode)cs.existingOrCopy(functionNode.thisNode);
   267         this.scopeNode         = (IdentNode)cs.existingOrCopy(functionNode.scopeNode);
   268         this.resultNode        = (IdentNode)cs.existingOrCopy(functionNode.resultNode);
   269         this.argumentsNode     = (IdentNode)cs.existingOrCopy(functionNode.argumentsNode);
   270         this.varArgsNode       = (IdentNode)cs.existingOrCopy(functionNode.varArgsNode);
   271         this.calleeNode        = (IdentNode)cs.existingOrCopy(functionNode.calleeNode);
   272         this.labelStack        = new Stack<>();
   273         this.controlStack      = new Stack<>();
   275         this.flags = functionNode.flags;
   277         this.funcVarNode = (VarNode)cs.existingOrCopy(functionNode.funcVarNode);
   278         /** VarNode for this function statement */
   280         this.compilationState = EnumSet.copyOf(functionNode.compilationState);
   281         this.specializedTypes = new HashMap<>();
   282     }
   284     @Override
   285     protected Node copy(final CopyState cs) {
   286         // deep clone all parent blocks
   287         return new FunctionNode(this, cs);
   288     }
   290     @Override
   291     public Node accept(final NodeVisitor visitor) {
   292         final FunctionNode  saveFunctionNode  = visitor.getCurrentFunctionNode();
   293         final Block         saveBlock         = visitor.getCurrentBlock();
   294         final MethodEmitter saveMethodEmitter = visitor.getCurrentMethodEmitter();
   295         final CompileUnit   saveCompileUnit   = visitor.getCurrentCompileUnit();
   297         visitor.setCurrentFunctionNode(this);
   298         visitor.setCurrentBlock(this);
   300         try {
   301             if (visitor.enterFunctionNode(this) != null) {
   302                 if (ident != null) {
   303                     ident = (IdentNode)ident.accept(visitor);
   304                 }
   306                 for (int i = 0, count = parameters.size(); i < count; i++) {
   307                     parameters.set(i, (IdentNode)parameters.get(i).accept(visitor));
   308                 }
   310                 for (int i = 0, count = statements.size(); i < count; i++) {
   311                     statements.set(i, statements.get(i).accept(visitor));
   312                 }
   314                 return visitor.leaveFunctionNode(this);
   315             }
   316         } finally {
   317             visitor.setCurrentBlock(saveBlock);
   318             visitor.setCurrentFunctionNode(saveFunctionNode);
   319             visitor.setCurrentCompileUnit(saveCompileUnit);
   320             visitor.setCurrentMethodEmitter(saveMethodEmitter);
   321         }
   323         return this;
   324     }
   326     @Override
   327     public boolean needsScope() {
   328         return super.needsScope() || isProgram();
   329     }
   331     /**
   332      * Check whether this FunctionNode has reached a give CompilationState.
   333      *
   334      * @param state the state to check for
   335      * @return true of the node is in the given state
   336      */
   337     public boolean hasState(final EnumSet<CompilationState> state) {
   338         return compilationState.equals(state);
   339     }
   341     /**
   342      * Check whether the state of this FunctionNode contains a given compilation
   343      * state.
   344      *
   345      * A node can be in many states at once, e.g. both lowered and initialized.
   346      * To check for an exact state, use {FunctionNode{@link #hasState(EnumSet)}
   347      *
   348      * @param state state to check for
   349      * @return true if state is present in the total compilation state of this FunctionNode
   350      */
   351     public boolean hasState(final CompilationState state) {
   352         return compilationState.contains(state);
   353     }
   355     /**
   356      * Add a state to the total CompilationState of this node, e.g. if
   357      * FunctionNode has been lowered, the compiler will add
   358      * {@code CompilationState#LOWERED} to the state vector
   359      *
   360      * @param state {@link CompilationState} to add
   361      */
   362     public void setState(final CompilationState state) {
   363         compilationState.add(state);
   364     }
   366     /*
   367      * Frame management.
   368      */
   370     /**
   371      * Push a new block frame.
   372      *
   373      * @return the new frame
   374      */
   375     public final Frame pushFrame() {
   376         frames = new Frame(frames);
   377         return frames;
   378     }
   380     /**
   381      * Pop a block frame.
   382      */
   383     public final void popFrame() {
   384         frames = frames.getPrevious();
   385     }
   387     /**
   388      * Create a temporary variable to the current frame.
   389      *
   390      * @param currentFrame Frame to add to - defaults to current function frame
   391      * @param type  Strong type of symbol.
   392      * @param node  Primary node to use symbol.
   393      *
   394      * @return Symbol used.
   395      */
   396     public Symbol newTemporary(final Frame currentFrame, final Type type, final Node node) {
   397         assert currentFrame != null;
   398         Symbol symbol = node.getSymbol();
   400         // If no symbol already present.
   401         if (symbol == null) {
   402             final String uname = uniqueName(TEMP_PREFIX.tag());
   403             symbol = new Symbol(uname, IS_TEMP, type);
   404             symbol.setNode(node);
   405         }
   407         // Assign a slot if it doesn't have one.
   408         if (!symbol.hasSlot()) {
   409             currentFrame.addSymbol(symbol);
   410         }
   412         // Set symbol to node.
   413         node.setSymbol(symbol);
   415         return symbol;
   416     }
   418     /**
   419      * Create a unique name in the namespace of this FunctionNode
   420      * @param base prefix for name
   421      * @return base if no collision exists, otherwise a name prefix with base
   422      */
   423     public String uniqueName(final String base) {
   424         return namespace.uniqueName(base);
   425     }
   427     /**
   428      * Add a new temporary variable to the current frame
   429      *
   430      * @param type Strong type of symbol
   431      * @param node Primary node to use symbol
   432      *
   433      * @return symbol used
   434      */
   435     public Symbol newTemporary(final Type type, final Node node) {
   436         return newTemporary(frames, type, node);
   437     }
   439     /**
   440      * Create a virtual symbol for a literal.
   441      *
   442      * @param literalNode Primary node to use symbol.
   443      *
   444      * @return Symbol used.
   445      */
   446     public Symbol newLiteral(final LiteralNode<?> literalNode) {
   447         final String uname = uniqueName(LITERAL_PREFIX.tag());
   448         final Symbol symbol = new Symbol(uname, IS_CONSTANT, literalNode.getType());
   449         symbol.setNode(literalNode);
   450         literalNode.setSymbol(symbol);
   452         return symbol;
   453     }
   455     @Override
   456     public void toString(final StringBuilder sb) {
   457         sb.append('[');
   458         sb.append(returnType);
   459         sb.append(']');
   460         sb.append(' ');
   462         sb.append("function");
   464         if (ident != null) {
   465             sb.append(' ');
   466             ident.toString(sb);
   467         }
   469         sb.append('(');
   470         boolean first = true;
   472         for (final IdentNode parameter : parameters) {
   473             if (!first) {
   474                 sb.append(", ");
   475             } else {
   476                 first = false;
   477             }
   479             parameter.toString(sb);
   480         }
   482         sb.append(')');
   483     }
   485     /**
   486      * Returns true if the function is the top-level program.
   487      * @return True if this function node represents the top-level program.
   488      */
   489     public boolean isProgram() {
   490         return (flags & IS_PROGRAM) != 0;
   491     }
   493     /**
   494      * Marks the function as representing the top-level program.
   495      */
   496     public void setProgram() {
   497         flags |= IS_PROGRAM;
   498     }
   500     /**
   501      * Get the control stack. Used when parsing to establish nesting depths of
   502      * different control structures
   503      *
   504      * @return the control stack
   505      */
   506     public Stack<Node> getControlStack() {
   507         return controlStack;
   508     }
   510     /**
   511      * Should this function node be lazily code generated, i.e. first at link time
   512      * @return true if lazy
   513      */
   514     public boolean isLazy() {
   515         return (flags & IS_LAZY) != 0;
   516     }
   518     /**
   519      * Set if this function should be lazily generated
   520      * @param isLazy is lazy
   521      */
   522     public void setIsLazy(final boolean isLazy) {
   523         this.flags = isLazy ? flags | IS_LAZY : flags & ~IS_LAZY;
   524     }
   526     /**
   527      * Check if the {@code with} keyword is used in this function
   528      *
   529      * @return true if {@code with} is used
   530      */
   531     public boolean hasWith() {
   532         return (flags & HAS_WITH) != 0;
   533     }
   535     /**
   536      * Flag this function as using the {@code with} keyword
   537      * @param ancestors the iterator over functions in this functions's containing lexical context
   538      */
   539     public void setHasWith(final Iterator<FunctionNode> ancestors) {
   540         if(!hasWith()) {
   541             this.flags |= HAS_WITH;
   542             // with requires scope in parents.
   543             // TODO: refine this. with should not force all variables in parents to be in scope, only those that are
   544             // actually referenced as identifiers by name
   545             markParentForWithOrEval(ancestors);
   546         }
   547     }
   549     private void markParentForWithOrEval(final Iterator<FunctionNode> ancestors) {
   550         // If this is invoked, then either us or a descendant uses with or eval, meaning we must have our own scope.
   551         setNeedsScope();
   553         if(ancestors.hasNext()) {
   554             ancestors.next().setDescendantHasWithOrEval(ancestors);
   555         }
   556     }
   558     private void setDescendantHasWithOrEval(final Iterator<FunctionNode> ancestors) {
   559         if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) {
   560             flags |= HAS_DESCENDANT_WITH_OR_EVAL;
   561             markParentForWithOrEval(ancestors);
   562         }
   563     }
   565     /**
   566      * Check if the {@code eval} keyword is used in this function
   567      *
   568      * @return true if {@code eval} is used
   569      */
   570     public boolean hasEval() {
   571         return (flags & HAS_EVAL) != 0;
   572     }
   574     /**
   575      * Flag this function as calling the {@code eval} function
   576      * @param ancestors the iterator over functions in this functions's containing lexical context
   577      */
   578     public void setHasEval(final Iterator<FunctionNode> ancestors) {
   579         if(!hasEval()) {
   580             this.flags |= HAS_EVAL;
   581             markParentForWithOrEval(ancestors);
   582         }
   583     }
   585     /**
   586      * Test whether this function or any of its nested functions contains a <tt>with</tt> statement
   587      * or an <tt>eval</tt> call.
   588      *
   589      * @see #hasWith()
   590      * @see #hasEval()
   591      * @return true if this or a nested function contains with or eval
   592      */
   593     public boolean hasDeepWithOrEval() {
   594         return (flags & HAS_DEEP_WITH_OR_EVAL) != 0;
   595     }
   597     /**
   598      * Get the first token for this function
   599      * @return the first token
   600      */
   601     public long getFirstToken() {
   602         return firstToken;
   603     }
   605     /**
   606      * Set the first token for this function
   607      * @param firstToken the first token
   608      */
   609     public void setFirstToken(final long firstToken) {
   610         this.firstToken = firstToken;
   611     }
   613     /**
   614      * Returns a list of functions declared by this function. Only includes declared functions, and does not include any
   615      * function expressions that might occur in its body.
   616      * @return a list of functions declared by this function.
   617      */
   618     public List<FunctionNode> getDeclaredFunctions() {
   619         // Note that the function does not have a dedicated list of declared functions, but rather relies on the
   620         // invariant that all function declarations are at the beginning of the statement list as VarNode with a
   621         // FunctionNode marked as statement with its variable initializer. Every VarNode is also preceded by a
   622         // LineNumberNode. This invariant is established by the parser and has to be preserved in visitors.
   623         final List<FunctionNode> fns = new ArrayList<>();
   624         for (final Node stmt : statements) {
   625             if(stmt instanceof LineNumberNode) {
   626                 continue;
   627             } else if(stmt instanceof VarNode) {
   628                 final Node init = ((VarNode)stmt).getInit();
   629                 if(init instanceof FunctionNode) {
   630                     final FunctionNode fn = (FunctionNode)init;
   631                     if(fn.isDeclared()) {
   632                         fns.add(fn);
   633                         continue;
   634                     }
   635                 }
   636             }
   637             // Node is neither a LineNumberNode, nor a function declaration VarNode. Since all function declarations are
   638             // at the start of the function, we've reached the end of function declarations.
   639             break;
   640         }
   641         return fns;
   642     }
   644     /**
   645      * Get the label stack. This is used by the parser to establish
   646      * label nesting depth
   647      *
   648      * @return the label stack
   649      */
   650     public Stack<LabelNode> getLabelStack() {
   651         return labelStack;
   652     }
   654     /**
   655      * If this function needs to use var args, return the identifier to the node used
   656      * for the var args structure
   657      *
   658      * @return IdentNode representing the var args structure
   659      */
   660     public IdentNode getVarArgsNode() {
   661         return varArgsNode;
   662     }
   664     /**
   665      * Set the identifier to the node used for the var args structure
   666      *
   667      * @param varArgsNode IdentNode representing the var args
   668      */
   669     public void setVarArgsNode(final IdentNode varArgsNode) {
   670         this.varArgsNode = varArgsNode;
   671     }
   673     /**
   674      * If this function uses the {@code callee} variable, return the node used
   675      * as this variable
   676      *
   677      * @return an IdentNode representing the {@code callee} variable
   678      */
   679     public IdentNode getCalleeNode() {
   680         return calleeNode;
   681     }
   683     /**
   684      * If this function uses the {@code callee} variable, set the node representing the
   685      * callee
   686      * @param calleeNode an IdentNode representing the callee
   687      */
   688     public void setCalleeNode(final IdentNode calleeNode) {
   689         this.calleeNode = calleeNode;
   690     }
   692     /**
   693      * Check if this function's generated Java method needs a {@code callee} parameter. Functions that need access to
   694      * their parent scope, functions that reference themselves, and non-strict functions that need an Arguments object
   695      * (since it exposes {@code arguments.callee} property) will need to have a callee parameter.
   696      *
   697      * @return true if the function's generated Java method needs a {@code callee} parameter.
   698      */
   699     public boolean needsCallee() {
   700         return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode());
   701     }
   703     /**
   704      * If this is a function where {@code arguments} is used, return the node used as the {@code arguments}
   705      * variable
   706      * @return an IdentNode representing {@code arguments}
   707      */
   708     public IdentNode getArgumentsNode() {
   709         return argumentsNode;
   710     }
   712     /**
   713      * If this is a Function where {@code arguments} is used, an identifier to the node representing
   714      * the {@code arguments} value has to be supplied by the compiler
   715      *
   716      * @param argumentsNode IdentNode that represents {@code arguments}
   717      */
   718     public void setArgumentsNode(final IdentNode argumentsNode) {
   719         this.argumentsNode = argumentsNode;
   720     }
   722     /**
   723      * Get the identifier for this function
   724      * @return the identifier as an IdentityNode
   725      */
   726     public IdentNode getIdent() {
   727         return ident;
   728     }
   730     /**
   731      * Reset the identifier for this function
   732      * @param ident IdentNode for new identifier
   733      */
   734     public void setIdent(final IdentNode ident) {
   735         this.ident = ident;
   736     }
   738     /**
   739      * Does this function's method needs to be variable arity (gather all script-declared parameters in a final
   740      * {@code Object[]} parameter. Functions that need to have the "arguments" object as well as functions that simply
   741      * declare too many arguments for JVM to handle with fixed arity will need to be variable arity.
   742      * @return true if the Java method in the generated code that implements this function needs to be variable arity.
   743      * @see #needsArguments()
   744      * @see LinkerCallSite#ARGLIMIT
   745      */
   746     public boolean isVarArg() {
   747         return needsArguments() || parameters.size() > LinkerCallSite.ARGLIMIT;
   748     }
   750     /**
   751      * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
   752      * name. This precludes it from needing to have an Arguments object defined as "arguments" local variable. Note that
   753      * defining a local variable named "arguments" still requires construction of the Arguments object (see
   754      * ECMAScript 5.1 Chapter 10.5).
   755      * @see #needsArguments()
   756      */
   757     public void setDefinesArguments() {
   758         this.flags |= DEFINES_ARGUMENTS;
   759     }
   761     /**
   762      * Returns true if this function needs to have an Arguments object defined as a local variable named "arguments".
   763      * Functions that use "arguments" as identifier and don't define it as a name of a parameter or a nested function
   764      * (see ECMAScript 5.1 Chapter 10.5), as well as any function that uses eval or with, or has a nested function that
   765      * does the same, will have an "arguments" object. Also, if this function is a script, it will not have an
   766      * "arguments" object, because it does not have local variables; rather the Global object will have an explicit
   767      * "arguments" property that provides command-line arguments for the script.
   768      * @return true if this function needs an arguments object.
   769      */
   770     public boolean needsArguments() {
   771         // uses "arguments" or calls eval, but it does not redefine "arguments", and finally, it's not a script, since
   772         // for top-level script, "arguments" is picked up from Context by Global.init() instead.
   773         return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isProgram();
   774     }
   776     /**
   777      * Flags this function as one that uses the "arguments" identifier.
   778      * @see #needsArguments()
   779      */
   780     public void setUsesArguments() {
   781         flags |= USES_ARGUMENTS;
   782     }
   784     /**
   785      * Returns true if this function needs access to its parent scope. Functions referencing variables outside their
   786      * scope (including global variables), as well as functions that call eval or have a with block, or have nested
   787      * functions that call eval or have a with block, will need a parent scope. Top-level script functions also need a
   788      * parent scope since they might be used from within eval, and eval will need an externally passed scope.
   789      * @return true if the function needs parent scope.
   790      */
   791     public boolean needsParentScope() {
   792         return (flags & NEEDS_PARENT_SCOPE) != 0 || isProgram();
   793     }
   795     /**
   796      * Return the kind of this function
   797      * @see FunctionNode.Kind
   798      * @return the kind
   799      */
   800     public Kind getKind() {
   801         return kind;
   802     }
   804     /**
   805      * Set the kind of this function
   806      * @see FunctionNode.Kind
   807      * @param kind the kind
   808      */
   809     public void setKind(final Kind kind) {
   810         this.kind = kind;
   811     }
   813     /**
   814      * Return the last token for this function's code
   815      * @return last token
   816      */
   817     public long getLastToken() {
   818         return lastToken;
   819     }
   821     /**
   822      * Set the last token for this function's code
   823      * @param lastToken the last token
   824      */
   825     public void setLastToken(final long lastToken) {
   826         this.lastToken = lastToken;
   827     }
   829     /**
   830      * Get the name of this function
   831      * @return the name
   832      */
   833     public String getName() {
   834         return name;
   835     }
   837     /**
   838      * Set the name of this function
   839      * @param name the name
   840      */
   841     public void setName(final String name) {
   842         this.name = name;
   843     }
   845     /**
   846      * Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and
   847      * functions having with and/or eval blocks are such.
   848      *
   849      * @return true if all variables should be in scope
   850      */
   851     public boolean allVarsInScope() {
   852         return isProgram() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0;
   853     }
   855     /**
   856      * Checks if this function is a sub-function generated by splitting a larger one
   857      *
   858      * @return true if this function is split from a larger one
   859      */
   860     public boolean isSplit() {
   861         return (flags & IS_SPLIT) != 0;
   862     }
   864     /**
   865      * Flag this function node as being a sub-function generated by the splitter
   866      */
   867     public void setIsSplit() {
   868         this.flags |= IS_SPLIT;
   869         setNeedsScope();
   870     }
   872     /**
   873      * Checks if this function has yet-to-be-generated child functions
   874      *
   875      * @return true if there are lazy child functions
   876      */
   877     public boolean hasLazyChildren() {
   878         return (flags & HAS_LAZY_CHILDREN) != 0;
   879     }
   881     /**
   882      * Flag this function node as having yet-to-be-generated child functions
   883      */
   884     public void setHasLazyChildren() {
   885         this.flags |= HAS_LAZY_CHILDREN;
   886         setNeedsScope();
   887     }
   889     /**
   890      * Get the parameters to this function
   891      * @return a list of IdentNodes which represent the function parameters, in order
   892      */
   893     public List<IdentNode> getParameters() {
   894         return Collections.unmodifiableList(parameters);
   895     }
   897     /**
   898      * Set the paremeters to this function
   899      * @param parameters a list of IdentNodes representing parameters in left to right order
   900      */
   901     public void setParameters(final List<IdentNode> parameters) {
   902         this.parameters = parameters;
   903     }
   905     /**
   906      * Get a specialized type for an identity, if one exists
   907      * @param node node to check specialized type for
   908      * @return null if no specialization exists, otherwise type
   909      */
   910     public Type getSpecializedType(final IdentNode node) {
   911         return specializedTypes.get(node);
   912     }
   914     /**
   915      * Set parameter type hints for specialization.
   916      * @param types types array of length equal to parameter list size
   917      */
   918     public void setParameterTypes(final Class<?>[] types) {
   919         assert types.length == parameters.size() : "Type vector length doesn't correspond to parameter types";
   920         //diff - skip the callee and this etc, they are not explicit params in the parse tree
   921         for (int i = 0; i < types.length ; i++) {
   922             specializedTypes.put(parameters.get(i), Type.typeFor(types[i]));
   923         }
   924     }
   926     /**
   927      * Get the identifier for the variable in which the function return value
   928      * should be stored
   929      * @return an IdentNode representing the return value
   930      */
   931     public IdentNode getResultNode() {
   932         return resultNode;
   933     }
   935     /**
   936      * Set the identifier representing the variable in which the function return
   937      * value should be stored
   938      * @param resultNode an IdentNode representing the return value
   939      */
   940     public void setResultNode(final IdentNode resultNode) {
   941         this.resultNode = resultNode;
   942     }
   944     /**
   945      * Get the identifier representing this function's scope
   946      * @return an IdentNode representing this function's scope
   947      */
   948     public IdentNode getScopeNode() {
   949         return scopeNode;
   950     }
   952     /**
   953      * Set the identifier representing this function's scope
   954      * @param scopeNode an IdentNode representing this function's scope
   955      */
   956     public void setScopeNode(final IdentNode scopeNode) {
   957         this.scopeNode = scopeNode;
   958     }
   960     /**
   961      * Check if this function is created as a function declaration (as opposed to function expression)
   962      * @return true if function is declared.
   963      */
   964     public boolean isDeclared() {
   965         return (flags & IS_DECLARED) != 0;
   966     }
   968     /**
   969      * Flag this function as being created as a function declaration (as opposed to a function expression).
   970      * @see Parser
   971      */
   972     public void setIsDeclared() {
   973         this.flags |= IS_DECLARED;
   974     }
   976     /**
   977      * Check if this function is anonymous
   978      * @return true if function is anonymous
   979      */
   980     public boolean isAnonymous() {
   981         return (flags & IS_ANONYMOUS) != 0;
   982     }
   984     /**
   985      * Flag this function as an anonymous function.
   986      * @see Parser
   987      */
   988     public void setIsAnonymous() {
   989         this.flags |= IS_ANONYMOUS;
   990     }
   992     /**
   993      * Does this function need a self symbol - this is needed only for self
   994      * referring functions
   995      * @return true if function needs a symbol for self
   996      */
   997     public boolean needsSelfSymbol() {
   998         return (flags & NEEDS_SELF_SYMBOL) != 0;
   999     }
  1001     /**
  1002      * Get the initializer statement for the __callee__ variable, where applicable
  1003      * for self references
  1004      * @return initialization
  1005      */
  1006     public Node getSelfSymbolInit() {
  1007         return this.selfSymbolInit;
  1010     /**
  1011      * Flag the function as needing a self symbol. This is needed only for
  1012      * self referring functions
  1013      * @param selfSymbolInit initialization expression for self symbol
  1014      */
  1015     public void setNeedsSelfSymbol(final Node selfSymbolInit) {
  1016         this.flags |= NEEDS_SELF_SYMBOL;
  1017         this.selfSymbolInit = selfSymbolInit;
  1020     /**
  1021      * Marks this function as using any of its ancestors' scopes.
  1022      */
  1023     public void setUsesAncestorScope() {
  1024         this.flags |= USES_ANCESTOR_SCOPE;
  1027     @Override
  1028     void setUsesParentScopeSymbol(Symbol symbol, Iterator<Block> ancestors) {
  1029         setUsesAncestorScope();
  1030         super.setUsesParentScopeSymbol(symbol, ancestors);
  1033     /**
  1034      * Return the node representing {@code this} in this function
  1035      * @return IdentNode representing {@code this}
  1036      */
  1037     public IdentNode getThisNode() {
  1038         return thisNode;
  1041     /**
  1042      * Set the node representing {@code this} in this function
  1043      * @param thisNode identifier representing {@code this}
  1044      */
  1045     public void setThisNode(final IdentNode thisNode) {
  1046         this.thisNode = thisNode;
  1049     /**
  1050      * Every function declared as {@code function x()} is internally hoisted
  1051      * and represented as {@code var x = function()  ... }. This getter returns
  1052      * the VarNode representing this virtual assignment
  1054      * @return the var node emitted for setting this function symbol
  1055      */
  1056     public VarNode getFunctionVarNode() {
  1057         return funcVarNode;
  1060     /**
  1061      * Set the virtual VarNode assignment for this function.
  1062      * @see FunctionNode#getFunctionVarNode()
  1064      * @param varNode the virtual var node assignment
  1065      */
  1066     public void setFunctionVarNode(final VarNode varNode) {
  1067         funcVarNode = varNode;
  1070     /**
  1071      * The line number information where the function was declared must be propagated
  1072      * to the virtual {@code var x = function() ... } assignment described in
  1073      * {@link FunctionNode#getFunctionVarNode()}
  1074      * This maintains the line number of the declaration
  1076      * @return a line number node representing the line this function was declared
  1077      */
  1078     public LineNumberNode getFunctionVarLineNumberNode() {
  1079         return funcVarLineNumberNode;
  1082     /**
  1083      * Set the virtual VarNode assignment for this function, along with
  1084      * a line number node for tracking the original start line of the function
  1085      * declaration
  1087      * @param varNode    the virtual var node assignment
  1088      * @param lineNumber the line number node for the function declaration
  1089      */
  1090     public void setFunctionVarNode(final VarNode varNode, final LineNumberNode lineNumber) {
  1091         funcVarNode           = varNode;
  1092         funcVarLineNumberNode = lineNumber;
  1095     /**
  1096      * Get the namespace this function uses for its symbols
  1097      * @return the namespace
  1098      */
  1099     public Namespace getNamespace() {
  1100         return namespace;
  1103     @Override
  1104     public Type getType() {
  1105         return FUNCTION_TYPE;
  1108     /**
  1109      * Get the return type for this function. Return types can be specialized
  1110      * if the compiler knows them, but parameters cannot, as they need to go through
  1111      * appropriate object conversion
  1113      * @return the return type
  1114      */
  1115     public Type getReturnType() {
  1116         return returnType;
  1119     /**
  1120      * Set the function return type
  1122      * @param returnType new return type
  1123      */
  1124     public void setReturnType(final Type returnType) {
  1125         //we never bother with object types narrower than objects, that will lead to byte code verification errors
  1126         //as for instance even if we know we are returning a string from a method, the code generator will always
  1127         //treat it as an object, at least for now
  1128         this.returnType = Type.widest(this.returnType,  returnType.isObject() ? Type.OBJECT : returnType);
  1131     /**
  1132      * Set strict mode on or off for this function
  1134      * @param isStrictMode true if strict mode should be enabled
  1135      */
  1136     public void setStrictMode(final boolean isStrictMode) {
  1137         flags = isStrictMode ? flags | IS_STRICT_MODE : flags & ~IS_STRICT_MODE;
  1140     /**
  1141      * Check if the function is generated in strict mode
  1142      * @return true if strict mode enabled for function
  1143      */
  1144     public boolean isStrictMode() {
  1145         return (flags & IS_STRICT_MODE) != 0;
  1148     /**
  1149      * Set the lowered state
  1150      */
  1151     public void setIsLowered() {
  1152         flags |= IS_LOWERED;
  1155     /**
  1156      * Get the lowered state
  1158      * @return true if function is lowered
  1159      */
  1160     public boolean isLowered() {
  1161         return (flags & IS_LOWERED) != 0;
  1164     /**
  1165      * Get the compile unit used to compile this function
  1166      * @see Compiler
  1167      * @return the compile unit
  1168      */
  1169     public CompileUnit getCompileUnit() {
  1170         return compileUnit;
  1173     /**
  1174      * Reset the compile unit used to compile this function
  1175      * @see Compiler
  1176      * @param compileUnit the compile unit
  1177      */
  1178     public void setCompileUnit(final CompileUnit compileUnit) {
  1179         this.compileUnit = compileUnit;
  1182     /**
  1183      * Return the method emitter used to write bytecode for this function
  1184      * @return the method emitter
  1185      */
  1186     public MethodEmitter getMethodEmitter() {
  1187         return method;
  1190     /**
  1191      * Set the method emitter that is to be used to write bytecode for this function
  1192      * @param method a method emitter
  1193      */
  1194     public void setMethodEmitter(final MethodEmitter method) {
  1195         this.method = method;

mercurial