8008199: Lazy compilation and trampoline implementation
Summary: The code pipeline now supports lazy compilation, which can be used to only compile certain FunctionNodes and leave others be, saving startup time. When these uncompiled nodes are hit, a trampoline will force them to be recompiled. This can also be used to specialize compilation fixing parameter types and return types to a callsite specific compilation. This will give performance.
Reviewed-by: attila, sundar
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Thu Feb 14 13:01:52 2013 +0100
@@ -84,7 +84,6 @@
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.Source;
/**
* This is the attribution pass of the code generator. Attr takes Lowered IR,
@@ -102,11 +101,8 @@
*/
final class Attr extends NodeOperatorVisitor {
- /** Current compiler. */
- private final Compiler compiler;
-
- /** Current source. */
- private final Source source;
+ /** Context compiler. */
+ private final Context context;
/**
* Local definitions in current block (to discriminate from function
@@ -122,16 +118,16 @@
*/
private Set<String> localUses;
- private static final DebugLogger LOG = new DebugLogger("attr");
+ private static final DebugLogger LOG = new DebugLogger("attr");
+ private static final boolean DEBUG = LOG.isEnabled();
/**
* Constructor.
*
* @param compiler the compiler
*/
- Attr(final Compiler compiler) {
- this.compiler = compiler;
- this.source = compiler.getSource();
+ Attr(final Context context) {
+ this.context = context;
}
@Override
@@ -231,6 +227,11 @@
@Override
public Node enter(final FunctionNode functionNode) {
start(functionNode, false);
+ if (functionNode.isLazy()) {
+ LOG.info("LAZY: " + functionNode.getName());
+ end(functionNode);
+ return null;
+ }
clearLocalDefs();
clearLocalUses();
@@ -257,7 +258,7 @@
}
if (functionNode.isScript()) {
- initFromPropertyMap(compiler.getContext(), functionNode);
+ initFromPropertyMap(context, functionNode);
}
// Add function name as local symbol
@@ -378,7 +379,7 @@
final FunctionNode functionNode = (FunctionNode)symbol.getNode();
assert functionNode.getCalleeNode() != null;
- final VarNode var = new VarNode(source, functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode());
+ final VarNode var = new VarNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode());
//newTemporary(Type.OBJECT, var); //ScriptFunction? TODO
functionNode.setNeedsSelfSymbol(var);
@@ -489,15 +490,21 @@
if (functionNode != null) {
functionNode.addReferencingParentBlock(getCurrentBlock());
}
- end(referenceNode);
-
return referenceNode;
}
@Override
public Node leave(final ReferenceNode referenceNode) {
newTemporary(Type.OBJECT, referenceNode); //reference node type is always an object, i.e. the scriptFunction. the function return type varies though
+
+ final FunctionNode functionNode = referenceNode.getReference();
+ //assert !functionNode.getType().isUnknown() || functionNode.isLazy() : functionNode.getType();
+ if (functionNode.isLazy()) {
+ LOG.info("Lazy function node call reference: " + functionNode.getName() + " => Promoting to OBJECT");
+ functionNode.setReturnType(Type.OBJECT);
+ }
end(referenceNode);
+
return referenceNode;
}
@@ -546,7 +553,7 @@
type = Type.OBJECT;
}
- switchNode.setTag(newInternal(compiler.uniqueName(SWITCH_TAG_PREFIX.tag()), type));
+ switchNode.setTag(newInternal(getCurrentFunctionNode().uniqueName(SWITCH_TAG_PREFIX.tag()), type));
end(switchNode);
@@ -1111,7 +1118,7 @@
@Override
public Node leave(final ForNode forNode) {
if (forNode.isForIn()) {
- forNode.setIterator(newInternal(getCurrentFunctionNode(), compiler.uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
+ forNode.setIterator(newInternal(getCurrentFunctionNode(), getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
/*
* Iterators return objects, so we need to widen the scope of the
* init variable if it, for example, has been assigned double type
@@ -1321,7 +1328,7 @@
}
private Symbol exceptionSymbol() {
- return newInternal(compiler.uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class));
+ return newInternal(getCurrentFunctionNode().uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class));
}
/**
@@ -1382,6 +1389,11 @@
}
}
+ @Override
+ public Node enter(final FunctionNode node) {
+ return node.isLazy() ? null : node;
+ }
+
/**
* Eg.
*
@@ -1553,17 +1565,19 @@
}
private Node start(final Node node, final boolean printNode) {
- final StringBuilder sb = new StringBuilder();
+ if (DEBUG) {
+ final StringBuilder sb = new StringBuilder();
- sb.append("[ENTER ").
- append(name(node)).
- append("] ").
- append(printNode ? node.toString() : "").
- append(" in '").
- append(getCurrentFunctionNode().getName()).
- append("'");
- LOG.info(sb.toString());
- LOG.indent();
+ sb.append("[ENTER ").
+ append(name(node)).
+ append("] ").
+ append(printNode ? node.toString() : "").
+ append(" in '").
+ append(getCurrentFunctionNode().getName()).
+ append("'");
+ LOG.info(sb.toString());
+ LOG.indent();
+ }
return node;
}
@@ -1573,24 +1587,26 @@
}
private Node end(final Node node, final boolean printNode) {
- final StringBuilder sb = new StringBuilder();
+ if (DEBUG) {
+ final StringBuilder sb = new StringBuilder();
- sb.append("[LEAVE ").
- append(name(node)).
- append("] ").
- append(printNode ? node.toString() : "").
- append(" in '").
- append(getCurrentFunctionNode().getName());
+ sb.append("[LEAVE ").
+ append(name(node)).
+ append("] ").
+ append(printNode ? node.toString() : "").
+ append(" in '").
+ append(getCurrentFunctionNode().getName());
- if (node.getSymbol() == null) {
- sb.append(" <NO SYMBOL>");
- } else {
- sb.append(" <symbol=").append(node.getSymbol()).append('>');
+ if (node.getSymbol() == null) {
+ sb.append(" <NO SYMBOL>");
+ } else {
+ sb.append(" <symbol=").append(node.getSymbol()).append('>');
+ }
+
+ LOG.unindent();
+ LOG.info(sb.toString());
}
- LOG.unindent();
- LOG.info(sb.toString());
-
return node;
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java Thu Feb 14 13:01:52 2013 +0100
@@ -52,6 +52,8 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
@@ -166,8 +168,8 @@
* @param unitClassName Compile unit class name.
* @param strictMode Should we generate this method in strict mode
*/
- ClassEmitter(final Compiler compiler, final String unitClassName, final boolean strictMode) {
- this(compiler.getContext(),
+ ClassEmitter(final Context context, final String sourceName, final String unitClassName, final boolean strictMode) {
+ this(context,
new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
private static final String OBJECT_CLASS = "java/lang/Object";
@@ -187,13 +189,23 @@
this.unitClassName = unitClassName;
this.constantMethodNeeded = new HashSet<>();
- cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, Compiler.pathName(jdk.nashorn.internal.scripts.JS$.class.getName()), null);
- cw.visitSource(compiler.getSource().getName(), null);
+ cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, pathName(jdk.nashorn.internal.scripts.JS$.class.getName()), null);
+ cw.visitSource(sourceName, null);
defineCommonStatics(strictMode);
}
/**
+ * Convert a binary name to a package/class name.
+ *
+ * @param name Binary name.
+ * @return Package/class name.
+ */
+ private static String pathName(final String name) {
+ return name.replace('.', '/');
+ }
+
+ /**
* Define the static fields common in all scripts.
* @param strictMode Should we generate this method in strict mode
*/
@@ -295,7 +307,7 @@
* Ensure a get constant method is issued for the class.
* @param cls Class of constant.
*/
- public void needGetConstantMethod(final Class<?> cls) {
+ void needGetConstantMethod(final Class<?> cls) {
constantMethodNeeded.add(cls);
}
@@ -348,22 +360,15 @@
/**
* Disassemble an array of byte code.
- *
- * @param context the context
* @param bytecode byte array representing bytecode
+ * @return disassembly as human readable string
*/
- public static void disassemble(final Context context, final byte[] bytecode) {
- new ClassReader(bytecode).accept(new TraceClassVisitor(context.getErr()), 0);
- }
-
- /**
- * Verify an array of byte code as a valid Java class
- *
- * @param context the context
- * @param bytecode the bytecode array
- */
- public static void verify(final Context context, final byte[] bytecode) {
- context.verify(bytecode);
+ public static String disassemble(final byte[] bytecode) {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (final PrintWriter pw = new PrintWriter(baos)) {
+ new ClassReader(bytecode).accept(new TraceClassVisitor(pw), 0);
+ }
+ return new String(baos.toByteArray());
}
/**
@@ -459,7 +464,7 @@
final MethodVisitor mv = cw.visitMethod(
ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
functionNode.getName(),
- FunctionSignature.functionSignature(functionNode),
+ new FunctionSignature(functionNode).toString(),
null,
null);
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Feb 14 13:01:52 2013 +0100
@@ -25,8 +25,10 @@
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;
@@ -35,6 +37,7 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
@@ -48,6 +51,7 @@
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;
@@ -63,8 +67,8 @@
import jdk.nashorn.internal.codegen.MethodEmitter.Label;
import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode;
import jdk.nashorn.internal.codegen.objects.FieldObjectCreator;
-import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
import jdk.nashorn.internal.codegen.objects.MapCreator;
+import jdk.nashorn.internal.codegen.objects.ObjectCreator;
import jdk.nashorn.internal.codegen.objects.ObjectMapCreator;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
@@ -111,11 +115,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.PropertyMap;
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;
@@ -143,12 +149,18 @@
*/
public final class CodeGenerator extends NodeOperatorVisitor {
- /** Current compiler */
+ /** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */
+ private static final String GLOBAL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "Global";
+
+ /** 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;
- /** Compiler context */
- private final Context context;
-
/** Call site flags given to the code generator to be used for all generated call sites */
private final int callSiteFlags;
@@ -168,17 +180,7 @@
*/
CodeGenerator(final Compiler compiler) {
this.compiler = compiler;
- this.context = compiler.getContext();
- this.callSiteFlags = context._callsite_flags;
- }
-
- /**
- * Get the compiler
- *
- * @return the compiler used
- */
- public Compiler getCompiler() {
- return compiler;
+ this.callSiteFlags = compiler.getContext()._callsite_flags;
}
/**
@@ -320,7 +322,7 @@
*/
final CodeGenerator codegen = this;
- node.accept(new NodeVisitor(compileUnit, method) {
+ node.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
@Override
public Node enter(final IdentNode identNode) {
loadIdent(identNode);
@@ -534,7 +536,7 @@
final FunctionNode currentFunction = getCurrentFunctionNode();
final Block currentBlock = getCurrentBlock();
- function.accept(new NodeVisitor(compileUnit, method) {
+ function.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
private void sharedScopeCall(final IdentNode identNode, final int flags) {
final Symbol symbol = identNode.getSymbol();
@@ -651,7 +653,7 @@
final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
if (callee.needsCallee()) {
- new FunctionObjectCreator(CodeGenerator.this, callee).makeObject(method);
+ newFunctionObject(callee);
}
if (callee.isStrictMode()) { // self is undefined
@@ -969,14 +971,18 @@
@Override
public Node enter(final FunctionNode functionNode) {
+ if (functionNode.isLazy()) {
+ return null;
+ }
+
if (functionNode.testResolved()) {
return null;
}
- compileUnit = functionNode.getCompileUnit();
- assert compileUnit != null;
-
- method = compileUnit.getClassEmitter().method(functionNode);
+ setCurrentCompileUnit(functionNode.getCompileUnit());
+ assert getCurrentCompileUnit() != null;
+
+ method = getCurrentCompileUnit().getClassEmitter().method(functionNode);
functionNode.setMethodEmitter(method);
// Mark end for variable tables.
method.begin();
@@ -1100,18 +1106,18 @@
final Type elementType = arrayType.getElementType();
if (units != null) {
- final CompileUnit savedCompileUnit = compileUnit;
- final MethodEmitter savedMethod = method;
+ final CompileUnit savedCompileUnit = getCurrentCompileUnit();
+ final MethodEmitter savedMethod = getCurrentMethodEmitter();
try {
for (final ArrayUnit unit : units) {
- compileUnit = unit.getCompileUnit();
-
- final String className = compileUnit.getUnitClassName();
- final String name = compiler.uniqueName(SPLIT_PREFIX.tag());
+ setCurrentCompileUnit(unit.getCompileUnit());
+
+ final String className = getCurrentCompileUnit().getUnitClassName();
+ final String name = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag());
final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
- method = compileUnit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
+ method = getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
method.setFunctionNode(getCurrentFunctionNode());
method.begin();
@@ -1135,8 +1141,8 @@
savedMethod.invokeStatic(className, name, signature);
}
} finally {
- compileUnit = savedCompileUnit;
- method = savedMethod;
+ setCurrentCompileUnit(savedCompileUnit);
+ setCurrentMethodEmitter(savedMethod);
}
return method;
@@ -1186,8 +1192,8 @@
* @param string string to load
*/
public void loadConstant(final String string) {
- final String unitClassName = compileUnit.getUnitClassName();
- final ClassEmitter classEmitter = compileUnit.getClassEmitter();
+ final String unitClassName = getCurrentCompileUnit().getUnitClassName();
+ final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
final int index = compiler.getConstantData().add(string);
method.load(index);
@@ -1202,8 +1208,8 @@
* @param object object to load
*/
public void loadConstant(final Object object) {
- final String unitClassName = compileUnit.getUnitClassName();
- final ClassEmitter classEmitter = compileUnit.getClassEmitter();
+ final String unitClassName = getCurrentCompileUnit().getUnitClassName();
+ final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
final int index = compiler.getConstantData().add(object);
final Class<?> cls = object.getClass();
@@ -1272,14 +1278,14 @@
return loadRegexToken(regexToken);
}
// emit field
- final String regexName = compiler.uniqueName(REGEX_PREFIX.tag());
- final ClassEmitter classEmitter = compileUnit.getClassEmitter();
+ final String regexName = getCurrentFunctionNode().uniqueName(REGEX_PREFIX.tag());
+ final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
regexFieldCount++;
// get field, if null create new regex, finally clone regex object
- method.getStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class));
+ method.getStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
method.dup();
final Label cachedLabel = new Label("cached");
method.ifnonnull(cachedLabel);
@@ -1287,7 +1293,7 @@
method.pop();
loadRegexToken(regexToken);
method.dup();
- method.putStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class));
+ method.putStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
method.label(cachedLabel);
globalRegExpCopy();
@@ -1409,7 +1415,7 @@
return null;
}
- new FunctionObjectCreator(this, referenceNode.getReference()).makeObject(method);
+ newFunctionObject(referenceNode.getReference());
return null;
}
@@ -1551,6 +1557,10 @@
return true;
}
+ private static boolean isReducible(final Request request) {
+ return Request.isComparison(request) || request == Request.ADD;
+ }
+
@Override
public Node enter(final RuntimeNode runtimeNode) {
if (runtimeNode.testResolved()) {
@@ -1563,9 +1573,10 @@
*
* TODO - remove this - Access Specializer will always know after Attr/Lower
*/
- if (runtimeNode.isPrimitive() && !runtimeNode.isFinal()) {
+ if (runtimeNode.isPrimitive() && !runtimeNode.isFinal() && isReducible(runtimeNode.getRequest())) {
final Node lhs = runtimeNode.getArgs().get(0);
- final Node rhs = runtimeNode.getArgs().size() > 1 ? runtimeNode.getArgs().get(1) : null;
+ assert runtimeNode.getArgs().size() > 1 : runtimeNode + " must have two args";
+ final Node rhs = runtimeNode.getArgs().get(1);
final Type type = runtimeNode.getType();
final Symbol symbol = runtimeNode.getSymbol();
@@ -1709,7 +1720,7 @@
method.end();
} catch (final Throwable t) {
Context.printStackTrace(t);
- final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + compiler.getSource().getName());
+ final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentFunctionNode().getSource().getName());
e.initCause(t);
throw e;
}
@@ -1898,7 +1909,7 @@
method._new(ECMAException.class).dup();
final Node expression = throwNode.getExpression();
- final Source source = compiler.getSource();
+ final Source source = throwNode.getSource();
final int position = throwNode.position();
final int line = source.getLine(position);
final int column = source.getColumn(position);
@@ -2928,7 +2939,7 @@
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
- scopeCall.setClassAndName(compileUnit, compiler);
+ scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
@@ -2947,7 +2958,7 @@
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
- scopeCall.setClassAndName(compileUnit, compiler);
+ scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
@@ -2959,12 +2970,12 @@
* @param ident identifier for block or function where applicable
*/
private void printSymbols(final Block block, final String ident) {
- if (!context._print_symbols) {
+ if (!compiler.getContext()._print_symbols) {
return;
}
@SuppressWarnings("resource")
- final PrintWriter out = context.getErr();
+ final PrintWriter out = compiler.getContext().getErr();
out.println("[BLOCK in '" + ident + "']");
if (!block.printSymbols(out)) {
out.println("<no symbols>");
@@ -3057,7 +3068,7 @@
* on the stack throughout the store and used at the end to execute it
*/
- target.accept(new NodeVisitor(compileUnit, method) {
+ target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
@Override
public Node enter(final IdentNode node) {
if (targetSymbol.isScope()) {
@@ -3124,7 +3135,7 @@
* @return the quick symbol
*/
private Symbol quickSymbol(final Type type, final String prefix) {
- final String name = compiler.uniqueName(prefix);
+ final String name = getCurrentFunctionNode().uniqueName(prefix);
final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL, null, null);
symbol.setType(type);
@@ -3166,7 +3177,7 @@
*/
method.convert(target.getType());
- target.accept(new NodeVisitor(compileUnit, method) {
+ target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
@Override
protected Node enterDefault(Node node) {
throw new AssertionError("Unexpected node " + node + " in store epilogue");
@@ -3228,42 +3239,83 @@
}
+ private void newFunctionObject(final FunctionNode functionNode) {
+ final boolean isLazy = functionNode.isLazy();
+ final Class<?>[] cparams = new Class<?>[] { ScriptFunctionData.class, ScriptObject.class, MethodHandle.class };
+
+ new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
+ @Override
+ public 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()));
+
+ if (isLazy || functionNode.needsParentScope()) {
+ method.loadScope();
+ } else {
+ method.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()])));
+ }
+ }.makeObject(method);
+ }
+
/*
* Globals are special. We cannot refer to any Global (or NativeObject) class by .class, as they are different
* for different contexts. As far as I can tell, the only NativeObject that we need to deal with like this
* is from the code pipeline is Global
*/
private MethodEmitter globalInstance() {
- return method.invokeStatic(Compiler.GLOBAL_OBJECT, "instance", "()L" + Compiler.GLOBAL_OBJECT + ';');
+ return method.invokeStatic(GLOBAL_OBJECT, "instance", "()L" + GLOBAL_OBJECT + ';');
}
private MethodEmitter globalObjectPrototype() {
- return method.invokeStatic(Compiler.GLOBAL_OBJECT, "objectPrototype", methodDescriptor(ScriptObject.class));
+ return method.invokeStatic(GLOBAL_OBJECT, "objectPrototype", methodDescriptor(ScriptObject.class));
}
private MethodEmitter globalAllocateArguments() {
- return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocateArguments", methodDescriptor(ScriptObject.class, Object[].class, Object.class, int.class));
+ return method.invokeStatic(GLOBAL_OBJECT, "allocateArguments", methodDescriptor(ScriptObject.class, Object[].class, Object.class, int.class));
}
private MethodEmitter globalNewRegExp() {
- return method.invokeStatic(Compiler.GLOBAL_OBJECT, "newRegExp", methodDescriptor(Object.class, String.class, String.class));
+ return method.invokeStatic(GLOBAL_OBJECT, "newRegExp", methodDescriptor(Object.class, String.class, String.class));
}
private MethodEmitter globalRegExpCopy() {
- return method.invokeStatic(Compiler.GLOBAL_OBJECT, "regExpCopy", methodDescriptor(Object.class, Object.class));
+ return method.invokeStatic(GLOBAL_OBJECT, "regExpCopy", methodDescriptor(Object.class, Object.class));
}
private MethodEmitter globalAllocateArray(final ArrayType type) {
//make sure the native array is treated as an array type
- return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocate", "(" + type.getDescriptor() + ")Ljdk/nashorn/internal/objects/NativeArray;");
+ return method.invokeStatic(GLOBAL_OBJECT, "allocate", "(" + type.getDescriptor() + ")Ljdk/nashorn/internal/objects/NativeArray;");
}
private MethodEmitter globalIsEval() {
- return method.invokeStatic(Compiler.GLOBAL_OBJECT, "isEval", methodDescriptor(boolean.class, Object.class));
+ return method.invokeStatic(GLOBAL_OBJECT, "isEval", methodDescriptor(boolean.class, Object.class));
}
private MethodEmitter globalDirectEval() {
- return method.invokeStatic(Compiler.GLOBAL_OBJECT, "directEval",
+ return method.invokeStatic(GLOBAL_OBJECT, "directEval",
methodDescriptor(Object.class, Object.class, Object.class, Object.class, Object.class, Object.class));
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Feb 14 13:01:52 2013 +0100
@@ -0,0 +1,366 @@
+package jdk.nashorn.internal.codegen;
+
+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.SPLIT;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.EnumSet;
+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.ir.debug.ASTWriter;
+import jdk.nashorn.internal.ir.debug.PrintVisitor;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ECMAErrors;
+
+/**
+ * A compilation phase is a step in the processes of turning a JavaScript FunctionNode
+ * into bytecode. It has an optional return value.
+ */
+enum CompilationPhase {
+
+ /*
+ * Lazy initialization - tag all function nodes not the script as lazy as
+ * default policy. The will get trampolines and only be generated when
+ * called
+ */
+ LAZY_INITIALIZATION_PHASE(EnumSet.of(FunctionNode.CompilationState.INITIALIZED)) {
+ @Override
+ boolean transform(final Compiler compiler, final FunctionNode fn) {
+
+ /*
+ * For lazy compilation, we might be given a node previously marked as lazy
+ * to compile as the outermost function node in the compiler. Unmark it
+ * so it can be compiled and not cause recursion. Make sure the return type
+ * is unknown so it can be correctly deduced. Return types are always
+ * Objects in Lazy nodes as we haven't got a change to generate code for
+ * them and decude its parameter specialization
+ *
+ * TODO: in the future specializations from a callsite will be passed here
+ * so we can generate a better non-lazy version of a function from a trampoline
+ */
+ //compute the signature from the callsite - todo - now just clone object params
+ final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
+ outermostFunctionNode.setIsLazy(false);
+ outermostFunctionNode.setReturnType(Type.UNKNOWN);
+
+ outermostFunctionNode.accept(new NodeVisitor() {
+ @Override
+ public Node enter(final FunctionNode node) {
+ assert Compiler.LAZY_JIT;
+ node.setIsLazy(node != outermostFunctionNode);
+ return node;
+ }
+ });
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[Lazy JIT Initialization]";
+ }
+ },
+
+ /*
+ * Constant folding pass
+ * Simple constant folding that will make elementary constructs go away
+ */
+ CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED), CONSTANT_FOLDED) {
+ @Override
+ boolean transform(final Compiler compiler, final FunctionNode fn) {
+ final Context context = compiler.getContext();
+
+ if (context._print_ast) {
+ context.getErr().println(new ASTWriter(fn));
+ }
+
+ if (context._print_parse) {
+ context.getErr().println(new PrintVisitor(fn));
+ }
+
+ fn.accept(new FoldConstants());
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[Constant Folding]";
+ }
+ },
+
+ /*
+ * Lower (Control flow pass)
+ * Finalizes the control flow. Clones blocks for finally constructs and
+ * similar things. Establishes termination criteria for nodes
+ * Guarantee return instructions to method making sure control flow
+ * cannot fall off the end. Replacing high level nodes with lower such
+ * as runtime nodes where applicable.
+ *
+ */
+ LOWERING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED), LOWERED) {
+ @Override
+ boolean transform(final Compiler compiler, final FunctionNode fn) {
+ fn.accept(new Lower());
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[Control Flow Lowering]";
+ }
+ },
+
+ /*
+ * Attribution
+ * Assign symbols and types to all nodes.
+ */
+ ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED), ATTR) {
+ @Override
+ boolean transform(final Compiler compiler, final FunctionNode fn) {
+ final Context context = compiler.getContext();
+ try {
+ fn.accept(new Attr(context));
+ return true;
+ } finally {
+ if (context._print_lower_ast) {
+ context.getErr().println(new ASTWriter(fn));
+ }
+
+ if (context._print_lower_parse) {
+ context.getErr().println(new PrintVisitor(fn));
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "[Type Attribution]";
+ }
+ },
+
+ /*
+ * Splitter
+ * Split the AST into several compile units based on a size heuristic
+ * Splitter needs attributed AST for weight calculations (e.g. is
+ * 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) {
+ @Override
+ boolean transform(final Compiler compiler, final FunctionNode fn) {
+ final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
+
+ new Splitter(compiler, fn, outermostCompileUnit).split();
+
+ assert fn.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + fn.getCompileUnit() + ") != " + outermostCompileUnit;
+
+ if (fn.isStrictMode()) {
+ assert compiler.getStrictMode();
+ compiler.setStrictMode(true);
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[Code Splitting]";
+ }
+ },
+
+ /*
+ * FinalizeTypes
+ *
+ * This pass finalizes the types for nodes. If Attr created wider types than
+ * known during the first pass, convert nodes are inserted or access nodes
+ * are specialized where scope accesses.
+ *
+ * Runtime nodes may be removed and primitivized or reintroduced depending
+ * on information that was established in Attr.
+ *
+ * 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) {
+ @Override
+ boolean transform(final Compiler compiler, final FunctionNode fn) {
+ fn.accept(new FinalizeTypes());
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[Type Finalization]";
+ }
+ },
+
+ /*
+ * Bytecode generation:
+ *
+ * Generate the byte code class(es) resulting from the compiled FunctionNode
+ */
+ BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED), EMITTED) {
+ @Override
+ boolean transform(final Compiler compiler, final FunctionNode fn) {
+ final Context context = compiler.getContext();
+
+ try {
+ final CodeGenerator codegen = new CodeGenerator(compiler);
+ fn.accept(codegen);
+ codegen.generateScopeCalls();
+
+ } catch (final VerifyError e) {
+ if (context._verify_code || context._print_code) {
+ context.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
+ if (context._dump_on_error) {
+ e.printStackTrace(context.getErr());
+ }
+ } else {
+ throw e;
+ }
+ }
+
+ for (final CompileUnit compileUnit : compiler.getCompileUnits()) {
+ final ClassEmitter classEmitter = compileUnit.getClassEmitter();
+ classEmitter.end();
+
+ final byte[] bytecode = classEmitter.toByteArray();
+ assert bytecode != null;
+
+ final String className = compileUnit.getUnitClassName();
+
+ compiler.addClass(className, bytecode);
+
+ //should could be printed to stderr for generate class?
+ if (context._print_code) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("class: " + className).
+ append('\n').
+ append(ClassEmitter.disassemble(bytecode)).
+ append("=====");
+ context.getErr().println(sb);
+ }
+
+ //should we verify the generated code?
+ if (context._verify_code) {
+ context.verify(bytecode);
+ }
+
+ //should code be dumped to disk - only valid in compile_only mode?
+ if (context._dest_dir != null && context._compile_only) {
+ final String fileName = className.replace('.', File.separatorChar) + ".class";
+ final int index = fileName.lastIndexOf(File.separatorChar);
+
+ if (index != -1) {
+ final File dir = new File(fileName.substring(0, index));
+ try {
+ if (!dir.exists() && !dir.mkdirs()) {
+ throw new IOException();
+ }
+ final File file = new File(context._dest_dir, fileName);
+ try (final FileOutputStream fos = new FileOutputStream(file)) {
+ fos.write(bytecode);
+ }
+ } catch (final IOException e) {
+ Compiler.LOG.warning("Skipping class dump for " + className + ": " + ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[Bytecode Generation]";
+ }
+ };
+
+ private final EnumSet<CompilationState> pre;
+ private final CompilationState post;
+ private long startTime;
+ private long endTime;
+ private boolean isFinished;
+
+ private static final long[] accumulatedTime = new long[CompilationPhase.values().length];
+
+ private CompilationPhase(final EnumSet<CompilationState> pre) {
+ this(pre, null);
+ }
+
+ private CompilationPhase(final EnumSet<CompilationState> pre, final CompilationState post) {
+ this.pre = pre;
+ this.post = post;
+ }
+
+ boolean isApplicable(final FunctionNode functionNode) {
+ return functionNode.hasState(pre);
+ }
+
+ protected void begin(final FunctionNode functionNode) {
+ if (pre != null) {
+ //check that everything in pre is present
+ for (final CompilationState state : pre) {
+ assert functionNode.hasState(state);
+ }
+ //check that nothing else is present
+ for (final CompilationState state : CompilationState.values()) {
+ assert !(functionNode.hasState(state) && !pre.contains(state));
+ }
+ }
+
+ startTime = System.currentTimeMillis();
+ }
+
+ protected void end(final FunctionNode functionNode) {
+ endTime = System.currentTimeMillis();
+ accumulatedTime[ordinal()] += (endTime - startTime);
+
+ if (post != null) {
+ functionNode.setState(post);
+ }
+
+ isFinished = true;
+ }
+
+ boolean isFinished() {
+ return isFinished;
+ }
+
+ long getStartTime() {
+ return startTime;
+ }
+
+ long getEndTime() {
+ return endTime;
+ }
+
+ public static long getAccumulatedTime(final CompilationPhase phase) {
+ return accumulatedTime[phase.ordinal()];
+ }
+
+ abstract boolean transform(final Compiler compiler, final FunctionNode functionNode);
+
+ final boolean apply(final Compiler compiler, final FunctionNode functionNode) {
+ try {
+ if (!isApplicable(functionNode)) {
+ return false;
+ }
+ begin(functionNode);
+ transform(compiler, functionNode);
+ return true;
+ } finally {
+ end(functionNode);
+ }
+ }
+
+}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java Thu Feb 14 13:01:52 2013 +0100
@@ -51,7 +51,7 @@
* Add weight to this compile unit
* @param w weight to add
*/
- public void addWeight(final long w) {
+ void addWeight(final long w) {
this.weight += w;
}
@@ -59,7 +59,7 @@
* Get the current weight of the compile unit.
* @return the unit's weight
*/
- public long getWeight() {
+ long getWeight() {
return weight;
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Thu Feb 14 13:01:52 2013 +0100
@@ -27,124 +27,377 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
-import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
+import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
-
import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
+import java.util.Arrays;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.TreeMap;
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.Node;
-import jdk.nashorn.internal.ir.debug.ASTWriter;
-import jdk.nashorn.internal.ir.debug.PrintVisitor;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-import jdk.nashorn.internal.parser.Parser;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.DebugLogger;
-import jdk.nashorn.internal.runtime.ECMAErrors;
-import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.options.Options;
import org.dynalang.dynalink.support.NameCodec;
/**
* Responsible for converting JavaScripts to java byte code. Main entry
- * point for code generator
+ * point for code generator. The compiler may also install classes given some
+ * predefined Code installation policy, given to it at construction time.
+ * @see CodeInstaller
*/
public final class Compiler {
- /** Compiler states available */
- public enum State {
- /** compiler is ready */
- INITIALIZED,
- /** method has been parsed */
- PARSED,
- /** constant folding pass */
- CONSTANT_FOLDED,
- /** method has been lowered */
- LOWERED,
- /** method hass been attributed */
- ATTR,
- /** method has been split */
- SPLIT,
- /** method has had its types finalized */
- FINALIZED,
- /** method has been emitted to bytecode */
- EMITTED
- }
-
- /** Current context */
- private final Context context;
-
- /** Currently compiled source */
- private final Source source;
-
- /** Current error manager */
- private final ErrorManager errors;
-
- /** Names uniqueName for this compile. */
- private final Namespace namespace;
-
- /** Current function node, or null if compiling from source until parsed */
- private FunctionNode functionNode;
-
- /** Current compiler state */
- private final EnumSet<State> state;
-
/** Name of the scripts package */
public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts";
/** Name of the objects package */
public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
- /** Name of the runtime package */
- public static final String RUNTIME_PACKAGE = "jdk/nashorn/internal/runtime";
-
- /** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */
- public static final String GLOBAL_OBJECT = OBJECTS_PACKAGE + '/' + "Global";
+ static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy");
- /** Name of the ScriptFunctionImpl, cannot be referred to as .class @see FunctionObjectCreator */
- public static final String SCRIPTFUNCTION_IMPL_OBJECT = OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl";
+ static final boolean TIME_COMPILATION = Options.getBooleanProperty("nashorn.compiler.time");
- /** Name of the Trampoline, cannot be referred to as .class @see FunctionObjectCreator */
- public static final String TRAMPOLINE_OBJECT = OBJECTS_PACKAGE + '/' + "Trampoline";
+ private final Map<String, byte[]> bytecode;
- /** Compile unit (class) table. */
private final Set<CompileUnit> compileUnits;
- /** All the "complex" constants used in the code. */
private final ConstantData constantData;
+ private final FunctionNode functionNode;
+
+ private final CompilationSequence sequence;
+
+ private final Context context;
+
+ private final String scriptName;
+
+ private boolean strict;
+
+ private CodeInstaller<Context> installer;
+
static final DebugLogger LOG = new DebugLogger("compiler");
- /** Script name */
- private String scriptName;
+ /**
+ * This array contains names that need to be reserved at the start
+ * of a compile, to avoid conflict with variable names later introduced.
+ * See {@link CompilerConstants} for special names used for structures
+ * during a compile.
+ */
+ private static String[] RESERVED_NAMES = {
+ SCOPE.tag(),
+ THIS.tag()
+ };
+
+ /**
+ * This class makes it possible to do your own compilation sequence
+ * from the code generation package. There are predefined compilation
+ * sequences already
+ */
+ @SuppressWarnings("serial")
+ static class CompilationSequence extends LinkedList<CompilationPhase> {
+
+ CompilationSequence(final CompilationPhase... phases) {
+ super(Arrays.asList(phases));
+ }
+
+ CompilationSequence(final CompilationSequence sequence) {
+ this(sequence.toArray(new CompilationPhase[sequence.size()]));
+ }
+
+ CompilationSequence insertAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
+ final CompilationSequence newSeq = new CompilationSequence();
+ for (final CompilationPhase elem : this) {
+ newSeq.add(phase);
+ if (elem.equals(phase)) {
+ newSeq.add(newPhase);
+ }
+ }
+ assert newSeq.contains(newPhase);
+ return newSeq;
+ }
+
+ CompilationSequence insertBefore(final CompilationPhase phase, final CompilationPhase newPhase) {
+ final CompilationSequence newSeq = new CompilationSequence();
+ for (final CompilationPhase elem : this) {
+ if (elem.equals(phase)) {
+ newSeq.add(newPhase);
+ }
+ newSeq.add(phase);
+ }
+ assert newSeq.contains(newPhase);
+ return newSeq;
+ }
+
+ CompilationSequence insertFirst(final CompilationPhase phase) {
+ final CompilationSequence newSeq = new CompilationSequence(this);
+ newSeq.addFirst(phase);
+ return newSeq;
+ }
+
+ CompilationSequence insertLast(final CompilationPhase phase) {
+ final CompilationSequence newSeq = new CompilationSequence(this);
+ newSeq.addLast(phase);
+ return newSeq;
+ }
+ }
+
+ /**
+ * Standard (non-lazy) compilation, that basically will take an entire script
+ * and JIT it at once. This can lead to long startup time and fewer type
+ * specializations
+ */
+ final static CompilationSequence SEQUENCE_NORMAL = new CompilationSequence(
+ CompilationPhase.CONSTANT_FOLDING_PHASE,
+ CompilationPhase.LOWERING_PHASE,
+ CompilationPhase.ATTRIBUTION_PHASE,
+ CompilationPhase.SPLITTING_PHASE,
+ CompilationPhase.TYPE_FINALIZATION_PHASE,
+ CompilationPhase.BYTECODE_GENERATION_PHASE);
- /** Should we dump classes to disk and compile only? */
- private final boolean dumpClass;
+ final static CompilationSequence SEQUENCE_LAZY =
+ SEQUENCE_NORMAL.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
+
+ final static CompilationSequence SEQUENCE_DEFAULT =
+ LAZY_JIT ?
+ SEQUENCE_LAZY :
+ SEQUENCE_NORMAL;
+
+ /**
+ * Constructor
+ *
+ * @param installer code installer from
+ * @param functionNode function node (in any available {@link CompilationState}) to compile
+ * @param sequence {@link Compiler#CompilationSequence} of {@link CompilationPhase}s to apply as this compilation
+ * @param strict should this compilation use strict mode semantics
+ */
+ Compiler(final Context context, final CodeInstaller<Context> installer, final FunctionNode functionNode, final CompilationSequence sequence, final boolean strict) {
+ this.context = context;
+ this.functionNode = functionNode;
+ this.sequence = sequence;
+ this.installer = installer;
+ this.strict = strict || functionNode.isStrictMode();
+ this.constantData = new ConstantData();
+ this.compileUnits = new HashSet<>();
+ this.bytecode = new HashMap<>();
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag())).
+ append('$').
+ append(safeSourceName(functionNode.getSource())).
+ append(functionNode.isLazy() ? LAZY.tag() : "");
+
+ this.scriptName = sb.toString();
+
+ LOG.info("Initializing compiler for scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'");
+ }
- /** Code map class name -> byte code for all classes generated from this Source or FunctionNode */
- private Map<String, byte[]> code;
+ /**
+ * Constructor
+ *
+ * @param installer code installer from context
+ * @param functionNode function node (in any available {@link CompilationState}) to compile
+ * @param strict should this compilation use strict mode semantics
+ */
+ public Compiler(final CodeInstaller<Context> installer, final FunctionNode functionNode, final boolean strict) {
+ this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, strict);
+ }
+
+ /**
+ * Constructor - compilation will use the same strict semantics as context
+ *
+ * @param installer code installer from context
+ * @param functionNode function node (in any available {@link CompilationState}) to compile
+ */
+ public Compiler(final CodeInstaller<Context> installer, final FunctionNode functionNode) {
+ this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, installer.getOwner()._strict);
+ }
+
+ /**
+ * Constructor - compilation needs no installer, but uses a context
+ * Used in "compile only" scenarios
+ * @param context a context
+ * @param functionNode functionNode to compile
+ */
+ public Compiler(final Context context, final FunctionNode functionNode) {
+ this(context, null, functionNode, SEQUENCE_DEFAULT, context._strict);
+ }
+
+ /**
+ * Execute the compilation this Compiler was created with
+ * @return true if compilation succeeds.
+ */
+ public boolean compile() {
+ for (final String reservedName : RESERVED_NAMES) {
+ functionNode.uniqueName(reservedName);
+ }
- /** Are we compiling in strict mode? */
- private boolean strict;
+ for (final CompilationPhase phase : sequence) {
+ LOG.info("Entering compile phase " + phase + " for function '" + functionNode.getName() + "'");
+ if (phase.isApplicable(functionNode)) {
+ if (!phase.apply(this, functionNode)) { //TODO exceptions, error logging
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Install compiled classes into a given loader
+ * @return root script class - if there are several compile units they will also be installed
+ */
+ public Class<?> install() {
+ Class<?> rootClass = null;
+
+ for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
+ final String className = entry.getKey();
+ LOG.info("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;
+ }
+
+ try {
+ //use reflection to write source and constants table to installed classes
+ clazz.getField(SOURCE.tag()).set(null, getSource());
+ clazz.getField(CONSTANTS.tag()).set(null, getConstantData().toArray());
+ } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ LOG.info("Root class: " + rootClass);
+ return rootClass;
+ }
+
+ Set<CompileUnit> getCompileUnits() {
+ return compileUnits;
+ }
+
+ boolean getStrictMode() {
+ return strict;
+ }
+
+ void setStrictMode(final boolean strict) {
+ this.strict = strict;
+ }
+
+ FunctionNode getFunctionNode() {
+ return functionNode;
+ }
+
+ ConstantData getConstantData() {
+ return constantData;
+ }
+
+ CodeInstaller<Context> getCodeInstaller() {
+ return installer;
+ }
+
+ Source getSource() {
+ return functionNode.getSource();
+ }
+
+ void addClass(final String name, final byte[] code) {
+ bytecode.put(name, code);
+ }
+
+ Context getContext() {
+ return this.context;
+ }
- /** Is this a lazy compilation - i.e. not from source, but jitting a previously parsed FunctionNode? */
- private boolean isLazy;
+ private static String safeSourceName(final Source source) {
+ String baseName = new File(source.getName()).getName();
+
+ final int index = baseName.lastIndexOf(".js");
+ if (index != -1) {
+ baseName = baseName.substring(0, index);
+ }
+
+ baseName = baseName.replace('.', '_').replace('-', '_');
+ final String mangled = NameCodec.encode(baseName);
+
+ return mangled != null ? mangled : baseName;
+ }
+
+ private int nextCompileUnitIndex() {
+ return compileUnits.size() + 1;
+ }
+
+ String firstCompileUnitName() {
+ return SCRIPTS_PACKAGE + '/' + scriptName;
+ }
+
+ private String nextCompileUnitName() {
+ return firstCompileUnitName() + '$' + nextCompileUnitIndex();
+ }
+
+ CompileUnit addCompileUnit(final long initialWeight) {
+ return addCompileUnit(nextCompileUnitName(), initialWeight);
+ }
+
+ CompileUnit addCompileUnit(final String unitClassName) {
+ return addCompileUnit(unitClassName, 0L);
+ }
- /** Lazy jitting is disabled by default */
- private static final boolean LAZY_JIT = false;
+ private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
+ final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
+ compileUnits.add(compileUnit);
+ LOG.info("Added compile unit " + compileUnit);
+ return compileUnit;
+ }
+
+ private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
+ final ClassEmitter classEmitter = new ClassEmitter(context, functionNode.getSource().getName(), unitClassName, strict);
+ final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight);
+
+ classEmitter.begin();
+
+ final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE));
+ initMethod.begin();
+ initMethod.load(Type.OBJECT, 0);
+ initMethod.newInstance(jdk.nashorn.internal.scripts.JS$.class);
+ initMethod.returnVoid();
+ initMethod.end();
+
+ return compileUnit;
+ }
+
+ CompileUnit findUnit(final long weight) {
+ for (final CompileUnit unit : compileUnits) {
+ if (unit.canHold(weight)) {
+ unit.addWeight(weight);
+ return unit;
+ }
+ }
+
+ return addCompileUnit(weight);
+ }
+
+ /**
+ * Convert a package/class name to a binary name.
+ *
+ * @param name Package/class name.
+ * @return Binary name.
+ */
+ public static String binaryName(final String name) {
+ return name.replace('/', '.');
+ }
/**
* Should we use integers for arithmetic operations as well?
@@ -164,573 +417,27 @@
static {
USE_INT_ARITH = Options.getBooleanProperty("nashorn.compiler.intarithmetic");
-
assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
}
- /**
- * Factory method for compiler that should compile from source to bytecode
- *
- * @param source the source
- * @param context context
- *
- * @return compiler instance
- */
- public static Compiler compiler(final Source source, final Context context) {
- return Compiler.compiler(source, context, context.getErrorManager(), context._strict);
- }
-
- /**
- * Factory method to get a compiler that goes from from source to bytecode
- *
- * @param source source code
- * @param context context
- * @param errors error manager
- * @param strict compilation in strict mode?
- *
- * @return compiler instance
- */
- public static Compiler compiler(final Source source, final Context context, final ErrorManager errors, final boolean strict) {
- return new Compiler(source, context, errors, strict);
- }
-
- /**
- * Factory method to get a compiler that goes from FunctionNode (parsed) to bytecode
- * Requires previous compiler for state
- *
- * @param compiler primordial compiler
- * @param functionNode functionNode to compile
- *
- * @return compiler
- */
- public static Compiler compiler(final Compiler compiler, final FunctionNode functionNode) {
- assert false : "lazy jit - not implemented";
- final Compiler newCompiler = new Compiler(compiler);
- newCompiler.state.add(State.PARSED);
- newCompiler.functionNode = functionNode;
- newCompiler.isLazy = true;
- return compiler;
- }
-
- private Compiler(final Compiler compiler) {
- this(compiler.source, compiler.context, compiler.errors, compiler.strict);
- }
-
- /**
- * Constructor
- *
- * @param source the source to compile
- * @param context context
- * @param errors error manager
- * @param strict compile in strict mode
- */
- private Compiler(final Source source, final Context context, final ErrorManager errors, final boolean strict) {
- this.source = source;
- this.context = context;
- this.errors = errors;
- this.strict = strict;
- this.namespace = new Namespace(context.getNamespace());
- this.compileUnits = new HashSet<>();
- this.constantData = new ConstantData();
- this.state = EnumSet.of(State.INITIALIZED);
- this.dumpClass = context._compile_only && context._dest_dir != null;
- }
-
- private String scriptsPackageName() {
- return dumpClass ? "" : (SCRIPTS_PACKAGE + '/');
- }
-
- private int nextCompileUnitIndex() {
- return compileUnits.size() + 1;
- }
-
- private String firstCompileUnitName() {
- return scriptsPackageName() + scriptName;
- }
-
- private String nextCompileUnitName() {
- return firstCompileUnitName() + '$' + nextCompileUnitIndex();
- }
-
- private CompileUnit addCompileUnit(final long initialWeight) {
- return addCompileUnit(nextCompileUnitName(), initialWeight);
- }
-
- private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
- final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
- compileUnits.add(compileUnit);
- LOG.info("Added compile unit " + compileUnit);
- return compileUnit;
- }
-
- private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
- final ClassEmitter classEmitter = new ClassEmitter(this, unitClassName, strict);
- final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight);
-
- classEmitter.begin();
-
- final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE));
- initMethod.begin();
- initMethod.load(Type.OBJECT, 0);
- initMethod.newInstance(jdk.nashorn.internal.scripts.JS$.class);
- initMethod.returnVoid();
- initMethod.end();
-
- return compileUnit;
- }
-
- /**
- * Perform compilation
- *
- * @return true if successful, false otherwise - if false check the error manager
- */
- public boolean compile() {
- assert state.contains(State.INITIALIZED);
-
- /** do we need to parse source? */
- if (!state.contains(State.PARSED)) {
- LOG.info("Parsing '" + source + "'");
-
- assert this.functionNode == null;
- this.functionNode = new Parser(this, strict).parse(RUN_SCRIPT.tag());
-
- state.add(State.PARSED);
- debugPrintParse();
-
- if (errors.hasErrors() || context._parse_only) {
- return false;
- }
-
- assert !isLazy;
- //tag lazy nodes for later code generation and trampolines
- functionNode.accept(new NodeVisitor() {
+ static {
+ if (TIME_COMPILATION) {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
- public Node enter(final FunctionNode node) {
- if (LAZY_JIT) {
- node.setIsLazy(!node.isScript());
+ public void run() {
+ for (final CompilationPhase phase : CompilationPhase.values()) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(phase);
+ while (sb.length() < 32) {
+ sb.append(' ');
+ }
+ sb.append(CompilationPhase.getAccumulatedTime(phase));
+ sb.append(' ');
+ sb.append(" ms");
+ System.err.println(sb.toString()); //Context err is gone by shutdown TODO
}
- return node;
- }
- });
- } else {
- assert isLazy;
- functionNode.accept(new NodeVisitor() {
- @Override
- public Node enter(final FunctionNode node) {
- node.setIsLazy(false);
- return null; //TODO do we want to do this recursively? then return "node" instead
}
});
}
-
- assert functionNode != null;
- final boolean oldStrict = strict;
-
- try {
- strict |= functionNode.isStrictMode();
-
- /*
- * These are the compile phases:
- *
- * Constant folding pass
- * Simple constant folding that will make elementary constructs go away
- *
- * Lower (Control flow pass)
- * Finalizes the control flow. Clones blocks for finally constructs and
- * similar things. Establishes termination criteria for nodes
- * Guarantee return instructions to method making sure control flow
- * cannot fall off the end. Replacing high level nodes with lower such
- * as runtime nodes where applicable.
- *
- * Attr
- * Assign symbols and types to all nodes.
- *
- * Splitter
- * Split the AST into several compile units based on a size heuristic
- * Splitter needs attributed AST for weight calculations (e.g. is
- * a + b a ScriptRuntime.ADD with call overhead or a dadd with much
- * less). Split IR can lead to scope information being changed.
- *
- * Contract: all variables must have slot assignments and scope assignments
- * before lowering.
- *
- * FinalizeTypes
- * This pass finalizes the types for nodes. If Attr created wider types than
- * known during the first pass, convert nodes are inserted or access nodes
- * are specialized where scope accesses.
- *
- * Runtime nodes may be removed and primitivized or reintroduced depending
- * on information that was established in Attr.
- *
- * CodeGeneration
- * Emit bytecode
- *
- */
-
- debugPrintAST();
-
- if (!state.contains(State.FINALIZED)) {
- LOG.info("Folding constants in '" + functionNode.getName() + "'");
- functionNode.accept(new FoldConstants());
- state.add(State.CONSTANT_FOLDED);
-
- LOG.info("Lowering '" + functionNode.getName() + "'");
- functionNode.accept(new Lower(this));
- state.add(State.LOWERED);
- debugPrintAST();
-
- LOG.info("Attributing types '" + functionNode.getName() + "'");
- functionNode.accept(new Attr(this));
- state.add(State.ATTR);
-
- this.scriptName = computeNames();
-
- // Main script code always goes to this compile unit. Note that since we start this with zero weight
- // and add script code last this class may end up slightly larger than others, but reserving one class
- // just for the main script seems wasteful.
- final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0L);
- LOG.info("Splitting '" + functionNode.getName() + "'");
- new Splitter(this, functionNode, scriptCompileUnit).split();
- state.add(State.SPLIT);
- assert functionNode.getCompileUnit() == scriptCompileUnit;
-
- assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode();
- if (functionNode.isStrictMode()) {
- strict = true;
- }
-
- LOG.info("Finalizing types for '" + functionNode.getName() + "'");
- functionNode.accept(new FinalizeTypes(this));
- state.add(State.FINALIZED);
-
- // print ast and parse if --print-lower-ast and/or --print-lower-parse are selected
- debugPrintAST();
- debugPrintParse();
-
- if (errors.hasErrors()) {
- return false;
- }
- }
-
- try {
- LOG.info("Emitting bytecode for '" + functionNode.getName() + "'");
- final CodeGenerator codegen = new CodeGenerator(this);
- functionNode.accept(codegen);
- codegen.generateScopeCalls();
- } catch (final VerifyError e) {
- if (context._verify_code || context._print_code) {
- context.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
- if (context._dump_on_error) {
- e.printStackTrace(context.getErr());
- }
- } else {
- throw e;
- }
- }
-
- state.add(State.EMITTED);
-
- code = new TreeMap<>();
- for (final CompileUnit compileUnit : compileUnits) {
- final ClassEmitter classEmitter = compileUnit.getClassEmitter();
- classEmitter.end();
-
- if (!errors.hasErrors()) {
- final byte[] bytecode = classEmitter.toByteArray();
- if (bytecode != null) {
- code.put(compileUnit.getUnitClassName(), bytecode);
- debugDisassemble();
- debugVerify();
- }
- }
- }
-
- if (code.isEmpty()) {
- return false;
- }
-
- try {
- dumpClassFiles();
- } catch (final IOException e) {
- throw new RuntimeException(e);
- }
-
- return true;
- } finally {
- strict = oldStrict;
- LOG.info("Done with '" + functionNode.getName() + "'");
- }
}
-
- /**
- * Install compiled classes into a given loader
- * @param installer that takes the generated classes and puts them in the system
- * @return root script class - if there are several compile units they will also be installed
- */
- public Class<?> install(final CodeInstaller installer) {
- assert state.contains(State.EMITTED);
- assert scriptName != null;
-
- Class<?> rootClass = null;
-
- for (final Entry<String, byte[]> entry : code.entrySet()) {
- final String className = entry.getKey();
- LOG.info("Installing class " + className);
-
- final byte[] bytecode = entry.getValue();
- final Class<?> clazz = installer.install(Compiler.binaryName(className), bytecode);
-
- if (rootClass == null && firstCompileUnitName().equals(className)) {
- rootClass = clazz;
- }
-
- try {
- //use reflection to write source and constants table to installed classes
- clazz.getField(SOURCE.tag()).set(null, source);
- clazz.getField(CONSTANTS.tag()).set(null, constantData.toArray());
- } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- }
-
- LOG.info("Root class: " + rootClass);
-
- return rootClass;
- }
-
- /**
- * Find a unit that will hold a node of the specified weight.
- *
- * @param weight Weight of a node
- * @return Unit to hold node.
- */
- CompileUnit findUnit(final long weight) {
- for (final CompileUnit unit : compileUnits) {
- if (unit.canHold(weight)) {
- unit.addWeight(weight);
- return unit;
- }
- }
-
- return addCompileUnit(weight);
- }
-
- /**
- * Generate a uniqueName name. Public as {@link Parser} is using this to
- * create symbols in a different package
- *
- * @param name to base unique name on
- * @return unique name
- */
- public String uniqueName(final String name) {
- return namespace.uniqueName(name);
- }
-
- /**
- * Internal function to compute reserved names and base names for class to
- * be generated
- *
- * @return scriptName
- */
- private String computeNames() {
- // Reserve internally used names.
- addReservedNames();
-
- if (dumpClass) {
- // get source file name and remove ".js"
- final String baseName = getSource().getName();
- final int index = baseName.lastIndexOf(".js");
- if (index != -1) {
- return baseName.substring(0, index);
- }
- return baseName;
- }
-
- return namespace.getParent().uniqueName(
- DEFAULT_SCRIPT_NAME.tag() +
- '$' +
- safeSourceName(source) +
- (isLazy ? CompilerConstants.LAZY.tag() : "")
- );
- }
-
- private static String safeSourceName(final Source source) {
- String baseName = new File(source.getName()).getName();
- final int index = baseName.lastIndexOf(".js");
- if (index != -1) {
- baseName = baseName.substring(0, index);
- }
-
- baseName = baseName.replace('.', '_').replace('-', '_');
- final String mangled = NameCodec.encode(baseName);
-
- baseName = mangled != null ? mangled : baseName;
- return baseName;
- }
-
- static void verify(final Context context, final byte[] code) {
- context.verify(code);
- }
-
- /**
- * Fill in the namespace with internally reserved names.
- */
- private void addReservedNames() {
- namespace.uniqueName(SCOPE.tag());
- namespace.uniqueName(THIS.tag());
- }
-
- /**
- * Get the constant data for this Compiler
- *
- * @return the constant data
- */
- public ConstantData getConstantData() {
- return constantData;
- }
-
- /**
- * Get the Context used for Compilation
- * @see Context
- * @return the context
- */
- public Context getContext() {
- return context;
- }
-
- /**
- * Get the Source being compiled
- * @see Source
- * @return the source
- */
- public Source getSource() {
- return source;
- }
-
- /**
- * Get the error manager used for this compiler
- * @return the error manager
- */
- public ErrorManager getErrors() {
- return errors;
- }
-
- /**
- * Get the namespace used for this Compiler
- * @see Namespace
- * @return the namespace
- */
- public Namespace getNamespace() {
- return namespace;
- }
-
- /*
- * Debugging
- */
-
- /**
- * Print the AST before or after lowering, see --print-ast, --print-lower-ast
- */
- private void debugPrintAST() {
- assert functionNode != null;
- if (context._print_lower_ast && state.contains(State.LOWERED) ||
- context._print_ast && !state.contains(State.LOWERED)) {
- context.getErr().println(new ASTWriter(functionNode));
- }
- }
-
- /**
- * Print the parsed code before or after lowering, see --print-parse, --print-lower-parse
- */
- private boolean debugPrintParse() {
- if (errors.hasErrors()) {
- return false;
- }
-
- assert functionNode != null;
-
- if (context._print_lower_parse && state.contains(State.LOWERED) ||
- context._print_parse && !state.contains(State.LOWERED)) {
- final PrintVisitor pv = new PrintVisitor();
- functionNode.accept(pv);
- context.getErr().print(pv);
- context.getErr().flush();
- }
-
- return true;
- }
-
- private void debugDisassemble() {
- assert code != null;
- if (context._print_code) {
- for (final Map.Entry<String, byte[]> entry : code.entrySet()) {
- context.getErr().println("CLASS: " + entry.getKey());
- context.getErr().println();
- ClassEmitter.disassemble(context, entry.getValue());
- context.getErr().println("======");
- }
- }
- }
-
- private void debugVerify() {
- if (context._verify_code) {
- for (final Map.Entry<String, byte[]> entry : code.entrySet()) {
- Compiler.verify(context, entry.getValue());
- }
- }
- }
-
- /**
- * Implements the "-d" option - dump class files from script to specified output directory
- *
- * @throws IOException if classes cannot be written
- */
- private void dumpClassFiles() throws IOException {
- if (context._dest_dir == null) {
- return;
- }
-
- assert code != null;
-
- for (final Entry<String, byte[]> entry : code.entrySet()) {
- final String className = entry.getKey();
- final String fileName = className.replace('.', File.separatorChar) + ".class";
- final int index = fileName.lastIndexOf(File.separatorChar);
-
- if (index != -1) {
- final File dir = new File(fileName.substring(0, index));
- if (!dir.exists() && !dir.mkdirs()) {
- throw new IOException(ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
- }
- }
-
- final byte[] bytecode = entry.getValue();
- final File outFile = new File(context._dest_dir, fileName);
- try (final FileOutputStream fos = new FileOutputStream(outFile)) {
- fos.write(bytecode);
- }
- }
- }
-
- /**
- * Convert a package/class name to a binary name.
- *
- * @param name Package/class name.
- * @return Binary name.
- */
- public static String binaryName(final String name) {
- return name.replace('/', '.');
- }
-
- /**
- * Convert a binary name to a package/class name.
- *
- * @param name Binary name.
- * @return Package/class name.
- */
- public static String pathName(final String name) {
- return name.replace('.', '/');
- }
-
-
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java Thu Feb 14 13:01:52 2013 +0100
@@ -35,7 +35,7 @@
* Manages constants needed by code generation. Objects are maintained in an
* interning maps to remove duplicates.
*/
-public class ConstantData {
+class ConstantData {
/** Constant table. */
final List<Object> constants;
--- a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Thu Feb 14 13:01:52 2013 +0100
@@ -66,7 +66,6 @@
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Source;
/**
* Lower to more primitive operations. After lowering, an AST has symbols and
@@ -83,18 +82,9 @@
final class FinalizeTypes extends NodeOperatorVisitor {
- /** Current source. */
- private final Source source;
-
private static final DebugLogger LOG = new DebugLogger("finalize");
- /**
- * Constructor.
- *
- * @param compiler the compiler
- */
- FinalizeTypes(final Compiler compiler) {
- this.source = compiler.getSource();
+ FinalizeTypes() {
}
@Override
@@ -424,16 +414,20 @@
@Override
public Node enter(final FunctionNode functionNode) {
+ if (functionNode.isLazy()) {
+ return null;
+ }
+
// If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
// this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
// need for the callee.
- if(!functionNode.needsCallee()) {
+ if (!functionNode.needsCallee()) {
functionNode.getCalleeNode().getSymbol().setNeedsSlot(false);
}
// Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope or its
// own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than
// this phase.
- if(!(functionNode.needsScope() || functionNode.needsParentScope())) {
+ if (!(functionNode.needsScope() || functionNode.needsParentScope())) {
functionNode.getScopeNode().getSymbol().setNeedsSlot(false);
}
@@ -443,8 +437,7 @@
@Override
public Node leave(final IfNode ifNode) {
- final Node test = convert(ifNode.getTest(), Type.BOOLEAN);
- ifNode.setTest(test);
+ ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
return ifNode;
}
@@ -518,6 +511,7 @@
@Override
public Node leave(final VarNode varNode) {
+
final Node rhs = varNode.getInit();
if (rhs != null) {
Type destType = specialize(varNode);
@@ -823,7 +817,7 @@
setTypeOverride(node, to);
return resultNode;
}
- resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), node);
+ resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node);
}
LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'");
@@ -836,11 +830,11 @@
return resultNode;
}
- private Node discard(final Node node) {
+ private static Node discard(final Node node) {
node.setDiscard(true);
if (node.getSymbol() != null) {
- final Node discard = new UnaryNode(source, Token.recast(node.getToken(), TokenType.DISCARD), node);
+ final Node discard = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.DISCARD), node);
//discard never has a symbol in the discard node - then it would be a nop
discard.copyTerminalFlags(node);
return discard;
--- a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Thu Feb 14 13:01:52 2013 +0100
@@ -44,10 +44,13 @@
/**
* Simple constant folding pass, executed before IR is starting to be lowered.
*/
-public class FoldConstants extends NodeVisitor {
+final class FoldConstants extends NodeVisitor {
private static final DebugLogger LOG = new DebugLogger("fold");
+ FoldConstants() {
+ }
+
@Override
public Node leave(final UnaryNode unaryNode) {
final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
--- a/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java Thu Feb 14 13:01:52 2013 +0100
@@ -25,6 +25,8 @@
package jdk.nashorn.internal.codegen;
+import java.lang.invoke.MethodType;
+import java.util.ArrayList;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -32,6 +34,8 @@
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
+import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
+
/**
* Class that generates function signatures for dynamic calls
*/
@@ -46,6 +50,9 @@
/** valid Java descriptor string for function */
private final String descriptor;
+ /** {@link MethodType} for function */
+ private final MethodType methodType;
+
/**
* Constructor
*
@@ -126,8 +133,31 @@
assert false : "isVarArgs cannot be false when argTypes are null";
}
- returnType = retType;
- descriptor = Type.getMethodDescriptor(returnType, paramTypes);
+ this.returnType = retType;
+ this.descriptor = Type.getMethodDescriptor(returnType, paramTypes);
+
+ final List<Class<?>> paramTypeList = new ArrayList<>();
+ for (final Type paramType : paramTypes) {
+ paramTypeList.add(paramType.getTypeClass());
+ }
+
+ this.methodType = MH.type(returnType.getTypeClass(), paramTypeList.toArray(new Class[paramTypes.length]));
+ }
+
+ /**
+ * Create a function signature given a function node, using as much
+ * type information for parameters and return types that is availabe
+ *
+ * @param functionNode the function node
+ */
+ public FunctionSignature(final FunctionNode functionNode) {
+ this(
+ true,
+ functionNode.needsCallee(),
+ functionNode.getReturnType(),
+ (functionNode.isVarArg() && !functionNode.isScript()) ?
+ null :
+ functionNode.getParameters());
}
/**
@@ -165,21 +195,14 @@
}
/**
- * Returns the generic signature of the function being compiled.
- *
- * @param functionNode function being compiled.
- * @return function signature.
+ * Return the {@link MethodType} for this function signature
+ * @return the method type
*/
- public static String functionSignature(final FunctionNode functionNode) {
- return new FunctionSignature(
- true,
- functionNode.needsCallee(),
- functionNode.getReturnType(),
- (functionNode.isVarArg() && !functionNode.isScript()) ?
- null :
- functionNode.getParameters()).toString();
+ public MethodType getMethodType() {
+ return methodType;
}
+
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 Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Thu Feb 14 13:01:52 2013 +0100
@@ -89,10 +89,6 @@
final class Lower extends NodeOperatorVisitor {
- private final Compiler compiler;
-
- private final Source source;
-
/**
* Nesting level stack. Currently just used for loops to avoid the problem
* with terminal bodies that end with throw/return but still do continues to
@@ -111,9 +107,7 @@
*
* @param compiler the compiler
*/
- Lower(final Compiler compiler) {
- this.compiler = compiler;
- this.source = compiler.getSource();
+ Lower() {
this.nesting = new ArrayDeque<>();
this.statements = new ArrayList<>();
}
@@ -204,7 +198,7 @@
if (getCurrentFunctionNode().isScript()) {
if (!(expr instanceof Block)) {
if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
- executeNode.setExpression(new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN),
+ executeNode.setExpression(new BinaryNode(executeNode.getSource(), Token.recast(executeNode.getToken(), TokenType.ASSIGN),
getCurrentFunctionNode().getResultNode(),
expr));
}
@@ -254,6 +248,11 @@
public Node enter(final FunctionNode functionNode) {
LOG.info("START FunctionNode: " + functionNode.getName());
+ if (functionNode.isLazy()) {
+ LOG.info("LAZY: " + functionNode.getName());
+ return null;
+ }
+
initFunctionNode(functionNode);
Node initialEvalResult = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
@@ -299,7 +298,7 @@
}
if (functionNode.isScript()) {
- new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
+ new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
}
//do the statements - this fills the block with code
@@ -379,6 +378,8 @@
if (tryNode != null) {
//we are inside a try block - we don't necessarily have a result node yet. attr will do that.
if (expr != null) {
+ final Source source = getCurrentFunctionNode().getSource();
+
//we need to evaluate the result of the return in case it is complex while
//still in the try block, store it in a result value and return it afterwards
final long token = returnNode.getToken();
@@ -518,6 +519,7 @@
* finally_body_inlined marked (*) will fix it before rethrowing
* whatever problem there was for identical semantic.
*/
+ final Source source = getCurrentFunctionNode().getSource();
// if try node does not contain a catch we can skip creation of a new
// try node and just append our synthetic catch to the existing try node.
@@ -559,7 +561,7 @@
final CatchNode catchAllNode;
final IdentNode exception;
- exception = new IdentNode(source, token, finish, compiler.uniqueName("catch_all"));
+ exception = new IdentNode(source, token, finish, getCurrentFunctionNode().uniqueName("catch_all"));
catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
catchAllNode.setIsSyntheticRethrow();
@@ -632,7 +634,7 @@
if (whileNode instanceof DoWhileNode) {
setTerminal(whileNode, true);
} else if (conservativeAlwaysTrue(test)) {
- node = new ForNode(source, whileNode.getToken(), whileNode.getFinish());
+ node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish());
((ForNode)node).setBody(body);
((ForNode)node).accept(this);
setTerminal(node, !escapes);
@@ -799,7 +801,7 @@
}
//create a return statement
- final Node returnNode = new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
+ final Node returnNode = new ReturnNode(functionNode.getSource(), functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
returnNode.accept(this);
}
@@ -897,7 +899,7 @@
finallyBody = (Block)finallyBody.clone();
final boolean hasTerminalFlags = finallyBody.hasTerminalFlags();
- new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
+ new ExecuteNode(finallyBody.getSource(), finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
if (hasTerminalFlags) {
getCurrentBlock().copyTerminalFlags(finallyBody);
@@ -951,17 +953,18 @@
* TODO : only create those that are needed.
* TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant
*/
- private void initFunctionNode(final FunctionNode functionNode) {
- final long token = functionNode.getToken();
- final int finish = functionNode.getFinish();
+ private static void initFunctionNode(final FunctionNode functionNode) {
+ final Source source = functionNode.getSource();
+ final long token = functionNode.getToken();
+ final int finish = functionNode.getFinish();
functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag()));
functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag()));
functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag()));
functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag()));
- if(functionNode.isVarArg()) {
+ if (functionNode.isVarArg()) {
functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
- if(functionNode.needsArguments()) {
+ if (functionNode.needsArguments()) {
functionNode.setArgumentsNode(new IdentNode(source, token, finish, ARGUMENTS.tag()));
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Thu Feb 14 13:01:52 2013 +0100
@@ -106,11 +106,11 @@
/**
* Set the compile unit and method name.
* @param compileUnit the compile unit
- * @param compiler the compiler to generate a unique method name
+ * @param methodName the method name
*/
- protected void setClassAndName(final CompileUnit compileUnit, final Compiler compiler) {
+ protected void setClassAndName(final CompileUnit compileUnit, final String methodName) {
this.compileUnit = compileUnit;
- this.methodName = compiler.uniqueName("scopeCall");
+ this.methodName = methodName;
}
/**
--- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Thu Feb 14 13:01:52 2013 +0100
@@ -49,13 +49,14 @@
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.options.Options;
/**
* Split the IR into smaller compile units.
*/
-public class Splitter extends NodeVisitor {
+final class Splitter extends NodeVisitor {
/** Current compiler. */
private final Compiler compiler;
@@ -63,7 +64,7 @@
private final FunctionNode functionNode;
/** Compile unit for the main script. */
- private final CompileUnit scriptCompileUnit;
+ private final CompileUnit outermostCompileUnit;
/** Cache for calculated block weights. */
private final Map<Node, Long> weightCache = new HashMap<>();
@@ -71,27 +72,35 @@
/** Weight threshold for when to start a split. */
public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024);
+ private static final DebugLogger LOG = Compiler.LOG;
+
/**
* Constructor.
*
- * @param compiler the compiler
- * @param functionNode function node to split
- * @param scriptCompileUnit script compile unit
+ * @param compiler the compiler
+ * @param functionNode function node to split
+ * @param outermostCompileUnit compile unit for outermost function, if non-lazy this is the script's compile unit
*/
- public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit scriptCompileUnit) {
- this.compiler = compiler;
- this.functionNode = functionNode;
- this.scriptCompileUnit = scriptCompileUnit;
+ public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
+ this.compiler = compiler;
+ this.functionNode = functionNode;
+ this.outermostCompileUnit = outermostCompileUnit;
}
/**
* Execute the split
*/
void split() {
+ if (functionNode.isLazy()) {
+ LOG.info("Postponing split of '" + functionNode.getName() + "' as it's lazy");
+ return;
+ }
+ LOG.info("Initiating split of '" + functionNode.getName() + "'");
+
long weight = WeighNodes.weigh(functionNode);
if (weight >= SPLIT_THRESHOLD) {
- Compiler.LOG.info("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
+ LOG.info("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
functionNode.accept(this);
@@ -111,20 +120,18 @@
assert functionNode.getCompileUnit() == null : "compile unit already set";
- if (functionNode.isScript()) {
- assert scriptCompileUnit != null : "script compile unit is null";
+ if (compiler.getFunctionNode() == functionNode) { //functionNode.isScript()) {
+ assert outermostCompileUnit != null : "outermost compile unit is null";
- functionNode.setCompileUnit(scriptCompileUnit);
- scriptCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
+ functionNode.setCompileUnit(outermostCompileUnit);
+ outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
} else {
functionNode.setCompileUnit(findUnit(weight));
}
// Recursively split nested functions
- final List<FunctionNode> functions = functionNode.getFunctions();
-
- for (final FunctionNode function : functions) {
- new Splitter(compiler, function, scriptCompileUnit).split();
+ for (final FunctionNode function : functionNode.getFunctions()) {
+ new Splitter(compiler, function, outermostCompileUnit).split();
}
}
@@ -192,7 +199,7 @@
final Source source = parent.getSource();
final long token = parent.getToken();
final int finish = parent.getFinish();
- final String name = compiler.uniqueName(SPLIT_PREFIX.tag());
+ final String name = parent.getFunction().uniqueName(SPLIT_PREFIX.tag());
final Block newBlock = new Block(source, token, finish, parent, functionNode);
newBlock.setFrame(new Frame(parent.getFrame()));
@@ -284,6 +291,10 @@
@Override
public Node enter(final FunctionNode node) {
+ if (node.isLazy()) {
+ return null;
+ }
+
final List<Node> statements = node.getStatements();
for (final Node statement : statements) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java Thu Feb 14 13:01:52 2013 +0100
@@ -66,7 +66,7 @@
* Computes the "byte code" weight of an AST segment. This is used
* for Splitting too large class files
*/
-public class WeighNodes extends NodeOperatorVisitor {
+final class WeighNodes extends NodeOperatorVisitor {
/*
* Weight constants.
*/
--- a/nashorn/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java Thu Feb 14 14:16:58 2013 +0530
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +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.codegen.objects;
-
-import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC;
-import static jdk.nashorn.internal.codegen.Compiler.SCRIPTFUNCTION_IMPL_OBJECT;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
-import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
-
-import java.lang.invoke.MethodHandle;
-import java.util.ArrayList;
-import java.util.EnumSet;
-
-import jdk.nashorn.internal.codegen.CodeGenerator;
-import jdk.nashorn.internal.codegen.FunctionSignature;
-import jdk.nashorn.internal.codegen.MethodEmitter;
-import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.Symbol;
-import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.ScriptFunctionData;
-import jdk.nashorn.internal.runtime.ScriptObject;
-
-/**
- * Analyze a function object's characteristics for appropriate code
- * generation. This generates code for the instantiation of ScriptFunctions.
- */
-public class FunctionObjectCreator extends ObjectCreator {
-
- private final FunctionNode functionNode;
-
- /**
- * Constructor
- *
- * @param codegen the code generator
- * @param functionNode the function node to turn into a ScriptFunction implementation
- */
- public FunctionObjectCreator(final CodeGenerator codegen, final FunctionNode functionNode) {
- super(codegen, new ArrayList<String>(), new ArrayList<Symbol>(), false, false);
- this.functionNode = functionNode;
- }
-
- private void loadHandle(final MethodEmitter method, final String signature) {
- method.loadHandle(functionNode.getCompileUnit().getUnitClassName(), functionNode.getName(), signature, EnumSet.of(HANDLE_STATIC)); // function
- }
-
- /**
- * Emit code for creating the object
- *
- * @param method the method emitter
- */
- @Override
- public void makeObject(final MethodEmitter method) {
-
- final PropertyMap map = makeMap();
- final String signature = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
- final ScriptFunctionData scriptFunctionData = new ScriptFunctionData(functionNode, map);
-
- /*
- * Instantiate the function object
- */
- method._new(SCRIPTFUNCTION_IMPL_OBJECT).dup();
- codegen.loadConstant(scriptFunctionData);
- loadHandle(method, signature);
- if(functionNode.needsParentScope()) {
- method.loadScope();
- } else {
- method.loadNull();
- }
- method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
-
- /*
- * Invoke the constructor
- */
- method.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_OBJECT, ScriptFunctionData.class, MethodHandle.class, ScriptObject.class, MethodHandle.class));
-
- }
-}
--- a/nashorn/src/jdk/nashorn/internal/codegen/objects/ObjectClassGenerator.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/objects/ObjectClassGenerator.java Thu Feb 14 13:01:52 2013 +0100
@@ -472,11 +472,11 @@
final byte[] code = classEmitter.toByteArray();
if (context != null && context._print_code) {
- ClassEmitter.disassemble(context, code);
+ Context.getCurrentErr().println(ClassEmitter.disassemble(code));
}
if (context != null && context._verify_code) {
- ClassEmitter.verify(context, code);
+ context.verify(code);
}
return code;
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Thu Feb 14 13:01:52 2013 +0100
@@ -32,15 +32,16 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
+
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Frame;
import jdk.nashorn.internal.codegen.MethodEmitter;
import jdk.nashorn.internal.codegen.Namespace;
-import jdk.nashorn.internal.codegen.Splitter;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -67,6 +68,28 @@
SETTER
}
+ /** Compilation states available */
+ public enum CompilationState {
+ /** compiler is ready */
+ INITIALIZED,
+ /** method has been parsed */
+ PARSED,
+ /** method has been parsed */
+ PARSE_ERROR,
+ /** constant folding pass */
+ CONSTANT_FOLDED,
+ /** method has been lowered */
+ LOWERED,
+ /** method hass been attributed */
+ ATTR,
+ /** method has been split */
+ SPLIT,
+ /** method has had its types finalized */
+ FINALIZED,
+ /** method has been emitted to bytecode */
+ EMITTED
+ }
+
/** External function identifier. */
@Ignore
private IdentNode ident;
@@ -147,6 +170,10 @@
@Ignore
private Node selfSymbolInit;
+ /** Current compilation state */
+ @Ignore
+ private final EnumSet<CompilationState> compilationState;
+
/** Function flags. */
private int flags;
@@ -200,16 +227,16 @@
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
- * @param compiler the compiler
- * @param parent the parent block
- * @param ident the identifier
- * @param name the name of the function
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param namespace the namespace
+ * @param parent the parent block
+ * @param ident the identifier
+ * @param name the name of the function
*/
@SuppressWarnings("LeakingThisInConstructor")
- public FunctionNode(final Source source, final long token, final int finish, final Compiler compiler, final Block parent, final IdentNode ident, final String name) {
+ public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final Block parent, final IdentNode ident, final String name) {
super(source, token, finish, parent, null);
this.ident = ident;
@@ -219,7 +246,7 @@
this.functions = new ArrayList<>();
this.firstToken = token;
this.lastToken = token;
- this.namespace = new Namespace(compiler.getNamespace().getParent());
+ this.namespace = namespace;
this.labelStack = new Stack<>();
this.controlStack = new Stack<>();
this.declarations = new ArrayList<>();
@@ -227,6 +254,7 @@
// it as such a leak - this is a false positive as we're setting this into a field of the object being
// constructed, so it can't be seen from other threads.
this.function = this;
+ this.compilationState = EnumSet.of(CompilationState.INITIALIZED);
}
@SuppressWarnings("LeakingThisInConstructor")
@@ -269,6 +297,8 @@
// it as such a leak - this is a false positive as we're setting this into a field of the object being
// constructed, so it can't be seen from other threads.
this.function = this;
+
+ this.compilationState = EnumSet.copyOf(functionNode.compilationState);
}
@Override
@@ -344,6 +374,41 @@
return super.needsScope() || isScript();
}
+ /**
+ * Check whether this FunctionNode has reached a give CompilationState.
+ *
+ * @param state the state to check for
+ * @return true of the node is in the given state
+ */
+ public boolean hasState(final EnumSet<CompilationState> state) {
+ return compilationState.equals(state);
+ }
+
+ /**
+ * Check whether the state of this FunctionNode contains a given compilation
+ * state.
+ *
+ * A node can be in many states at once, e.g. both lowered and initialized.
+ * To check for an exact state, use {FunctionNode{@link #hasState(EnumSet)}
+ *
+ * @param state state to check for
+ * @return true if state is present in the total compilation state of this FunctionNode
+ */
+ public boolean hasState(final CompilationState state) {
+ return compilationState.contains(state);
+ }
+
+ /**
+ * Add a state to the total CompilationState of this node, e.g. if
+ * FunctionNode has been lowered, the compiler will add
+ * {@code CompilationState#LOWERED} to the state vector
+ *
+ * @param state {@link CompilationState} to add
+ */
+ public void setState(final CompilationState state) {
+ compilationState.add(state);
+ }
+
/*
* Frame management.
*/
@@ -366,17 +431,6 @@
}
/**
- * return a unique name in the scope of the function.
- *
- * @param base Base string.
- *
- * @return Unique name.
- */
- public String uniqueName(final String base) {
- return namespace.uniqueName(base);
- }
-
- /**
* Create a temporary variable to the current frame.
*
* @param currentFrame Frame to add to - defaults to current function frame
@@ -408,6 +462,15 @@
}
/**
+ * Create a unique name in the namespace of this FunctionNode
+ * @param base prefix for name
+ * @return base if no collision exists, otherwise a name prefix with base
+ */
+ public String uniqueName(final String base) {
+ return namespace.uniqueName(base);
+ }
+
+ /**
* Add a new temporary variable to the current frame
*
* @param type Strong type of symbol
@@ -428,11 +491,11 @@
*/
public Symbol newLiteral(final LiteralNode<?> literalNode) {
final String uname = uniqueName(LITERAL_PREFIX.tag());
- final Symbol sym = new Symbol(uname, IS_CONSTANT, literalNode.getType());
- sym.setNode(literalNode);
- literalNode.setSymbol(sym);
+ final Symbol symbol = new Symbol(uname, IS_CONSTANT, literalNode.getType());
+ symbol.setNode(literalNode);
+ literalNode.setSymbol(symbol);
- return sym;
+ return symbol;
}
@Override
@@ -806,7 +869,6 @@
/**
* Checks if this function is a sub-function generated by splitting a larger one
- * @see Splitter
*
* @return true if this function is split from a larger one
*/
@@ -816,7 +878,6 @@
/**
* Flag this function node as being a sub-function generated by the splitter
- * @see Splitter
*/
public void setIsSplit() {
this.flags |= IS_SPLIT;
@@ -1149,7 +1210,6 @@
/**
* Get the compile unit used to compile this function
* @see Compiler
- * @see Splitter
* @return the compile unit
*/
public CompileUnit getCompileUnit() {
@@ -1159,7 +1219,6 @@
/**
* Reset the compile unit used to compile this function
* @see Compiler
- * @see Splitter
* @param compileUnit the compile unit
*/
public void setCompileUnit(final CompileUnit compileUnit) {
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Thu Feb 14 13:01:52 2013 +0100
@@ -25,11 +25,8 @@
package jdk.nashorn.internal.ir.debug;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.List;
-import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
@@ -87,8 +84,7 @@
* @return JSON string representation of AST of the supplied code
*/
public static String parse(final Context context, final String code, final String name, final boolean includeLoc) {
- final Compiler compiler = Compiler.compiler(new Source(name, code), context, new Context.ThrowErrorManager(), context._strict);
- final Parser parser = new Parser(compiler, context._strict);
+ final Parser parser = new Parser(context, new Source(name, code), new Context.ThrowErrorManager(), context._strict);
final JSONWriter jsonWriter = new JSONWriter(includeLoc);
try {
final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.tag());
--- a/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Thu Feb 14 13:01:52 2013 +0100
@@ -70,7 +70,7 @@
private FunctionNode currentFunctionNode;
/** Current compile unit used for class generation. */
- protected CompileUnit compileUnit;
+ private CompileUnit compileUnit;
/**
* Current method visitor used for method generation.
--- a/nashorn/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java Thu Feb 14 13:01:52 2013 +0100
@@ -32,7 +32,7 @@
/**
* A {@code ScriptFunctionImpl} subclass for functions created using {@code Function.prototype.bind}. Such functions
- * must track their {@code [[TargetFunction]] property for purposes of correctly implementing {@code [[HasInstance]]};
+ * must track their {@code [[TargetFunction]]} property for purposes of correctly implementing {@code [[HasInstance]]};
* see {@link ScriptFunction#isInstance(ScriptObject)}.
*/
class BoundScriptFunctionImpl extends ScriptFunctionImpl {
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java Thu Feb 14 13:01:52 2013 +0100
@@ -351,6 +351,8 @@
/**
* Constructor
+ *
+ * @param context the context
*/
public Global(final Context context) {
this.context = context;
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java Thu Feb 14 13:01:52 2013 +0100
@@ -385,6 +385,7 @@
public static Object extend(final Object self, final Object... types) {
if(types == null || types.length == 0) {
typeError("extend.expects.at.least.one.argument");
+ throw new AssertionError(); //circumvent warning for types == null below
}
final Class<?>[] stypes = new Class<?>[types.length];
try {
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Thu Feb 14 13:01:52 2013 +0100
@@ -28,7 +28,7 @@
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
-import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
+
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
@@ -94,14 +94,14 @@
/**
* Constructor called by (compiler) generated code for {@link ScriptObject}s.
- * Code is generated by {@link FunctionObjectCreator}
+ * Code is generated by {@link jdk.nashorn.internal.codegen.CodeGenerator#newFunctionObject}
*
* @param data static function data
* @param methodHandle handle for invocation
* @param scope scope object
* @param allocator instance constructor for function
*/
- public ScriptFunctionImpl(final ScriptFunctionData data, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle allocator) {
+ public ScriptFunctionImpl(final MethodHandle methodHandle, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) {
super(data, getMap(data.isStrict()), scope);
// Set method handles in script data
data.setMethodHandles(methodHandle, allocator);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java Thu Feb 14 13:01:52 2013 +0100
@@ -0,0 +1,122 @@
+package jdk.nashorn.internal.objects;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+
+import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
+
+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.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<Context> 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<Context> 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() {
+ final Compiler compiler = new Compiler(installer, functionNode);
+ if (!compiler.compile()) {
+ assert false : "compilation error in trampoline for " + functionNode.getName();
+ return null;
+ }
+
+ 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);
+ 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) {
+ /** Create a new compiler for the lazy node, using the same installation policy as the old one */
+
+ MethodHandle mh = compile();
+
+ // 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/Lexer.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/Lexer.java Thu Feb 14 13:01:52 2013 +0100
@@ -289,6 +289,11 @@
add(type, start, position);
}
+ /**
+ * Return the String of valid whitespace characters for regular
+ * expressions in JavaScript
+ * @return regexp whitespace string
+ */
public static String getWhitespaceRegExp() {
return JAVASCRIPT_WHITESPACE_IN_REGEXP;
}
@@ -959,6 +964,8 @@
// Add string token without editing.
add(type, stringState.position, stringState.limit);
break;
+ default:
+ break;
}
} else {
/// Add string token without editing.
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java Thu Feb 14 13:01:52 2013 +0100
@@ -39,7 +39,6 @@
import static jdk.nashorn.internal.parser.TokenType.ELSE;
import static jdk.nashorn.internal.parser.TokenType.EOF;
import static jdk.nashorn.internal.parser.TokenType.EOL;
-import static jdk.nashorn.internal.parser.TokenType.EXECSTRING;
import static jdk.nashorn.internal.parser.TokenType.FINALLY;
import static jdk.nashorn.internal.parser.TokenType.FUNCTION;
import static jdk.nashorn.internal.parser.TokenType.IDENT;
@@ -60,7 +59,9 @@
import java.util.List;
import java.util.Map;
import java.util.Stack;
-import jdk.nashorn.internal.codegen.Compiler;
+
+import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
@@ -97,8 +98,10 @@
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
import jdk.nashorn.internal.runtime.ParserException;
+import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.ScriptingFunctions;
/**
@@ -106,49 +109,72 @@
*
*/
public class Parser extends AbstractParser {
- /** Code generator. */
- private final Compiler compiler;
-
/** Current context. */
private final Context context;
/** Is scripting mode. */
private final boolean scripting;
- /** Top level script being compiled. */
+ /** Top level script being parsed. */
private FunctionNode script;
- /** Current function being compiled. */
+ /** Current function being parsed. */
private FunctionNode function;
/** Current parsing block. */
private Block block;
+ /** Namespace for function names where not explicitly given */
+ private final Namespace namespace;
+
/**
- * Construct a parser.
- * @param compiler Compiler state used to parse.
+ * Constructor
+ *
+ * @param context parser context
+ * @param source source to parse
+ * @param errors error manager
*/
- public Parser(final Compiler compiler) {
- this(compiler, compiler.getContext()._strict);
+ public Parser(final Context context, final Source source, final ErrorManager errors) {
+ this(context, source, errors, context._strict);
}
/**
* Construct a parser.
- * @param compiler Compiler state used to parse.
- * @param strict parser created with strict mode enabled.
+ *
+ * @param context parser context
+ * @param source source to parse
+ * @param errors error manager
+ * @param strict parser created with strict mode enabled.
*/
- public Parser(final Compiler compiler, final boolean strict) {
- super(compiler.getSource(), compiler.getErrors(), strict);
-
- this.compiler = compiler;
- this.context = compiler.getContext();
- this.scripting = this.context._scripting;
+ public Parser(final Context context, final Source source, final ErrorManager errors, final boolean strict) {
+ super(source, errors, strict);
+ this.context = context;
+ this.namespace = new Namespace(context.getNamespace());
+ this.scripting = context._scripting;
}
/**
- * Parse source content.
- * @param scriptName file name for script
- * @return Top level function (script).
+ * Execute parse and return the resulting function node.
+ * Errors will be thrown and the error manager will contain information
+ * if parsing should fail
+ *
+ * This is the default parse call, which will name the function node
+ * "runScript" {@link CompilerConstants#RUN_SCRIPT}
+ *
+ * @return function node resulting from successful parse
+ */
+ public FunctionNode parse() {
+ return parse(RUN_SCRIPT.tag());
+ }
+
+ /**
+ * Execute parse and return the resulting function node.
+ * Errors will be thrown and the error manager will contain information
+ * if parsing should fail
+ *
+ * @param scriptName name for the script, given to the parsed FunctionNode
+ *
+ * @return function node resulting from successful parse
*/
public FunctionNode parse(final String scriptName) {
try {
@@ -257,11 +283,11 @@
}
sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.tag());
- final String name = compiler.uniqueName(sb.toString());
+ final String name = namespace.uniqueName(sb.toString());
assert function != null || name.equals(RUN_SCRIPT.tag()) : "name = " + name;// must not rename runScript().
// Start new block.
- final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), compiler, block, ident, name);
+ final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, block, ident, name);
block = function = functionBlock;
function.setStrictMode(isStrictMode);
--- a/nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java Thu Feb 14 13:01:52 2013 +0100
@@ -34,14 +34,21 @@
* The compiler still retains most of the state around code emission
* and management internally, so this is to avoid passing around any
* logic that isn't directly related to installing a class
+ * @param <T> owner class type for this code installer
*
*/
-public interface CodeInstaller {
+public interface CodeInstaller<T> {
+ /**
+ * Return the owner for the CodeInstaller, e.g. a {@link Context}
+ * @return owner
+ */
+ public T getOwner();
+
/**
* Install a class
* @param className name of the class with / separation
- * @param bytecode bytecode
- * @return the installed class
+ * @param bytecode bytecode
+ * @return the installed class
*/
public Class<?> install(final String className, final byte[] bytecode);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java Thu Feb 14 13:01:52 2013 +0100
@@ -45,12 +45,16 @@
import java.security.PrivilegedAction;
import java.util.Locale;
import java.util.TimeZone;
+
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
import jdk.nashorn.internal.codegen.ClassEmitter;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.codegen.objects.ObjectClassGenerator;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.debug.PrintVisitor;
+import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.options.KeyValueOption;
@@ -63,6 +67,36 @@
*/
public final class Context {
+ /**
+ * ContextCodeInstaller that has the privilege of installing classes in the Context.
+ * Can only be instantiated from inside the context and is opaque to other classes
+ */
+ public static class ContextCodeInstaller implements CodeInstaller<Context> {
+ private final Context context;
+ private final ScriptLoader loader;
+ private final CodeSource codeSource;
+
+ private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) {
+ this.context = context;
+ this.loader = loader;
+ this.codeSource = codeSource;
+ }
+
+ /**
+ * Return the context for this installer
+ * @return context
+ */
+ @Override
+ public Context getOwner() {
+ return context;
+ }
+
+ @Override
+ public Class<?> install(final String className, final byte[] bytecode) {
+ return loader.installClass(className, bytecode, codeSource);
+ }
+ }
+
/** Is Context global debug mode enabled ? */
public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
@@ -751,6 +785,7 @@
/**
* Initialize given global scope object.
*
+ * @param global the global
* @return the initialized global scope object.
*/
public ScriptObject initGlobal(final ScriptObject global) {
@@ -877,22 +912,24 @@
}
}
- final Compiler compiler = Compiler.compiler(source, this, errMan, strict);
-
- if (!compiler.compile()) {
+ final FunctionNode functionNode = new Parser(this, source, errMan, strict).parse();
+ if (errors.hasErrors() || _parse_only) {
return null;
}
- final URL url = source.getURL();
+ if (_print_lower_parse) {
+ getErr().println(new PrintVisitor(functionNode));
+ }
+
+ final URL url = source.getURL();
final ScriptLoader loader = _loader_per_compile ? createNewLoader() : scriptLoader;
final CodeSource cs = url == null ? null : new CodeSource(url, (CodeSigner[])null);
+ final CodeInstaller<Context> installer = new ContextCodeInstaller(this, loader, cs);
- script = compiler.install(new CodeInstaller() {
- @Override
- public Class<?> install(final String className, final byte[] bytecode) {
- return loader.installClass(className, bytecode, cs);
- }
- });
+ final Compiler compiler = new Compiler(installer, functionNode, strict);
+
+ compiler.compile();
+ script = compiler.install();
if (global != null) {
global.cacheClass(source, script);
@@ -917,15 +954,14 @@
private ScriptObject newGlobalTrusted() {
try {
final Class<?> clazz = Class.forName("jdk.nashorn.internal.objects.Global", true, scriptLoader);
- final Constructor cstr = clazz.getConstructor(Context.class);
+ final Constructor<?> cstr = clazz.getConstructor(Context.class);
return (ScriptObject) cstr.newInstance(this);
} catch (final Exception e) {
printStackTrace(e);
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
- } else {
- throw new RuntimeException(e);
}
+ throw new RuntimeException(e);
}
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java Thu Feb 14 13:01:52 2013 +0100
@@ -73,8 +73,11 @@
* Get the output writer for the logger. Loggers always default to
* stderr for output as they are used mainly to output debug info
*
+ * Can be inherited so this should not be static.
+ *
* @return print writer for log output.
*/
+ @SuppressWarnings("static-method")
public PrintWriter getOutputStream() {
return Context.getCurrentErr();
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java Thu Feb 14 13:01:52 2013 +0100
@@ -36,7 +36,7 @@
public final class FindProperty {
/** Object where search began. */
private final ScriptObject self;
- ;
+
/** Object where search finish. */
private final ScriptObject prototype;
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu Feb 14 13:01:52 2013 +0100
@@ -78,12 +78,14 @@
/**
* Constructor
*
- * @param name function name
- * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
- * @param map property map
- * @param scope scope
- * @param specs specialized version of this function - other method handles
- *
+ * @param name function name
+ * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
+ * @param map property map
+ * @param scope scope
+ * @param specs specialized version of this function - other method handles
+ * @param strict is this a strict mode function?
+ * @param builtin is this a built in function?
+ * @param isConstructor is this a constructor?
*/
protected ScriptFunction(
final String name,
@@ -240,10 +242,17 @@
* @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
* @return a function with the specified self and parameters bound.
*/
- protected ScriptFunction makeBoundFunction(Object self, Object[] args) {
+ protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
return makeBoundFunction(data.makeBoundFunctionData(this, self, args));
}
+ /**
+ * Create a version of this function as in {@link ScriptFunction#makeBoundFunction(Object, Object[])},
+ * but using a {@link ScriptFunctionData} for the bound data.
+ *
+ * @param boundData ScriptFuntionData for the bound function
+ * @return a function with the bindings performed according to the given data
+ */
protected abstract ScriptFunction makeBoundFunction(ScriptFunctionData boundData);
@Override
@@ -350,6 +359,15 @@
}
/**
+ * 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
*
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Thu Feb 14 13:01:52 2013 +0100
@@ -101,11 +101,13 @@
/**
* 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
*/
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);
@@ -432,7 +434,7 @@
* @param invoker the invoker handle
* @param allocator the allocator handle
*/
- public void setMethodHandles(MethodHandle invoker, MethodHandle allocator) {
+ 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) {
@@ -443,6 +445,16 @@
}
/**
+ * 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.
*/
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Thu Feb 14 13:01:52 2013 +0100
@@ -54,14 +54,21 @@
/** Handle to implementation of {@link ScriptingFunctions#exec} - Nashorn extension */
public static final MethodHandle EXEC = findOwnMH("exec", Object.class, Object.class, Object.class, Object.class);
- /** Names of special properties used by $EXEC API. */
- public static final String EXEC_NAME = "$EXEC";
- public static final String OUT_NAME = "$OUT";
- public static final String ERR_NAME = "$ERR";
- public static final String EXIT_NAME = "$EXIT";
+ /** EXEC name - special property used by $EXEC API. */
+ public static final String EXEC_NAME = "$EXEC";
+
+ /** OUT name - special property used by $EXEC API. */
+ public static final String OUT_NAME = "$OUT";
+
+ /** ERR name - special property used by $EXEC API. */
+ public static final String ERR_NAME = "$ERR";
+
+ /** EXIT name - special property used by $EXEC API. */
+ public static final String EXIT_NAME = "$EXIT";
/** Names of special properties used by $ENV API. */
public static final String ENV_NAME = "$ENV";
+
private static final String PWD_NAME = "PWD";
private ScriptingFunctions() {
@@ -114,8 +121,11 @@
*
* @param self self reference
* @param string string to execute
+ * @param input
*
* @return output string from the request
+ * @throws IOException
+ * @throws InterruptedException
*/
public static Object exec(final Object self, final Object string, final Object input) throws IOException, InterruptedException {
// Current global is need to fetch additional inputs and for additional results.
--- a/nashorn/src/jdk/nashorn/tools/Shell.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/src/jdk/nashorn/tools/Shell.java Thu Feb 14 13:01:52 2013 +0100
@@ -41,6 +41,8 @@
import java.util.ResourceBundle;
import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.ScriptFunction;
@@ -241,13 +243,14 @@
// For each file on the command line.
for (final String fileName : files) {
- final File file = new File(fileName);
- final Source source = new Source(fileName, file);
- final Compiler compiler = Compiler.compiler(source, context);
- compiler.compile();
+ final FunctionNode functionNode = new Parser(context, new Source(fileName, new File(fileName)), errors).parse();
+
if (errors.getNumberOfErrors() != 0) {
return COMPILATION_ERROR;
}
+
+ //null - pass no code installer - this is compile only
+ new Compiler(context, functionNode).compile();
}
} finally {
context.getOut().flush();
@@ -282,7 +285,7 @@
// For each file on the command line.
for (final String fileName : files) {
final File file = new File(fileName);
- ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
+ final ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
if (script == null || errors.getNumberOfErrors() != 0) {
return COMPILATION_ERROR;
}
--- a/nashorn/test/script/trusted/JDK-8006529.js Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/test/script/trusted/JDK-8006529.js Thu Feb 14 13:01:52 2013 +0100
@@ -39,17 +39,15 @@
* 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 Source = Java.type("jdk.nashorn.internal.runtime.Source")
var FunctionNode = Java.type("jdk.nashorn.internal.ir.FunctionNode")
// Compiler class methods and fields
-
-// Compiler.compile(Source, Context)
-var compilerMethod = Compiler.class.getMethod("compiler", Source.class, Context.class);
-// Compiler.compile()
-var compileMethod = Compiler.class.getMethod("compile");
+var parseMethod = Parser.class.getMethod("parse");
+var compileMethod = Compiler.class.getMethod("compile");
// NOTE: private field. But this is a trusted test!
// Compiler.functionNode
@@ -90,10 +88,14 @@
// source code, returns a jdk.nashorn.internal.ir.FunctionNode object
// representing it.
function compile(source) {
- var compiler = compilerMethod.invoke(null,
- new Source("<no name>", source), Context.getContext())
- compileMethod.invoke(compiler);
- return getScriptNode(compiler)
+ var source = new Source("<no name>", source);
+ var parser = new Parser(Context.getContext(), source, null);
+ var func = parseMethod.invoke(parser);
+ var compiler = new Compiler(Context.getContext(), func);
+
+ compileMethod.invoke(compiler);
+
+ return getScriptNode(compiler);
};
var allAssertions = (function() {
--- a/nashorn/test/src/jdk/nashorn/internal/parser/ParserTest.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/test/src/jdk/nashorn/internal/parser/ParserTest.java Thu Feb 14 13:01:52 2013 +0100
@@ -156,10 +156,7 @@
Context.setGlobal(global);
}
final Source source = new Source(file.getAbsolutePath(), buffer);
- final Compiler compiler = Compiler.compiler(source, context, errors, context._strict);
-
- final Parser parser = new Parser(compiler);
- parser.parse(CompilerConstants.RUN_SCRIPT.tag());
+ new Parser(context, source, errors).parse();
if (errors.getNumberOfErrors() > 0) {
log("Parse failed: " + file.getAbsolutePath());
failed++;
--- a/nashorn/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java Thu Feb 14 14:16:58 2013 +0530
+++ b/nashorn/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java Thu Feb 14 13:01:52 2013 +0100
@@ -126,6 +126,7 @@
}
final File file = new File(fileName);
ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
+
if (script == null || errors.getNumberOfErrors() != 0) {
return COMPILATION_ERROR;
}