8009718: Lazy execution architecture continued - ScriptFunctionData is either final or recompilable. Moved ScriptFunctionData creation logic away from runtime to compile time. Prepared for method generation/specialization. Got rid of ScriptFunctionImplTrampoline whose semantics could be done as part of the relinking anyway. Merge with the lookup package change.
Reviewed-by: attila, jlaskey
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Tue Mar 12 15:30:53 2013 +0100
@@ -42,6 +42,7 @@
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -51,6 +52,7 @@
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CallNode.EvalArgs;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -332,8 +334,14 @@
functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this));
}
+ if (functionNode.hasLazyChildren()) {
+ objectifySymbols(functionNode);
+ }
+
functionNode.popFrame();
+ functionNode.setState(CompilationState.ATTR);
+
end(functionNode, false);
return null;
@@ -957,20 +965,17 @@
@Override
public Node leaveBIT_AND(final BinaryNode binaryNode) {
- newTemporary(Type.INT, binaryNode);
- return binaryNode;
+ return end(coerce(binaryNode, Type.INT));
}
@Override
public Node leaveBIT_OR(final BinaryNode binaryNode) {
- newTemporary(Type.INT, binaryNode);
- return binaryNode;
+ return end(coerce(binaryNode, Type.INT));
}
@Override
public Node leaveBIT_XOR(final BinaryNode binaryNode) {
- newTemporary(Type.INT, binaryNode);
- return binaryNode;
+ return end(coerce(binaryNode, Type.INT));
}
@Override
@@ -1002,14 +1007,28 @@
return binaryNode;
}
+ private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) {
+ // TODO we currently don't support changing inferred type based on uses, only on
+ // definitions. we would need some additional logic. We probably want to do that
+ // in the future, if e.g. a specialized method gets parameter that is only used
+ // as, say, an int : function(x) { return x & 4711 }, and x is not defined in
+ // the function. to make this work, uncomment the following two type inferences
+ // and debug.
+
+ //newType(binaryNode.lhs().getSymbol(), operandType);
+ //newType(binaryNode.rhs().getSymbol(), operandType);
+ newTemporary(destType, binaryNode);
+ return binaryNode;
+ }
+
+ private Node coerce(final BinaryNode binaryNode, final Type type) {
+ return coerce(binaryNode, type, type);
+ }
+
//leave a binary node and inherit the widest type of lhs , rhs
private Node leaveBinaryArithmetic(final BinaryNode binaryNode) {
- if (!Compiler.shouldUseIntegerArithmetic()) {
- newTemporary(Type.NUMBER, binaryNode);
- return binaryNode;
- }
- newTemporary(Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType(), Type.NUMBER), binaryNode);
- return binaryNode;
+ assert !Compiler.shouldUseIntegerArithmetic();
+ return end(coerce(binaryNode, Type.NUMBER));
}
@Override
@@ -1089,23 +1108,17 @@
@Override
public Node leaveSAR(final BinaryNode binaryNode) {
- newTemporary(Type.INT, binaryNode);
- end(binaryNode);
- return binaryNode;
+ return end(coerce(binaryNode, Type.INT));
}
@Override
public Node leaveSHL(final BinaryNode binaryNode) {
- newTemporary(Type.INT, binaryNode);
- end(binaryNode);
- return binaryNode;
+ return end(coerce(binaryNode, Type.INT));
}
@Override
public Node leaveSHR(final BinaryNode binaryNode) {
- newTemporary(Type.LONG, binaryNode);
- end(binaryNode);
- return binaryNode;
+ return end(coerce(binaryNode, Type.LONG));
}
@Override
@@ -1211,11 +1224,17 @@
// type or its parameters with the widest (OBJECT) type for safety.
functionNode.setReturnType(Type.UNKNOWN);
- for (final IdentNode ident : functionNode.getParameters()) {
- addLocalDef(ident.getName());
- final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident);
+ for (final IdentNode param : functionNode.getParameters()) {
+ addLocalDef(param.getName());
+ final Symbol paramSymbol = functionNode.defineSymbol(param.getName(), IS_PARAM, param);
if (paramSymbol != null) {
- newType(paramSymbol, Type.UNKNOWN);
+ final Type callSiteParamType = functionNode.getSpecializedType(param);
+ if (callSiteParamType != null) {
+ LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that.");
+
+ System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that.");
+ }
+ newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
}
LOG.info("Initialized param " + paramSymbol);
@@ -1229,36 +1248,29 @@
* @param functionNode functionNode
*/
private static void finalizeParameters(final FunctionNode functionNode) {
- boolean nonObjectParams = false;
- List<Type> paramSpecializations = new ArrayList<>();
+ final boolean isVarArg = functionNode.isVarArg();
for (final IdentNode ident : functionNode.getParameters()) {
final Symbol paramSymbol = ident.getSymbol();
- if (paramSymbol != null) {
- Type type = paramSymbol.getSymbolType();
- if (type.isUnknown()) {
- type = Type.OBJECT;
- }
- paramSpecializations.add(type);
- if (!type.isObject()) {
- nonObjectParams = true;
- }
- newType(paramSymbol, Type.OBJECT);
+
+ assert paramSymbol != null;
+ Type type = functionNode.getSpecializedType(ident);
+ if (type == null) {
+ type = Type.OBJECT;
}
- }
- if (!nonObjectParams) {
- paramSpecializations = null;
- // Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters
- // here. If the callee has parameter specializations, we can regenerate it with those particular types for speed.
- } else {
- LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations);
- }
+ // if we know that a parameter is only used as a certain type throughout
+ // this function, we can tell the runtime system that no matter what the
+ // call site is, use this information. TODO
+ if (!paramSymbol.getSymbolType().isObject()) {
+ LOG.finest("Parameter " + ident + " could profit from specialization to " + paramSymbol.getSymbolType());
+ }
- // parameters should not be slots for a function that uses variable arity signature
- if (functionNode.isVarArg()) {
- for (final IdentNode param : functionNode.getParameters()) {
- param.getSymbol().setNeedsSlot(false);
+ newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
+
+ // parameters should not be slots for a function that uses variable arity signature
+ if (isVarArg) {
+ paramSymbol.setNeedsSlot(false);
}
}
}
@@ -1548,6 +1560,39 @@
localUses.add(name);
}
+ /**
+ * Pessimistically promote all symbols in current function node to Object types
+ * This is done when the function contains unevaluated black boxes such as
+ * lazy sub-function nodes that have not been compiled.
+ *
+ * @param functionNode function node in whose scope symbols should conservatively be made objects
+ */
+ private static void objectifySymbols(final FunctionNode functionNode) {
+ functionNode.accept(new NodeVisitor() {
+ private void toObject(final Block block) {
+ for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) {
+ final Symbol symbol = iter.next();
+ newType(symbol, Type.OBJECT);
+ }
+ }
+
+ @Override
+ public Node enter(final Block block) {
+ toObject(block);
+ return block;
+ }
+
+ @Override
+ public Node enter(final FunctionNode node) {
+ toObject(node);
+ if (node.isLazy()) {
+ return null;
+ }
+ return node;
+ }
+ });
+ }
+
private static String name(final Node node) {
final String cn = node.getClass().getName();
int lastDot = cn.lastIndexOf('.');
--- a/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java Tue Mar 12 15:30:53 2013 +0100
@@ -32,7 +32,6 @@
import static jdk.nashorn.internal.codegen.Condition.LT;
import static jdk.nashorn.internal.codegen.Condition.NE;
-import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Node;
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Mar 12 15:30:53 2013 +0100
@@ -25,10 +25,8 @@
package jdk.nashorn.internal.codegen;
-import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF;
@@ -50,7 +48,6 @@
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
import java.io.PrintWriter;
-import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
@@ -84,6 +81,7 @@
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
import jdk.nashorn.internal.ir.Node;
@@ -108,14 +106,13 @@
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Lexer.RegexToken;
import jdk.nashorn.internal.parser.TokenType;
-import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptFunction;
-import jdk.nashorn.internal.runtime.ScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
@@ -149,8 +146,6 @@
/** Name of the ScriptFunctionImpl, cannot be referred to as .class @see FunctionObjectCreator */
private static final String SCRIPTFUNCTION_IMPL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl";
- private static final String SCRIPTFUNCTION_TRAMPOLINE_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "ScriptFunctionTrampolineImpl";
-
/** Constant data & installation. The only reason the compiler keeps this is because it is assigned
* by reflection in class installation */
private final Compiler compiler;
@@ -982,6 +977,7 @@
method.label(functionNode.getEntryLabel());
initLocals(functionNode);
+ functionNode.setState(CompilationState.EMITTED);
return functionNode;
}
@@ -3234,42 +3230,23 @@
}
private void newFunctionObject(final FunctionNode functionNode) {
- final boolean isLazy = functionNode.isLazy();
- final Class<?>[] cparams = new Class<?>[] { ScriptFunctionData.class, ScriptObject.class, MethodHandle.class };
+ final boolean isLazy = functionNode.isLazy();
+ final Class<?>[] cparams = new Class<?>[] { RecompilableScriptFunctionData.class, ScriptObject.class };
new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
@Override
- protected void makeObject(final MethodEmitter method) {
- final String className = isLazy ? SCRIPTFUNCTION_TRAMPOLINE_OBJECT : SCRIPTFUNCTION_IMPL_OBJECT;
-
- method._new(className).dup();
- if (isLazy) {
- loadConstant(compiler.getCodeInstaller());
- loadConstant(functionNode);
- } else {
- final String signature = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
- method.loadHandle(functionNode.getCompileUnit().getUnitClassName(), functionNode.getName(), signature, EnumSet.of(HANDLE_STATIC)); // function
- }
- loadConstant(new ScriptFunctionData(functionNode, makeMap()));
+ protected void makeObject(final MethodEmitter m) {
+ final String className = SCRIPTFUNCTION_IMPL_OBJECT;
+
+ m._new(className).dup();
+ loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap()));
if (isLazy || functionNode.needsParentScope()) {
- method.loadScope();
+ m.loadScope();
} else {
- method.loadNull();
+ m.loadNull();
}
-
- method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
-
- final List<Class<?>> cparamList = new ArrayList<>();
- if (isLazy) {
- cparamList.add(CodeInstaller.class);
- cparamList.add(FunctionNode.class);
- } else {
- cparamList.add(MethodHandle.class);
- }
- cparamList.addAll(Arrays.asList(cparams));
-
- method.invoke(constructorNoLookup(className, cparamList.toArray(new Class<?>[cparamList.size()])));
+ m.invoke(constructorNoLookup(className, cparams));
}
}.makeObject(method);
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Tue Mar 12 15:30:53 2013 +0100
@@ -2,10 +2,10 @@
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.ATTR;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.EMITTED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.FINALIZED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
import java.io.File;
@@ -39,7 +39,7 @@
* default policy. The will get trampolines and only be generated when
* called
*/
- LAZY_INITIALIZATION_PHASE(EnumSet.of(FunctionNode.CompilationState.INITIALIZED)) {
+ LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override
void transform(final Compiler compiler, final FunctionNode fn) {
@@ -79,9 +79,11 @@
if (node == outermostFunctionNode) {
return node;
}
- assert Compiler.LAZY_JIT;
+ assert compiler.isLazy();
lazy.add(node);
+ //also needs scope, potentially needs arguments etc etc
+
return node;
}
});
@@ -113,7 +115,7 @@
* Constant folding pass
* Simple constant folding that will make elementary constructs go away
*/
- CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED), CONSTANT_FOLDED) {
+ CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override
void transform(final Compiler compiler, final FunctionNode fn) {
fn.accept(new FoldConstants());
@@ -134,7 +136,7 @@
* as runtime nodes where applicable.
*
*/
- LOWERING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED), LOWERED) {
+ LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
@Override
void transform(final Compiler compiler, final FunctionNode fn) {
fn.accept(new Lower());
@@ -150,19 +152,10 @@
* Attribution
* Assign symbols and types to all nodes.
*/
- ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED), ATTR) {
+ ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
@Override
void transform(final Compiler compiler, final FunctionNode fn) {
- final ScriptEnvironment env = compiler.getEnv();
-
fn.accept(new Attr());
- if (env._print_lower_ast) {
- env.getErr().println(new ASTWriter(fn));
- }
-
- if (env._print_lower_parse) {
- env.getErr().println(new PrintVisitor(fn));
- }
}
@Override
@@ -178,7 +171,7 @@
* a + b a ScriptRuntime.ADD with call overhead or a dadd with much
* less). Split IR can lead to scope information being changed.
*/
- SPLITTING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR), SPLIT) {
+ SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
@Override
void transform(final Compiler compiler, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
@@ -212,10 +205,20 @@
* Contract: all variables must have slot assignments and scope assignments
* before type finalization.
*/
- TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT), FINALIZED) {
+ TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
@Override
void transform(final Compiler compiler, final FunctionNode fn) {
+ final ScriptEnvironment env = compiler.getEnv();
+
fn.accept(new FinalizeTypes());
+
+ if (env._print_lower_ast) {
+ env.getErr().println(new ASTWriter(fn));
+ }
+
+ if (env._print_lower_parse) {
+ env.getErr().println(new PrintVisitor(fn));
+ }
}
@Override
@@ -229,7 +232,7 @@
*
* Generate the byte code class(es) resulting from the compiled FunctionNode
*/
- BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED), EMITTED) {
+ BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
@Override
void transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv();
@@ -306,18 +309,12 @@
};
private final EnumSet<CompilationState> pre;
- private final CompilationState post;
private long startTime;
private long endTime;
private boolean isFinished;
private CompilationPhase(final EnumSet<CompilationState> pre) {
- this(pre, null);
- }
-
- private CompilationPhase(final EnumSet<CompilationState> pre, final CompilationState post) {
- this.pre = pre;
- this.post = post;
+ this.pre = pre;
}
boolean isApplicable(final FunctionNode functionNode) {
@@ -343,10 +340,6 @@
endTime = System.currentTimeMillis();
Timing.accumulateTime(toString(), endTime - startTime);
- if (post != null) {
- functionNode.setState(post);
- }
-
isFinished = true;
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java Tue Mar 12 15:30:53 2013 +0100
@@ -37,6 +37,8 @@
private long weight;
+ private Class<?> clazz;
+
CompileUnit(final String className, final ClassEmitter classEmitter) {
this(className, classEmitter, 0L);
}
@@ -48,6 +50,24 @@
}
/**
+ * Return the class that contains the code for this unit, null if not
+ * generated yet
+ *
+ * @return class with compile unit code
+ */
+ public Class<?> getCode() {
+ return clazz;
+ }
+
+ /**
+ * Set class when it exists. Only accessible from compiler
+ * @param clazz class with code for this compile unit
+ */
+ void setCode(final Class<?> clazz) {
+ this.clazz = clazz;
+ }
+
+ /**
* Add weight to this compile unit
* @param w weight to add
*/
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Tue Mar 12 15:30:53 2013 +0100
@@ -45,11 +45,15 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.logging.Level;
+
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
@@ -71,8 +75,6 @@
/** Name of the objects package */
public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
- static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy");
-
private final Map<String, byte[]> bytecode;
private final Set<CompileUnit> compileUnits;
@@ -164,7 +166,7 @@
* and JIT it at once. This can lead to long startup time and fewer type
* specializations
*/
- final static CompilationSequence SEQUENCE_NORMAL = new CompilationSequence(
+ final static CompilationSequence SEQUENCE_EAGER = new CompilationSequence(
CompilationPhase.CONSTANT_FOLDING_PHASE,
CompilationPhase.LOWERING_PHASE,
CompilationPhase.ATTRIBUTION_PHASE,
@@ -173,12 +175,15 @@
CompilationPhase.BYTECODE_GENERATION_PHASE);
final static CompilationSequence SEQUENCE_LAZY =
- SEQUENCE_NORMAL.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
+ SEQUENCE_EAGER.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
- final static CompilationSequence SEQUENCE_DEFAULT =
- LAZY_JIT ?
- SEQUENCE_LAZY :
- SEQUENCE_NORMAL;
+ private static CompilationSequence sequence(final boolean lazy) {
+ return lazy ? SEQUENCE_LAZY : SEQUENCE_EAGER;
+ }
+
+ boolean isLazy() {
+ return sequence == SEQUENCE_LAZY;
+ }
private static String lazyTag(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
@@ -213,10 +218,7 @@
this.scriptName = sb.toString();
- LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'");
- if (functionNode.isLazy()) {
- LOG.info(">>> This is a lazy recompilation triggered by a trampoline");
- }
+ LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "' lazy=" + functionNode.isLazy());
}
/**
@@ -227,7 +229,7 @@
* @param strict should this compilation use strict mode semantics
*/
public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final boolean strict) {
- this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, strict);
+ this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), strict);
}
/**
@@ -237,7 +239,7 @@
* @param functionNode function node (in any available {@link CompilationState}) to compile
*/
public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode) {
- this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, installer.getOwner()._strict);
+ this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict);
}
/**
@@ -247,28 +249,104 @@
* @param functionNode functionNode to compile
*/
public Compiler(final ScriptEnvironment env, final FunctionNode functionNode) {
- this(env, null, functionNode, SEQUENCE_DEFAULT, env._strict);
+ this(env, null, functionNode, sequence(env._lazy_compilation), env._strict);
+ }
+
+ /**
+ * Execute the compilation this Compiler was created with
+ * @params param types if known, for specialization
+ * @throws CompilationException if something goes wrong
+ * @return this compiler, for possible chaining
+ */
+ public Compiler compile() throws CompilationException {
+ return compile(null);
}
/**
* Execute the compilation this Compiler was created with
+ * @param paramTypes param types if known, for specialization
* @throws CompilationException if something goes wrong
+ * @return this compiler, for possible chaining
*/
- public void compile() throws CompilationException {
+ public Compiler compile(final Class<?> paramTypes) throws CompilationException {
for (final String reservedName : RESERVED_NAMES) {
functionNode.uniqueName(reservedName);
}
+ final boolean fine = !LOG.levelAbove(Level.FINE);
+ final boolean info = !LOG.levelAbove(Level.INFO);
+
+ long time = 0L;
+
for (final CompilationPhase phase : sequence) {
phase.apply(this, functionNode);
- final String end = phase.toString() + " done for function '" + functionNode.getName() + "'";
- if (Timing.isEnabled()) {
- final long duration = phase.getEndTime() - phase.getStartTime();
- LOG.info(end + " in " + duration + " ms");
- } else {
- LOG.info(end);
+
+ final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
+ time += duration;
+
+ if (fine) {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append(phase.toString()).
+ append(" done for function '").
+ append(functionNode.getName()).
+ append('\'');
+
+ if (duration > 0L) {
+ sb.append(" in ").
+ append(duration).
+ append(" ms ");
+ }
+
+ LOG.fine(sb.toString());
}
}
+
+ if (info) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Compile job for '").
+ append(functionNode.getName()).
+ append("' finished");
+
+ if (time > 0L) {
+ sb.append(" in ").
+ append(time).
+ append(" ms");
+ }
+
+ LOG.info(sb.toString());
+ }
+
+ return this;
+ }
+
+ private Class<?> install(final String className, final byte[] code) {
+ LOG.fine("Installing class " + className);
+
+ final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
+
+ try {
+ final Source source = getSource();
+ final Object[] constants = getConstantData().toArray();
+ // Need doPrivileged because these fields are private
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ //use reflection to write source and constants table to installed classes
+ final Field sourceField = clazz.getDeclaredField(SOURCE.tag());
+ final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
+ sourceField.setAccessible(true);
+ constantsField.setAccessible(true);
+ sourceField.set(null, source);
+ constantsField.set(null, constants);
+ return null;
+ }
+ });
+ } catch (final PrivilegedActionException e) {
+ throw new RuntimeException(e);
+ }
+
+ return clazz;
}
/**
@@ -280,42 +358,38 @@
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
- Class<?> rootClass = null;
+ final Map<String, Class<?>> installedClasses = new HashMap<>();
+
+ final String rootClassName = firstCompileUnitName();
+ final Class<?> rootClass = install(rootClassName, bytecode.get(rootClassName));
+
+ installedClasses.put(rootClassName, rootClass);
for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
- final String className = entry.getKey();
- LOG.fine("Installing class " + className);
-
- final byte[] code = entry.getValue();
- final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
-
- if (rootClass == null && firstCompileUnitName().equals(className)) {
- rootClass = clazz;
+ final String className = entry.getKey();
+ if (className.equals(rootClassName)) {
+ continue;
}
+ installedClasses.put(className, install(className, entry.getValue()));
+ }
- try {
- final Source source = getSource();
- final Object[] constants = getConstantData().toArray();
- // Need doPrivileged because these fields are private
- AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
- @Override
- public Void run() throws Exception {
- //use reflection to write source and constants table to installed classes
- final Field sourceField = clazz.getDeclaredField(SOURCE.tag());
- final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
- sourceField.setAccessible(true);
- constantsField.setAccessible(true);
- sourceField.set(null, source);
- constantsField.set(null, constants);
- return null;
- }
- });
- } catch (final PrivilegedActionException e) {
- throw new RuntimeException(e);
- }
+ for (final CompileUnit unit : compileUnits) {
+ unit.setCode(installedClasses.get(unit.getUnitClassName()));
}
+ functionNode.accept(new NodeVisitor() {
+ @Override
+ public Node enter(final FunctionNode node) {
+ if (node.isLazy()) {
+ return null;
+ }
+ node.setState(CompilationState.INSTALLED);
+ return node;
+ }
+ });
+
LOG.info("Installed root class: " + rootClass + " and " + bytecode.size() + " compile unit classes");
+
if (Timing.isEnabled()) {
final long duration = System.currentTimeMillis() - t0;
Timing.accumulateTime("[Code Installation]", duration);
@@ -444,8 +518,6 @@
* TODO: We currently generate no overflow checks so this is
* disabled
*
- * @see #shouldUseIntegers()
- *
* @return true if arithmetic operations should not widen integer
* operands by default.
*/
@@ -460,4 +532,5 @@
assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
}
+
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Tue Mar 12 15:30:53 2013 +0100
@@ -34,6 +34,7 @@
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CallNode.EvalArgs;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.DoWhileNode;
@@ -235,19 +236,19 @@
@Override
public Node leaveBIT_AND(BinaryNode binaryNode) {
- assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : binaryNode.getSymbol();
+ assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@Override
public Node leaveBIT_OR(BinaryNode binaryNode) {
- assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
+ assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@Override
public Node leaveBIT_XOR(BinaryNode binaryNode) {
- assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
+ assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@@ -255,7 +256,7 @@
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null;
binaryNode.setRHS(discard(binaryNode.rhs()));
- // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
+ // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
// in that case, update the node type as well
propagateType(binaryNode, binaryNode.lhs().getType());
return binaryNode;
@@ -344,7 +345,7 @@
@Override
public Node leaveSHR(final BinaryNode binaryNode) {
- assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong();
+ assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong() : "long coercion expected: " + binaryNode.getSymbol();
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@@ -432,6 +433,8 @@
}
updateSymbols(functionNode);
+ functionNode.setState(CompilationState.FINALIZED);
+
return functionNode;
}
@@ -511,7 +514,6 @@
@Override
public Node leave(final VarNode varNode) {
-
final Node rhs = varNode.getInit();
if (rhs != null) {
Type destType = specialize(varNode);
--- a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Tue Mar 12 15:30:53 2013 +0100
@@ -30,11 +30,13 @@
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
+import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.UnaryNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.JSType;
@@ -72,6 +74,20 @@
}
@Override
+ public Node enter(final FunctionNode functionNode) {
+ if (functionNode.isLazy()) {
+ return null;
+ }
+ return functionNode;
+ }
+
+ @Override
+ public Node leave(final FunctionNode functionNode) {
+ functionNode.setState(CompilationState.CONSTANT_FOLDED);
+ return functionNode;
+ }
+
+ @Override
public Node leave(final IfNode ifNode) {
final Node test = ifNode.getTest();
if (test instanceof LiteralNode) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java Tue Mar 12 15:30:53 2013 +0100
@@ -146,7 +146,7 @@
/**
* Create a function signature given a function node, using as much
- * type information for parameters and return types that is availabe
+ * type information for parameters and return types that is available
*
* @param functionNode the function node
*/
@@ -202,6 +202,14 @@
return methodType;
}
+ /**
+ * Return the return type for this function signature
+ * @return the return type
+ */
+ public Type getReturnType() {
+ return returnType;
+ }
+
private static Type[] objectArgs(final int nArgs) {
final Type[] array = new Type[nArgs];
for (int i = 0; i < nArgs; i++) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Tue Mar 12 15:30:53 2013 +0100
@@ -69,6 +69,7 @@
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
@@ -332,6 +333,8 @@
LOG.info("END FunctionNode: " + functionNode.getName());
unnest(functionNode);
+ functionNode.setState(CompilationState.LOWERED);
+
return null;
}
@@ -636,7 +639,7 @@
} else if (conservativeAlwaysTrue(test)) {
node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish());
((ForNode)node).setBody(body);
- ((ForNode)node).accept(this);
+ node.accept(this);
setTerminal(node, !escapes);
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Tue Mar 12 15:30:53 2013 +0100
@@ -39,6 +39,7 @@
import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
@@ -92,15 +93,16 @@
*/
void split() {
if (functionNode.isLazy()) {
- LOG.fine("Postponing split of '" + functionNode.getName() + "' as it's lazy");
+ LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy");
return;
}
- LOG.fine("Initiating split of '" + functionNode.getName() + "'");
+
+ LOG.finest("Initiating split of '" + functionNode.getName() + "'");
long weight = WeighNodes.weigh(functionNode);
if (weight >= SPLIT_THRESHOLD) {
- LOG.fine("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
+ LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
functionNode.accept(this);
@@ -133,6 +135,8 @@
for (final FunctionNode function : functionNode.getFunctions()) {
new Splitter(compiler, function, outermostCompileUnit).split();
}
+
+ functionNode.setState(CompilationState.SPLIT);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java Tue Mar 12 15:30:53 2013 +0100
@@ -647,21 +647,20 @@
}
private static void swap(final MethodVisitor method, final Type above, final Type below) {
- final MethodVisitor mv = method;
if (below.isCategory2()) {
if (above.isCategory2()) {
- mv.visitInsn(DUP2_X2);
- mv.visitInsn(POP2);
+ method.visitInsn(DUP2_X2);
+ method.visitInsn(POP2);
} else {
- mv.visitInsn(DUP_X2);
- mv.visitInsn(POP);
+ method.visitInsn(DUP_X2);
+ method.visitInsn(POP);
}
} else {
if (above.isCategory2()) {
- mv.visitInsn(DUP2_X1);
- mv.visitInsn(POP2);
+ method.visitInsn(DUP2_X1);
+ method.visitInsn(POP2);
} else {
- mv.visitInsn(SWAP);
+ method.visitInsn(SWAP);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java Tue Mar 12 15:30:53 2013 +0100
@@ -38,26 +38,25 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.codegen.Frame;
import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.ir.annotations.ParentNode;
+import jdk.nashorn.internal.ir.annotations.Reference;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for a list of statements and functions. All provides the
* basis for script body.
- *
*/
public class Block extends Node {
/** Parent context */
- @ParentNode @Ignore
+ @Reference
private Block parent;
- /** Owning function. */
- @Ignore //don't print it, it is apparent in the tree
+ /** Owning function - a FunctionNode has itself as function */
+ @Reference
protected FunctionNode function;
/** List of statements */
@@ -274,6 +273,14 @@
}
/**
+ * Get an iterator for all the symbols defined in this block
+ * @return symbol iterator
+ */
+ public Iterator<Symbol> symbolIterator() {
+ return symbols.values().iterator();
+ }
+
+ /**
* Search for symbol.
*
* @param name Symbol name.
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Tue Mar 12 15:30:53 2013 +0100
@@ -33,8 +33,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Stack;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
@@ -45,13 +47,13 @@
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Parser;
+import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.UserAccessorProperty;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
/**
* IR representation for function (or script.)
- *
*/
public class FunctionNode extends Block {
@@ -86,7 +88,9 @@
/** method has had its types finalized */
FINALIZED,
/** method has been emitted to bytecode */
- EMITTED
+ EMITTED,
+ /** code installed in a class loader */
+ INSTALLED
}
/** External function identifier. */
@@ -173,6 +177,9 @@
@Ignore
private final EnumSet<CompilationState> compilationState;
+ /** Type hints, e.g based on parameters at call site */
+ private final Map<IdentNode, Type> specializedTypes;
+
/** Function flags. */
private int flags;
@@ -256,22 +263,27 @@
// constructed, so it can't be seen from other threads.
this.function = this;
this.compilationState = EnumSet.of(CompilationState.INITIALIZED);
+ this.specializedTypes = new HashMap<>();
}
@SuppressWarnings("LeakingThisInConstructor")
private FunctionNode(final FunctionNode functionNode, final CopyState cs) {
super(functionNode, cs);
+ this.functions = new ArrayList<>();
+ for (final FunctionNode f : functionNode.getFunctions()) {
+ this.functions.add((FunctionNode)cs.existingOrCopy(f));
+ }
+
this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident);
this.name = functionNode.name;
this.kind = functionNode.kind;
this.parameters = new ArrayList<>();
for (final IdentNode param : functionNode.getParameters()) {
- this.parameters.add((IdentNode) cs.existingOrCopy(param));
+ this.parameters.add((IdentNode)cs.existingOrCopy(param));
}
- this.functions = new ArrayList<>();
this.firstToken = functionNode.firstToken;
this.lastToken = functionNode.lastToken;
this.namespace = functionNode.getNamespace();
@@ -300,6 +312,7 @@
this.function = this;
this.compilationState = EnumSet.copyOf(functionNode.compilationState);
+ this.specializedTypes = new HashMap<>();
}
@Override
@@ -710,10 +723,14 @@
* 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 conservatively need a callee if we have lazy children, i.e. nested function nodes that have not yet
+ * been evaluated. _They_ may need the callee and we don't know it
+ *
* @return true if the function's generated Java method needs a {@code callee} parameter.
*/
public boolean needsCallee() {
- return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode());
+ return hasLazyChildren() || needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode());
}
/**
@@ -919,6 +936,27 @@
}
/**
+ * Get a specialized type for an identity, if one exists
+ * @param node node to check specialized type for
+ * @return null if no specialization exists, otherwise type
+ */
+ public Type getSpecializedType(final IdentNode node) {
+ return specializedTypes.get(node);
+ }
+
+ /**
+ * Set parameter type hints for specialization.
+ * @param types types array of length equal to parameter list size
+ */
+ public void setParameterTypes(final Class<?>[] types) {
+ assert types.length == parameters.size() : "Type vector length doesn't correspond to parameter types";
+ //diff - skip the callee and this etc, they are not explicit params in the parse tree
+ for (int i = 0; i < types.length ; i++) {
+ specializedTypes.put(parameters.get(i), Type.typeFor(types[i]));
+ }
+ }
+
+ /**
* Get the identifier for the variable in which the function return value
* should be stored
* @return an IdentNode representing the return value
@@ -1266,7 +1304,7 @@
* @param parentBlock a block to remember as parent
*/
public void addReferencingParentBlock(final Block parentBlock) {
- assert parentBlock.getFunction() == function.findParentFunction(); // all parent blocks must be in the same function
+ assert parentBlock.getFunction() == function.findParentFunction() : Debug.id(parentBlock.getFunction()) + "!=" + Debug.id(function.findParentFunction()); // all parent blocks must be in the same function
if (parentBlock != function.getParent()) {
if (referencingParentBlocks == null) {
referencingParentBlocks = new LinkedList<>();
--- a/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java Tue Mar 12 15:30:53 2013 +0100
@@ -28,7 +28,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
@@ -36,9 +35,6 @@
* IR representation of an object literal.
*/
public class ObjectNode extends Node {
- /** Literal context. */
- @Ignore
- private Block context;
/** Literal elements. */
private final List<Node> elements;
@@ -49,13 +45,11 @@
* @param source the source
* @param token token
* @param finish finish
- * @param context the block for this ObjectNode
* @param elements the elements used to initialize this ObjectNode
*/
- public ObjectNode(final Source source, final long token, final int finish, final Block context, final List<Node> elements) {
+ public ObjectNode(final Source source, final long token, final int finish, final List<Node> elements) {
super(source, token, finish);
- this.context = context;
this.elements = elements;
}
@@ -68,7 +62,6 @@
newElements.add(cs.existingOrCopy(element));
}
- this.context = (Block)cs.existingOrCopy(objectNode.context);
this.elements = newElements;
}
@@ -80,10 +73,6 @@
@Override
public Node accept(final NodeVisitor visitor) {
if (visitor.enter(this) != null) {
- if (context != null) {
- context = (Block)context.accept(visitor);
- }
-
for (int i = 0, count = elements.size(); i < count; i++) {
elements.set(i, elements.get(i).accept(visitor));
}
@@ -117,14 +106,6 @@
}
/**
- * Get the block that is this ObjectNode's literal context
- * @return the block
- */
- public Block getContext() {
- return context;
- }
-
- /**
* Get the elements of this literal node
* @return a list of elements
*/
--- a/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java Tue Mar 12 15:30:53 2013 +0100
@@ -152,9 +152,9 @@
if (isConvert) {
convertPos = sb.length();
- sb.append("((");
+ sb.append("(");
sb.append(getType());
- sb.append(")");
+ sb.append(")(");
}
if (!isPostfix && !isConvert) {
@@ -191,8 +191,6 @@
sb.setCharAt(convertPos, ' ');
}
}
-
- //TODO - conversions still have too many parenthesis - makes --print-lower-parse hard to read
}
/**
--- a/nashorn/src/jdk/nashorn/internal/ir/annotations/ChildNode.java Tue Mar 12 18:12:42 2013 +0530
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * 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.annotations;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * This is a child node, a real node, not a reference, to an IR node that should
- * be traversed.
- * <p>
- * TODO Currently not in use. Would make e.g. accept methods simple and unified
- * @see jdk.nashorn.internal.ir.Node
- */
-@Retention(value=RetentionPolicy.RUNTIME)
-public @interface ChildNode {
- /** order of traversal compared to other children */
- public int order() default -1;
-}
--- a/nashorn/src/jdk/nashorn/internal/ir/annotations/ParentNode.java Tue Mar 12 18:12:42 2013 +0530
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * 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.annotations;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Signifies a parent of a node, i.e. node that should not be traversed if we
- * go down the AST. In automatic parsing this can be handled by @Reference
- * annotations instead, as all parents are references.
- * <p>
- * TODO The use case is automating and creating one implementation of something like
- * Node.getParent()
- *
- * @see jdk.nashorn.internal.ir.Node
- */
-@Retention(value=RetentionPolicy.RUNTIME)
-public @interface ParentNode {
- // EMPTY
-}
--- a/nashorn/src/jdk/nashorn/internal/ir/annotations/Reference.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/annotations/Reference.java Tue Mar 12 15:30:53 2013 +0100
@@ -33,7 +33,6 @@
* AST traversal and cloning. Cloning currently as a rule uses
* existingOrSame for references and otherwise existingOrCopy
* <p>
- * TODO this could probably be automated using the @Reference annotation.
*/
@Retention(value=RetentionPolicy.RUNTIME)
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Tue Mar 12 15:30:53 2013 +0100
@@ -27,6 +27,7 @@
import java.lang.reflect.Field;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
@@ -36,10 +37,10 @@
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.ir.annotations.ParentNode;
import jdk.nashorn.internal.ir.annotations.Reference;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Debug;
/**
* AST-as-text visualizer. Sometimes you want tree form and not source
@@ -47,7 +48,6 @@
*
* see the flags --print-ast and --print-ast-lower
*/
-
public final class ASTWriter {
/** Root node from which to start the traversal */
private final Node root;
@@ -71,12 +71,22 @@
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
- printAST(sb, null, "root", root, 0);
+ printAST(sb, null, null, "root", root, 0);
return sb.toString();
}
+ /**
+ * Return the visited nodes in an ordered list
+ * @return the list of nodes in order
+ */
+ public Node[] toArray() {
+ final List<Node> preorder = new ArrayList<>();
+ printAST(new StringBuilder(), preorder, null, "root", root, 0);
+ return preorder.toArray(new Node[preorder.size()]);
+ }
+
@SuppressWarnings("unchecked")
- private void printAST(final StringBuilder sb, final Field field, final String name, final Node node, final int indent) {
+ private void printAST(final StringBuilder sb, final List<Node> preorder, final Field field, final String name, final Node node, final int indent) {
ASTWriter.indent(sb, indent);
if (node == null) {
sb.append("[Object ");
@@ -85,13 +95,23 @@
return;
}
+ if (preorder != null) {
+ preorder.add(node);
+ }
+
final boolean isReference = field != null && field.getAnnotation(Reference.class) != null;
Class<?> clazz = node.getClass();
String type = clazz.getName();
type = type.substring(type.lastIndexOf('.') + 1, type.length());
-// type += "@" + Debug.id(node) + "#" + node.getSymbol();
+ if (isReference) {
+ type = "ref: " + type;
+ }
+ type += "@" + Debug.id(node);
+ if (node.getSymbol() != null) {
+ type += "#" + node.getSymbol();
+ }
final List<Field> children = new LinkedList<>();
@@ -153,9 +173,7 @@
append('\n');
for (final Field child : children) {
- if (child.getAnnotation(ParentNode.class) != null) {
- continue;
- } else if (child.getAnnotation(Ignore.class) != null) {
+ if (child.getAnnotation(Ignore.class) != null) {
continue;
}
@@ -168,7 +186,7 @@
}
if (value instanceof Node) {
- printAST(sb, child, child.getName(), (Node)value, indent + 1);
+ printAST(sb, preorder, child, child.getName(), (Node)value, indent + 1);
} else if (value instanceof Collection) {
int pos = 0;
ASTWriter.indent(sb, indent + 1);
@@ -180,7 +198,7 @@
append('\n');
for (final Node member : (Collection<Node>)value) {
- printAST(sb, child, child.getName() + "[" + pos++ + "]", member, indent + 2);
+ printAST(sb, preorder, child, child.getName() + "[" + pos++ + "]", member, indent + 2);
}
}
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java Tue Mar 12 15:30:53 2013 +0100
@@ -605,7 +605,7 @@
final boolean strict = sobj.isStrictContext();
if (bulkable(sobj)) {
- return ((NativeArray)sobj).getArray().pop();
+ return sobj.getArray().pop();
}
final long len = JSType.toUint32(sobj.getLength());
@@ -725,7 +725,7 @@
first = sobj.get(0);
if (bulkable(sobj)) {
- ((NativeArray) sobj).getArray().shiftLeft(1);
+ sobj.getArray().shiftLeft(1);
} else {
for (long k = 1; k < len; k++) {
sobj.set(k - 1, sobj.get(k), strict);
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java Tue Mar 12 15:30:53 2013 +0100
@@ -162,21 +162,6 @@
}
/**
- * Nashorn extension: get invocation handle from {@link ScriptFunction}
- *
- * @param self self reference
- * @param obj script function
- * @return the invocation handle for the given ScriptFunction
- */
- @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
- public static Object methodHandle(final Object self, final Object obj) {
- if (obj instanceof ScriptFunction) {
- return ((ScriptFunction)obj).getInvokeHandle();
- }
- return UNDEFINED;
- }
-
- /**
* Check object identity comparison regardless of type
*
* @param self self reference
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeError.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeError.java Tue Mar 12 15:30:53 2013 +0100
@@ -317,7 +317,7 @@
return name;
}
// Step 10 : return name + ": " + msg
- return (String)name + ": " + (String)msg;
+ return name + ": " + msg;
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Tue Mar 12 15:30:53 2013 +0100
@@ -31,6 +31,7 @@
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -86,8 +87,8 @@
* @param builtin is this a built-in function
* @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted).
*/
- ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) {
- super(name, methodHandle, getMap(strict), scope, specs, strict, builtin, isConstructor);
+ ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
+ super(name, methodHandle, getMap(isStrict), scope, specs, isStrict, isBuiltin, isConstructor);
init();
}
@@ -95,14 +96,10 @@
* Constructor called by (compiler) generated code for {@link ScriptObject}s.
*
* @param data static function data
- * @param methodHandle handle for invocation
* @param scope scope object
- * @param allocator instance constructor for function
*/
- public ScriptFunctionImpl(final MethodHandle methodHandle, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) {
+ public ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope) {
super(data, getMap(data.isStrict()), scope);
- // Set method handles in script data
- data.setMethodHandles(methodHandle, allocator);
init();
}
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java Tue Mar 12 18:12:42 2013 +0530
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-package jdk.nashorn.internal.objects;
-
-import static jdk.nashorn.internal.lookup.Lookup.MH;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import jdk.nashorn.internal.codegen.CompilationException;
-import jdk.nashorn.internal.codegen.Compiler;
-import jdk.nashorn.internal.codegen.FunctionSignature;
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.runtime.CodeInstaller;
-import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.ScriptEnvironment;
-import jdk.nashorn.internal.runtime.ScriptFunction;
-import jdk.nashorn.internal.runtime.ScriptFunctionData;
-import jdk.nashorn.internal.runtime.ScriptObject;
-
-/**
- * A trampoline is a promise to compile a {@link ScriptFunction} later. It just looks like
- * the call to the script function, but when invoked it will compile the script function
- * (in a new compile unit) and invoke it
- */
-public final class ScriptFunctionTrampolineImpl extends ScriptFunctionImpl {
-
- private CodeInstaller<ScriptEnvironment> installer;
-
- /** Function node to lazily recompile when trampoline is hit */
- private FunctionNode functionNode;
-
- /**
- * Constructor
- *
- * @param installer opaque code installer from context
- * @param functionNode function node to lazily compile when trampoline is hit
- * @param data {@link ScriptFunctionData} for function
- * @param scope scope
- * @param allocator allocator
- */
- public ScriptFunctionTrampolineImpl(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) {
- super(null, data, scope, allocator);
-
- this.installer = installer;
- this.functionNode = functionNode;
-
- data.setMethodHandles(makeTrampoline(), allocator);
- }
-
- private final MethodHandle makeTrampoline() {
- final MethodType mt =
- new FunctionSignature(
- true,
- functionNode.needsCallee(),
- Type.OBJECT,
- functionNode.getParameters().size()).
- getMethodType();
-
- return
- MH.bindTo(
- MH.asCollector(
- findOwnMH(
- "trampoline",
- Object.class,
- Object[].class),
- Object[].class,
- mt.parameterCount()),
- this);
- }
-
- private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
- return MH.findVirtual(MethodHandles.lookup(), ScriptFunctionTrampolineImpl.class, name, MH.type(rtype, types));
- }
-
- @Override
- protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) {
- //prevent trampoline recompilation cycle if a function is bound before use
- compile();
- return super.makeBoundFunction(data);
- }
-
- private MethodHandle compile() throws CompilationException {
- final Compiler compiler = new Compiler(installer, functionNode);
-
- compiler.compile();
-
- final Class<?> clazz = compiler.install();
- /* compute function signature for lazy method. this can be done first after compilation, as only then do we know
- * the final state about callees, scopes and specialized parameter types */
- final FunctionSignature signature = new FunctionSignature(true, functionNode.needsCallee(), Type.OBJECT, functionNode.getParameters().size());
- final MethodType mt = signature.getMethodType();
-
- MethodHandle mh = MH.findStatic(MethodHandles.publicLookup(), clazz, functionNode.getName(), mt);
- if (functionNode.needsCallee()) {
- mh = MH.bindTo(mh, this);
- }
-
- // now the invoker method looks like the one our superclass is expecting
- resetInvoker(mh);
-
- return mh;
- }
-
- @SuppressWarnings("unused")
- private Object trampoline(final Object... args) throws CompilationException {
- Compiler.LOG.info(">>> TRAMPOLINE: Hitting trampoline for '" + functionNode.getName() + "'");
- MethodHandle mh = compile();
-
- Compiler.LOG.info("<<< COMPILED TO: " + mh);
- // spread the array to invididual args of the correct type
- mh = MH.asSpreader(mh, Object[].class, mh.type().parameterCount());
-
- try {
- //invoke the real method the trampoline points to. this only happens once
- return mh.invoke(args);
- } catch (final RuntimeException | Error e) {
- throw e;
- } catch (final Throwable t) {
- throw new RuntimeException(t);
- }
- }
-}
--- a/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java Tue Mar 12 15:30:53 2013 +0100
@@ -313,7 +313,7 @@
}
// Construct new object literal.
- return new ObjectNode(source, objectToken, finish, null, elements);
+ return new ObjectNode(source, objectToken, finish, elements);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java Tue Mar 12 15:30:53 2013 +0100
@@ -59,6 +59,7 @@
import java.util.List;
import java.util.Map;
import java.util.Stack;
+
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.ir.AccessNode;
@@ -96,7 +97,7 @@
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
-import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
@@ -116,9 +117,6 @@
/** Is scripting mode. */
private final boolean scripting;
- /** Top level script being parsed. */
- private FunctionNode script;
-
/** Current function being parsed. */
private FunctionNode function;
@@ -304,6 +302,7 @@
final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, block, ident, name);
block = function = functionBlock;
function.setStrictMode(isStrictMode);
+ function.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
return functionBlock;
}
@@ -312,7 +311,7 @@
* Restore the current block.
*/
private void restoreBlock() {
- block = block.getParent();
+ block = block.getParent();
function = block.getFunction();
}
@@ -338,7 +337,7 @@
popControlNode();
}
- final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
+ final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
// Block closing brace.
if (needsBraces) {
@@ -438,7 +437,7 @@
}
if (lhs instanceof IdentNode) {
- if (! checkIdentLValue((IdentNode)lhs)) {
+ if (!checkIdentLValue((IdentNode)lhs)) {
return referenceError(lhs, rhs);
}
verifyStrictIdent((IdentNode)lhs, "assignment");
@@ -621,15 +620,13 @@
// Make a fake token for the script.
final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
// Set up the script to append elements.
- script = newFunctionBlock(new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName));
- // set kind to be SCRIPT
+
+ final FunctionNode script = newFunctionBlock(new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName));
+
script.setKind(FunctionNode.Kind.SCRIPT);
- // Set the first token of the script.
script.setFirstToken(functionToken);
- // Gather source elements.
sourceElements();
expect(EOF);
- // Set the last token of the script.
script.setLastToken(token);
script.setFinish(source.getLength() - 1);
@@ -1231,7 +1228,7 @@
}
if (init instanceof IdentNode) {
- if (! checkIdentLValue((IdentNode)init)) {
+ if (!checkIdentLValue((IdentNode)init)) {
error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
}
verifyStrictIdent((IdentNode)init, "for-in iterator");
@@ -2026,7 +2023,7 @@
break;
default:
- if (! elision) {
+ if (!elision) {
error(AbstractParser.message("expected.comma", type.getNameOrType()));
}
// Add expression element.
@@ -2067,15 +2064,11 @@
next();
// Object context.
- Block objectContext = null;
// Prepare to accumulate elements.
final List<Node> elements = new ArrayList<>();
final Map<Object, PropertyNode> map = new HashMap<>();
- try {
- // Create a block for the object literal.
- objectContext = newBlock();
-
+ // Create a block for the object literal.
boolean commaSeen = true;
loop:
while (true) {
@@ -2090,97 +2083,90 @@
break;
default:
- if (! commaSeen) {
+ if (!commaSeen) {
error(AbstractParser.message("expected.comma", type.getNameOrType()));
- }
-
- commaSeen = false;
- // Get and add the next property.
- final PropertyNode property = propertyAssignment();
- final Object key = property.getKeyName();
- final PropertyNode existingProperty = map.get(key);
-
- if (existingProperty != null) {
- // ECMA section 11.1.5 Object Initialiser
- // point # 4 on property assignment production
- final Node value = property.getValue();
- final Node getter = property.getGetter();
- final Node setter = property.getSetter();
-
- final Node prevValue = existingProperty.getValue();
- final Node prevGetter = existingProperty.getGetter();
- final Node prevSetter = existingProperty.getSetter();
-
- boolean redefinitionOk = true;
- // ECMA 11.1.5 strict mode restrictions
- if (isStrictMode) {
- if (value != null && prevValue != null) {
- redefinitionOk = false;
- }
- }
-
- final boolean isPrevAccessor = prevGetter != null || prevSetter != null;
- final boolean isAccessor = getter != null || setter != null;
-
- // data property redefined as accessor property
- if (prevValue != null && isAccessor) {
- redefinitionOk = false;
- }
-
- // accessor property redefined as data
- if (isPrevAccessor && value != null) {
+ }
+
+ commaSeen = false;
+ // Get and add the next property.
+ final PropertyNode property = propertyAssignment();
+ final Object key = property.getKeyName();
+ final PropertyNode existingProperty = map.get(key);
+
+ if (existingProperty != null) {
+ // ECMA section 11.1.5 Object Initialiser
+ // point # 4 on property assignment production
+ final Node value = property.getValue();
+ final Node getter = property.getGetter();
+ final Node setter = property.getSetter();
+
+ final Node prevValue = existingProperty.getValue();
+ final Node prevGetter = existingProperty.getGetter();
+ final Node prevSetter = existingProperty.getSetter();
+
+ boolean redefinitionOk = true;
+ // ECMA 11.1.5 strict mode restrictions
+ if (isStrictMode) {
+ if (value != null && prevValue != null) {
redefinitionOk = false;
}
-
- if (isAccessor && isPrevAccessor) {
- if (getter != null && prevGetter != null ||
- setter != null && prevSetter != null) {
- redefinitionOk = false;
- }
- }
-
- if (! redefinitionOk) {
- error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
- }
-
- if (value != null) {
- final Node existingValue = existingProperty.getValue();
-
- if (existingValue == null) {
- existingProperty.setValue(value);
- } else {
- final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT);
- existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value));
- }
-
- existingProperty.setGetter(null);
- existingProperty.setSetter(null);
+ }
+
+ final boolean isPrevAccessor = prevGetter != null || prevSetter != null;
+ final boolean isAccessor = getter != null || setter != null;
+
+ // data property redefined as accessor property
+ if (prevValue != null && isAccessor) {
+ redefinitionOk = false;
+ }
+
+ // accessor property redefined as data
+ if (isPrevAccessor && value != null) {
+ redefinitionOk = false;
+ }
+
+ if (isAccessor && isPrevAccessor) {
+ if (getter != null && prevGetter != null ||
+ setter != null && prevSetter != null) {
+ redefinitionOk = false;
}
-
- if (getter != null) {
- existingProperty.setGetter(getter);
+ }
+
+ if (!redefinitionOk) {
+ error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
+ }
+
+ if (value != null) {
+ final Node existingValue = existingProperty.getValue();
+
+ if (existingValue == null) {
+ existingProperty.setValue(value);
+ } else {
+ final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT);
+ existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value));
}
- if (setter != null) {
- existingProperty.setSetter(setter);
- }
- } else {
- map.put(key, property);
- elements.add(property);
+ existingProperty.setGetter(null);
+ existingProperty.setSetter(null);
}
- break;
+ if (getter != null) {
+ existingProperty.setGetter(getter);
+ }
+
+ if (setter != null) {
+ existingProperty.setSetter(setter);
+ }
+ } else {
+ map.put(key, property);
+ elements.add(property);
}
+
+ break;
}
- } finally {
- restoreBlock();
}
- // Construct new object literal.
- objectContext.setFinish(finish);
- objectContext.setStart(Token.descPosition(objectToken));
-
- return new ObjectNode(source, objectToken, finish, objectContext, elements);
+ return new ObjectNode(source, objectToken, finish, elements);
}
/**
@@ -2845,7 +2831,7 @@
}
if (lhs instanceof IdentNode) {
- if (! checkIdentLValue((IdentNode)lhs)) {
+ if (!checkIdentLValue((IdentNode)lhs)) {
return referenceError(lhs, null);
}
verifyStrictIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator");
@@ -2872,7 +2858,7 @@
return referenceError(lhs, null);
}
if (lhs instanceof IdentNode) {
- if (! checkIdentLValue((IdentNode)lhs)) {
+ if (!checkIdentLValue((IdentNode)lhs)) {
next();
return referenceError(lhs, null);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Tue Mar 12 15:30:53 2013 +0100
@@ -164,7 +164,6 @@
super(key, flags, slot);
/*
- *
* primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also
* works in dual field mode, it only means that the property never has a primitive
* representation.
@@ -348,11 +347,10 @@
private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
if (DEBUG_FIELDS) {
- final MethodHandle mhd = MethodHandleFactory.addDebugPrintout(
+ return MethodHandleFactory.addDebugPrintout(
LOG,
mh,
tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", forType=" + stripName(forType) + ", type=" + stripName(type) + ')');
- return mhd;
}
return mh;
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java Tue Mar 12 15:30:53 2013 +0100
@@ -25,6 +25,8 @@
package jdk.nashorn.internal.runtime;
+import jdk.nashorn.internal.codegen.ClassEmitter;
+
/**
* Interface for installing classes passed to the compiler.
* As only the code generating package (i.e. Context) knows about
@@ -52,12 +54,12 @@
*/
public Class<?> install(final String className, final byte[] bytecode);
- /*
+ /**
* Verify generated bytecode before emission. This is called back from the
* {@link ClassEmitter} or the {@link Compiler}. If the "--verify-code" parameter
* hasn't been given, this is a nop
*
- * @param bytecode bytecode to verify
+ * @param code bytecode to verify
*/
public void verify(final byte[] code);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,162 @@
+/*
+ * 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.runtime;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+import jdk.nashorn.internal.codegen.types.Type;
+
+/**
+ * An version of a JavaScript function, native or JavaScript.
+ * Supports lazily generating a constructor version of the invocation.
+ */
+final class CompiledFunction implements Comparable<CompiledFunction> {
+
+ private final MethodHandle invoker;
+ private MethodHandle constructor;
+
+ CompiledFunction(final MethodHandle invoker) {
+ this(invoker, null);
+ }
+
+ CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) {
+ this.invoker = invoker;
+ this.constructor = constructor; //isConstructor
+ }
+
+ @Override
+ public String toString() {
+ return "<invoker=" + invoker + " ctor=" + constructor + ">";
+ }
+
+ MethodHandle getInvoker() {
+ return invoker;
+ }
+
+ MethodHandle getConstructor() {
+ return constructor;
+ }
+
+ void setConstructor(final MethodHandle constructor) {
+ this.constructor = constructor;
+ }
+
+ boolean hasConstructor() {
+ return constructor != null;
+ }
+
+ MethodType type() {
+ return invoker.type();
+ }
+
+ @Override
+ public int compareTo(final CompiledFunction o) {
+ return weight() - o.weight();
+ }
+
+ private int weight() {
+ return weight(type());
+ }
+
+ private static int weight(final MethodType type) {
+ if (isVarArgsType(type)) {
+ return Integer.MAX_VALUE; //if there is a varargs it should be the heavist and last fallback
+ }
+
+ int weight = Type.typeFor(type.returnType()).getWeight();
+ for (final Class<?> paramType : type.parameterArray()) {
+ final int pweight = Type.typeFor(paramType).getWeight();
+ weight += pweight;
+ }
+ return weight;
+ }
+
+ private static boolean isVarArgsType(final MethodType type) {
+ assert type.parameterCount() >= 1 : type;
+ return type.parameterType(type.parameterCount() - 1) == Object[].class;
+ }
+
+ boolean moreGenericThan(final CompiledFunction o) {
+ return weight() > o.weight();
+ }
+
+ boolean moreGenericThan(final MethodType type) {
+ return weight() > weight(type);
+ }
+
+ /**
+ * Check whether a given method descriptor is compatible with this invocation.
+ * It is compatible if the types are narrower than the invocation type so that
+ * a semantically equivalent linkage can be performed.
+ *
+ * @param typesc
+ * @return
+ */
+ boolean typeCompatible(final MethodType type) {
+ final Class<?>[] wantedParams = type.parameterArray();
+ final Class<?>[] existingParams = type().parameterArray();
+
+ //if we are not examining a varargs type, the number of parameters must be the same
+ if (wantedParams.length != existingParams.length && !isVarArgsType(type)) {
+ return false;
+ }
+
+ //we only go as far as the shortest array. the only chance to make this work if
+ //parameters lengths do not match is if our type ends with a varargs argument.
+ //then every trailing parameter in the given callsite can be folded into it, making
+ //us compatible (albeit slower than a direct specialization)
+ final int lastParamIndex = Math.min(wantedParams.length, existingParams.length);
+ for (int i = 0; i < lastParamIndex; i++) {
+ final Type w = Type.typeFor(wantedParams[i]);
+ final Type e = Type.typeFor(existingParams[i]);
+
+ //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution
+ //we also currently don't support boolean as a javascript function callsite type.
+ //it will always box.
+ if (w.isBoolean()) {
+ return false;
+ }
+
+ //This callsite type has a vararg here. it will swallow all remaining args.
+ //for consistency, check that it's the last argument
+ if (e.isArray()) {
+ return true;
+ }
+
+ //Our arguments must be at least as wide as the wanted one, if not wider
+ if (Type.widest(w, e) != e) {
+ //e.g. this invocation takes double and callsite says "object". reject. won't fit
+ //but if invocation takes a double and callsite says "int" or "long" or "double", that's fine
+ return false;
+ }
+ }
+
+ return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic.
+ }
+
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,73 @@
+/*
+ * 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.runtime;
+
+import java.lang.invoke.MethodType;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+/**
+ * This is a list of code versions of a function.
+ * The list is sorted in ascending order of generic descriptors
+ */
+@SuppressWarnings("serial")
+final class CompiledFunctions extends TreeSet<CompiledFunction> {
+
+ CompiledFunction best(final MethodType type) {
+ final Iterator<CompiledFunction> iter = iterator();
+ while (iter.hasNext()) {
+ final CompiledFunction next = iter.next();
+ if (next.typeCompatible(type)) {
+ return next;
+ }
+ }
+ return mostGeneric();
+ }
+
+ boolean needsCallee() {
+ for (final CompiledFunction inv : this) {
+ assert ScriptFunctionData.needsCallee(inv.getInvoker()) == ScriptFunctionData.needsCallee(mostGeneric().getInvoker());
+ }
+ return ScriptFunctionData.needsCallee(mostGeneric().getInvoker());
+ }
+
+ CompiledFunction mostGeneric() {
+ return last();
+ }
+
+ /**
+ * Is the given type even more specific than this entire list? That means
+ * we have an opportunity for more specific versions of the method
+ * through lazy code generation
+ *
+ * @param type type to check against
+ * @return true if the given type is more specific than all invocations available
+ */
+ boolean isLessSpecificThan(final MethodType type) {
+ return best(type).moreGenericThan(type);
+ }
+
+
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java Tue Mar 12 15:30:53 2013 +0100
@@ -80,7 +80,7 @@
/**
* Return the context for this installer
- * @return context
+ * @return ScriptEnvironment
*/
@Override
public ScriptEnvironment getOwner() {
--- a/nashorn/src/jdk/nashorn/internal/runtime/ECMAException.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ECMAException.java Tue Mar 12 15:30:53 2013 +0100
@@ -237,7 +237,7 @@
return (String)name;
}
- return (String)name + ": " + (String)msg;
+ return name + ": " + msg;
}
private static Throwable asThrowable(final Object obj) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,100 @@
+/*
+ * 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.runtime;
+
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+
+import java.lang.invoke.MethodHandle;
+
+/**
+ * This is a subclass that represents a script function that may not be regenerated.
+ * This is used for example for bound functions and builtins.
+ */
+public final class FinalScriptFunctionData extends ScriptFunctionData {
+
+ /**
+ * Constructor - used for bind
+ *
+ * @param name name
+ * @param arity arity
+ * @param list precompiled code
+ * @param isStrict strict
+ * @param isBuiltin builtin
+ * @param isConstructor constructor
+ */
+ FinalScriptFunctionData(final String name, int arity, CompiledFunctions functions, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
+ super(name, arity, isStrict, isBuiltin, isConstructor);
+ code.addAll(functions);
+ }
+
+ /**
+ * Constructor - used from ScriptFunction. This assumes that we have code alraedy for the
+ * method (typically a native method) and possibly specializations.
+ *
+ * @param name name
+ * @param mh method handle for generic version of method
+ * @param specs specializations
+ * @param isStrict strict
+ * @param isBuiltin builtin
+ * @param isConstructor constructor
+ */
+ FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
+ super(name, arity(mh), isStrict, isBuiltin, isConstructor);
+
+ addInvoker(mh);
+ if (specs != null) {
+ for (final MethodHandle spec : specs) {
+ addInvoker(spec);
+ }
+ }
+ }
+
+ private void addInvoker(final MethodHandle mh) {
+ boolean needsCallee = needsCallee(mh);
+ if (isConstructor(mh)) {
+ //only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
+ //is too conservative a check. However, isConstructor(mh) always implies isConstructor param
+ assert isConstructor();
+ code.add(new CompiledFunction(MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor
+ } else {
+ code.add(new CompiledFunction(mh));
+ }
+ }
+
+ private static int arity(final MethodHandle mh) {
+ if (isVarArg(mh)) {
+ return -1;
+ }
+
+ //drop self, callee and boolean constructor flag to get real arity
+ return mh.type().parameterCount() - 1 - (needsCallee(mh) ? 1 : 0) - (isConstructor(mh) ? 1 : 0);
+ }
+
+ private static boolean isConstructor(final MethodHandle mh) {
+ return mh.type().parameterCount() >= 1 && mh.type().parameterType(0) == boolean.class;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,185 @@
+/*
+ * 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.runtime;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.parser.Token;
+import jdk.nashorn.internal.parser.TokenType;
+
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+
+/**
+ * This is a subclass that represents a script function that may be regenerated,
+ * for example with specialization based on call site types, or lazily generated.
+ * The common denominator is that it can get new invokers during its lifespan,
+ * unlike {@link FinalScriptFunctionData}
+ */
+public final class RecompilableScriptFunctionData extends ScriptFunctionData {
+
+ private final FunctionNode functionNode;
+ private final PropertyMap allocatorMap;
+ private final CodeInstaller<ScriptEnvironment> installer;
+ private final String allocatorClassName;
+
+ /** lazily generated allocator */
+ private MethodHandle allocator;
+
+ private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+ /**
+ * Constructor - public as scripts use it
+ *
+ * @param functionNode functionNode that represents this function code
+ * @param installer installer for code regeneration versions of this function
+ * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
+ * @param allocatorMap allocator map to seed instances with, when constructing
+ */
+ public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) {
+ super(functionNode.isAnonymous() ?
+ "" :
+ functionNode.getIdent().getName(),
+ functionNode.getParameters().size(),
+ functionNode.isStrictMode(),
+ false,
+ true);
+
+ this.functionNode = functionNode;
+ this.installer = installer;
+ this.allocatorClassName = allocatorClassName;
+ this.allocatorMap = allocatorMap;
+ }
+
+ @Override
+ String toSource() {
+ final Source source = functionNode.getSource();
+ final long token = tokenFor(functionNode);
+
+ if (source != null && token != 0) {
+ return source.getString(Token.descPosition(token), Token.descLength(token));
+ }
+
+ return "function " + (name == null ? "" : name) + "() { [native code] }";
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ final Source source = functionNode.getSource();
+ final long token = tokenFor(functionNode);
+
+ if (source != null) {
+ sb.append(source.getName())
+ .append(':')
+ .append(source.getLine(Token.descPosition(token)))
+ .append(' ');
+ }
+
+ return sb.toString() + super.toString();
+ }
+
+ private static long tokenFor(final FunctionNode fn) {
+ final int position = Token.descPosition(fn.getFirstToken());
+ final int length = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken());
+
+ return Token.toDesc(TokenType.FUNCTION, position, length);
+ }
+
+ @Override
+ ScriptObject allocate() {
+ try {
+ ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
+ return allocator == null ? null : (ScriptObject)allocator.invokeExact(allocatorMap);
+ } catch (final RuntimeException | Error e) {
+ throw e;
+ } catch (final Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ private void ensureHasAllocator() throws ClassNotFoundException {
+ if (allocator == null && allocatorClassName != null) {
+ this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.tag(), MH.type(ScriptObject.class, PropertyMap.class));
+ }
+ }
+
+ @Override
+ protected void ensureCodeGenerated() {
+ if (!code.isEmpty()) {
+ return; // nothing to do, we have code, at least some.
+ }
+
+ // check if function node is lazy, need to compile it.
+ // note that currently function cloning is not working completely, which
+ // means that the compiler will mutate the function node it has been given
+ // once it has been compiled, it cannot be recompiled. This means that
+ // lazy compilation works (not compiled yet) but e.g. specializations won't
+ // until the copy-on-write changes for IR are in, making cloning meaningless.
+ // therefore, currently method specialization is disabled. TODO
+
+ if (functionNode.isLazy()) {
+ Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '" + functionNode.getName() + "'");
+ new Compiler(installer, functionNode).compile().install();
+
+ // we don't need to update any flags - varArgs and needsCallee are instrincic
+ // in the function world we need to get a destination node from the compile instead
+ // and replace it with our function node. TODO
+ }
+
+ // we can't get here unless we have bytecode, either from eager compilation or from
+ // running a lazy compile on the lines above
+
+ assert functionNode.hasState(CompilationState.INSTALLED);
+
+ // code exists - look it up and add it into the automatically sorted invoker list
+ code.add(
+ new CompiledFunction(
+ MH.findStatic(
+ LOOKUP,
+ functionNode.getCompileUnit().getCode(),
+ functionNode.getName(),
+ new FunctionSignature(functionNode).
+ getMethodType())));
+ }
+
+ @Override
+ MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
+ final MethodHandle mh = super.getBestInvoker(callSiteType, args);
+ if (code.isLessSpecificThan(callSiteType)) {
+ // opportunity for code specialization - we can regenerate a better version of this method
+ }
+ return mh;
+ }
+
+}
+
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Tue Mar 12 15:30:53 2013 +0100
@@ -82,6 +82,9 @@
/** Show full Nashorn version */
public final boolean _fullversion;
+ /** Should lazy compilation take place */
+ public final boolean _lazy_compilation;
+
/** Create a new class loaded for each compilation */
public final boolean _loader_per_compile;
@@ -155,6 +158,7 @@
_early_lvalue_error = options.getBoolean("early.lvalue.error");
_empty_statements = options.getBoolean("empty.statements");
_fullversion = options.getBoolean("fullversion");
+ _lazy_compilation = options.getBoolean("lazy.compilation");
_loader_per_compile = options.getBoolean("loader.per.compile");
_no_syntax_extensions = options.getBoolean("no.syntax.extensions");
_package = options.getString("package");
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Tue Mar 12 15:30:53 2013 +0100
@@ -33,11 +33,11 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
-import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
@@ -48,16 +48,16 @@
public abstract class ScriptFunction extends ScriptObject {
/** Method handle for prototype getter for this ScriptFunction */
- public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class);
+ public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class);
/** Method handle for prototype setter for this ScriptFunction */
- public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class);
+ public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class);
/** Method handle for length getter for this ScriptFunction */
- public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class);
+ public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class);
/** Method handle for name getter for this ScriptFunction */
- public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
+ public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
/** Method handle for allocate function for this ScriptFunction */
static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
@@ -67,7 +67,9 @@
/** method handle to scope getter for this ScriptFunction */
public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
- private final ScriptFunctionData data;
+ private static final MethodHandle IS_FUNCTION_MH = findOwnMH("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class);
+
+ private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
/** Reference to constructor prototype. */
protected Object prototype;
@@ -75,6 +77,8 @@
/** The parent scope. */
private final ScriptObject scope;
+ private final ScriptFunctionData data;
+
/**
* Constructor
*
@@ -97,7 +101,7 @@
final boolean builtin,
final boolean isConstructor) {
- this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope);
+ this(new FinalScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope);
}
/**
@@ -118,8 +122,8 @@
constructorCount++;
}
- this.data = data;
- this.scope = scope;
+ this.data = data;
+ this.scope = scope;
}
@Override
@@ -295,20 +299,20 @@
/**
* Return the most appropriate invoke handle if there are specializations
* @param type most specific method type to look for invocation with
+ * @param callsite args for trampoline invocation
* @return invoke method handle
*/
- private final MethodHandle getBestInvoker(final MethodType type) {
- return data.getBestInvoker(type);
+ private MethodHandle getBestInvoker(final MethodType type, final Object[] args) {
+ return data.getBestInvoker(type, args);
}
/**
- * Get the invoke handle - the most generic (and if no specializations are in place, only) invocation
- * method handle for this ScriptFunction
- * @see SpecializedFunction
- * @return invokeHandle
+ * Return the most appropriate invoke handle if there are specializations
+ * @param type most specific method type to look for invocation with
+ * @return invoke method handle
*/
- public final MethodHandle getInvokeHandle() {
- return data.getInvoker();
+ public MethodHandle getBestInvoker(final MethodType type) {
+ return getBestInvoker(type, null);
}
/**
@@ -319,7 +323,7 @@
* @return bound invoke handle
*/
public final MethodHandle getBoundInvokeHandle(final ScriptObject self) {
- return MH.bindTo(bindToCalleeIfNeeded(getInvokeHandle()), self);
+ return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker()), self);
}
/**
@@ -329,7 +333,8 @@
* @return the potentially bound method handle
*/
private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) {
- return data.needsCallee() ? MH.bindTo(methodHandle, this) : methodHandle;
+ return ScriptFunctionData.needsCallee(methodHandle) ? MH.bindTo(methodHandle, this) : methodHandle;
+
}
/**
@@ -340,15 +345,6 @@
return data.getName();
}
- /**
- * Does this script function need to be compiled. This determined by
- * null checking invokeHandle
- *
- * @return true if this needs compilation
- */
- public final boolean needsCompilation() {
- return data.getInvoker() == null;
- }
/**
* Get the scope for this function
@@ -359,15 +355,6 @@
}
/**
- * Reset the invoker handle. This is used by trampolines for
- * lazy code generation
- * @param invoker new invoker
- */
- protected void resetInvoker(final MethodHandle invoker) {
- data.resetInvoker(invoker);
- }
-
- /**
* Prototype getter for this ScriptFunction - follows the naming convention
* used by Nasgen and the code generator
*
@@ -464,7 +451,7 @@
@Override
protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
final MethodType type = desc.getMethodType();
- return new GuardedInvocation(pairArguments(data.getBestConstructor(type), type), null, NashornGuards.getFunctionGuard(this));
+ return new GuardedInvocation(pairArguments(data.getBestConstructor(type.changeParameterType(0, ScriptFunction.class), null), type), null, getFunctionGuard(this));
}
@SuppressWarnings("unused")
@@ -472,7 +459,7 @@
if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
return obj;
}
- return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj);
+ return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(obj);
}
/**
@@ -506,8 +493,7 @@
MethodHandle guard = null;
if (data.needsCallee()) {
- final MethodHandle callHandle = getBestInvoker(type);
-
+ final MethodHandle callHandle = getBestInvoker(type, request.getArguments());
if (NashornCallSiteDescriptor.isScope(desc)) {
// Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
// (callee, this, args...) => (callee, args...)
@@ -525,13 +511,12 @@
if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) {
boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
} else {
- guard = NashornGuards.getNonStrictFunctionGuard(this);
+ guard = getNonStrictFunctionGuard(this);
}
}
}
} else {
- final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1));
-
+ final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1), request.getArguments());
if (NashornCallSiteDescriptor.isScope(desc)) {
// Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
// (this, args...) => (args...)
@@ -545,7 +530,8 @@
}
boundHandle = pairArguments(boundHandle, type);
- return new GuardedInvocation(boundHandle, guard == null ? NashornGuards.getFunctionGuard(this) : guard);
+
+ return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this) : guard);
}
/**
@@ -554,13 +540,50 @@
* These don't want a callee parameter, so bind that. Name binding is optional.
*/
MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
- return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(getBestInvoker(type)), bindName), type);
+ return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(getBestInvoker(type, null)), bindName), type);
}
private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
return bindName == null ? methodHandle : MH.insertArguments(methodHandle, 1, bindName);
}
+ /**
+ * Get the guard that checks if a {@link ScriptFunction} is equal to
+ * a known ScriptFunction, using reference comparison
+ *
+ * @param function The ScriptFunction to check against. This will be bound to the guard method handle
+ *
+ * @return method handle for guard
+ */
+ private static MethodHandle getFunctionGuard(final ScriptFunction function) {
+ assert function.data != null;
+ return MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
+ }
+
+ /**
+ * Get a guard that checks if a {@link ScriptFunction} is equal to
+ * a known ScriptFunction using reference comparison, and whether the type of
+ * the second argument (this-object) is not a JavaScript primitive type.
+ *
+ * @param function The ScriptFunction to check against. This will be bound to the guard method handle
+ *
+ * @return method handle for guard
+ */
+ private static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) {
+ assert function.data != null;
+ return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.data);
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) {
+ return self instanceof ScriptFunction && ((ScriptFunction)self).data == data;
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) {
+ return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject;
+ }
+
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
final Class<?> own = ScriptFunction.class;
final MethodType mt = MH.type(rtype, types);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Tue Mar 12 15:30:53 2013 +0100
@@ -32,227 +32,94 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
-import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.parser.Token;
-import jdk.nashorn.internal.parser.TokenType;
+
+import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
/**
* A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
* Instances of this class are created during codegen and stored in script classes'
* constants array to reduce function instantiation overhead during runtime.
*/
-public final class ScriptFunctionData {
- private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
- private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
-
- // per-function object flags
- private static final int IS_STRICT = 0b0000_0001;
- private static final int IS_BUILTIN = 0b0000_0010;
- private static final int HAS_CALLEE = 0b0000_0100;
- private static final int IS_VARARGS = 0b0000_1000;
- private static final int IS_CONSTRUCTOR = 0b0001_0000;
+public abstract class ScriptFunctionData {
- /** Name of the function or "" */
- private final String name;
- /** Source of this function, or null */
- private final Source source;
- /** Map for new instance constructor */
- private PropertyMap allocatorMap;
- /** Start position and length in source */
- private final long token;
- /** Number of expected arguments, either taken from FunctionNode or calculated from method handle signature*/
- private int arity;
- private final int flags;
+ /** Name of the function or "" for anonynous functions */
+ protected final String name;
+
+ /** All versions of this function that have been generated to code */
+ protected final CompiledFunctions code;
- /** Reference to code for this method. */
- private MethodHandle invoker;
- /** Reference to code for this method when called to create "new" object. This must always be populated with a
- * result of calling {@link #composeConstructor(MethodHandle)} on the value of the {@link #invoker} field. */
- private MethodHandle constructor;
- /** Constructor to create a new instance. */
- private MethodHandle allocator;
- /** Generic invoker to used in {@link ScriptFunction#invoke(Object, Object...)}. */
- private MethodHandle genericInvoker;
- /** Specializations - see @SpecializedFunction */
- private MethodHandle[] invokeSpecializations;
- /** Specializations - see @SpecializedFunction. Same restrictions as for {@link #constructor} apply; only populate
- * with method handles returned from {@link #composeConstructor(MethodHandle)}. */
- private MethodHandle[] constructSpecializations;
+ private int arity;
+
+ private final boolean isStrict;
- /**
- * Constructor
- * @param fn the function node
- * @param allocatorMap the allocator property map
- */
- public ScriptFunctionData(final FunctionNode fn, final PropertyMap allocatorMap) {
+ private final boolean isBuiltin;
- final long firstToken = fn.getFirstToken();
- final long lastToken = fn.getLastToken();
- final int position = Token.descPosition(firstToken);
- final int length = Token.descPosition(lastToken) - position + Token.descLength(lastToken);
+ private final boolean isConstructor;
- this.name = fn.isAnonymous() ? "" : fn.getIdent().getName();
- this.source = fn.getSource();
- this.allocatorMap = allocatorMap;
- this.token = Token.toDesc(TokenType.FUNCTION, position, length);
- this.arity = fn.getParameters().size();
- this.flags = makeFlags(fn.needsCallee(), fn.isVarArg(), fn.isStrictMode(), false, true);
- }
+ private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
+ private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
/**
* Constructor
*
- * @param name the function name
- * @param methodHandle the method handle
- * @param specs array of specialized method handles
- * @param strict strict flag
- * @param builtin builtin flag
- * @param isConstructor constructor flags
+ * @param name script function name
+ * @param arity arity
+ * @param isStrict is the function strict
+ * @param isBuiltin is the function built in
+ * @param isConstructor is the function a constructor
*/
- public ScriptFunctionData(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) {
- this(name, null, 0L, methodHandle, specs, strict, builtin, isConstructor);
+ protected ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
+ this.name = name;
+ this.arity = arity;
+ this.code = new CompiledFunctions();
+ this.isStrict = isStrict;
+ this.isBuiltin = isBuiltin;
+ this.isConstructor = isConstructor;
}
- private ScriptFunctionData(final String name, final Source source, final long token, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) {
- this.name = name;
- this.source = source;
- this.token = token;
-
- final boolean isVarArg = isVarArg(methodHandle);
- final boolean needsCallee = needsCallee(methodHandle);
-
- this.flags = makeFlags(needsCallee, isVarArg, strict, builtin, isConstructor);
- int lArity = isVarArg ? -1 : methodHandle.type().parameterCount() - 1; //drop the self param for arity
-
- if (needsCallee && !isVarArg) {
- lArity--;
- }
-
- if (isConstructor(methodHandle)) {
- assert isConstructor;
- if (!isVarArg) {
- lArity--; // drop the boolean flag for arity
- }
- /*
- * We insert a boolean argument to tell if the method was invoked as constructor or not if the method
- * handle's first argument is boolean.
- */
- this.invoker = MH.insertArguments(methodHandle, 0, false);
- this.constructor = composeConstructor(MH.insertArguments(methodHandle, 0, true));
-
- if (specs != null) {
- this.invokeSpecializations = new MethodHandle[specs.length];
- this.constructSpecializations = new MethodHandle[specs.length];
- for (int i = 0; i < specs.length; i++) {
- this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false);
- this.constructSpecializations[i] = composeConstructor(MH.insertArguments(specs[i], 0, true));
- }
- }
- } else {
- this.invoker = methodHandle;
- this.constructor = null; // delay composition of the constructor
- this.invokeSpecializations = specs;
- this.constructSpecializations = null; // delay composition of the constructors
- }
- this.arity = lArity;
- }
-
- /**
- * Get the arity of the function.
- * @return the arity
- */
- int getArity() {
+ final int getArity() {
return arity;
}
/**
- * Set the arity of the function.
- * @param arity the arity
+ * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final
+ * @param arity new arity
*/
- void setArity(int arity) {
+ void setArity(final int arity) {
this.arity = arity;
}
- /**
- * Get the function name.
- * @return function name
- */
- String getName() {
- return name;
+ CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) {
+ final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args);
+
+ if (isConstructor()) {
+ ensureConstructor(originalInv);
+ return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args));
+ }
+
+ return new CompiledFunction(boundInvoker);
}
/**
- * Get this function as a String containing its source code. If no source code
- * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
- * @return string representation of this function's source
+ * Is this a ScriptFunction generated with strict semantics?
+ * @return true if strict, false otherwise
*/
- String toSource() {
- if (source != null && token != 0) {
- return source.getString(Token.descPosition(token), Token.descLength(token));
- }
-
- return "function " + (name == null ? "" : name) + "() { [native code] }";
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
-
- sb.append(super.toString())
- .append(" [ ")
- .append(invoker)
- .append(", ")
- .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
-
- if (source != null) {
- sb.append(" @ ")
- .append(source.getName())
- .append(':')
- .append(source.getLine(Token.descPosition(token)));
- }
- sb.append(" ]");
-
- return sb.toString();
+ public boolean isStrict() {
+ return isStrict;
}
- /**
- * Returns true if the function needs a callee argument.
- * @return the needsCallee flag
- */
- boolean needsCallee() {
- return (flags & HAS_CALLEE) != 0;
- }
-
- /**
- * Returns true if this is a strict-mode function.
- * @return the strict flag
- */
- public boolean isStrict() {
- return (flags & IS_STRICT) != 0;
+ boolean isBuiltin() {
+ return isBuiltin;
}
- /**
- * Returns true if this is a built-in function.
- * @return the built-in flag
- */
- private boolean isBuiltin() {
- return (flags & IS_BUILTIN) != 0;
+ boolean isConstructor() {
+ return isConstructor;
}
- /**
- * Returns true if this function can be used as a constructor.
- * @return the constructor flag
- */
- private boolean isConstructor() {
- return (flags & IS_CONSTRUCTOR) != 0;
- }
-
- /**
- * Returns true if this is a var-arg function.
- * @return the var-arg flag
- */
- private boolean isVarArg() {
- return (flags & IS_VARARGS) != 0;
+ boolean needsCallee() {
+ // we don't know if we need a callee or not unless we are generated
+ ensureCodeGenerated();
+ return code.needsCallee();
}
/**
@@ -261,127 +128,408 @@
* @return true if this argument must be an object
*/
boolean needsWrappedThis() {
- return (flags & (IS_STRICT | IS_BUILTIN)) == 0;
+ return !isStrict && !isBuiltin;
+ }
+
+ String toSource() {
+ return "function " + (name == null ? "" : name) + "() { [native code] }";
+ }
+
+ String getName() {
+ return name;
+ }
+
+ /**
+ * Get this function as a String containing its source code. If no source code
+ * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
+ *
+ * @return string representation of this function
+ */
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append("name='").
+ append(name.isEmpty() ? "<anonymous>" : name).
+ append("' ").
+ append(code.size()).
+ append(" invokers=").
+ append(code);
+
+ return sb.toString();
}
/**
- * Get the method handle used to invoke this function.
- * @return the invoke handle
+ * Pick the best invoker, i.e. the one version of this method with as narrow and specific
+ * types as possible. If the call site arguments are objects, but boxed primitives we can
+ * also try to get a primitive version of the method and do an unboxing filter, but then
+ * we need to insert a guard that checks the argument is really always a boxed primitive
+ * and not suddenly a "real" object
+ *
+ * @param callSiteType callsite type
+ * @param args arguments at callsite on first trampoline invocation
+ * @return method handle to best invoker
*/
- MethodHandle getInvoker() {
- return invoker;
+ MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
+ return getBest(callSiteType).getInvoker();
}
- MethodHandle getBestInvoker(final MethodType type) {
- return SpecializedMethodChooser.candidateWithLowestWeight(type, invoker, invokeSpecializations);
+ MethodHandle getBestInvoker(final MethodType callSiteType) {
+ return getBestInvoker(callSiteType, null);
+ }
+
+ MethodHandle getBestConstructor(final MethodType callSiteType, final Object[] args) {
+ if (!isConstructor()) {
+ throw typeError("not.a.constructor", toSource());
+ }
+ ensureCodeGenerated();
+
+ final CompiledFunction best = getBest(callSiteType);
+ ensureConstructor(best);
+ return best.getConstructor();
+ }
+
+ MethodHandle getBestConstructor(final MethodType callSiteType) {
+ return getBestConstructor(callSiteType, null);
}
/**
- * Get the method handle used to invoke this function as a constructor.
- * @return the constructor handle
+ * Subclass responsibility. If we can have lazy code generation, this is a hook to ensure that
+ * code exists before performing an operation.
*/
- private MethodHandle getConstructor() {
- if (constructor == null) {
- constructor = composeConstructor(invoker);
- }
+ protected void ensureCodeGenerated() {
+ //empty
+ }
- return constructor;
+ /**
+ * Return a generic Object/Object invoker for this method. It will ensure code
+ * is generated, get the most generic of all versions of this function and adapt it
+ * to Objects.
+ *
+ * TODO this is only public because {@link JavaAdapterFactory} can't supply us with
+ * a MethodType that we can use for lookup due to boostrapping problems. Can be fixed
+ *
+ * @return generic invoker of this script function
+ */
+ public final MethodHandle getGenericInvoker() {
+ ensureCodeGenerated();
+ return composeGenericMethod(code.mostGeneric().getInvoker());
+ }
+
+ private CompiledFunction getBest(final MethodType callSiteType) {
+ ensureCodeGenerated();
+ return code.best(callSiteType);
}
- MethodHandle getBestConstructor(MethodType descType) {
- if (!isConstructor()) {
- throw typeError("not.a.constructor", toSource());
- }
- return SpecializedMethodChooser.candidateWithLowestWeight(descType, getConstructor(), getConstructSpecializations());
+ /**
+ * Allocates an object using this function's allocator.
+ * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
+ */
+ ScriptObject allocate() {
+ return null;
}
- private MethodHandle composeConstructor(MethodHandle ctor) {
+ /**
+ * This method is used to create the immutable portion of a bound function.
+ * See {@link ScriptFunction#makeBoundFunction(Object, Object[])}
+ *
+ * @param fn the original function being bound
+ * @param self this reference to bind. Can be null.
+ * @param args additional arguments to bind. Can be null.
+ */
+ ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
+ ensureCodeGenerated();
+
+ final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
+ final int length = args == null ? 0 : args.length;
+
+ CompiledFunctions boundList = new CompiledFunctions();
+ for (final CompiledFunction inv : code) {
+ boundList.add(bind(inv, fn, self, allArgs));
+ }
+ ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor());
+ return boundData;
+ }
+
+ /**
+ * Compose a constructor given a primordial constructor handle
+ *
+ * @param ctor primordial constructor handle
+ * @param needsCallee do we need to pass a callee
+ *
+ * @return the composed constructor
+ */
+ protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) {
// If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
// "this" in the first argument position is what allows the elegant folded composition of
// (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
// always returns Object.
- MethodHandle composedCtor = changeReturnTypeToObject(swapCalleeAndThis(ctor));
+ MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor;
+
+ composedCtor = changeReturnTypeToObject(composedCtor);
final MethodType ctorType = composedCtor.type();
+
// Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually
// captured as "allocation" parameter of NEWFILTER after we fold the constructor into it.
// (this, [callee, ]args...) => ([callee, ]args...)
final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray();
+
// Fold constructor into newFilter that replaces the return value from the constructor with the originally
// allocated value when the originally allocated value is a primitive.
// (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...)
composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor);
// allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
- if (needsCallee()) {
+ if (needsCallee) {
// ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and
// the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...),
// or...
return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE);
}
+
// ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
// (this, args...) filter (callee) => (callee, args...)
return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE);
}
/**
- * Get an adapted version of the invoker handle that only uses {@code Object} as parameter and return types.
- * @return the generic invoke handle
+ * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed
+ * method handle. If this function's method handles don't need a callee parameter, returns the original method
+ * handle unchanged.
+ *
+ * @param mh a method handle with order of arguments {@code (callee, this, args...)}
+ *
+ * @return a method handle with order of arguments {@code (this, callee, args...)}
+ */
+ private static MethodHandle swapCalleeAndThis(final MethodHandle mh) {
+ final MethodType type = mh.type();
+ assert type.parameterType(0) == ScriptFunction.class : type;
+ assert type.parameterType(1) == Object.class : type;
+ final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
+ final int[] reorder = new int[type.parameterCount()];
+ reorder[0] = 1;
+ assert reorder[1] == 0;
+ for (int i = 2; i < reorder.length; ++i) {
+ reorder[i] = i;
+ }
+ return MethodHandles.permuteArguments(mh, newType, reorder);
+ }
+
+ /**
+ * Convert this argument for non-strict functions according to ES 10.4.3
+ *
+ * @param thiz the this argument
+ *
+ * @return the converted this object
+ */
+ private Object convertThisObject(final Object thiz) {
+ if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
+ if (JSType.nullOrUndefined(thiz)) {
+ return Context.getGlobalTrusted();
+ }
+
+ if (isPrimitiveThis(thiz)) {
+ return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
+ }
+ }
+
+ return thiz;
+ }
+
+ static boolean isPrimitiveThis(final Object obj) {
+ return obj instanceof String || obj instanceof ConsString ||
+ obj instanceof Number || obj instanceof Boolean;
+ }
+
+ /**
+ * Creates an invoker method handle for a bound function.
+ *
+ * @param targetFn the function being bound
+ * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
+ * any of its specializations.
+ * @param self the "this" value being bound
+ * @param args additional arguments being bound
+ *
+ * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
+ * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
+ * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
+ * to the original invoker on invocation.
*/
- private MethodHandle getGenericInvoker() {
- if (genericInvoker == null) {
- assert invoker != null : "invoker is null";
- genericInvoker = makeGenericMethod(invoker);
+ private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
+ // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
+ // in the target and will be ignored anyway.
+ final boolean isTargetBound = targetFn.isBoundFunction();
+
+ final boolean needsCallee = needsCallee(originalInvoker);
+ assert needsCallee == needsCallee() : "callee contract violation 2";
+ assert !(isTargetBound && needsCallee); // already bound functions don't need a callee
+
+ final Object boundSelf = isTargetBound ? null : convertThisObject(self);
+ final MethodHandle boundInvoker;
+
+ if (isVarArg(originalInvoker)) {
+ // First, bind callee and this without arguments
+ final MethodHandle noArgBoundInvoker;
+
+ if (isTargetBound) {
+ // Don't bind either callee or this
+ noArgBoundInvoker = originalInvoker;
+ } else if (needsCallee) {
+ // Bind callee and this
+ noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
+ } else {
+ // Only bind this
+ noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
+ }
+ // Now bind arguments
+ if (args.length > 0) {
+ boundInvoker = varArgBinder(noArgBoundInvoker, args);
+ } else {
+ boundInvoker = noArgBoundInvoker;
+ }
+ } else {
+ final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount(), args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))];
+ int next = 0;
+ if (!isTargetBound) {
+ if (needsCallee) {
+ boundArgs[next++] = targetFn;
+ }
+ boundArgs[next++] = boundSelf;
+ }
+ // If more bound args were specified than the function can take, we'll just drop those.
+ System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
+ // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
+ // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
+ // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
+ // start at position 1. If the function is not bound, we start inserting arguments at position 0.
+ boundInvoker = MH.insertArguments(originalInvoker, isTargetBound ? 1 : 0, boundArgs);
+ }
+
+ if (isTargetBound) {
+ return boundInvoker;
}
- return genericInvoker;
+
+ // If the target is not already bound, add a dropArguments that'll throw away the passed this
+ return MH.dropArguments(boundInvoker, 0, Object.class);
+ }
+
+ /**
+ * Creates a constructor method handle for a bound function using the passed constructor handle.
+ *
+ * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
+ * @param fn the function being bound
+ * @param args arguments being bound
+ *
+ * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
+ * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
+ * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
+ * this script function data object has no constructor handle, null is returned.
+ */
+ private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
+ assert originalConstructor != null;
+
+ // If target function is already bound, don't bother binding the callee.
+ final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
+ MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
+
+ if (args.length == 0) {
+ return calleeBoundConstructor;
+ }
+
+ if (isVarArg(calleeBoundConstructor)) {
+ return varArgBinder(calleeBoundConstructor, args);
+ }
+
+ final Object[] boundArgs;
+
+ final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
+ if (args.length <= maxArgCount) {
+ boundArgs = args;
+ } else {
+ boundArgs = new Object[maxArgCount];
+ System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
+ }
+
+ return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
+ }
+
+ /**
+ * Takes a method handle, and returns a potentially different method handle that can be used in
+ * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
+ * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
+ * {@code Object} as well, except for the following ones:
+ * <ul>
+ * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
+ * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
+ * (callee) as an argument.</li>
+ * </ul>
+ *
+ * @param mh the original method handle
+ *
+ * @return the new handle, conforming to the rules above.
+ */
+ protected MethodHandle composeGenericMethod(final MethodHandle mh) {
+ final MethodType type = mh.type();
+ MethodType newType = type.generic();
+ if (isVarArg(mh)) {
+ newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
+ }
+ if (needsCallee(mh)) {
+ newType = newType.changeParameterType(0, ScriptFunction.class);
+ }
+ return type.equals(newType) ? mh : mh.asType(newType);
}
/**
* Execute this script function.
+ *
* @param self Target object.
* @param arguments Call arguments.
* @return ScriptFunction result.
+ *
* @throws Throwable if there is an exception/error with the invocation or thrown from it
*/
Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
- final MethodHandle genInvoker = getGenericInvoker();
- final Object selfObj = convertThisObject(self);
- final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
+ final MethodHandle mh = getGenericInvoker();
+
+ final Object selfObj = convertThisObject(self);
+ final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
- if (isVarArg()) {
- if (needsCallee()) {
- return genInvoker.invokeExact(fn, selfObj, args);
+ if (isVarArg(mh)) {
+ if (needsCallee(mh)) {
+ return mh.invokeExact(fn, selfObj, args);
}
- return genInvoker.invokeExact(selfObj, args);
+ return mh.invokeExact(selfObj, args);
}
- final int paramCount = genInvoker.type().parameterCount();
- if (needsCallee()) {
+ final int paramCount = mh.type().parameterCount();
+ if (needsCallee(mh)) {
switch (paramCount) {
case 2:
- return genInvoker.invokeExact(fn, selfObj);
+ return mh.invokeExact(fn, selfObj);
case 3:
- return genInvoker.invokeExact(fn, selfObj, getArg(args, 0));
+ return mh.invokeExact(fn, selfObj, getArg(args, 0));
case 4:
- return genInvoker.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
+ return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
case 5:
- return genInvoker.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return genInvoker.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
+ return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
}
}
switch (paramCount) {
case 1:
- return genInvoker.invokeExact(selfObj);
+ return mh.invokeExact(selfObj);
case 2:
- return genInvoker.invokeExact(selfObj, getArg(args, 0));
+ return mh.invokeExact(selfObj, getArg(args, 0));
case 3:
- return genInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
+ return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
case 4:
- return genInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+ return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
default:
- return genInvoker.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
+ return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
}
}
@@ -389,15 +537,13 @@
return i < args.length ? args[i] : UNDEFINED;
}
- private Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
+ private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
final Object[] finalArgs = new Object[argCount];
int nextArg = 0;
- if (needsCallee()) {
- assert fn != null;
+ if (fn != null) {
+ //needs callee
finalArgs[nextArg++] = fn;
- } else {
- assert fn == null;
}
finalArgs[nextArg++] = self;
@@ -413,255 +559,14 @@
return finalArgs;
}
-
- /**
- * Get the specialized construct handles for this function.
- * @return array of specialized construct handles
- */
- private MethodHandle[] getConstructSpecializations() {
- if(constructSpecializations == null && invokeSpecializations != null) {
- final MethodHandle[] ctors = new MethodHandle[invokeSpecializations.length];
- for(int i = 0; i < ctors.length; ++i) {
- ctors[i] = composeConstructor(invokeSpecializations[i]);
- }
- constructSpecializations = ctors;
- }
- return constructSpecializations;
- }
-
- /**
- * Set the method handles for this function.
- * @param invoker the invoker handle
- * @param allocator the allocator handle
- */
- public void setMethodHandles(final MethodHandle invoker, final MethodHandle allocator) {
- // We can't make method handle fields final because they're not available during codegen
- // and they're set when first called, so we enforce set-once here.
- if (this.invoker == null) {
- this.invoker = invoker;
- this.constructor = null; // delay constructor composition
- this.allocator = allocator;
- }
- }
-
- /**
- * Used by the trampoline. Must not be any wider than package
- * private
- * @param invoker new invoker
- */
- void resetInvoker(final MethodHandle invoker) {
- this.invoker = invoker;
- this.constructor = null; //delay constructor composition
- }
-
- /**
- * Allocates an object using this function's allocator.
- * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
- */
- ScriptObject allocate() {
- if (allocator == null) {
- return null;
- }
-
- try {
- return (ScriptObject)allocator.invokeExact(allocatorMap);
- } catch (final RuntimeException | Error e) {
- throw e;
- } catch (final Throwable t) {
- throw new RuntimeException(t);
- }
- }
-
- /**
- * This method is used to create the immutable portion of a bound function.
- * See {@link ScriptFunction#makeBoundFunction(Object, Object[])}
- *
- * @param fn the original function being bound
- * @param self this reference to bind. Can be null.
- * @param args additional arguments to bind. Can be null.
- */
- ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
- final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
-
- final boolean isConstructor = isConstructor();
- // Note that the new ScriptFunctionData's method handle will not need a callee regardless of whether the
- // original did.
- final ScriptFunctionData boundData = new ScriptFunctionData(name, source, token,
- bindInvokeHandle(invoker, fn, self, allArgs), bindInvokeSpecializations(fn, self, allArgs), isStrict(), isBuiltin(), isConstructor);
- if(isConstructor) {
- // Can't just rely on bound invoke as a basis for constructor, as it ignores the passed "this" in favor of the
- // bound "this"; constructor on the other hand must see the actual "this" received from the allocator.
-
- // Binding a function will force constructor composition in getConstructor(); not really any way around that
- // as it's the composed constructor that has to be bound to the function.
- boundData.constructor = bindConstructHandle(getConstructor(), fn, allArgs);
- boundData.constructSpecializations = bindConstructorSpecializations(fn, allArgs);
- }
- assert boundData.allocator == null;
- final int thisArity = getArity();
- if(thisArity != -1) {
- boundData.setArity(Math.max(0, thisArity - args.length));
- } else {
- assert boundData.getArity() == -1;
- }
- return boundData;
- }
-
- /**
- * Convert this argument for non-strict functions according to ES 10.4.3
- *
- * @param thiz the this argument
- *
- * @return the converted this object
- */
- Object convertThisObject(final Object thiz) {
- if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
- if (JSType.nullOrUndefined(thiz)) {
- return Context.getGlobalTrusted();
- }
-
- if (isPrimitiveThis(thiz)) {
- return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz);
- }
- }
-
- return thiz;
- }
-
- static boolean isPrimitiveThis(Object obj) {
- return obj instanceof String || obj instanceof ConsString ||
- obj instanceof Number || obj instanceof Boolean;
- }
-
- /**
- * Creates an invoker method handle for a bound function.
- * @param targetFn the function being bound
- * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
- * any of its specializations.
- * @param self the "this" value being bound
- * @param args additional arguments being bound
- * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
- * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
- * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
- * to the original invoker on invocation.
- */
- private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
- // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
- // in the target and will be ignored anyway.
- final boolean isTargetBound = targetFn.isBoundFunction();
- assert !(isTargetBound && needsCallee()); // already bound functions don't need a callee
- final Object boundSelf = isTargetBound ? null : convertThisObject(self);
- final MethodHandle boundInvoker;
- if(isVarArg(originalInvoker)) {
- // First, bind callee and this without arguments
- final MethodHandle noArgBoundInvoker;
- if(isTargetBound) {
- // Don't bind either callee or this
- noArgBoundInvoker = originalInvoker;
- } else if(needsCallee()) {
- // Bind callee and this
- noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
- } else {
- // Only bind this
- noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
- }
- // Now bind arguments
- if(args.length > 0) {
- boundInvoker = varArgBinder(noArgBoundInvoker, args);
- } else {
- boundInvoker = noArgBoundInvoker;
- }
- } else {
- final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount(),
- args.length + (isTargetBound ? 0 : (needsCallee() ? 2 : 1)))];
- int next = 0;
- if(!isTargetBound) {
- if(needsCallee()) {
- boundArgs[next++] = targetFn;
- }
- boundArgs[next++] = boundSelf;
- }
- // If more bound args were specified than the function can take, we'll just drop those.
- System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
- // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
- // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
- // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
- // start at position 1. If the function is not bound, we start inserting arguments at position 0.
- boundInvoker = MH.insertArguments(originalInvoker, isTargetBound ? 1 : 0, boundArgs);
- }
- if(isTargetBound) {
- return boundInvoker;
- }
- // If the target is not already bound, add a dropArguments that'll throw away the passed this
- return MH.dropArguments(boundInvoker, 0, Object.class);
- }
-
- private MethodHandle[] bindInvokeSpecializations(final ScriptFunction fn, final Object self, final Object[] args) {
- if(invokeSpecializations == null) {
- return null;
- }
- final MethodHandle[] boundSpecializations = new MethodHandle[invokeSpecializations.length];
- for(int i = 0; i < invokeSpecializations.length; ++i) {
- boundSpecializations[i] = bindInvokeHandle(invokeSpecializations[i], fn, self, args);
- }
- return boundSpecializations;
- }
-
- /**
- * Creates a constructor method handle for a bound function using the passed constructor handle.
- * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
- * @param fn the function being bound
- * @param args arguments being bound
- * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
- * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
- * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
- * this script function data object has no constructor handle, null is returned.
- */
- private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
- if(originalConstructor == null) {
- return null;
- }
-
- // If target function is already bound, don't bother binding the callee.
- final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
- MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
- if(args.length == 0) {
- return calleeBoundConstructor;
- }
-
- if(isVarArg(calleeBoundConstructor)) {
- return varArgBinder(calleeBoundConstructor, args);
- }
-
- final Object[] boundArgs;
- final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
- if (args.length <= maxArgCount) {
- boundArgs = args;
- } else {
- boundArgs = new Object[maxArgCount];
- System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
- }
- return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
- }
-
- private MethodHandle[] bindConstructorSpecializations(final ScriptFunction fn, final Object[] args) {
- final MethodHandle[] ctorSpecs = getConstructSpecializations();
- if(ctorSpecs == null) {
- return null;
- }
- final MethodHandle[] boundSpecializations = new MethodHandle[ctorSpecs.length];
- for(int i = 0; i < ctorSpecs.length; ++i) {
- boundSpecializations[i] = bindConstructHandle(ctorSpecs[i], fn, args);
- }
- return boundSpecializations;
- }
-
/**
* Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the
* vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on
* invocation
+ *
* @param mh the handle
* @param args the bound arguments
+ *
* @return the bound method handle
*/
private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) {
@@ -671,41 +576,20 @@
}
/**
- * Convert boolean flags to int.
- * @param needsCallee needs-callee flag
- * @param isVarArg var-arg flag
- * @param isStrict strict flag
- * @param isBuiltin builtin flag
- * @return int flags
+ * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
+ * {@code Object}, the handle is returned unchanged.
+ *
+ * @param mh the handle to adapt
+ * @return the adapted handle
*/
- private static int makeFlags(final boolean needsCallee, final boolean isVarArg, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
- int flags = 0;
- if (needsCallee) {
- flags |= HAS_CALLEE;
- }
- if (isVarArg) {
- flags |= IS_VARARGS;
- }
- if (isStrict) {
- flags |= IS_STRICT;
- }
- if (isBuiltin) {
- flags |= IS_BUILTIN;
- }
- if (isConstructor) {
- flags |= IS_CONSTRUCTOR;
- }
-
- return flags;
+ private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
+ return MH.asType(mh, mh.type().changeReturnType(Object.class));
}
- /**
- * Test if a methodHandle refers to a constructor.
- * @param methodHandle MethodHandle to test.
- * @return True if method is a constructor.
- */
- private static boolean isConstructor(final MethodHandle methodHandle) {
- return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class;
+ private void ensureConstructor(final CompiledFunction inv) {
+ if (!inv.hasConstructor()) {
+ inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker())));
+ }
}
/**
@@ -713,102 +597,56 @@
* {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
* a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
* assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
- * they also always receive a callee.
- * @param methodHandle the examined method handle
+ * they also always receive a callee).
+ *
+ * @param mh the examined method handle
+ *
* @return true if the method handle expects a callee, false otherwise
*/
- private static boolean needsCallee(MethodHandle methodHandle) {
- final MethodType type = methodHandle.type();
- final int len = type.parameterCount();
- if(len == 0) {
+ protected static boolean needsCallee(final MethodHandle mh) {
+ final MethodType type = mh.type();
+ final int length = type.parameterCount();
+
+ if (length == 0) {
return false;
}
- if(type.parameterType(0) == boolean.class) {
- return len > 1 && type.parameterType(1) == ScriptFunction.class;
+
+ if (type.parameterType(0) == boolean.class) {
+ return length > 1 && type.parameterType(1) == ScriptFunction.class;
}
+
return type.parameterType(0) == ScriptFunction.class;
}
- private static boolean isVarArg(MethodHandle methodHandle) {
- final MethodType type = methodHandle.type();
- return type.parameterType(type.parameterCount() - 1).isArray();
- }
-
/**
- * Takes a method handle, and returns a potentially different method handle that can be used in
- * {@link ScriptFunction#invoke(Object, Object...)} or {@link ScriptFunction#construct(Object, Object...)}.
- * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
- * {@code Object} as well, except for the following ones:
- * <ul>
- * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
- * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
- * (callee) as an argument.</li>
- * </ul>
+ * Check if a javascript function methodhandle is a vararg handle
*
- * @param handle the original method handle
- * @return the new handle, conforming to the rules above.
+ * @param mh method handle to check
+ *
+ * @return true if vararg
*/
- private MethodHandle makeGenericMethod(final MethodHandle handle) {
- final MethodType type = handle.type();
- MethodType newType = type.generic();
- if (isVarArg()) {
- newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
- }
- if (needsCallee()) {
- newType = newType.changeParameterType(0, ScriptFunction.class);
- }
- return type.equals(newType) ? handle : handle.asType(newType);
- }
-
- /**
- * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
- * {@code Object}, the handle is returned unchanged.
- * @param mh the handle to adapt
- * @return the adapted handle
- */
- private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
- return MH.asType(mh, mh.type().changeReturnType(Object.class));
- }
-
-
- /**
- * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed
- * method handle. If this function's method handles don't need a callee parameter, returns the original method
- * handle unchanged.
- * @param mh a method handle with order of arguments {@code (callee, this, args...)}
- * @return a method handle with order of arguments {@code (this, callee, args...)}
- */
- private MethodHandle swapCalleeAndThis(final MethodHandle mh) {
- if (!needsCallee()) {
- return mh;
- }
+ protected static boolean isVarArg(final MethodHandle mh) {
final MethodType type = mh.type();
- assert type.parameterType(0) == ScriptFunction.class;
- assert type.parameterType(1) == Object.class;
- final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
- final int[] reorder = new int[type.parameterCount()];
- reorder[0] = 1;
- assert reorder[1] == 0;
- for (int i = 2; i < reorder.length; ++i) {
- reorder[i] = i;
- }
- return MethodHandles.permuteArguments(mh, newType, reorder);
+ return type.parameterType(type.parameterCount() - 1).isArray();
}
@SuppressWarnings("unused")
private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) {
- if(array2 == null) {
+ if (array2 == null) {
// Must clone it, as we can't allow the receiving method to alter the array
return array1.clone();
}
+
final int l2 = array2.length;
- if(l2 == 0) {
+ if (l2 == 0) {
return array1.clone();
}
+
final int l1 = array1.length;
final Object[] concat = new Object[l1 + l2];
System.arraycopy(array1, 0, concat, 0, l1);
System.arraycopy(array2, 0, concat, l1, l2);
+
return concat;
}
@@ -820,5 +658,4 @@
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types));
}
-
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java Tue Mar 12 18:12:42 2013 +0530
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/*
- * 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.runtime;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodType;
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.runtime.options.Options;
-
-class SpecializedMethodChooser {
- /** Should specialized function and specialized constructors for the builtin be used if available? */
- private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
-
- static MethodHandle candidateWithLowestWeight(final MethodType descType, final MethodHandle initialCandidate, final MethodHandle[] specs) {
- if (DISABLE_SPECIALIZATION || specs == null) {
- return initialCandidate;
- }
-
- int minimumWeight = Integer.MAX_VALUE;
- MethodHandle candidate = initialCandidate;
-
- for (final MethodHandle spec : specs) {
- final MethodType specType = spec.type();
-
- if (!typeCompatible(descType, specType)) {
- continue;
- }
-
- //return type is ok. we want a wider or equal one for our callsite.
- final int specWeight = weigh(specType);
- if (specWeight < minimumWeight) {
- candidate = spec;
- minimumWeight = specWeight;
- }
- }
-
- return candidate;
- }
-
- private static boolean typeCompatible(final MethodType desc, final MethodType spec) {
- //spec must fit in desc
- final Class<?>[] dparray = desc.parameterArray();
- final Class<?>[] sparray = spec.parameterArray();
-
- if (dparray.length != sparray.length) {
- return false;
- }
-
- for (int i = 0; i < dparray.length; i++) {
- final Type dp = Type.typeFor(dparray[i]);
- final Type sp = Type.typeFor(sparray[i]);
-
- if (dp.isBoolean()) {
- return false; //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution
- }
-
- //specialization arguments must be at least as wide as dp, if not wider
- if (Type.widest(dp, sp) != sp) {
- //e.g. specialization takes double and callsite says "object". reject.
- //but if specialization says double and callsite says "int" or "long" or "double", that's fine
- return false;
- }
- }
-
- return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic.
- }
-
- private static int weigh(final MethodType t) {
- int weight = Type.typeFor(t.returnType()).getWeight();
- for (final Class<?> paramType : t.parameterArray()) {
- final int pweight = Type.typeFor(paramType).getWeight();
- weight += pweight;
- }
- return weight;
- }
-}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java Tue Mar 12 15:30:53 2013 +0100
@@ -39,7 +39,7 @@
import jdk.nashorn.internal.runtime.ScriptObject;
/**
- * Utility class shared by {@link NashornLinker} and {@code NashornPrimitiveLinker} for converting JS values to Java
+ * Utility class shared by {@code NashornLinker} and {@code NashornPrimitiveLinker} for converting JS values to Java
* types.
*/
public class JavaArgumentConverters {
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java Tue Mar 12 15:30:53 2013 +0100
@@ -40,8 +40,6 @@
private static final MethodHandle IS_SCRIPTOBJECT = findOwnMH("isScriptObject", boolean.class, Object.class);
private static final MethodHandle IS_SCRIPTFUNCTION = findOwnMH("isScriptFunction", boolean.class, Object.class);
private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class);
- private static final MethodHandle IS_FUNCTION_MH = findOwnMH("isFunctionMH", boolean.class, Object.class, MethodHandle.class);
- private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, MethodHandle.class);
private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class);
// don't create me!
@@ -87,33 +85,6 @@
return MH.insertArguments(IS_INSTANCEOF_2, 1, class1, class2);
}
- /**
- * Get the guard that checks if a {@link ScriptFunction} is equal to
- * a known ScriptFunction, using reference comparison
- *
- * @param function The ScriptFunction to check against. This will be bound to the guard method handle
- *
- * @return method handle for guard
- */
- public static MethodHandle getFunctionGuard(final ScriptFunction function) {
- assert function.getInvokeHandle() != null;
- return MH.insertArguments(IS_FUNCTION_MH, 1, function.getInvokeHandle());
- }
-
- /**
- * Get a guard that checks if a {@link ScriptFunction} is equal to
- * a known ScriptFunction using reference comparison, and whether the type of
- * the second argument (this-object) is not a JavaScript primitive type.
- *
- * @param function The ScriptFunction to check against. This will be bound to the guard method handle
- *
- * @return method handle for guard
- */
- public static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) {
- assert function.getInvokeHandle() != null;
- return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.getInvokeHandle());
- }
-
@SuppressWarnings("unused")
private static boolean isScriptObject(final Object self) {
return self instanceof ScriptObject;
@@ -130,16 +101,6 @@
}
@SuppressWarnings("unused")
- private static boolean isFunctionMH(final Object self, final MethodHandle mh) {
- return self instanceof ScriptFunction && ((ScriptFunction)self).getInvokeHandle() == mh;
- }
-
- @SuppressWarnings("unused")
- private static boolean isNonStrictFunction(final Object self, final Object arg, final MethodHandle mh) {
- return self instanceof ScriptFunction && ((ScriptFunction)self).getInvokeHandle() == mh && arg instanceof ScriptObject;
- }
-
- @SuppressWarnings("unused")
private static boolean isInstanceOf2(final Object self, final Class<?> class1, final Class<?> class2) {
return class1.isInstance(self) || class2.isInstance(self);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java Tue Mar 12 15:30:53 2013 +0100
@@ -278,7 +278,7 @@
this.valueNextArg = Boolean.parseBoolean(arg);
break;
default:
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException(keyToken);
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties Tue Mar 12 15:30:53 2013 +0100
@@ -165,6 +165,12 @@
desc="Generate local variable table in .class files." \
}
+nashorn.option.lazy.compilation = { \
+ name="--lazy-compilation", \
+ is_undocumented=true, \
+ desc="EXPERIMENTAL: Use lazy code generation strategies - do not compile the entire script at once." \
+}
+
nashorn.option.loader.per.compile = { \
name="--loader-per-compile", \
is_undocumented=true, \
--- a/nashorn/test/script/currently-failing/JDK-8006529.js Tue Mar 12 18:12:42 2013 +0530
+++ b/nashorn/test/script/currently-failing/JDK-8006529.js Tue Mar 12 15:30:53 2013 +0100
@@ -39,12 +39,13 @@
* and FunctionNode because of package-access check and so reflective calls.
*/
-var Parser = Java.type("jdk.nashorn.internal.parser.Parser")
-var Compiler = Java.type("jdk.nashorn.internal.codegen.Compiler")
-var Context = Java.type("jdk.nashorn.internal.runtime.Context")
+var Parser = Java.type("jdk.nashorn.internal.parser.Parser")
+var Compiler = Java.type("jdk.nashorn.internal.codegen.Compiler")
+var Context = Java.type("jdk.nashorn.internal.runtime.Context")
var ScriptEnvironment = Java.type("jdk.nashorn.internal.runtime.ScriptEnvironment")
-var Source = Java.type("jdk.nashorn.internal.runtime.Source")
-var FunctionNode = Java.type("jdk.nashorn.internal.ir.FunctionNode")
+var Source = Java.type("jdk.nashorn.internal.runtime.Source")
+var FunctionNode = Java.type("jdk.nashorn.internal.ir.FunctionNode")
+var ThrowErrorManager = Java.type("jdk.nashorn.internal.runtime.Context$ThrowErrorManager");
// Compiler class methods and fields
var parseMethod = Parser.class.getMethod("parse");
@@ -90,7 +91,7 @@
// representing it.
function compile(source) {
var source = new Source("<no name>", source);
- var parser = new Parser(Context.getContext().getEnv(), source, null);
+ var parser = new Parser(Context.getContext().getEnv(), source, new ThrowErrorManager());
var func = parseMethod.invoke(parser);
var compiler = new Compiler(Context.getContext().getEnv(), func);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/currently-failing/clone_ir.js Tue Mar 12 15:30:53 2013 +0100
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/**
+ * clone_ir : Check that functionNode.clone copies all nodes and that they
+ * are not the same references
+ *
+ * @test
+ * @run
+ */
+
+var js1 = "var tuple = { func : function f(x) { if (x) { print('true'); { print('block_under-true'); } } else { print('false'); } } }";
+
+var Parser = Java.type("jdk.nashorn.internal.parser.Parser");
+var ASTWriter = Java.type("jdk.nashorn.internal.ir.debug.ASTWriter");
+var Context = Java.type("jdk.nashorn.internal.runtime.Context");
+var ScriptEnvironment = Java.type("jdk.nashorn.internal.runtime.ScriptEnvironment");
+var Source = Java.type("jdk.nashorn.internal.runtime.Source");
+var FunctionNode = Java.type("jdk.nashorn.internal.ir.FunctionNode");
+var ThrowErrorManager = Java.type("jdk.nashorn.internal.runtime.Context$ThrowErrorManager");
+var System = Java.type("java.lang.System");
+
+var toArrayMethod = ASTWriter.class.getMethod("toArray");
+var parseMethod = Parser.class.getMethod("parse");
+
+function toString(obj) {
+ var output = "{ ";
+ for (property in obj) {
+ output += property + ': ' + obj[property]+'; ';
+ }
+ return output + '}'
+}
+
+function flatten(func) {
+ var writer = new ASTWriter(func);
+ var funcList = toArrayMethod.invoke(writer);
+
+ var res = [];
+ for each (x in funcList) {
+ res.push({ name: x.getClass().getName(), id: System.identityHashCode(x) });
+ }
+ return res;
+}
+
+function check(contents) {
+ return check_src(new Source("<no name>", contents));
+}
+
+function check_src(src) {
+ var parser = new Parser(Context.getContext().getEnv(), src, new ThrowErrorManager());
+
+ var func = parseMethod.invoke(parser);
+ print(func);
+ var func2 = func.clone();
+
+ var f1 = flatten(func);
+ var f2 = flatten(func2);
+
+ print(f1.map(toString));
+ print(f2.map(toString));
+
+ if (f1.length != f2.length) {
+ print("length difference between original and clone " + f1.length + " != " + f2.length);
+ return false;
+ }
+
+ for (var i = 0; i < f1.length; i++) {
+ if (f1[i].name !== f2[i].name) {
+ print("name conflict at " + i + " " + f1[i].name + " != " + f2[i].name);
+ return false;
+ } else if (f1[i].id === f2[i].id) {
+ print("id problem at " + i + " " + toString(f1[i]) + " was not deep copied to " + toString(f2[i]) + " became " + f1[i].id + " != " + f2[i].id);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+print(check(js1));