aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/ir/FunctionNode.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/ir/FunctionNode.java')
-rw-r--r--src/jdk/nashorn/internal/ir/FunctionNode.java818
1 files changed, 589 insertions, 229 deletions
diff --git a/src/jdk/nashorn/internal/ir/FunctionNode.java b/src/jdk/nashorn/internal/ir/FunctionNode.java
index a0f6a788..0806fea9 100644
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java
@@ -25,12 +25,19 @@
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.EnumSet;
-import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+import java.util.function.Function;
+import jdk.nashorn.internal.AssertsEnabled;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -39,6 +46,7 @@ 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;
@@ -48,7 +56,8 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
* IR representation for function (or script.)
*/
@Immutable
-public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode> {
+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);
@@ -77,26 +86,41 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
CONSTANT_FOLDED,
/** method has been lowered */
LOWERED,
- /** method hass been attributed */
- ATTR,
+ /** program points have been assigned to unique locations */
+ PROGRAM_POINTS_ASSIGNED,
+ /** any transformations of builtins have taken place, e.g. apply=&gt;call */
+ BUILTINS_TRANSFORMED,
/** method has been split */
SPLIT,
- /** method has had its types finalized */
- FINALIZED,
+ /** method has had symbols assigned */
+ SYMBOLS_ASSIGNED,
+ /** computed scope depths for symbols */
+ SCOPE_DEPTHS_COMPUTED,
+ /** method has had types calculated*/
+ OPTIMISTIC_TYPES_ASSIGNED,
+ /** method has had types calculated */
+ LOCAL_VARIABLE_TYPES_CALCULATED,
+ /** compile units reused (optional) */
+ COMPILE_UNITS_REUSED,
/** method has been emitted to bytecode */
- EMITTED
+ BYTECODE_GENERATED,
+ /** method has been installed */
+ BYTECODE_INSTALLED
}
+
/** Source of entity. */
- private final Source source;
+ 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;
- /** Parsed version of functionNode */
- @Ignore
- private final FunctionNode snapshot;
-
/** The body of the function node */
private final Block body;
@@ -118,32 +142,26 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Last token of function. **/
private final long lastToken;
- /** Declared symbols in this function node */
- @Ignore
- private final Set<Symbol> declaredSymbols;
-
/** Method's namespace. */
- private final Namespace namespace;
+ private transient final Namespace namespace;
/** Current compilation state */
@Ignore
private final EnumSet<CompilationState> compilationState;
+ /** Number of properties of "this" object assigned in this function */
@Ignore
- private final Compiler.Hints hints;
-
- /** Properties of this object assigned in this function */
- @Ignore
- private HashSet<String> thisProperties;
+ private final int thisProperties;
/** Function flags. */
private final int flags;
- /** //@ sourceURL or //# sourceURL for program function nodes */
- private final String sourceURL;
-
+ /** 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;
@@ -156,7 +174,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Does the function use the "arguments" identifier ? */
public static final int USES_ARGUMENTS = 1 << 3;
- /** Has this node been split because it was too large? */
+ /** 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
@@ -182,36 +200,87 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** 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;
- /** Is this function lazily compiled? */
- public static final int IS_LAZY = 1 << 10;
+ /** Does this function have nested declarations? */
+ public static final int HAS_FUNCTION_DECLARATIONS = 1 << 10;
- /** Does this function have lazy, yet uncompiled children */
- public static final int HAS_LAZY_CHILDREN = 1 << 11;
+ /** Does this function have optimistic expressions? (If it does, it can undergo deoptimizing recompilation.) */
+ public static final int IS_DEOPTIMIZABLE = 1 << 11;
- /** Does this function have lazy, yet uncompiled children */
- public static final int IS_PROGRAM = 1 << 12;
+ /** 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;
- /** Does this function have nested declarations? */
- public static final int HAS_FUNCTION_DECLARATIONS = 1 << 13;
+ /**
+ * Is this function the top-level program?
+ */
+ public static final int IS_PROGRAM = 1 << 13;
- /** Can this function be specialized? */
- public static final int CAN_SPECIALIZE = 1 << 14;
+ /**
+ * 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;
+ 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;
+
+ /** 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 | IS_SPLIT | HAS_LAZY_CHILDREN;
+ 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.
- * 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_EVAL | HAS_LAZY_CHILDREN;
+ /** 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;
@@ -223,14 +292,13 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @param lineNumber line number
* @param token token
* @param finish finish
- * @param firstToken first token of the funtion node (including the function declaration)
+ * @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
- * @param sourceURL sourceURL specified in script (optional)
*/
public FunctionNode(
final Source source,
@@ -243,8 +311,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
final String name,
final List<IdentNode> parameters,
final FunctionNode.Kind kind,
- final int flags,
- final String sourceURL) {
+ final int flags) {
super(token, finish);
this.source = source;
@@ -257,32 +324,33 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.lastToken = token;
this.namespace = namespace;
this.compilationState = EnumSet.of(CompilationState.INITIALIZED);
- this.declaredSymbols = new HashSet<>();
this.flags = flags;
- this.sourceURL = sourceURL;
this.compileUnit = null;
this.body = null;
- this.snapshot = null;
- this.hints = 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 sourceURL,
final String name,
final Type returnType,
final CompileUnit compileUnit,
final EnumSet<CompilationState> compilationState,
final Block body,
final List<IdentNode> parameters,
- final FunctionNode snapshot,
- final Compiler.Hints hints) {
+ final int thisProperties,
+ final Class<?> rootClass,
+ final Source source, Namespace namespace) {
super(functionNode);
+
+ this.endParserState = endParserState;
this.lineNumber = functionNode.lineNumber;
this.flags = flags;
- this.sourceURL = sourceURL;
this.name = name;
this.returnType = returnType;
this.compileUnit = compileUnit;
@@ -290,17 +358,15 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.compilationState = compilationState;
this.body = body;
this.parameters = parameters;
- this.snapshot = snapshot;
- this.hints = hints;
+ 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.source = functionNode.source;
this.ident = functionNode.ident;
- this.namespace = functionNode.namespace;
- this.declaredSymbols = functionNode.declaredSymbols;
this.kind = functionNode.kind;
this.firstToken = functionNode.firstToken;
- this.thisProperties = functionNode.thisProperties;
}
@Override
@@ -312,98 +378,156 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/**
- * Get the source for this function
- * @return the source
+ * 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 Source getSource() {
- return source;
+ public List<IdentNode> visitParameters(final NodeVisitor<? extends LexicalContext> visitor) {
+ return Node.accept(visitor, parameters);
}
/**
- * get source name - sourceURL or name derived from Source.
+ * Get additional callsite flags to be used specific to this function.
*
- * @return name for the script source
+ * @return callsite flags
*/
- public String getSourceName() {
- return (sourceURL != null)? sourceURL : source.getName();
+ 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 sourceURL
- * @return the sourceURL
+ * Get the source for this function
+ * @return the source
*/
- public String getSourceURL() {
- return sourceURL;
+ public Source getSource() {
+ return source;
}
/**
- * Set the sourceURL
- *
- * @param lc lexical context
- * @param newSourceURL source url string to set
- * @return function node or a new one if state was changed
+ * 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 setSourceURL(final LexicalContext lc, final String newSourceURL) {
- if (Objects.equals(sourceURL, newSourceURL)) {
+ 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 Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints));
+ return new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass, source, namespace);
}
/**
- * Returns the line number.
- * @return the line number.
+ * Get the unique ID for this function within the script file.
+ * @return the id
*/
- public int getLineNumber() {
- return lineNumber;
+ public int getId() {
+ return position();
}
/**
- * Get the version of this function node's code as it looked upon construction
- * i.e typically parsed and nothing else
- * @return initial version of function node
+ * get source name - sourceURL or name derived from Source.
+ *
+ * @return name for the script source
*/
- public FunctionNode getSnapshot() {
- return snapshot;
+ public String getSourceName() {
+ return getSourceName(source);
}
/**
- * Throw away the snapshot, if any, to save memory. Used when heuristic
- * determines that a method is not worth specializing
+ * Static source name getter
*
- * @param lc lexical context
- * @return new function node if a snapshot was present, now with snapsnot null
+ * @param source the source
+ * @return source name
*/
- public FunctionNode clearSnapshot(final LexicalContext lc) {
- if (this.snapshot == null) {
- return this;
- }
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints));
+ public static String getSourceName(final Source source) {
+ final String explicitURL = source.getExplicitURL();
+ return explicitURL != null ? explicitURL : source.getName();
}
/**
- * Take a snapshot of this function node at a given point in time
- * and store it in the function node
- * @param lc lexical context
- * @return function node
+ * Function to parse nashorn per-function extension directive comments.
+ *
+ * @param directive nashorn extension directive string
+ * @return integer flag for the given directive.
*/
- public FunctionNode snapshot(final LexicalContext lc) {
- if (this.snapshot == this) {
- return this;
- }
- if (isProgram() || parameters.isEmpty()) {
- return this; //never specialize anything that won't be recompiled
+ 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;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, this, hints));
}
/**
- * Can this function node be regenerated with more specific type args?
- * @return true if specialization is possible
+ * Returns the line number.
+ * @return the line number.
*/
- public boolean canSpecialize() {
- return snapshot != null && getFlag(CAN_SPECIALIZE);
+ public int getLineNumber() {
+ return lineNumber;
}
/**
@@ -421,21 +545,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @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);
+ return !AssertsEnabled.assertsEnabled() || compilationState.containsAll(state);
}
/**
@@ -448,35 +558,52 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @return function node or a new one if state was changed
*/
public FunctionNode setState(final LexicalContext lc, final CompilationState state) {
- if (this.compilationState.contains(state)) {
+ if (!AssertsEnabled.assertsEnabled() || this.compilationState.contains(state)) {
return this;
}
final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
newState.add(state);
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters, snapshot, hints));
- }
-
- /**
- * Get any compiler hints that may associated with the function
- * @return compiler hints
- */
- public Compiler.Hints getHints() {
- return this.hints == null ? Compiler.Hints.EMPTY : hints;
+ return setCompilationState(lc, newState);
}
/**
- * Set compiler hints for this function
- * @param lc lexical context
- * @param hints compiler hints
- * @return new function if hints changed
+ * Copy a compilation state from an original function to this function. Used when creating synthetic
+ * function nodes by the splitter.
+ *
+ * @param lc lexical context
+ * @param original the original function node to copy compilation state from
+ * @return function node or a new one if state was changed
*/
- public FunctionNode setHints(final LexicalContext lc, final Compiler.Hints hints) {
- if (this.hints == hints) {
+ public FunctionNode copyCompilationState(final LexicalContext lc, final FunctionNode original) {
+ final EnumSet<CompilationState> origState = original.compilationState;
+ if (!AssertsEnabled.assertsEnabled() || this.compilationState.containsAll(origState)) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+ final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
+ newState.addAll(origState);
+ return setCompilationState(lc, newState);
}
+ private FunctionNode setCompilationState(final LexicalContext lc, final EnumSet<CompilationState> compilationState) {
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass, source, namespace));
+ }
+
+
/**
* Create a unique name in the namespace of this FunctionNode
* @param base prefix for name
@@ -486,48 +613,67 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return namespace.uniqueName(base);
}
-
@Override
- public void toString(final StringBuilder sb) {
- sb.append('[');
- sb.append(returnType);
- sb.append(']');
- sb.append(' ');
+ 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);
+ ident.toString(sb, printTypes);
}
sb.append('(');
- boolean first = true;
- for (final IdentNode parameter : parameters) {
- if (!first) {
+ 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(", ");
- } else {
- first = false;
}
-
- parameter.toString(sb);
}
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, int flags) {
+ public FunctionNode setFlags(final LexicalContext lc, final int flags) {
if (this.flags == flags) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass, source, namespace));
}
@Override
@@ -549,23 +695,32 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/**
- * Should this function node be lazily code generated, i.e. first at link time
- * @return true if lazy
+ * 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 isLazy() {
- return getFlag(IS_LAZY);
+ public boolean canBeDeoptimized() {
+ return getFlag(IS_DEOPTIMIZABLE);
}
/**
- * Check if the {@code eval} keyword is used in this function
+ * Check if this function has a call expression for the identifier "eval" (that is, {@code eval(...)}).
*
- * @return true if {@code eval} is used
+ * @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
*/
@@ -587,10 +742,14 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* (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() {
- return needsParentScope() || needsSelfSymbol() || isSplit() || (needsArguments() && !isStrict());
+ // 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()) || hasOptimisticApplyToCall();
}
/**
@@ -602,30 +761,21 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return getFlag(USES_THIS);
}
- /**
- * Get the identifier for this function, this is its symbol.
- * @return the identifier as an IdentityNode
- */
- public IdentNode getIdent() {
- return ident;
- }
/**
- * Return a set of symbols declared in this function node. This
- * is only relevant after Attr, otherwise it will be an empty
- * set as no symbols have been introduced
- * @return set of declared symbols in function
+ * Return true if function contains an apply to call transform
+ * @return true if this function has transformed apply to call
*/
- public Set<Symbol> getDeclaredSymbols() {
- return Collections.unmodifiableSet(declaredSymbols);
+ public boolean hasOptimisticApplyToCall() {
+ return getFlag(HAS_APPLY_TO_CALL_SPECIALIZATION);
}
/**
- * Add a declared symbol to this function node
- * @param symbol symbol that is declared
+ * Get the identifier for this function, this is its symbol.
+ * @return the identifier as an IdentityNode
*/
- public void addDeclaredSymbol(final Symbol symbol) {
- declaredSymbols.add(symbol);
+ public IdentNode getIdent() {
+ return ident;
}
/**
@@ -643,10 +793,28 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @return new function node if body changed, same if not
*/
public FunctionNode setBody(final LexicalContext lc, final Block body) {
- if(this.body == body) {
+ if (this.body == body) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags |
+ (body.needsScope() ?
+ FunctionNode.HAS_SCOPE_BLOCK :
+ 0),
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass, source, namespace));
}
/**
@@ -662,6 +830,36 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/**
+ * 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
@@ -684,26 +882,43 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @return true if the function needs parent scope.
*/
public boolean needsParentScope() {
- return getFlag(NEEDS_PARENT_SCOPE) || isProgram();
+ return getFlag(NEEDS_PARENT_SCOPE);
}
/**
- * Register a property assigned to the this object in this function.
- * @param key the property name
+ * 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 void addThisProperty(final String key) {
- if (thisProperties == null) {
- thisProperties = new HashSet<>();
+ public FunctionNode setThisProperties(final LexicalContext lc, final int thisProperties) {
+ if (this.thisProperties == thisProperties) {
+ return this;
}
- thisProperties.add(key);
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ 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 countThisProperties() {
- return thisProperties == null ? 0 : thisProperties.size();
+ public int getThisProperties() {
+ return thisProperties;
}
/**
@@ -741,7 +956,60 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if (this.lastToken == lastToken) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ 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,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass,
+ source,
+ namespace));
}
/**
@@ -752,7 +1020,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return name;
}
-
/**
* Set the internal name for this function
* @param lc lexical context
@@ -763,38 +1030,46 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if (this.name.equals(name)) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass,
+ source,
+ namespace));
}
/**
- * Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and
+ * 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 isProgram() || getFlag(HAS_ALL_VARS_IN_SCOPE);
+ return getFlag(HAS_ALL_VARS_IN_SCOPE);
}
/**
- * Checks if this function is a sub-function generated by splitting a larger one
+ * Checks if this function is split into several smaller fragments.
*
- * @return true if this function is split from a larger one
+ * @return true if this function is split into several smaller fragments.
*/
public boolean isSplit() {
return getFlag(IS_SPLIT);
}
/**
- * Checks if this function has yet-to-be-generated child functions
- *
- * @return true if there are lazy child functions
- */
- public boolean hasLazyChildren() {
- return getFlag(HAS_LAZY_CHILDREN);
- }
-
- /**
* Get the parameters to this function
* @return a list of IdentNodes which represent the function parameters, in order
*/
@@ -803,6 +1078,16 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/**
+ * 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
@@ -813,7 +1098,22 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if (this.parameters == parameters) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass, source, namespace));
}
/**
@@ -833,16 +1133,22 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/**
- * Does this function need a self symbol - this is needed only for self
- * referring functions
- * @return true if function needs a symbol for self
+ * 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 needsSelfSymbol() {
- return body.getFlag(Block.NEEDS_SELF_SYMBOL);
+ public boolean usesSelfSymbol() {
+ return getFlag(USES_SELF_SYMBOL);
+ }
+
+ @Override
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
+ return FUNCTION_TYPE;
}
@Override
- public Type getType() {
+ public Type getWidestOperationType() {
return FUNCTION_TYPE;
}
@@ -867,27 +1173,28 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
//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
- if (this.returnType == returnType) {
+ final Type type = returnType.isObject() ? Type.OBJECT : returnType;
+ if (this.returnType == type) {
return this;
}
- final Type type = Type.widest(this.returnType, returnType.isObject() ? Type.OBJECT : returnType);
return Node.replaceInLexicalContext(
lc,
this,
new FunctionNode(
this,
lastToken,
+ endParserState,
flags,
- sourceURL,
name,
type,
compileUnit,
compilationState,
- body.setReturnType(type),
+ body,
parameters,
- snapshot,
- hints));
- }
+ thisProperties,
+ rootClass, source, namespace
+ ));
+ }
/**
* Check if the function is generated in strict mode
@@ -902,6 +1209,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @see Compiler
* @return the compile unit
*/
+ @Override
public CompileUnit getCompileUnit() {
return compileUnit;
}
@@ -917,7 +1225,22 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
if (this.compileUnit == compileUnit) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass, source, namespace));
}
/**
@@ -938,4 +1261,41 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
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,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass, source, namespace));
+ }
}