| /* |
| * 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)); |
| } |
| } |