blob: 02bf160c5016b0e38628c7bd2a157acd3cf18858 [file] [log] [blame]
/*
* 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.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROFILE;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_ENTEREXIT;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
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.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
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.)
*/
@Immutable
public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode>, CompileUnitHolder {
private static final long serialVersionUID = 1L;
/** Type used for all FunctionNodes */
public 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
}
/** Source of entity. */
private transient final Source source;
/**
* Opaque object representing parser state at the end of the function. Used when reparsing outer functions
* to skip parsing inner functions.
*/
private final Object endParserState;
/** External function identifier. */
@Ignore
private final IdentNode ident;
/** The body of the function node */
private final Block body;
/** Internal function name. */
private final String name;
/** Compilation unit. */
private final CompileUnit compileUnit;
/** Function kind. */
private final Kind kind;
/** List of parameters. */
private final List<IdentNode> parameters;
/** First token of function. **/
private final long firstToken;
/** Last token of function. **/
private final long lastToken;
/** Method's namespace. */
private transient final Namespace namespace;
/** Number of properties of "this" object assigned in this function */
@Ignore
private final int thisProperties;
/** Function flags. */
private final int flags;
/** Line number of function start */
private final int lineNumber;
/** Root class for function */
private final Class<?> rootClass;
/** Is anonymous function flag. */
public static final int IS_ANONYMOUS = 1 << 0;
/** Is the function created in a function declaration (as opposed to a function expression) */
public static final int IS_DECLARED = 1 << 1;
/** is this a strict mode function? */
public static final int IS_STRICT = 1 << 2;
/** Does the function use the "arguments" identifier ? */
public static final int USES_ARGUMENTS = 1 << 3;
/** Has this function been split because it was too large? */
public static final int IS_SPLIT = 1 << 4;
/** Does the function call eval? If it does, then all variables in this function might be get/set by it and it can
* introduce new variables into this function's scope too.*/
public static final int HAS_EVAL = 1 << 5;
/** Does a nested function contain eval? If it does, then all variables in this function might be get/set by it. */
public static final int HAS_NESTED_EVAL = 1 << 6;
/** Does this function have any blocks that create a scope? This is used to determine if the function needs to
* have a local variable slot for the scope symbol. */
public static final int HAS_SCOPE_BLOCK = 1 << 7;
/**
* 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 static final int DEFINES_ARGUMENTS = 1 << 8;
/** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
public static final int USES_ANCESTOR_SCOPE = 1 << 9;
/** Does this function have nested declarations? */
public static final int HAS_FUNCTION_DECLARATIONS = 1 << 10;
/** Does this function have optimistic expressions? (If it does, it can undergo deoptimizing recompilation.) */
public static final int IS_DEOPTIMIZABLE = 1 << 11;
/** Are we vararg, but do we just pass the arguments along to apply or call */
public static final int HAS_APPLY_TO_CALL_SPECIALIZATION = 1 << 12;
/**
* Is this function the top-level program?
*/
public static final int IS_PROGRAM = 1 << 13;
/**
* Flag indicating whether this function uses the local variable symbol for itself. Only named function expressions
* can have this flag set if they reference themselves (e.g. "(function f() { return f })". Declared functions will
* use the symbol in their parent scope instead when they reference themselves by name.
*/
public static final int USES_SELF_SYMBOL = 1 << 14;
/** Does this function use the "this" keyword? */
public static final int USES_THIS = 1 << 15;
/** Is this declared in a dynamic context */
public static final int IN_DYNAMIC_CONTEXT = 1 << 16;
/**
* The following flags are derived from directive comments within this function.
* Note that even IS_STRICT is one such flag but that requires special handling.
*/
/** parser, print parse tree */
public static final int IS_PRINT_PARSE = 1 << 17;
/** parser, print lower parse tree */
public static final int IS_PRINT_LOWER_PARSE = 1 << 18;
/** parser, print AST */
public static final int IS_PRINT_AST = 1 << 19;
/** parser, print lower AST */
public static final int IS_PRINT_LOWER_AST = 1 << 20;
/** parser, print symbols */
public static final int IS_PRINT_SYMBOLS = 1 << 21;
// callsite tracing, profiling within this function
/** profile callsites in this function? */
public static final int IS_PROFILE = 1 << 22;
/** trace callsite enterexit in this function? */
public static final int IS_TRACE_ENTEREXIT = 1 << 23;
/** trace callsite misses in this function? */
public static final int IS_TRACE_MISSES = 1 << 24;
/** trace callsite values in this function? */
public static final int IS_TRACE_VALUES = 1 << 25;
/**
* Whether this function needs the callee {@link ScriptFunction} instance passed to its code as a
* parameter on invocation. Note that we aren't, in fact using this flag in function nodes.
* Rather, it is always calculated (see {@link #needsCallee()}). {@link RecompilableScriptFunctionData}
* will, however, cache the value of this flag.
*/
public static final int NEEDS_CALLEE = 1 << 26;
/**
* Is the function node cached?
*/
public static final int IS_CACHED = 1 << 27;
/** extension callsite flags mask */
public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE |
IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST |
IS_PRINT_SYMBOLS | IS_PROFILE | IS_TRACE_ENTEREXIT |
IS_TRACE_MISSES | IS_TRACE_VALUES;
/** Does this function or any nested functions contain an eval? */
private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
/** Does this function need to store all its variables in scope? */
private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_EVAL;
/** 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 eval, or it's the program. */
public static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL | IS_PROGRAM;
/** What is the return type of this function? */
private Type returnType = Type.UNKNOWN;
/**
* Constructor
*
* @param source the source
* @param lineNumber line number
* @param token token
* @param finish finish
* @param firstToken first token of the function node (including the function declaration)
* @param namespace the namespace
* @param ident the identifier
* @param name the name of the function
* @param parameters parameter list
* @param kind kind of function as in {@link FunctionNode.Kind}
* @param flags initial flags
*/
public FunctionNode(
final Source source,
final int lineNumber,
final long token,
final int finish,
final long firstToken,
final Namespace namespace,
final IdentNode ident,
final String name,
final List<IdentNode> parameters,
final FunctionNode.Kind kind,
final int flags) {
super(token, finish);
this.source = source;
this.lineNumber = lineNumber;
this.ident = ident;
this.name = name;
this.kind = kind;
this.parameters = parameters;
this.firstToken = firstToken;
this.lastToken = token;
this.namespace = namespace;
this.flags = flags;
this.compileUnit = null;
this.body = null;
this.thisProperties = 0;
this.rootClass = null;
this.endParserState = null;
}
private FunctionNode(
final FunctionNode functionNode,
final long lastToken,
final Object endParserState,
final int flags,
final String name,
final Type returnType,
final CompileUnit compileUnit,
final Block body,
final List<IdentNode> parameters,
final int thisProperties,
final Class<?> rootClass,
final Source source, final Namespace namespace) {
super(functionNode);
this.endParserState = endParserState;
this.lineNumber = functionNode.lineNumber;
this.flags = flags;
this.name = name;
this.returnType = returnType;
this.compileUnit = compileUnit;
this.lastToken = lastToken;
this.body = body;
this.parameters = parameters;
this.thisProperties = thisProperties;
this.rootClass = rootClass;
this.source = source;
this.namespace = namespace;
// the fields below never change - they are final and assigned in constructor
this.ident = functionNode.ident;
this.kind = functionNode.kind;
this.firstToken = functionNode.firstToken;
}
@Override
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterFunctionNode(this)) {
return visitor.leaveFunctionNode(setBody(lc, (Block)body.accept(visitor)));
}
return this;
}
/**
* Visits the parameter nodes of this function. Parameters are normally not visited automatically.
* @param visitor the visitor to apply to the nodes.
* @return a list of parameter nodes, potentially modified from original ones by the visitor.
*/
public List<IdentNode> visitParameters(final NodeVisitor<? extends LexicalContext> visitor) {
return Node.accept(visitor, parameters);
}
/**
* Get additional callsite flags to be used specific to this function.
*
* @return callsite flags
*/
public int getCallSiteFlags() {
int callsiteFlags = 0;
if (getFlag(IS_STRICT)) {
callsiteFlags |= CALLSITE_STRICT;
}
// quick check for extension callsite flags turned on by directives.
if ((flags & EXTENSION_CALLSITE_FLAGS) == 0) {
return callsiteFlags;
}
if (getFlag(IS_PROFILE)) {
callsiteFlags |= CALLSITE_PROFILE;
}
if (getFlag(IS_TRACE_MISSES)) {
callsiteFlags |= CALLSITE_TRACE | CALLSITE_TRACE_MISSES;
}
if (getFlag(IS_TRACE_VALUES)) {
callsiteFlags |= CALLSITE_TRACE | CALLSITE_TRACE_ENTEREXIT | CALLSITE_TRACE_VALUES;
}
if (getFlag(IS_TRACE_ENTEREXIT)) {
callsiteFlags |= CALLSITE_TRACE | CALLSITE_TRACE_ENTEREXIT;
}
return callsiteFlags;
}
/**
* Get the source for this function
* @return the source
*/
public Source getSource() {
return source;
}
/**
* Sets the source and namespace for this function. It can only set a non-null source and namespace for a function
* that currently has both a null source and a null namespace. This is used to re-set the source and namespace for
* a deserialized function node.
* @param source the source for the function.
* @param namespace the namespace for the function
* @return a new function node with the set source and namespace
* @throws IllegalArgumentException if the specified source or namespace is null
* @throws IllegalStateException if the function already has either a source or namespace set.
*/
public FunctionNode initializeDeserialized(final Source source, final Namespace namespace) {
if (source == null || namespace == null) {
throw new IllegalArgumentException();
} else if (this.source == source && this.namespace == namespace) {
return this;
} else if (this.source != null || this.namespace != null) {
throw new IllegalStateException();
}
return new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass, source, namespace);
}
/**
* Get the unique ID for this function within the script file.
* @return the id
*/
public int getId() {
return position();
}
/**
* get source name - sourceURL or name derived from Source.
*
* @return name for the script source
*/
public String getSourceName() {
return getSourceName(source);
}
/**
* Static source name getter
*
* @param source the source
* @return source name
*/
public static String getSourceName(final Source source) {
final String explicitURL = source.getExplicitURL();
return explicitURL != null ? explicitURL : source.getName();
}
/**
* Function to parse nashorn per-function extension directive comments.
*
* @param directive nashorn extension directive string
* @return integer flag for the given directive.
*/
public static int getDirectiveFlag(final String directive) {
switch (directive) {
case "nashorn callsite trace enterexit":
return IS_TRACE_ENTEREXIT;
case "nashorn callsite trace misses":
return IS_TRACE_MISSES;
case "nashorn callsite trace objects":
return IS_TRACE_VALUES;
case "nashorn callsite profile":
return IS_PROFILE;
case "nashorn print parse":
return IS_PRINT_PARSE;
case "nashorn print lower parse":
return IS_PRINT_LOWER_PARSE;
case "nashorn print ast":
return IS_PRINT_AST;
case "nashorn print lower ast":
return IS_PRINT_LOWER_AST;
case "nashorn print symbols":
return IS_PRINT_SYMBOLS;
default:
// unknown/unsupported directive
return 0;
}
}
/**
* Returns the line number.
* @return the line number.
*/
public int getLineNumber() {
return lineNumber;
}
/**
* 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);
}
@Override
public void toString(final StringBuilder sb, final boolean printTypes) {
sb.append('[').
append(returnType).
append(']').
append(' ');
sb.append("function");
if (ident != null) {
sb.append(' ');
ident.toString(sb, printTypes);
}
sb.append('(');
for (final Iterator<IdentNode> iter = parameters.iterator(); iter.hasNext(); ) {
final IdentNode parameter = iter.next();
if (parameter.getSymbol() != null) {
sb.append('[').append(parameter.getType()).append(']').append(' ');
}
parameter.toString(sb, printTypes);
if (iter.hasNext()) {
sb.append(", ");
}
}
sb.append(')');
}
@Override
public int getFlags() {
return flags;
}
@Override
public boolean getFlag(final int flag) {
return (flags & flag) != 0;
}
@Override
public FunctionNode setFlags(final LexicalContext lc, final int flags) {
if (this.flags == flags) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass, source, namespace));
}
@Override
public FunctionNode clearFlag(final LexicalContext lc, final int flag) {
return setFlags(lc, flags & ~flag);
}
@Override
public FunctionNode setFlag(final LexicalContext lc, final int flag) {
return setFlags(lc, flags | flag);
}
/**
* 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 getFlag(IS_PROGRAM);
}
/**
* Returns true if the function contains at least one optimistic operation (and thus can be deoptimized).
* @return true if the function contains at least one optimistic operation (and thus can be deoptimized).
*/
public boolean canBeDeoptimized() {
return getFlag(IS_DEOPTIMIZABLE);
}
/**
* Check if this function has a call expression for the identifier "eval" (that is, {@code eval(...)}).
*
* @return true if {@code eval} is called.
*/
public boolean hasEval() {
return getFlag(HAS_EVAL);
}
/**
* Returns true if a function nested (directly or transitively) within this function {@link #hasEval()}.
*
* @return true if a nested function calls {@code eval}.
*/
public boolean hasNestedEval() {
return getFlag(HAS_NESTED_EVAL);
}
/**
* Get the first token for this function
* @return the first token
*/
public long getFirstToken() {
return firstToken;
}
/**
* Check whether this function has nested function declarations
* @return true if nested function declarations exist
*/
public boolean hasDeclaredFunctions() {
return getFlag(HAS_FUNCTION_DECLARATIONS);
}
/**
* 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. We also return true
* for split functions to make sure symbols slots are the same in the main and split methods.
*
* A function that has had an apply(this,arguments) turned into a call doesn't need arguments anymore, but still
* has to fit the old callsite, thus, we require a dummy callee parameter for those functions as well
*
* @return true if the function's generated Java method needs a {@code callee} parameter.
*/
public boolean needsCallee() {
// NOTE: we only need isSplit() here to ensure that :scope can never drop below slot 2 for splitting array units.
return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasApplyToCallSpecialization();
}
/**
* Return {@code true} if this function makes use of the {@code this} object.
*
* @return true if function uses {@code this} object
*/
public boolean usesThis() {
return getFlag(USES_THIS);
}
/**
* Return true if function contains an apply to call transform
* @return true if this function has transformed apply to call
*/
public boolean hasApplyToCallSpecialization() {
return getFlag(HAS_APPLY_TO_CALL_SPECIALIZATION);
}
/**
* Get the identifier for this function, this is its symbol.
* @return the identifier as an IdentityNode
*/
public IdentNode getIdent() {
return ident;
}
/**
* Get the function body
* @return the function body
*/
public Block getBody() {
return body;
}
/**
* Reset the function body
* @param lc lexical context
* @param body new body
* @return new function node if body changed, same if not
*/
public FunctionNode setBody(final LexicalContext lc, final Block body) {
if (this.body == body) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags |
(body.needsScope() ?
FunctionNode.HAS_SCOPE_BLOCK :
0),
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass, source, namespace));
}
/**
* 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;
}
/**
* Was this function declared in a dynamic context, i.e. in a with or eval style
* chain
* @return true if in dynamic context
*/
public boolean inDynamicContext() {
return getFlag(IN_DYNAMIC_CONTEXT);
}
/**
* Check whether a function would need dynamic scope, which is does if it has
* evals and isn't strict.
* @return true if dynamic scope is needed
*/
public boolean needsDynamicScope() {
// Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new
// variable into the function's scope), and it isn't strict (as evals in strict functions get an
// isolated scope).
return hasEval() && !isStrict();
}
/**
* Flag this function as declared in a dynamic context
* @param lc lexical context
* @return new function node, or same if unmodified
*/
public FunctionNode setInDynamicContext(final LexicalContext lc) {
return setFlag(lc, IN_DYNAMIC_CONTEXT);
}
/**
* 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 getFlag(MAYBE_NEEDS_ARGUMENTS) && !getFlag(DEFINES_ARGUMENTS) && !isProgram();
}
/**
* 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 getFlag(NEEDS_PARENT_SCOPE);
}
/**
* Set the number of properties assigned to the this object in this function.
* @param lc the current lexical context.
* @param thisProperties number of properties
* @return a potentially modified function node
*/
public FunctionNode setThisProperties(final LexicalContext lc, final int thisProperties) {
if (this.thisProperties == thisProperties) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass, source, namespace));
}
/**
* Get the number of properties assigned to the this object in this function.
* @return number of properties
*/
public int getThisProperties() {
return thisProperties;
}
/**
* Returns true if any of the blocks in this function create their own scope.
* @return true if any of the blocks in this function create their own scope.
*/
public boolean hasScopeBlock() {
return getFlag(HAS_SCOPE_BLOCK);
}
/**
* Return the kind of this function
* @see FunctionNode.Kind
* @return the kind
*/
public Kind getKind() {
return 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 lc lexical context
* @param lastToken the last token
* @return function node or a new one if state was changed
*/
public FunctionNode setLastToken(final LexicalContext lc, final long lastToken) {
if (this.lastToken == lastToken) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass, source, namespace));
}
/**
* Returns the end parser state for this function.
* @return the end parser state for this function.
*/
public Object getEndParserState() {
return endParserState;
}
/**
* Set the end parser state for this function.
* @param lc lexical context
* @param endParserState the parser state to set
* @return function node or a new one if state was changed
*/
public FunctionNode setEndParserState(final LexicalContext lc, final Object endParserState) {
if (this.endParserState == endParserState) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass,
source,
namespace));
}
/**
* Get the name of this function
* @return the name
*/
public String getName() {
return name;
}
/**
* Set the internal name for this function
* @param lc lexical context
* @param name new name
* @return new function node if changed, otherwise the same
*/
public FunctionNode setName(final LexicalContext lc, final String name) {
if (this.name.equals(name)) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass,
source,
namespace));
}
/**
* Check if this function should have all its variables in its own scope. 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 getFlag(HAS_ALL_VARS_IN_SCOPE);
}
/**
* Checks if this function is split into several smaller fragments.
*
* @return true if this function is split into several smaller fragments.
*/
public boolean isSplit() {
return getFlag(IS_SPLIT);
}
/**
* 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);
}
/**
* Return the number of parameters to this function
* @return the number of parameters
*/
public int getNumOfParams() {
return parameters.size();
}
/**
* Returns the identifier for a named parameter at the specified position in this function's parameter list.
* @param index the parameter's position.
* @return the identifier for the requested named parameter.
* @throws IndexOutOfBoundsException if the index is invalid.
*/
public IdentNode getParameter(final int index) {
return parameters.get(index);
}
/**
* Reset the compile unit used to compile this function
* @see Compiler
* @param lc lexical context
* @param parameters the compile unit
* @return function node or a new one if state was changed
*/
public FunctionNode setParameters(final LexicalContext lc, final List<IdentNode> parameters) {
if (this.parameters == parameters) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass, source, namespace));
}
/**
* 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 getFlag(IS_DECLARED);
}
/**
* Check if this function is anonymous
* @return true if function is anonymous
*/
public boolean isAnonymous() {
return getFlag(IS_ANONYMOUS);
}
/**
* Does this function use its self symbol - this is needed only for self-referencing named function expressions.
* Self-referencing declared functions won't have this flag set, as they can access their own symbol through the
* scope (since they're bound to the symbol with their name in their enclosing scope).
* @return true if this function node is a named function expression that uses the symbol for itself.
*/
public boolean usesSelfSymbol() {
return getFlag(USES_SELF_SYMBOL);
}
/**
* Returns true if this is a named function expression (that is, it isn't a declared function, it isn't an
* anonymous function expression, and it isn't a program).
* @return true if this is a named function expression
*/
public boolean isNamedFunctionExpression() {
return !getFlag(IS_PROGRAM | IS_ANONYMOUS | IS_DECLARED);
}
@Override
public Type getType() {
return FUNCTION_TYPE;
}
@Override
public Type getWidestOperationType() {
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 lc lexical context
* @param returnType new return type
* @return function node or a new one if state was changed
*/
public FunctionNode setReturnType(final LexicalContext lc, 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
final Type type = returnType.isObject() ? Type.OBJECT : returnType;
if (this.returnType == type) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
type,
compileUnit,
body,
parameters,
thisProperties,
rootClass, source, namespace
));
}
/**
* Check if the function is generated in strict mode
* @return true if strict mode enabled for function
*/
public boolean isStrict() {
return getFlag(IS_STRICT);
}
/**
* Returns true if this function node has been cached.
* @return true if this function node has been cached.
*/
public boolean isCached() {
return getFlag(IS_CACHED);
}
/**
* Mark this function node as having been cached.
* @param lc the current lexical context
* @return a function node equivalent to this one, with the flag set.
*/
public FunctionNode setCached(final LexicalContext lc) {
return setFlag(lc, IS_CACHED);
}
/**
* Get the compile unit used to compile this function
* @see Compiler
* @return the compile unit
*/
@Override
public CompileUnit getCompileUnit() {
return compileUnit;
}
/**
* Reset the compile unit used to compile this function
* @see Compiler
* @param lc lexical context
* @param compileUnit the compile unit
* @return function node or a new one if state was changed
*/
public FunctionNode setCompileUnit(final LexicalContext lc, final CompileUnit compileUnit) {
if (this.compileUnit == compileUnit) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass, source, namespace));
}
/**
* Create a temporary variable to the current frame.
*
* @param block that needs the temporary
* @param type Strong type of symbol.
* @param node Primary node to use symbol.
*
* @return Symbol used.
*/
/**
* Get the symbol for a compiler constant, or null if not available (yet)
* @param cc compiler constant
* @return symbol for compiler constant, or null if not defined yet (for example in Lower)
*/
public Symbol compilerConstant(final CompilerConstants cc) {
return body.getExistingSymbol(cc.symbolName());
}
/**
* Get the root class that this function node compiles to
* @return root class
*/
public Class<?> getRootClass() {
return rootClass;
}
/**
* Reset the root class that this function is compiled to
* @see Compiler
* @param lc lexical context
* @param rootClass root class
* @return function node or a new one if state was changed
*/
public FunctionNode setRootClass(final LexicalContext lc, final Class<?> rootClass) {
if (this.rootClass == rootClass) {
return this;
}
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
endParserState,
flags,
name,
returnType,
compileUnit,
body,
parameters,
thisProperties,
rootClass, source, namespace));
}
}