Mercurial > hg > openjdk > jigsaw > nashorn
view src/jdk/nashorn/internal/ir/FunctionNode.java @ 143:4be452026847
8010652: Eliminate non-child references in Block/FunctionNode, and make few node types immutable
Reviewed-by: jlaskey, lagergren
author | attila |
---|---|
date | Sat, 23 Mar 2013 00:58:39 +0100 |
parents | 390d44ba90cf |
children |
line wrap: on
line source
/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.nashorn.internal.ir; import static jdk.nashorn.internal.codegen.CompilerConstants.LITERAL_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX; import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT; import static jdk.nashorn.internal.ir.Symbol.IS_TEMP; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Frame; import jdk.nashorn.internal.codegen.MethodEmitter; import jdk.nashorn.internal.codegen.Namespace; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Parser; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.UserAccessorProperty; import jdk.nashorn.internal.runtime.linker.LinkerCallSite; /** * IR representation for function (or script.) */ public class FunctionNode extends Block { private static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class); /** Function kinds */ public enum Kind { /** a normal function - nothing special */ NORMAL, /** a script function */ SCRIPT, /** a getter, @see {@link UserAccessorProperty} */ GETTER, /** a setter, @see {@link UserAccessorProperty} */ SETTER } /** Compilation states available */ public enum CompilationState { /** compiler is ready */ INITIALIZED, /** method has been parsed */ PARSED, /** method has been parsed */ PARSE_ERROR, /** constant folding pass */ CONSTANT_FOLDED, /** method has been lowered */ LOWERED, /** method hass been attributed */ ATTR, /** method has been split */ SPLIT, /** method has had its types finalized */ FINALIZED, /** method has been emitted to bytecode */ EMITTED, /** code installed in a class loader */ INSTALLED } /** External function identifier. */ @Ignore private IdentNode ident; /** Internal function name. */ private String name; /** Compilation unit. */ private CompileUnit compileUnit; /** Method emitter for current method. */ private MethodEmitter method; /** Function kind. */ private Kind kind; /** List of parameters. */ private List<IdentNode> parameters; /** First token of function. **/ private long firstToken; /** Last token of function. **/ private long lastToken; /** Variable frames. */ private Frame frames; /** Method's namespace. */ private final Namespace namespace; /** Node representing current this. */ @Ignore private IdentNode thisNode; /** Node representing current scope. */ @Ignore private IdentNode scopeNode; /** Node representing return value. */ @Ignore private IdentNode resultNode; /** Node representing current arguments. */ @Ignore private IdentNode argumentsNode; /** Node representing callee */ @Ignore private IdentNode calleeNode; /** Node representing varargs */ @Ignore private IdentNode varArgsNode; /** Pending label list. */ private final Stack<LabelNode> labelStack; /** Pending control list. */ private final Stack<Node> controlStack; /** VarNode for this function statement */ @Ignore //this is explicit code anyway and should not be traversed after lower private VarNode funcVarNode; /** Line number for function declaration */ @Ignore private LineNumberNode funcVarLineNumberNode; /** Initializer var func = __callee__, where applicable */ @Ignore private Node selfSymbolInit; /** Current compilation state */ @Ignore private final EnumSet<CompilationState> compilationState; /** Type hints, e.g based on parameters at call site */ private final Map<IdentNode, Type> specializedTypes; /** Function flags. */ private int flags; /** Is anonymous function flag. */ private static final int IS_ANONYMOUS = 1 << 0; /** Is the function created in a function declaration (as opposed to a function expression) */ private static final int IS_DECLARED = 1 << 1; /** is this a strict mode function? */ private static final int IS_STRICT_MODE = 1 << 2; /** Does the function use the "arguments" identifier ? */ private static final int USES_ARGUMENTS = 1 << 3; /** Are we lowered ? */ private static final int IS_LOWERED = 1 << 4; /** Has this node been split because it was too large? */ private static final int IS_SPLIT = 1 << 5; /** Does the function call eval? */ private static final int HAS_EVAL = 1 << 6; /** Does the function contain a with block ? */ private static final int HAS_WITH = 1 << 7; /** Does a descendant function contain a with or eval? */ private static final int HAS_DESCENDANT_WITH_OR_EVAL = 1 << 8; /** Does the function define "arguments" identifier as a parameter of nested function name? */ private static final int DEFINES_ARGUMENTS = 1 << 9; /** Does the function need a self symbol? */ private static final int NEEDS_SELF_SYMBOL = 1 << 10; /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */ private static final int USES_ANCESTOR_SCOPE = 1 << 11; /** Is this function lazily compiled? */ private static final int IS_LAZY = 1 << 12; /** Does this function have lazy, yet uncompiled children */ private static final int HAS_LAZY_CHILDREN = 1 << 13; /** Does this function have lazy, yet uncompiled children */ private static final int IS_PROGRAM = 1 << 14; /** Does this function or any nested functions contain a with or an eval? */ private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL; /** Does this function need to store all its variables in scope? */ private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN; /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */ private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL; /** 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. * We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */ private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL | HAS_LAZY_CHILDREN; /** What is the return type of this function? */ private Type returnType = Type.UNKNOWN; /** * Constructor * * @param source the source * @param token token * @param finish finish * @param namespace the namespace * @param ident the identifier * @param name the name of the function */ public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final IdentNode ident, final String name) { super(source, token, finish); this.ident = ident; this.name = name; this.kind = Kind.NORMAL; this.parameters = new ArrayList<>(); this.firstToken = token; this.lastToken = token; this.namespace = namespace; this.labelStack = new Stack<>(); this.controlStack = new Stack<>(); this.compilationState = EnumSet.of(CompilationState.INITIALIZED); this.specializedTypes = new HashMap<>(); } private FunctionNode(final FunctionNode functionNode, final CopyState cs) { super(functionNode, cs); this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident); this.name = functionNode.name; this.kind = functionNode.kind; this.parameters = new ArrayList<>(); for (final IdentNode param : functionNode.getParameters()) { this.parameters.add((IdentNode)cs.existingOrCopy(param)); } this.firstToken = functionNode.firstToken; this.lastToken = functionNode.lastToken; this.namespace = functionNode.getNamespace(); this.thisNode = (IdentNode)cs.existingOrCopy(functionNode.thisNode); this.scopeNode = (IdentNode)cs.existingOrCopy(functionNode.scopeNode); this.resultNode = (IdentNode)cs.existingOrCopy(functionNode.resultNode); this.argumentsNode = (IdentNode)cs.existingOrCopy(functionNode.argumentsNode); this.varArgsNode = (IdentNode)cs.existingOrCopy(functionNode.varArgsNode); this.calleeNode = (IdentNode)cs.existingOrCopy(functionNode.calleeNode); this.labelStack = new Stack<>(); this.controlStack = new Stack<>(); this.flags = functionNode.flags; this.funcVarNode = (VarNode)cs.existingOrCopy(functionNode.funcVarNode); /** VarNode for this function statement */ this.compilationState = EnumSet.copyOf(functionNode.compilationState); this.specializedTypes = new HashMap<>(); } @Override protected Node copy(final CopyState cs) { // deep clone all parent blocks return new FunctionNode(this, cs); } @Override public Node accept(final NodeVisitor visitor) { final FunctionNode saveFunctionNode = visitor.getCurrentFunctionNode(); final Block saveBlock = visitor.getCurrentBlock(); final MethodEmitter saveMethodEmitter = visitor.getCurrentMethodEmitter(); final CompileUnit saveCompileUnit = visitor.getCurrentCompileUnit(); visitor.setCurrentFunctionNode(this); visitor.setCurrentBlock(this); try { if (visitor.enterFunctionNode(this) != null) { if (ident != null) { ident = (IdentNode)ident.accept(visitor); } for (int i = 0, count = parameters.size(); i < count; i++) { parameters.set(i, (IdentNode)parameters.get(i).accept(visitor)); } for (int i = 0, count = statements.size(); i < count; i++) { statements.set(i, statements.get(i).accept(visitor)); } return visitor.leaveFunctionNode(this); } } finally { visitor.setCurrentBlock(saveBlock); visitor.setCurrentFunctionNode(saveFunctionNode); visitor.setCurrentCompileUnit(saveCompileUnit); visitor.setCurrentMethodEmitter(saveMethodEmitter); } return this; } @Override public boolean needsScope() { return super.needsScope() || isProgram(); } /** * Check whether this FunctionNode has reached a give CompilationState. * * @param state the state to check for * @return true of the node is in the given state */ public boolean hasState(final EnumSet<CompilationState> state) { return compilationState.equals(state); } /** * Check whether the state of this FunctionNode contains a given compilation * state. * * A node can be in many states at once, e.g. both lowered and initialized. * To check for an exact state, use {FunctionNode{@link #hasState(EnumSet)} * * @param state state to check for * @return true if state is present in the total compilation state of this FunctionNode */ public boolean hasState(final CompilationState state) { return compilationState.contains(state); } /** * Add a state to the total CompilationState of this node, e.g. if * FunctionNode has been lowered, the compiler will add * {@code CompilationState#LOWERED} to the state vector * * @param state {@link CompilationState} to add */ public void setState(final CompilationState state) { compilationState.add(state); } /* * Frame management. */ /** * Push a new block frame. * * @return the new frame */ public final Frame pushFrame() { frames = new Frame(frames); return frames; } /** * Pop a block frame. */ public final void popFrame() { frames = frames.getPrevious(); } /** * Create a temporary variable to the current frame. * * @param currentFrame Frame to add to - defaults to current function frame * @param type Strong type of symbol. * @param node Primary node to use symbol. * * @return Symbol used. */ public Symbol newTemporary(final Frame currentFrame, final Type type, final Node node) { assert currentFrame != null; Symbol symbol = node.getSymbol(); // If no symbol already present. if (symbol == null) { final String uname = uniqueName(TEMP_PREFIX.tag()); symbol = new Symbol(uname, IS_TEMP, type); symbol.setNode(node); } // Assign a slot if it doesn't have one. if (!symbol.hasSlot()) { currentFrame.addSymbol(symbol); } // Set symbol to node. node.setSymbol(symbol); return symbol; } /** * Create a unique name in the namespace of this FunctionNode * @param base prefix for name * @return base if no collision exists, otherwise a name prefix with base */ public String uniqueName(final String base) { return namespace.uniqueName(base); } /** * Add a new temporary variable to the current frame * * @param type Strong type of symbol * @param node Primary node to use symbol * * @return symbol used */ public Symbol newTemporary(final Type type, final Node node) { return newTemporary(frames, type, node); } /** * Create a virtual symbol for a literal. * * @param literalNode Primary node to use symbol. * * @return Symbol used. */ public Symbol newLiteral(final LiteralNode<?> literalNode) { final String uname = uniqueName(LITERAL_PREFIX.tag()); final Symbol symbol = new Symbol(uname, IS_CONSTANT, literalNode.getType()); symbol.setNode(literalNode); literalNode.setSymbol(symbol); return symbol; } @Override public void toString(final StringBuilder sb) { sb.append('['); sb.append(returnType); sb.append(']'); sb.append(' '); sb.append("function"); if (ident != null) { sb.append(' '); ident.toString(sb); } sb.append('('); boolean first = true; for (final IdentNode parameter : parameters) { if (!first) { sb.append(", "); } else { first = false; } parameter.toString(sb); } sb.append(')'); } /** * Returns true if the function is the top-level program. * @return True if this function node represents the top-level program. */ public boolean isProgram() { return (flags & IS_PROGRAM) != 0; } /** * Marks the function as representing the top-level program. */ public void setProgram() { flags |= IS_PROGRAM; } /** * Get the control stack. Used when parsing to establish nesting depths of * different control structures * * @return the control stack */ public Stack<Node> getControlStack() { return controlStack; } /** * Should this function node be lazily code generated, i.e. first at link time * @return true if lazy */ public boolean isLazy() { return (flags & IS_LAZY) != 0; } /** * Set if this function should be lazily generated * @param isLazy is lazy */ public void setIsLazy(final boolean isLazy) { this.flags = isLazy ? flags | IS_LAZY : flags & ~IS_LAZY; } /** * Check if the {@code with} keyword is used in this function * * @return true if {@code with} is used */ public boolean hasWith() { return (flags & HAS_WITH) != 0; } /** * Flag this function as using the {@code with} keyword * @param ancestors the iterator over functions in this functions's containing lexical context */ public void setHasWith(final Iterator<FunctionNode> ancestors) { if(!hasWith()) { this.flags |= HAS_WITH; // with requires scope in parents. // TODO: refine this. with should not force all variables in parents to be in scope, only those that are // actually referenced as identifiers by name markParentForWithOrEval(ancestors); } } private void markParentForWithOrEval(final Iterator<FunctionNode> ancestors) { // If this is invoked, then either us or a descendant uses with or eval, meaning we must have our own scope. setNeedsScope(); if(ancestors.hasNext()) { ancestors.next().setDescendantHasWithOrEval(ancestors); } } private void setDescendantHasWithOrEval(final Iterator<FunctionNode> ancestors) { if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) { flags |= HAS_DESCENDANT_WITH_OR_EVAL; markParentForWithOrEval(ancestors); } } /** * Check if the {@code eval} keyword is used in this function * * @return true if {@code eval} is used */ public boolean hasEval() { return (flags & HAS_EVAL) != 0; } /** * Flag this function as calling the {@code eval} function * @param ancestors the iterator over functions in this functions's containing lexical context */ public void setHasEval(final Iterator<FunctionNode> ancestors) { if(!hasEval()) { this.flags |= HAS_EVAL; markParentForWithOrEval(ancestors); } } /** * Test whether this function or any of its nested functions contains a <tt>with</tt> statement * or an <tt>eval</tt> call. * * @see #hasWith() * @see #hasEval() * @return true if this or a nested function contains with or eval */ public boolean hasDeepWithOrEval() { return (flags & HAS_DEEP_WITH_OR_EVAL) != 0; } /** * Get the first token for this function * @return the first token */ public long getFirstToken() { return firstToken; } /** * Set the first token for this function * @param firstToken the first token */ public void setFirstToken(final long firstToken) { this.firstToken = firstToken; } /** * Returns a list of functions declared by this function. Only includes declared functions, and does not include any * function expressions that might occur in its body. * @return a list of functions declared by this function. */ public List<FunctionNode> getDeclaredFunctions() { // Note that the function does not have a dedicated list of declared functions, but rather relies on the // invariant that all function declarations are at the beginning of the statement list as VarNode with a // FunctionNode marked as statement with its variable initializer. Every VarNode is also preceded by a // LineNumberNode. This invariant is established by the parser and has to be preserved in visitors. final List<FunctionNode> fns = new ArrayList<>(); for (final Node stmt : statements) { if(stmt instanceof LineNumberNode) { continue; } else if(stmt instanceof VarNode) { final Node init = ((VarNode)stmt).getInit(); if(init instanceof FunctionNode) { final FunctionNode fn = (FunctionNode)init; if(fn.isDeclared()) { fns.add(fn); continue; } } } // Node is neither a LineNumberNode, nor a function declaration VarNode. Since all function declarations are // at the start of the function, we've reached the end of function declarations. break; } return fns; } /** * Get the label stack. This is used by the parser to establish * label nesting depth * * @return the label stack */ public Stack<LabelNode> getLabelStack() { return labelStack; } /** * If this function needs to use var args, return the identifier to the node used * for the var args structure * * @return IdentNode representing the var args structure */ public IdentNode getVarArgsNode() { return varArgsNode; } /** * Set the identifier to the node used for the var args structure * * @param varArgsNode IdentNode representing the var args */ public void setVarArgsNode(final IdentNode varArgsNode) { this.varArgsNode = varArgsNode; } /** * If this function uses the {@code callee} variable, return the node used * as this variable * * @return an IdentNode representing the {@code callee} variable */ public IdentNode getCalleeNode() { return calleeNode; } /** * If this function uses the {@code callee} variable, set the node representing the * callee * @param calleeNode an IdentNode representing the callee */ public void setCalleeNode(final IdentNode calleeNode) { this.calleeNode = calleeNode; } /** * Check if this function's generated Java method needs a {@code callee} parameter. Functions that need access to * their parent scope, functions that reference themselves, and non-strict functions that need an Arguments object * (since it exposes {@code arguments.callee} property) will need to have a callee parameter. * * @return true if the function's generated Java method needs a {@code callee} parameter. */ public boolean needsCallee() { return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode()); } /** * If this is a function where {@code arguments} is used, return the node used as the {@code arguments} * variable * @return an IdentNode representing {@code arguments} */ public IdentNode getArgumentsNode() { return argumentsNode; } /** * If this is a Function where {@code arguments} is used, an identifier to the node representing * the {@code arguments} value has to be supplied by the compiler * * @param argumentsNode IdentNode that represents {@code arguments} */ public void setArgumentsNode(final IdentNode argumentsNode) { this.argumentsNode = argumentsNode; } /** * Get the identifier for this function * @return the identifier as an IdentityNode */ public IdentNode getIdent() { return ident; } /** * Reset the identifier for this function * @param ident IdentNode for new identifier */ public void setIdent(final IdentNode ident) { this.ident = ident; } /** * Does this function's method needs to be variable arity (gather all script-declared parameters in a final * {@code Object[]} parameter. Functions that need to have the "arguments" object as well as functions that simply * declare too many arguments for JVM to handle with fixed arity will need to be variable arity. * @return true if the Java method in the generated code that implements this function needs to be variable arity. * @see #needsArguments() * @see LinkerCallSite#ARGLIMIT */ public boolean isVarArg() { return needsArguments() || parameters.size() > LinkerCallSite.ARGLIMIT; } /** * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function * name. This precludes it from needing to have an Arguments object defined as "arguments" local variable. Note that * defining a local variable named "arguments" still requires construction of the Arguments object (see * ECMAScript 5.1 Chapter 10.5). * @see #needsArguments() */ public void setDefinesArguments() { this.flags |= DEFINES_ARGUMENTS; } /** * Returns true if this function needs to have an Arguments object defined as a local variable named "arguments". * Functions that use "arguments" as identifier and don't define it as a name of a parameter or a nested function * (see ECMAScript 5.1 Chapter 10.5), as well as any function that uses eval or with, or has a nested function that * does the same, will have an "arguments" object. Also, if this function is a script, it will not have an * "arguments" object, because it does not have local variables; rather the Global object will have an explicit * "arguments" property that provides command-line arguments for the script. * @return true if this function needs an arguments object. */ public boolean needsArguments() { // uses "arguments" or calls eval, but it does not redefine "arguments", and finally, it's not a script, since // for top-level script, "arguments" is picked up from Context by Global.init() instead. return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isProgram(); } /** * Flags this function as one that uses the "arguments" identifier. * @see #needsArguments() */ public void setUsesArguments() { flags |= USES_ARGUMENTS; } /** * Returns true if this function needs access to its parent scope. Functions referencing variables outside their * scope (including global variables), as well as functions that call eval or have a with block, or have nested * functions that call eval or have a with block, will need a parent scope. Top-level script functions also need a * parent scope since they might be used from within eval, and eval will need an externally passed scope. * @return true if the function needs parent scope. */ public boolean needsParentScope() { return (flags & NEEDS_PARENT_SCOPE) != 0 || isProgram(); } /** * Return the kind of this function * @see FunctionNode.Kind * @return the kind */ public Kind getKind() { return kind; } /** * Set the kind of this function * @see FunctionNode.Kind * @param kind the kind */ public void setKind(final Kind kind) { this.kind = kind; } /** * Return the last token for this function's code * @return last token */ public long getLastToken() { return lastToken; } /** * Set the last token for this function's code * @param lastToken the last token */ public void setLastToken(final long lastToken) { this.lastToken = lastToken; } /** * Get the name of this function * @return the name */ public String getName() { return name; } /** * Set the name of this function * @param name the name */ public void setName(final String name) { this.name = name; } /** * Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and * functions having with and/or eval blocks are such. * * @return true if all variables should be in scope */ public boolean allVarsInScope() { return isProgram() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0; } /** * Checks if this function is a sub-function generated by splitting a larger one * * @return true if this function is split from a larger one */ public boolean isSplit() { return (flags & IS_SPLIT) != 0; } /** * Flag this function node as being a sub-function generated by the splitter */ public void setIsSplit() { this.flags |= IS_SPLIT; setNeedsScope(); } /** * Checks if this function has yet-to-be-generated child functions * * @return true if there are lazy child functions */ public boolean hasLazyChildren() { return (flags & HAS_LAZY_CHILDREN) != 0; } /** * Flag this function node as having yet-to-be-generated child functions */ public void setHasLazyChildren() { this.flags |= HAS_LAZY_CHILDREN; setNeedsScope(); } /** * Get the parameters to this function * @return a list of IdentNodes which represent the function parameters, in order */ public List<IdentNode> getParameters() { return Collections.unmodifiableList(parameters); } /** * Set the paremeters to this function * @param parameters a list of IdentNodes representing parameters in left to right order */ public void setParameters(final List<IdentNode> parameters) { this.parameters = parameters; } /** * Get a specialized type for an identity, if one exists * @param node node to check specialized type for * @return null if no specialization exists, otherwise type */ public Type getSpecializedType(final IdentNode node) { return specializedTypes.get(node); } /** * Set parameter type hints for specialization. * @param types types array of length equal to parameter list size */ public void setParameterTypes(final Class<?>[] types) { assert types.length == parameters.size() : "Type vector length doesn't correspond to parameter types"; //diff - skip the callee and this etc, they are not explicit params in the parse tree for (int i = 0; i < types.length ; i++) { specializedTypes.put(parameters.get(i), Type.typeFor(types[i])); } } /** * Get the identifier for the variable in which the function return value * should be stored * @return an IdentNode representing the return value */ public IdentNode getResultNode() { return resultNode; } /** * Set the identifier representing the variable in which the function return * value should be stored * @param resultNode an IdentNode representing the return value */ public void setResultNode(final IdentNode resultNode) { this.resultNode = resultNode; } /** * Get the identifier representing this function's scope * @return an IdentNode representing this function's scope */ public IdentNode getScopeNode() { return scopeNode; } /** * Set the identifier representing this function's scope * @param scopeNode an IdentNode representing this function's scope */ public void setScopeNode(final IdentNode scopeNode) { this.scopeNode = scopeNode; } /** * Check if this function is created as a function declaration (as opposed to function expression) * @return true if function is declared. */ public boolean isDeclared() { return (flags & IS_DECLARED) != 0; } /** * Flag this function as being created as a function declaration (as opposed to a function expression). * @see Parser */ public void setIsDeclared() { this.flags |= IS_DECLARED; } /** * Check if this function is anonymous * @return true if function is anonymous */ public boolean isAnonymous() { return (flags & IS_ANONYMOUS) != 0; } /** * Flag this function as an anonymous function. * @see Parser */ public void setIsAnonymous() { this.flags |= IS_ANONYMOUS; } /** * Does this function need a self symbol - this is needed only for self * referring functions * @return true if function needs a symbol for self */ public boolean needsSelfSymbol() { return (flags & NEEDS_SELF_SYMBOL) != 0; } /** * Get the initializer statement for the __callee__ variable, where applicable * for self references * @return initialization */ public Node getSelfSymbolInit() { return this.selfSymbolInit; } /** * Flag the function as needing a self symbol. This is needed only for * self referring functions * @param selfSymbolInit initialization expression for self symbol */ public void setNeedsSelfSymbol(final Node selfSymbolInit) { this.flags |= NEEDS_SELF_SYMBOL; this.selfSymbolInit = selfSymbolInit; } /** * Marks this function as using any of its ancestors' scopes. */ public void setUsesAncestorScope() { this.flags |= USES_ANCESTOR_SCOPE; } @Override void setUsesParentScopeSymbol(Symbol symbol, Iterator<Block> ancestors) { setUsesAncestorScope(); super.setUsesParentScopeSymbol(symbol, ancestors); } /** * Return the node representing {@code this} in this function * @return IdentNode representing {@code this} */ public IdentNode getThisNode() { return thisNode; } /** * Set the node representing {@code this} in this function * @param thisNode identifier representing {@code this} */ public void setThisNode(final IdentNode thisNode) { this.thisNode = thisNode; } /** * Every function declared as {@code function x()} is internally hoisted * and represented as {@code var x = function() ... }. This getter returns * the VarNode representing this virtual assignment * * @return the var node emitted for setting this function symbol */ public VarNode getFunctionVarNode() { return funcVarNode; } /** * Set the virtual VarNode assignment for this function. * @see FunctionNode#getFunctionVarNode() * * @param varNode the virtual var node assignment */ public void setFunctionVarNode(final VarNode varNode) { funcVarNode = varNode; } /** * The line number information where the function was declared must be propagated * to the virtual {@code var x = function() ... } assignment described in * {@link FunctionNode#getFunctionVarNode()} * This maintains the line number of the declaration * * @return a line number node representing the line this function was declared */ public LineNumberNode getFunctionVarLineNumberNode() { return funcVarLineNumberNode; } /** * Set the virtual VarNode assignment for this function, along with * a line number node for tracking the original start line of the function * declaration * * @param varNode the virtual var node assignment * @param lineNumber the line number node for the function declaration */ public void setFunctionVarNode(final VarNode varNode, final LineNumberNode lineNumber) { funcVarNode = varNode; funcVarLineNumberNode = lineNumber; } /** * Get the namespace this function uses for its symbols * @return the namespace */ public Namespace getNamespace() { return namespace; } @Override public Type getType() { return FUNCTION_TYPE; } /** * Get the return type for this function. Return types can be specialized * if the compiler knows them, but parameters cannot, as they need to go through * appropriate object conversion * * @return the return type */ public Type getReturnType() { return returnType; } /** * Set the function return type * * @param returnType new return type */ public void setReturnType(final Type returnType) { //we never bother with object types narrower than objects, that will lead to byte code verification errors //as for instance even if we know we are returning a string from a method, the code generator will always //treat it as an object, at least for now this.returnType = Type.widest(this.returnType, returnType.isObject() ? Type.OBJECT : returnType); } /** * Set strict mode on or off for this function * * @param isStrictMode true if strict mode should be enabled */ public void setStrictMode(final boolean isStrictMode) { flags = isStrictMode ? flags | IS_STRICT_MODE : flags & ~IS_STRICT_MODE; } /** * Check if the function is generated in strict mode * @return true if strict mode enabled for function */ public boolean isStrictMode() { return (flags & IS_STRICT_MODE) != 0; } /** * Set the lowered state */ public void setIsLowered() { flags |= IS_LOWERED; } /** * Get the lowered state * * @return true if function is lowered */ public boolean isLowered() { return (flags & IS_LOWERED) != 0; } /** * Get the compile unit used to compile this function * @see Compiler * @return the compile unit */ public CompileUnit getCompileUnit() { return compileUnit; } /** * Reset the compile unit used to compile this function * @see Compiler * @param compileUnit the compile unit */ public void setCompileUnit(final CompileUnit compileUnit) { this.compileUnit = compileUnit; } /** * Return the method emitter used to write bytecode for this function * @return the method emitter */ public MethodEmitter getMethodEmitter() { return method; } /** * Set the method emitter that is to be used to write bytecode for this function * @param method a method emitter */ public void setMethodEmitter(final MethodEmitter method) { this.method = method; } }