8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
Reviewed-by: hannesw, attila
--- a/nashorn/bin/runopt.sh Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/bin/runopt.sh Mon May 19 15:29:42 2014 +0200
@@ -1,13 +1,43 @@
#!/bin/sh
+###########################################################################################
+# This is a helper script to evaluate nashorn with optimistic types
+# it produces a flight recording for every run, and uses the best
+# known flags for performance for the current configration
+###########################################################################################
+
+# Flags to instrument lambdaform computation, caching, interpretation and compilation
+# Default compile threshold for lambdaforms is 30
#FLAGS="-Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
+
+
+# Flags to run trusted tests from the Nashorn test suite
#FLAGS="-Djava.security.manager -Djava.security.policy=../build/nashorn.policy -Dnashorn.debug"
-FILENAME="./optimistic_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+# Unique timestamped file name for JFR recordings. For JFR, we also have to
+# crank up the stack cutoff depth to 1024, because of ridiculously long lambda form
+# stack traces.
+#
+# It is also recommended that you go into $JAVA_HOME/jre/lib/jfr/default.jfc and
+# set the "method-sampling-interval" Normal and Maximum sample time as low as you
+# can go (10 ms on most platforms). The default is normally higher. The increased
+# sampling overhead is usually negligible for Nashorn runs, but the data is better
+JFR_FILENAME="./nashorn_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+
+
+# Directory where to look for nashorn.jar in a dist folder. The default is "..", assuming
+# that we run the script from the make dir
DIR=..
NASHORN_JAR=$DIR/dist/nashorn.jar
+
+# The built Nashorn jar is placed first in the bootclasspath to override the JDK
+# nashorn.jar in $JAVA_HOME/jre/lib/ext. Thus, we also need -esa, as assertions in
+# nashorn count as system assertions in this configuration
+
+# Type profiling default level is 111, 222 adds some compile time, but is faster
+
$JAVA_HOME/bin/java \
$FLAGS \
-ea \
@@ -16,17 +46,33 @@
-Xms2G -Xmx2G \
-XX:+UnlockCommercialFeatures \
-XX:+FlightRecorder \
--XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=1024 \
+-XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$JFR_FILENAME,stackdepth=1024 \
-XX:TypeProfileLevel=222 \
--XX:+UnlockExperimentalVMOptions \
--XX:+UseTypeSpeculation \
--XX:+UseMathExactIntrinsics \
--XX:+UnlockDiagnosticVMOptions \
-cp $CLASSPATH:../build/test/classes/ \
jdk.nashorn.tools.Shell ${@}
-#-Djava.security.manager= -Djava.security.policy=$DIR/build/nashorn.policy \
+# Below are flags that may come in handy, but aren't used for default runs
+
+
+# Type specialization and math intrinsic replacement should be enabled by default in 8u20 and nine,
+# keeping this flag around for experimental reasons. Replace + with - to switch it off
+#-XX:+UseTypeSpeculation \
+
+
+# Same with math intrinsics. They should be enabled by default in 8u20 and 9
+#-XX:+UseMathExactIntrinsics \
+
+
+# Add -Dnashorn.time to time the compilation phases.
+#-Dnashorn.time \
+
+
+# Add ShowHiddenFrames to get lambda form internals on the stack traces
#-XX:+ShowHiddenFrames \
-#-XX:+PrintOptoAssembly \
-#-XX:-TieredCompilation \
-#-XX:CICompilerCount=1 \
+
+
+# Add print optoassembly to get an asm dump. This requires 1) a debug build, not product,
+# That tired compilation is switched off, for C2 only output and that the number of
+# compiler threads is set to 1 for determinsm.
+#-XX:+PrintOptoAssembly -XX:-TieredCompilation -XX:CICompilerCount=1 \
+
--- a/nashorn/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Mon May 19 15:29:42 2014 +0200
@@ -35,6 +35,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.Expression;
@@ -45,8 +46,6 @@
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.Debug;
-import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
@@ -82,39 +81,31 @@
*/
@Logger(name="apply2call")
-public final class ApplySpecialization implements Loggable {
+public final class ApplySpecialization extends NodeVisitor<LexicalContext> implements Loggable {
private static final boolean USE_APPLY2CALL = Options.getBooleanProperty("nashorn.apply2call", true);
- private final RecompilableScriptFunctionData data;
-
- private FunctionNode functionNode;
-
- private final MethodType actualCallSiteType;
-
private final DebugLogger log;
- private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
+ private final Compiler compiler;
+
+ private final Set<Integer> changed = new HashSet<>();
- private boolean changed;
+ private final Deque<List<IdentNode>> explodedArguments = new ArrayDeque<>();
- private boolean finished;
+ private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
/**
* Apply specialization optimization. Try to explode arguments and call
* applies as calls if they just pass on the "arguments" array and
* "arguments" doesn't escape.
*
- * @param context context
- * @param data recompilable script function data, which contains e.g. needs callee information
- * @param functionNode functionNode
- * @param actualCallSiteType actual call site type that we use (not Object[] varargs)
+ * @param compiler compiler
*/
- public ApplySpecialization(final Context context, final RecompilableScriptFunctionData data, final FunctionNode functionNode, final MethodType actualCallSiteType) {
- this.data = data;
- this.functionNode = functionNode;
- this.actualCallSiteType = actualCallSiteType;
- this.log = initLogger(context);
+ public ApplySpecialization(final Compiler compiler) {
+ super(new LexicalContext());
+ this.compiler = compiler;
+ this.log = initLogger(compiler.getContext());
}
@Override
@@ -128,14 +119,6 @@
}
/**
- * Return the function node, possibly after transformation
- * @return function node
- */
- public FunctionNode getFunctionNode() {
- return functionNode;
- }
-
- /**
* Arguments may only be used as args to the apply. Everything else is disqualified
* We cannot control arguments if they escape from the method and go into an unknown
* scope, thus we are conservative and treat any access to arguments outside the
@@ -143,12 +126,12 @@
*
* @return true if arguments escape
*/
- private boolean argumentsEscape() {
+ private boolean argumentsEscape(final FunctionNode functionNode) {
final Deque<Set<Expression>> stack = new ArrayDeque<>();
//ensure that arguments is only passed as arg to apply
try {
- functionNode = (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private boolean isCurrentArg(final Expression expr) {
return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
}
@@ -202,53 +185,60 @@
return false;
}
- private boolean finish() {
- finished = true;
- return changed;
+ @Override
+ public boolean enterCallNode(final CallNode callNode) {
+ return !explodedArguments.isEmpty();
}
- /**
- * Try to do the apply to call transformation
- * @return true if successful, false otherwise
- */
- public boolean transform() {
- if (!USE_APPLY2CALL) {
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ //apply needs to be a global symbol or we don't allow it
+
+ final List<IdentNode> newParams = explodedArguments.peek();
+ if (isApply(callNode)) {
+ final List<Expression> newArgs = new ArrayList<>();
+ for (final Expression arg : callNode.getArgs()) {
+ if (arg instanceof IdentNode && ARGUMENTS.equals(((IdentNode)arg).getName())) {
+ newArgs.addAll(newParams);
+ } else {
+ newArgs.add(arg);
+ }
+ }
+
+ changed.add(lc.getCurrentFunction().getId());
+
+ final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
+
+ log.fine("Transformed ",
+ callNode,
+ " from apply to call => ",
+ newCallNode,
+ " in ",
+ DebugLogger.quote(lc.getCurrentFunction().getName()));
+
+ return newCallNode;
+ }
+
+ return callNode;
+ }
+
+ private boolean pushExplodedArgs(final FunctionNode functionNode) {
+ int start = 0;
+
+ final MethodType actualCallSiteType = compiler.getCallSiteType(functionNode);
+ if (actualCallSiteType == null) {
return false;
}
-
- if (finished) {
- throw new AssertionError("Can't apply transform twice");
- }
-
-
assert actualCallSiteType.parameterType(actualCallSiteType.parameterCount() - 1) != Object[].class : "error vararg callsite passed to apply2call " + functionNode.getName() + " " + actualCallSiteType;
- changed = false;
-
- if (!Global.instance().isSpecialNameValid("apply")) {
- log.fine("Apply transform disabled: apply/call overridden");
- assert !Global.instance().isSpecialNameValid("call") : "call and apply should have the same SwitchPoint";
- return finish();
- }
-
- //eval can do anything to escape arguments so that is not ok
- if (functionNode.hasEval()) {
- return finish();
- }
-
- if (argumentsEscape()) {
- return finish();
- }
-
- int start = 0;
-
- if (data.needsCallee()) {
+ final TypeMap ptm = compiler.getTypeMap();
+ if (ptm.needsCallee()) {
start++;
}
start++; //we always uses this
- final List<IdentNode> params = functionNode.getParameters();
+ final List<IdentNode> params = functionNode.getParameters();
final List<IdentNode> newParams = new ArrayList<>();
final long to = Math.max(params.size(), actualCallSiteType.parameterCount() - start);
for (int i = 0; i < to; i++) {
@@ -259,45 +249,66 @@
}
}
- // expand arguments
- // this function has to be guarded with call and apply being builtins
- functionNode = (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
- @Override
- public Node leaveCallNode(final CallNode callNode) {
- //apply needs to be a global symbol or we don't allow it
+ explodedArguments.push(newParams);
+ return true;
+ }
+
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ if (!USE_APPLY2CALL) {
+ return false;
+ }
- if (isApply(callNode)) {
- final List<Expression> newArgs = new ArrayList<>();
- for (final Expression arg : callNode.getArgs()) {
- if (arg instanceof IdentNode && ARGUMENTS.equals(((IdentNode)arg).getName())) {
- newArgs.addAll(newParams);
- } else {
- newArgs.add(arg);
- }
- }
-
- changed = true;
+ if (!Global.instance().isSpecialNameValid("apply")) {
+ log.fine("Apply transform disabled: apply/call overridden");
+ assert !Global.instance().isSpecialNameValid("call") : "call and apply should have the same SwitchPoint";
+ return false;
+ }
- final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
- log.fine("Transformed " + callNode + " from apply to call => " + newCallNode + " in '" + functionNode.getName() + "'");
- return newCallNode;
- }
+ if (!compiler.isOnDemandCompilation()) {
+ return false;
+ }
- return callNode;
- }
- });
+ if (functionNode.hasEval()) {
+ return false;
+ }
- if (changed) {
- functionNode = functionNode.clearFlag(null, FunctionNode.USES_ARGUMENTS).
- setFlag(null, FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION).
- setParameters(null, newParams);
+ if (argumentsEscape(functionNode)) {
+ return false;
}
- if (log.isEnabled()) {
- log.info(Debug.id(data) + " Successfully specialized apply to call in '" + functionNode.getName() + "' id=" + functionNode.getId() + " signature=" + actualCallSiteType + " needsCallee=" + data.needsCallee() + " " + functionNode.getSource().getURL());
+ return pushExplodedArgs(functionNode);
+ }
+
+ /**
+ * Try to do the apply to call transformation
+ * @return true if successful, false otherwise
+ */
+ @Override
+ public Node leaveFunctionNode(final FunctionNode functionNode0) {
+ FunctionNode newFunctionNode = functionNode0;
+ final String functionName = newFunctionNode.getName();
+
+ if (changed.contains(newFunctionNode.getId())) {
+ newFunctionNode = newFunctionNode.clearFlag(lc, FunctionNode.USES_ARGUMENTS).
+ setFlag(lc, FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION).
+ setParameters(lc, explodedArguments.peek());
+
+ if (log.isEnabled()) {
+ log.info("Successfully specialized apply to call in '",
+ functionName,
+ " params=",
+ explodedArguments.peek(),
+ "' id=",
+ newFunctionNode.getId(),
+ " source=",
+ newFunctionNode.getSource().getURL());
+ }
}
- return finish();
+ explodedArguments.pop();
+
+ return newFunctionNode;
}
private static boolean isApply(final CallNode callNode) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java Mon May 19 15:29:42 2014 +0200
@@ -152,12 +152,12 @@
private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
- private final CompilationEnvironment env;
+ private final Compiler compiler;
- public AssignSymbols(final CompilationEnvironment env) {
+ public AssignSymbols(final Compiler compiler) {
super(new LexicalContext());
- this.env = env;
- this.log = initLogger(env.getContext());
+ this.compiler = compiler;
+ this.log = initLogger(compiler.getContext());
this.debug = log.isEnabled();
}
@@ -216,7 +216,7 @@
});
}
- private IdentNode compilerConstantIdentifier(CompilerConstants cc) {
+ private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
}
@@ -382,8 +382,8 @@
symbol.setFlags(flags);
}
- if((isVar || isParam) && env.useOptimisticTypes() && env.isOnDemandCompilation()) {
- env.declareLocalSymbol(name);
+ if((isVar || isParam) && compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
+ compiler.declareLocalSymbol(name);
}
return symbol;
@@ -751,7 +751,7 @@
}
@Override
- public Node leaveFunctionNode(FunctionNode functionNode) {
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
return markProgramBlock(
removeUnusedSlots(
@@ -842,8 +842,8 @@
return runtimeNode;
}
- private FunctionNode markProgramBlock(FunctionNode functionNode) {
- if (env.isOnDemandCompilation() || !functionNode.isProgram()) {
+ private FunctionNode markProgramBlock(final FunctionNode functionNode) {
+ if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) {
return functionNode;
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Mon May 19 15:29:42 2014 +0200
@@ -253,16 +253,20 @@
// Number of live locals on entry to (and thus also break from) labeled blocks.
private final IntDeque labeledBlockBreakLiveLocals = new IntDeque();
+ //is this a rest of compilation
+ private final int[] continuationEntryPoints;
+
/**
* Constructor.
*
* @param compiler
*/
- CodeGenerator(final Compiler compiler) {
+ CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
super(new CodeGeneratorLexicalContext());
- this.compiler = compiler;
- this.callSiteFlags = compiler.getEnv()._callsite_flags;
- this.log = initLogger(compiler.getCompilationEnvironment().getContext());
+ this.compiler = compiler;
+ this.continuationEntryPoints = continuationEntryPoints;
+ this.callSiteFlags = compiler.getEnv()._callsite_flags;
+ this.log = initLogger(compiler.getContext());
}
@Override
@@ -324,8 +328,36 @@
return method;
}
+ private boolean isRestOf() {
+ return continuationEntryPoints != null;
+ }
+
private boolean isOptimisticOrRestOf() {
- return useOptimisticTypes() || compiler.getCompilationEnvironment().isCompileRestOf();
+ return useOptimisticTypes() || isRestOf();
+ }
+
+ private boolean isCurrentContinuationEntryPoint(final int programPoint) {
+ return isRestOf() && getCurrentContinuationEntryPoint() == programPoint;
+ }
+
+ private int[] getContinuationEntryPoints() {
+ return isRestOf() ? continuationEntryPoints : null;
+ }
+
+ private int getCurrentContinuationEntryPoint() {
+ return isRestOf() ? continuationEntryPoints[0] : INVALID_PROGRAM_POINT;
+ }
+
+ private boolean isContinuationEntryPoint(final int programPoint) {
+ if (isRestOf()) {
+ assert continuationEntryPoints != null;
+ for (final int cep : continuationEntryPoints) {
+ if (cep == programPoint) {
+ return true;
+ }
+ }
+ }
+ return false;
}
/**
@@ -406,6 +438,7 @@
}
void getProto() {
+ //empty
}
@Override
@@ -443,7 +476,7 @@
//information.
final FunctionNode fn = lc.getCurrentFunction();
final int fnId = fn.getId();
- final int externalDepth = compiler.getCompilationEnvironment().getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName());
+ final int externalDepth = compiler.getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName());
//count the number of scopes from this place to the start of the function
@@ -589,11 +622,11 @@
return maybeNew(objectToNumber(narrowest), objectToNumber(widest));
}
- private static Type booleanToInt(Type t) {
+ private static Type booleanToInt(final Type t) {
return t == Type.BOOLEAN ? Type.INT : t;
}
- private static Type objectToNumber(Type t) {
+ private static Type objectToNumber(final Type t) {
return t.isObject() ? Type.NUMBER : t;
}
@@ -805,7 +838,7 @@
}
@Override
- public boolean enterSUB(UnaryNode unaryNode) {
+ public boolean enterSUB(final UnaryNode unaryNode) {
loadSUB(unaryNode, resultBounds);
return false;
}
@@ -877,19 +910,19 @@
}
@Override
- public boolean enterNOT(UnaryNode unaryNode) {
+ public boolean enterNOT(final UnaryNode unaryNode) {
loadNOT(unaryNode);
return false;
}
@Override
- public boolean enterADD(UnaryNode unaryNode) {
+ public boolean enterADD(final UnaryNode unaryNode) {
loadADD(unaryNode, resultBounds);
return false;
}
@Override
- public boolean enterBIT_NOT(UnaryNode unaryNode) {
+ public boolean enterBIT_NOT(final UnaryNode unaryNode) {
loadBIT_NOT(unaryNode);
return false;
}
@@ -913,7 +946,7 @@
}
@Override
- public boolean enterVOID(UnaryNode unaryNode) {
+ public boolean enterVOID(final UnaryNode unaryNode) {
loadVOID(unaryNode, resultBounds);
return false;
}
@@ -1041,7 +1074,7 @@
}
private boolean useOptimisticTypes() {
- return !lc.inSplitNode() && compiler.getCompilationEnvironment().useOptimisticTypes();
+ return !lc.inSplitNode() && compiler.useOptimisticTypes();
}
@Override
@@ -1438,7 +1471,7 @@
* @param flags the flags that need optimism stripped from them.
* @return flags without optimism
*/
- static int nonOptimisticFlags(int flags) {
+ static int nonOptimisticFlags(final int flags) {
return flags & ~(CALLSITE_OPTIMISTIC | -1 << CALLSITE_PROGRAM_POINT_SHIFT);
}
@@ -1626,12 +1659,14 @@
} else if (symbol.isParam() && (varsInScope || hasArguments || symbol.isScope())) {
assert symbol.isScope() : "scope for " + symbol + " should have been set in AssignSymbols already " + function.getName() + " varsInScope="+varsInScope+" hasArguments="+hasArguments+" symbol.isScope()=" + symbol.isScope();
assert !(hasArguments && symbol.hasSlot()) : "slot for " + symbol + " should have been removed in Lower already " + function.getName();
- final Type paramType;
+
+ final Type paramType;
final Symbol paramSymbol;
- if(hasArguments) {
+
+ if (hasArguments) {
assert !symbol.hasSlot() : "slot for " + symbol + " should have been removed in Lower already ";
paramSymbol = null;
- paramType = null;
+ paramType = null;
} else {
paramSymbol = symbol;
// NOTE: We're relying on the fact here that Block.symbols is a LinkedHashMap, hence it will
@@ -1647,11 +1682,15 @@
}
}
}
+
tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, paramType, paramSymbol) {
//this symbol will be put fielded, we can't initialize it as undefined with a known type
@Override
public Class<?> getValueType() {
- return OBJECT_FIELDS_ONLY || value == null || paramType.isBoolean() ? Object.class : paramType.getTypeClass();
+ if (OBJECT_FIELDS_ONLY || value == null || paramType == null) {
+ return Object.class;
+ }
+ return paramType.isBoolean() ? Object.class : paramType.getTypeClass();
}
});
}
@@ -1718,13 +1757,13 @@
}
}
- private void initializeInternalFunctionParameter(CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
+ private void initializeInternalFunctionParameter(final CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
final Symbol symbol = initializeInternalFunctionOrSplitParameter(cc, fn, functionStart, slot);
// Internal function params (:callee, this, and :varargs) are never expanded to multiple slots
assert symbol.getFirstSlot() == slot;
}
- private Symbol initializeInternalFunctionOrSplitParameter(CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
+ private Symbol initializeInternalFunctionOrSplitParameter(final CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
final Symbol symbol = fn.getBody().getExistingSymbol(cc.symbolName());
final Type type = Type.typeFor(cc.type());
method.initializeMethodParameter(symbol, type, functionStart);
@@ -1739,7 +1778,7 @@
* and we need to spread them into their new locations.
* @param function the function for which parameter-spreading code needs to be emitted
*/
- private void expandParameterSlots(FunctionNode function) {
+ private void expandParameterSlots(final FunctionNode function) {
final List<IdentNode> parameters = function.getParameters();
// Calculate the total number of incoming parameter slots
int currentIncomingSlot = function.needsCallee() ? 2 : 1;
@@ -1787,7 +1826,7 @@
* doing an on-demand ("just-in-time") compilation, then we aren't generating code for inner functions.
*/
private boolean compileOutermostOnly() {
- return RecompilableScriptFunctionData.LAZY_COMPILATION || compiler.getCompilationEnvironment().isOnDemandCompilation();
+ return RecompilableScriptFunctionData.LAZY_COMPILATION || compiler.isOnDemandCompilation();
}
@Override
@@ -1818,10 +1857,8 @@
unit = lc.pushCompileUnit(functionNode.getCompileUnit());
assert lc.hasCompileUnits();
- final CompilationEnvironment compEnv = compiler.getCompilationEnvironment();
- final boolean isRestOf = compEnv.isCompileRestOf();
final ClassEmitter classEmitter = unit.getClassEmitter();
- pushMethodEmitter(isRestOf ? classEmitter.restOfMethod(functionNode) : classEmitter.method(functionNode));
+ pushMethodEmitter(isRestOf() ? classEmitter.restOfMethod(functionNode) : classEmitter.method(functionNode));
method.setPreventUndefinedLoad();
if(useOptimisticTypes()) {
lc.pushUnwarrantedOptimismHandlers();
@@ -1832,7 +1869,7 @@
method.begin();
- if (isRestOf) {
+ if (isRestOf()) {
final ContinuationInfo ci = new ContinuationInfo();
fnIdToContinuationInfo.put(fnId, ci);
method.gotoLoopStart(ci.getHandlerLabel());
@@ -1868,8 +1905,8 @@
markOptimistic = false;
}
- FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED);
- if(markOptimistic) {
+ FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.BYTECODE_GENERATED);
+ if (markOptimistic) {
newFunctionNode = newFunctionNode.setFlag(lc, FunctionNode.IS_DEOPTIMIZABLE);
}
@@ -1972,6 +2009,7 @@
unit = lc.pushCompileUnit(arrayUnit.getCompileUnit());
final String className = unit.getUnitClassName();
+ assert unit != null;
final String name = currentFunction.uniqueName(SPLIT_PREFIX.symbolName());
final String signature = methodDescriptor(type, ScriptFunction.class, Object.class, ScriptObject.class, type);
@@ -2213,7 +2251,7 @@
public Boolean get() {
value.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
- public boolean enterFunctionNode(FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
return false;
}
@@ -2240,11 +2278,10 @@
final List<MapTuple<Expression>> tuples = new ArrayList<>();
final List<PropertyNode> gettersSetters = new ArrayList<>();
+ final int ccp = getCurrentContinuationEntryPoint();
+
Expression protoNode = null;
-
boolean restOfProperty = false;
- final CompilationEnvironment env = compiler.getCompilationEnvironment();
- final int ccp = env.getCurrentContinuationEntryPoint();
for (final PropertyNode propertyNode : elements) {
final Expression value = propertyNode.getValue();
@@ -2366,15 +2403,17 @@
final Symbol rhsSymbol = rhs instanceof IdentNode ? ((IdentNode)rhs).getSymbol() : null;
// One must be a "undefined" identifier, otherwise we can't get here
assert lhsSymbol != null || rhsSymbol != null;
+
final Symbol undefinedSymbol;
- if(isUndefinedSymbol(lhsSymbol)) {
+ if (isUndefinedSymbol(lhsSymbol)) {
undefinedSymbol = lhsSymbol;
} else {
assert isUndefinedSymbol(rhsSymbol);
undefinedSymbol = rhsSymbol;
}
- if (!undefinedSymbol.isScope()) {
+ assert undefinedSymbol != null; //remove warning
+ if (undefinedSymbol == null || !undefinedSymbol.isScope()) {
return false; //disallow undefined as local var or parameter
}
@@ -2391,15 +2430,14 @@
return false;
}
- final CompilationEnvironment env = compiler.getCompilationEnvironment();
// TODO: why?
- if (env.isCompileRestOf()) {
+ if (isRestOf()) {
return false;
}
//make sure that undefined has not been overridden or scoped as a local var
//between us and global
- if (!env.isGlobalSymbol(lc.getCurrentFunction(), "undefined")) {
+ if (!compiler.isGlobalSymbol(lc.getCurrentFunction(), "undefined")) {
return false;
}
@@ -2532,8 +2570,7 @@
* @return true if the expression or any of its subexpressions was deoptimized in the current recompilation chain.
*/
private boolean isDeoptimizedExpression(final Expression rootExpr) {
- final CompilationEnvironment env = compiler.getCompilationEnvironment();
- if(!env.isCompileRestOf()) {
+ if(!isRestOf()) {
return false;
}
return new Supplier<Boolean>() {
@@ -2542,14 +2579,14 @@
public Boolean get() {
rootExpr.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
- public boolean enterFunctionNode(FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
return false;
}
@Override
public boolean enterDefault(final Node node) {
if(!contains && node instanceof Optimistic) {
final int pp = ((Optimistic)node).getProgramPoint();
- contains = isValid(pp) && env.isContinuationEntryPoint(pp);
+ contains = isValid(pp) && isContinuationEntryPoint(pp);
}
return !contains;
}
@@ -2806,12 +2843,11 @@
return false;
}
- final CaseNode defaultCase = switchNode.getDefaultCase();
- final Label breakLabel = switchNode.getBreakLabel();
- final int liveLocalsOnBreak = method.getUsedSlotsWithLiveTemporaries();
-
- final boolean hasDefault = defaultCase != null;
- if(hasDefault && cases.size() == 1) {
+ final CaseNode defaultCase = switchNode.getDefaultCase();
+ final Label breakLabel = switchNode.getBreakLabel();
+ final int liveLocalsOnBreak = method.getUsedSlotsWithLiveTemporaries();
+
+ if (defaultCase != null && cases.size() == 1) {
// default case only
assert cases.get(0) == defaultCase;
loadAndDiscard(expression);
@@ -2822,7 +2858,7 @@
// NOTE: it can still change in the tableswitch/lookupswitch case if there's no default case
// but we need to add a synthetic default case for local variable conversions
- Label defaultLabel = hasDefault? defaultCase.getEntry() : breakLabel;
+ Label defaultLabel = defaultCase != null ? defaultCase.getEntry() : breakLabel;
final boolean hasSkipConversion = LocalVariableConversion.hasLiveConversion(switchNode);
if (switchNode.isInteger()) {
@@ -2922,7 +2958,8 @@
method.ifne(caseNode.getEntry());
}
}
- if(hasDefault) {
+
+ if (defaultCase != null) {
method._goto(defaultLabel);
} else {
method.beforeJoinPoint(switchNode);
@@ -3244,9 +3281,10 @@
method.label(repeatLabel);
final int liveLocalsOnContinue = method.getUsedSlotsWithLiveTemporaries();
- final Block body = loopNode.getBody();
- final Label breakLabel = loopNode.getBreakLabel();
+ final Block body = loopNode.getBody();
+ final Label breakLabel = loopNode.getBreakLabel();
final boolean testHasLiveConversion = test != null && LocalVariableConversion.hasLiveConversion(test);
+
if(Expression.isAlwaysTrue(test)) {
if(test != null) {
loadAndDiscard(test);
@@ -3254,12 +3292,14 @@
method.beforeJoinPoint(test);
}
}
- } else if(testHasLiveConversion) {
- emitBranch(test.getExpression(), body.getEntryLabel(), true);
- method.beforeJoinPoint(test);
- method._goto(breakLabel);
- } else {
- emitBranch(test.getExpression(), breakLabel, false);
+ } else if (test != null) {
+ if (testHasLiveConversion) {
+ emitBranch(test.getExpression(), body.getEntryLabel(), true);
+ method.beforeJoinPoint(test);
+ method._goto(breakLabel);
+ } else {
+ emitBranch(test.getExpression(), breakLabel, false);
+ }
}
body.accept(this);
@@ -3376,7 +3416,7 @@
method._try(tryLabel, endLabel, catchLabel);
}
- boolean reachable = method.isReachable();
+ final boolean reachable = method.isReachable();
if(reachable) {
popScope();
if(bodyCanThrow) {
@@ -3955,13 +3995,13 @@
}
@Override
- public boolean enterLabelNode(LabelNode labelNode) {
+ public boolean enterLabelNode(final LabelNode labelNode) {
labeledBlockBreakLiveLocals.push(lc.getUsedSlotCount());
return true;
}
@Override
- protected boolean enterDefault(Node node) {
+ protected boolean enterDefault(final Node node) {
throw new AssertionError("Code generator entered node of type " + node.getClass().getName());
}
@@ -3973,7 +4013,7 @@
final Label falseLabel = new Label("ternary_false");
final Label exitLabel = new Label("ternary_exit");
- Type outNarrowest = Type.narrowest(resultBounds.widest, Type.generic(Type.widestReturnType(trueExpr.getType(), falseExpr.getType())));
+ final Type outNarrowest = Type.narrowest(resultBounds.widest, Type.generic(Type.widestReturnType(trueExpr.getType(), falseExpr.getType())));
final TypeBounds outBounds = resultBounds.notNarrowerThan(outNarrowest);
emitBranch(test, falseLabel, false);
@@ -4255,9 +4295,8 @@
assert lc.peek() == functionNode;
final int fnId = functionNode.getId();
- final CompilationEnvironment env = compiler.getCompilationEnvironment();
-
- final RecompilableScriptFunctionData data = env.getScriptFunctionData(fnId);
+
+ final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fnId);
assert data != null : functionNode.getName() + " has no data";
@@ -4276,7 +4315,7 @@
createFunction.end();
}
- if (addInitializer && !initializedFunctionIds.contains(fnId) && !env.isOnDemandCompilation()) {
+ if (addInitializer && !initializedFunctionIds.contains(fnId) && !compiler.isOnDemandCompilation()) {
functionNode.getCompileUnit().addFunctionInitializer(data, functionNode);
initializedFunctionIds.add(fnId);
}
@@ -4359,11 +4398,10 @@
}
MethodEmitter emit(final int ignoredArgCount) {
- final CompilationEnvironment env = compiler.getCompilationEnvironment();
- final int programPoint = optimistic.getProgramPoint();
- final boolean optimisticOrContinuation = isOptimistic || env.isContinuationEntryPoint(programPoint);
- final boolean currentContinuationEntryPoint = env.isCurrentContinuationEntryPoint(programPoint);
- final int stackSizeOnEntry = method.getStackSize() - ignoredArgCount;
+ final int programPoint = optimistic.getProgramPoint();
+ final boolean optimisticOrContinuation = isOptimistic || isContinuationEntryPoint(programPoint);
+ final boolean currentContinuationEntryPoint = isCurrentContinuationEntryPoint(programPoint);
+ final int stackSizeOnEntry = method.getStackSize() - ignoredArgCount;
// First store the values on the stack opportunistically into local variables. Doing it before loadStack()
// allows us to not have to pop/load any arguments that are pushed onto it by loadStack() in the second
@@ -4387,7 +4425,8 @@
final Label afterConsumeStack = isOptimistic || currentContinuationEntryPoint ? new Label("after_consume_stack") : null;
if(isOptimistic) {
beginTry = new Label("try_optimistic");
- catchLabel = new Label(afterConsumeStack.toString() + "_handler");
+ final String catchLabelName = (afterConsumeStack == null ? "" : afterConsumeStack.toString()) + "_handler";
+ catchLabel = new Label(catchLabelName);
method.label(beginTry);
} else {
beginTry = catchLabel = null;
@@ -4414,6 +4453,7 @@
}
if(currentContinuationEntryPoint) {
final ContinuationInfo ci = getContinuationInfo();
+ assert ci != null : "no continuation info found for " + lc.getCurrentFunction();
assert !ci.hasTargetLabel(); // No duplicate program points
ci.setTargetLabel(afterConsumeStack);
ci.getHandlerLabel().markAsOptimisticContinuationHandlerFor(afterConsumeStack);
@@ -4907,9 +4947,8 @@
method.dup(2);
method.pop();
loadConstant(getByteCodeSymbolNames(fn));
- final CompilationEnvironment env = compiler.getCompilationEnvironment();
- if (env.isCompileRestOf()) {
- loadConstant(env.getContinuationEntryPoints());
+ if (isRestOf()) {
+ loadConstant(getContinuationEntryPoints());
method.invoke(INIT_REWRITE_EXCEPTION_REST_OF);
} else {
method.invoke(INIT_REWRITE_EXCEPTION);
@@ -5079,7 +5118,7 @@
}
private void generateContinuationHandler() {
- if (!compiler.getCompilationEnvironment().isCompileRestOf()) {
+ if (!isRestOf()) {
return;
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java Thu May 15 15:28:51 2014 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,529 +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;
-
-import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.AccessNode;
-import jdk.nashorn.internal.ir.Expression;
-import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.IdentNode;
-import jdk.nashorn.internal.ir.IndexNode;
-import jdk.nashorn.internal.ir.Optimistic;
-import jdk.nashorn.internal.objects.NativeArray;
-import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.FindProperty;
-import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Property;
-import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
-import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
-
-/**
- * Class for managing metadata during a compilation, e.g. which phases
- * should be run, should we use optimistic types, is this a lazy compilation
- * and various parameter types known to the runtime system
- */
-public final class CompilationEnvironment {
- private final CompilationPhases phases;
- private final boolean optimistic;
-
- private final Context context;
-
- private final ParamTypeMap paramTypes;
-
- private RecompilableScriptFunctionData compiledFunction;
-
- // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
- // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
- private final ScriptObject runtimeScope;
-
- private boolean strict;
-
- private final boolean onDemand;
-
- /**
- * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means
- * that using whatever was at program point 17 as an int failed.
- */
- private final Map<Integer, Type> invalidatedProgramPoints;
-
- /**
- * Contains the program point that should be used as the continuation entry point, as well as all previous
- * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if
- * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program
- * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program
- * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only
- * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have
- * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on.
- */
- private final int[] continuationEntryPoints;
-
- /**
- * Compilation phases that a compilation goes through
- */
- public static final class CompilationPhases implements Iterable<CompilationPhase> {
-
- /**
- * 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 CompilationPhase[] SEQUENCE_EAGER_ARRAY = new CompilationPhase[] {
- CompilationPhase.CONSTANT_FOLDING_PHASE,
- CompilationPhase.LOWERING_PHASE,
- CompilationPhase.SPLITTING_PHASE,
- CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
- CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
- CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
- CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
- CompilationPhase.BYTECODE_GENERATION_PHASE
- };
-
- private final static List<CompilationPhase> SEQUENCE_EAGER;
- static {
- final LinkedList<CompilationPhase> eager = new LinkedList<>();
- for (final CompilationPhase phase : SEQUENCE_EAGER_ARRAY) {
- eager.add(phase);
- }
- SEQUENCE_EAGER = Collections.unmodifiableList(eager);
- }
-
- /** Singleton that describes a standard eager compilation */
- public static CompilationPhases EAGER = new CompilationPhases(SEQUENCE_EAGER);
-
- private final List<CompilationPhase> phases;
-
- private CompilationPhases(final List<CompilationPhase> phases) {
- this.phases = phases;
- }
-
- @SuppressWarnings("unused")
- private CompilationPhases addFirst(final CompilationPhase phase) {
- if (phases.contains(phase)) {
- return this;
- }
- final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
- list.addFirst(phase);
- return new CompilationPhases(Collections.unmodifiableList(list));
- }
-
- private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
- final LinkedList<CompilationPhase> list = new LinkedList<>();
- for (final CompilationPhase p : phases) {
- list.add(p);
- if (p == phase) {
- list.add(newPhase);
- }
- }
- return new CompilationPhases(Collections.unmodifiableList(list));
- }
-
- /**
- * Turn a CompilationPhases into an optimistic one. NOP if already optimistic
- * @param isOptimistic should this be optimistic
- * @return new CompilationPhases that is optimistic or same if already optimistic
- */
- public CompilationPhases makeOptimistic(final boolean isOptimistic) {
- return isOptimistic ? addAfter(CompilationPhase.LOWERING_PHASE, CompilationPhase.PROGRAM_POINT_PHASE) : this;
- }
-
- /**
- * Turn a CompilationPhases into an optimistic one. NOP if already optimistic
- * @return new CompilationPhases that is optimistic or same if already optimistic
- */
- public CompilationPhases makeOptimistic() {
- return makeOptimistic(true);
- }
-
- private boolean contains(final CompilationPhase phase) {
- return phases.contains(phase);
- }
-
- @Override
- public Iterator<CompilationPhase> iterator() {
- return phases.iterator();
- }
-
- }
-
- /**
- * Constructor
- * @param context context
- * @param phases compilation phases
- * @param strict strict mode
- */
- public CompilationEnvironment(
- final Context context,
- final CompilationPhases phases,
- final boolean strict) {
- this(context, phases, null, null, null, null, null, strict, false);
- }
-
- /**
- * Constructor for compilation environment of the rest-of method
- *
- * @param context context
- * @param phases compilation phases
- * @param strict strict mode
- * @param compiledFunction the function being compiled
- * @param runtimeScope the runtime scope in effect at the time of compilation. It can be used to evaluate types of
- * scoped variables to guide the optimistic compilation, should the call to this method trigger code compilation.
- * Can be null if current runtime scope is not known, but that might cause compilation of code that will need more
- * subsequent deoptimization passes.
- * @param paramTypeMap known parameter types if any exist
- * @param invalidatedProgramPoints map of invalidated program points to their type
- * @param continuationEntryPoint program points used as the continuation entry points in the current rest-of sequence
- * @param onDemand is this an on demand compilation
- */
- public CompilationEnvironment(
- final Context context,
- final CompilationPhases phases,
- final boolean strict,
- final RecompilableScriptFunctionData compiledFunction,
- final ScriptObject runtimeScope,
- final ParamTypeMap paramTypeMap,
- final Map<Integer, Type> invalidatedProgramPoints,
- final int[] continuationEntryPoint,
- final boolean onDemand) {
- this(context, phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, runtimeScope, continuationEntryPoint, strict, onDemand);
- }
-
- /**
- * Constructor
- *
- * @param context context
- * @param phases compilation phases
- * @param strict strict mode
- * @param compiledFunction recompiled function
- * @param runtimeScope the runtime scope in effect at the time of compilation. It can be used to evaluate types of
- * scoped variables to guide the optimistic compilation, should the call to this method trigger code compilation.
- * Can be null if current runtime scope is not known, but that might cause compilation of code that will need more
- * subsequent deoptimization passes.
- * @param paramTypeMap known parameter types
- * @param invalidatedProgramPoints map of invalidated program points to their type
- * @param onDemand is this an on demand compilation
- */
- public CompilationEnvironment(
- final Context context,
- final CompilationPhases phases,
- final boolean strict,
- final RecompilableScriptFunctionData compiledFunction,
- final ScriptObject runtimeScope,
- final ParamTypeMap paramTypeMap,
- final Map<Integer, Type> invalidatedProgramPoints,
- final boolean onDemand) {
- this(context, phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, runtimeScope, null, strict, onDemand);
- }
-
- private CompilationEnvironment(
- final Context context,
- final CompilationPhases phases,
- final ParamTypeMap paramTypes,
- final Map<Integer, Type> invalidatedProgramPoints,
- final RecompilableScriptFunctionData compiledFunction,
- final ScriptObject runtimeScope,
- final int[] continuationEntryPoints,
- final boolean strict,
- final boolean onDemand) {
- this.context = context;
- this.phases = phases;
- this.paramTypes = paramTypes;
- this.continuationEntryPoints = continuationEntryPoints;
- this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
- this.compiledFunction = compiledFunction;
- this.runtimeScope = runtimeScope;
- this.strict = strict;
- this.optimistic = phases.contains(CompilationPhase.PROGRAM_POINT_PHASE);
- this.onDemand = onDemand;
-
- // If entry point array is passed, it must have at least one element
- assert continuationEntryPoints == null || continuationEntryPoints.length > 0;
- assert !isCompileRestOf() || isOnDemandCompilation(); // isCompileRestOf => isRecompilation
- // continuation entry points must be among the invalidated program points
- assert !isCompileRestOf() || invalidatedProgramPoints != null && containsAll(invalidatedProgramPoints.keySet(), continuationEntryPoints);
- }
-
- Context getContext() {
- return context;
- }
-
- private static boolean containsAll(final Set<Integer> set, final int[] array) {
- for (int i = 0; i < array.length; ++i) {
- if (!set.contains(array[i])) {
- return false;
- }
- }
- return true;
- }
-
- void setData(final RecompilableScriptFunctionData data) {
- assert this.compiledFunction == null : data;
- this.compiledFunction = data;
- }
-
- boolean isStrict() {
- return strict;
- }
-
- void setIsStrict(final boolean strict) {
- this.strict = strict;
- }
-
- CompilationPhases getPhases() {
- return phases;
- }
-
- /**
- * Get the parameter type at a parameter position if known from previous runtime calls
- * or optimistic profiles.
- *
- * @param functionNode function node to query
- * @param pos parameter position
- * @return known type of parameter 'pos' or null if no information exists
- */
- Type getParamType(final FunctionNode functionNode, final int pos) {
- return paramTypes == null ? null : paramTypes.get(functionNode, pos);
- }
-
- /**
- * Is this a compilation that generates the rest of method
- * @return true if rest of generation
- */
- boolean isCompileRestOf() {
- return continuationEntryPoints != null;
- }
-
- /**
- * Is this an on-demand compilation triggered by a {@code RecompilableScriptFunctionData} - either a type
- * specializing compilation, a deoptimizing recompilation, or a rest-of method compilation.
- * @return true if this is an on-demand compilation, false if this is an eager compilation.
- */
- boolean isOnDemandCompilation() {
- return onDemand; //data != null;
- }
-
- /**
- * Is this program point one of the continuation entry points for the rest-of method being compiled?
- * @param programPoint program point
- * @return true if it is a continuation entry point
- */
- boolean isContinuationEntryPoint(final int programPoint) {
- if (continuationEntryPoints != null) {
- for (final int continuationEntryPoint : continuationEntryPoints) {
- if (continuationEntryPoint == programPoint) {
- return true;
- }
- }
- }
- return false;
- }
-
- int[] getContinuationEntryPoints() {
- return continuationEntryPoints;
- }
-
- /**
- * Is this program point the continuation entry points for the current rest-of method being compiled?
- * @param programPoint program point
- * @return true if it is the current continuation entry point
- */
- boolean isCurrentContinuationEntryPoint(final int programPoint) {
- return hasCurrentContinuationEntryPoint() && getCurrentContinuationEntryPoint() == programPoint;
- }
-
- boolean hasCurrentContinuationEntryPoint() {
- return continuationEntryPoints != null;
- }
-
- int getCurrentContinuationEntryPoint() {
- // NOTE: we assert in the constructor that if the array is non-null, it has at least one element
- return hasCurrentContinuationEntryPoint() ? continuationEntryPoints[0] : INVALID_PROGRAM_POINT;
- }
-
- /**
- * Are optimistic types enabled ?
- * @param node get the optimistic type for a node
- * @return most optimistic type in current environment
- */
- Type getOptimisticType(final Optimistic node) {
- assert useOptimisticTypes();
- final int programPoint = node.getProgramPoint();
- final Type validType = invalidatedProgramPoints.get(programPoint);
- if (validType != null) {
- return validType;
- }
- final Type mostOptimisticType = node.getMostOptimisticType();
- final Type evaluatedType = getEvaluatedType(node);
- if(evaluatedType != null) {
- if(evaluatedType.widerThan(mostOptimisticType)) {
- final Type newValidType = evaluatedType.isObject() || evaluatedType.isBoolean() ? Type.OBJECT : evaluatedType;
- // Update invalidatedProgramPoints so we don't re-evaluate the expression next time. This is a heuristic
- // as we're doing a tradeoff. Re-evaluating expressions on each recompile takes time, but it might
- // notice a widening in the type of the expression and thus prevent an unnecessary deoptimization later.
- // We'll presume though that the types of expressions are mostly stable, so if we evaluated it in one
- // compilation, we'll keep to that and risk a low-probability deoptimization if its type gets widened
- // in the future.
- invalidatedProgramPoints.put(node.getProgramPoint(), newValidType);
- }
- return evaluatedType;
- }
- return mostOptimisticType;
- }
-
- /**
- * Tells the compilation environment that a symbol of a particular name is a local variables in a function. Used
- * with on-demand compilation, this will hide symbols of the same name from a parent scope and prevent them from
- * being mistakenly found by the optimistic types heuristics.
- * @param symbolName the name of the symbols to declare.
- */
- void declareLocalSymbol(final String symbolName) {
- assert useOptimisticTypes() && isOnDemandCompilation() && runtimeScope != null;
- if(runtimeScope.findProperty(symbolName, false) == null) {
- runtimeScope.set(symbolName, ScriptRuntime.UNDEFINED, true);
- }
- }
-
- private Type getEvaluatedType(final Optimistic expr) {
- if(expr instanceof IdentNode) {
- return runtimeScope == null ? null : getPropertyType(runtimeScope, ((IdentNode)expr).getName());
- } else if(expr instanceof AccessNode) {
- final AccessNode accessNode = (AccessNode)expr;
- final Object base = evaluateSafely(accessNode.getBase());
- if(!(base instanceof ScriptObject)) {
- return null;
- }
- return getPropertyType((ScriptObject)base, accessNode.getProperty());
- } else if(expr instanceof IndexNode) {
- final IndexNode indexNode = (IndexNode)expr;
- final Object base = evaluateSafely(indexNode.getBase());
- if(!(base instanceof NativeArray)) {
- // We only know how to deal with NativeArray. TODO: maybe manage buffers too
- return null;
- }
- // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying
- // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every
- // optimistic int linkage attempt, even if the long value being returned in the first invocation would be
- // representable as int. That way, we can presume that the array's optimistic type is the most optimistic
- // type for which an element getter has a chance of executing successfully.
- return ((NativeArray)base).getArray().getOptimisticType();
- }
- return null;
- }
-
- private static Type getPropertyType(final ScriptObject sobj, final String name) {
- final FindProperty find = sobj.findProperty(name, true);
- if(find == null) {
- return null;
- }
-
- final Property property = find.getProperty();
- final Class<?> propertyClass = property.getCurrentType();
- if (propertyClass == null) {
- // propertyClass == null means its value is Undefined. It is probably not initialized yet, so we won't make
- // a type assumption yet.
- return null;
- } else if (propertyClass.isPrimitive()) {
- return Type.typeFor(propertyClass);
- }
-
- final ScriptObject owner = find.getOwner();
- if(property.hasGetterFunction(owner)) {
- // Can have side effects, so we can't safely evaluate it; since !propertyClass.isPrimitive(), it's Object.
- return Type.OBJECT;
- }
-
- // Safely evaluate the property, and return the narrowest type for the actual value (e.g. Type.INT for a boxed
- // integer). Continue not making guesses for undefined.
- final Object value = property.getObjectValue(owner, owner);
- if(value == ScriptRuntime.UNDEFINED) {
- return null;
- }
- return Type.typeFor(JSType.unboxedFieldType(value));
- }
-
- private Object evaluateSafely(final Expression expr) {
- if(expr instanceof IdentNode) {
- return runtimeScope == null ? null : evaluatePropertySafely(runtimeScope, ((IdentNode)expr).getName());
- } else if(expr instanceof AccessNode) {
- final AccessNode accessNode = (AccessNode)expr;
- final Object base = evaluateSafely(accessNode.getBase());
- if(!(base instanceof ScriptObject)) {
- return null;
- }
- return evaluatePropertySafely((ScriptObject)base, accessNode.getProperty());
- }
- return null;
- }
-
- private static Object evaluatePropertySafely(final ScriptObject sobj, final String name) {
- final FindProperty find = sobj.findProperty(name, true);
- if(find == null) {
- return null;
- }
- final Property property = find.getProperty();
- final ScriptObject owner = find.getOwner();
- if(property.hasGetterFunction(owner)) {
- // Possible side effects; can't evaluate safely
- return null;
- }
- return property.getObjectValue(owner, owner);
- }
-
- /**
- * Should this compilation use optimistic types in general.
- * If this is false we will only set non-object types to things that can
- * be statically proven to be true.
- * @return true if optimistic types should be used.
- */
- boolean useOptimisticTypes() {
- return optimistic;
- }
-
- RecompilableScriptFunctionData getProgram() {
- if (compiledFunction == null) {
- return null;
- }
- return compiledFunction.getProgram();
- }
-
- RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
- return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
- }
-
- boolean isGlobalSymbol(final FunctionNode functionNode, final String name) {
- final RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId());
- return data.isGlobalSymbol(functionNode, name);
- }
-}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Mon May 19 15:29:42 2014 +0200
@@ -25,6 +25,11 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BUILTINS_TRANSFORMED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_GENERATED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_INSTALLED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED;
@@ -34,14 +39,39 @@
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED;
+import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
+import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.SplitNode;
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.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.Timing;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
/**
* A compilation phase is a step in the processes of turning a JavaScript
@@ -52,15 +82,18 @@
* Constant folding pass Simple constant folding that will make elementary
* constructs go away
*/
- CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
+ CONSTANT_FOLDING_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED)) {
@Override
- FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
- return (FunctionNode)fn.accept(new FoldConstants(compiler.getCompilationEnvironment()));
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new FoldConstants(compiler));
}
@Override
public String toString() {
- return "[Constant Folding]";
+ return "'Constant Folding'";
}
},
@@ -71,15 +104,19 @@
* flow cannot fall off the end. Replacing high level nodes with lower such
* as runtime nodes where applicable.
*/
- LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
+ LOWERING_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED)) {
@Override
- FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
return (FunctionNode)fn.accept(new Lower(compiler));
}
@Override
public String toString() {
- return "[Control Flow Lowering]";
+ return "'Control Flow Lowering'";
}
},
@@ -88,15 +125,44 @@
* optimistic ops a program point so that an UnwarrantedException knows from where
* a guess went wrong when creating the continuation to roll back this execution
*/
- PROGRAM_POINT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
+ PROGRAM_POINT_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED)) {
@Override
- FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
return (FunctionNode)fn.accept(new ProgramPoints());
}
@Override
public String toString() {
- return "[Program Point Calculation]";
+ return "'Program Point Calculation'";
+ }
+ },
+
+ TRANSFORM_BUILTINS_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED)) {
+ //we only do this if we have a param type map, otherwise this is not a specialized recompile
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new ApplySpecialization(compiler));
+ return (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public Node leaveFunctionNode(final FunctionNode node) {
+ return node.setState(lc, BUILTINS_TRANSFORMED);
+ }
+ });
+ }
+
+ @Override
+ public String toString() {
+ return "'Builtin Replacement'";
}
},
@@ -104,18 +170,120 @@
* Splitter Split the AST into several compile units based on a heuristic size calculation.
* Split IR can lead to scope information being changed.
*/
- SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
+ SPLITTING_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED)) {
@Override
- FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
- final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
-
- final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn, true);
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ final CompileUnit outermostCompileUnit = compiler.addCompileUnit(0L);
+ final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn, true);
assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
+ assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName());
- if (newFunctionNode.isStrict()) {
- assert compiler.getCompilationEnvironment().isStrict();
- compiler.getCompilationEnvironment().setIsStrict(true);
+ return newFunctionNode;
+ }
+
+ @Override
+ public String toString() {
+ return "'Code Splitting'";
+ }
+ },
+
+ SYMBOL_ASSIGNMENT_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT)) {
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new AssignSymbols(compiler));
+ }
+
+ @Override
+ public String toString() {
+ return "'Symbol Assignment'";
+ }
+ },
+
+ SCOPE_DEPTH_COMPUTATION_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT,
+ SYMBOLS_ASSIGNED)) {
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new FindScopeDepths(compiler));
+ }
+
+ @Override
+ public String toString() {
+ return "'Scope Depth Computation'";
+ }
+ },
+
+ OPTIMISTIC_TYPE_ASSIGNMENT_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT,
+ SYMBOLS_ASSIGNED,
+ SCOPE_DEPTHS_COMPUTED)) {
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ if (compiler.useOptimisticTypes()) {
+ return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler));
+ }
+ return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED);
+ }
+
+ @Override
+ public String toString() {
+ return "'Optimistic Type Assignment'";
+ }
+ },
+
+ LOCAL_VARIABLE_TYPE_CALCULATION_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT,
+ SYMBOLS_ASSIGNED,
+ SCOPE_DEPTHS_COMPUTED,
+ OPTIMISTIC_TYPES_ASSIGNED)) {
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler));
+
+ final ScriptEnvironment senv = compiler.getEnv();
+ final PrintWriter err = senv.getErr();
+
+ //TODO separate phase for the debug printouts for abstraction and clarity
+ if (senv._print_lower_ast) {
+ err.println("Lower AST for: " + quote(newFunctionNode.getName()));
+ err.println(new ASTWriter(newFunctionNode));
+ }
+
+ if (senv._print_lower_parse) {
+ err.println("Lower AST for: " + quote(newFunctionNode.getName()));
+ err.println(new PrintVisitor(newFunctionNode));
}
return newFunctionNode;
@@ -123,90 +291,150 @@
@Override
public String toString() {
- return "[Code Splitting]";
- }
- },
-
- SYMBOL_ASSIGNMENT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT)) {
- @Override
- FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
- return (FunctionNode)fn.accept(new AssignSymbols(compiler.getCompilationEnvironment()));
- }
-
- @Override
- public String toString() {
- return "[Symbol Assignment]";
+ return "'Local Variable Type Calculation'";
}
},
- SCOPE_DEPTH_COMPUTATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED)) {
- @Override
- FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
- return (FunctionNode)fn.accept(new FindScopeDepths(compiler));
- }
-
+ /**
+ * Reuse compile units, if they are already present. We are using the same compiler
+ * to recompile stuff
+ */
+ REUSE_COMPILE_UNITS_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT,
+ SYMBOLS_ASSIGNED,
+ SCOPE_DEPTHS_COMPUTED,
+ OPTIMISTIC_TYPES_ASSIGNED,
+ LOCAL_VARIABLE_TYPES_CALCULATED)) {
@Override
- public String toString() {
- return "[Scope Depth Computation]";
- }
- },
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods";
+
+ final Map<CompileUnit, CompileUnit> map = new HashMap<>();
+ final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet();
+
+ final DebugLogger log = compiler.getLogger();
+
+ log.fine("Clearing bytecode cache");
+
+ for (final CompileUnit oldUnit : compiler.getCompileUnits()) {
+ CompileUnit newUnit = map.get(oldUnit);
+ assert map.get(oldUnit) == null;
+ final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName());
+ if (phases.isRestOfCompilation()) {
+ sb.append("$restOf");
+ }
+ newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight());
+ log.info("Creating new compile unit ", oldUnit, " => ", newUnit);
+ map.put(oldUnit, newUnit);
+ assert newUnit != null;
+ newUnits.add(newUnit);
+ }
+
+ log.info("Replacing compile units in Compiler...");
+ compiler.replaceCompileUnits(newUnits);
+ log.info("Done");
+
+ //replace old compile units in function nodes, if any are assigned,
+ //for example by running the splitter on this function node in a previous
+ //partial code generation
+ final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public Node leaveFunctionNode(final FunctionNode node) {
+ final CompileUnit oldUnit = node.getCompileUnit();
+ assert oldUnit != null : "no compile unit in function node";
+
+ final CompileUnit newUnit = map.get(oldUnit);
+ assert newUnit != null : "old unit has no mapping to new unit " + oldUnit;
- OPTIMISTIC_TYPE_ASSIGNMENT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED, SCOPE_DEPTHS_COMPUTED)) {
- @Override
- FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
- if(compiler.getCompilationEnvironment().useOptimisticTypes()) {
- return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler.getCompilationEnvironment()));
- }
- return fn.setState(null, OPTIMISTIC_TYPES_ASSIGNED);
+ log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName()));
+ return node.setCompileUnit(lc, newUnit).setState(lc, CompilationState.COMPILE_UNITS_REUSED);
+ }
+
+ @Override
+ public Node leaveSplitNode(final SplitNode node) {
+ final CompileUnit oldUnit = node.getCompileUnit();
+ assert oldUnit != null : "no compile unit in function node";
+
+ final CompileUnit newUnit = map.get(oldUnit);
+ assert newUnit != null : "old unit has no mapping to new unit " + oldUnit;
+
+ log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName()));
+ return node.setCompileUnit(lc, newUnit);
+ }
+
+ @Override
+ public Node leaveLiteralNode(final LiteralNode<?> node) {
+ if (node instanceof ArrayLiteralNode) {
+ final ArrayLiteralNode aln = (ArrayLiteralNode)node;
+ if (aln.getUnits() == null) {
+ return node;
+ }
+ final List<ArrayUnit> newArrayUnits = new ArrayList<>();
+ for (final ArrayUnit au : aln.getUnits()) {
+ final CompileUnit newUnit = map.get(au.getCompileUnit());
+ assert newUnit != null;
+ newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi()));
+ }
+ aln.setUnits(newArrayUnits);
+ }
+ return node;
+ }
+
+ @Override
+ public Node leaveDefault(final Node node) {
+ return node.ensureUniqueLabels(lc);
+ }
+ });
+
+ return newFunctionNode;
}
@Override
public String toString() {
- return "[Optimistic Type Assignment]";
+ return "'Reuse Compile Units'";
}
},
- LOCAL_VARIABLE_TYPE_CALCULATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED, SCOPE_DEPTHS_COMPUTED, OPTIMISTIC_TYPES_ASSIGNED)) {
- @Override
- FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
- return (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler.getCompilationEnvironment()));
- }
-
- @Override
- public String toString() {
- return "[Local Variable Type Calculation]";
- }
- },
-
- /**
+ /**
* Bytecode generation:
*
* Generate the byte code class(es) resulting from the compiled FunctionNode
*/
- BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, SYMBOLS_ASSIGNED, SCOPE_DEPTHS_COMPUTED, OPTIMISTIC_TYPES_ASSIGNED, LOCAL_VARIABLE_TYPES_CALCULATED)) {
- @Override
- FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
- final ScriptEnvironment env = compiler.getEnv();
+ BYTECODE_GENERATION_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT,
+ SYMBOLS_ASSIGNED,
+ SCOPE_DEPTHS_COMPUTED,
+ OPTIMISTIC_TYPES_ASSIGNED,
+ LOCAL_VARIABLE_TYPES_CALCULATED)) {
- if (env._print_lower_ast) {
- env.getErr().println(new ASTWriter(fn));
- }
-
- if (env._print_lower_parse) {
- env.getErr().println(new PrintVisitor(fn));
- }
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ final ScriptEnvironment senv = compiler.getEnv();
FunctionNode newFunctionNode = fn;
- final CodeGenerator codegen = new CodeGenerator(compiler);
+ compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation());
+ final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null);
try {
newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
codegen.generateScopeCalls();
} catch (final VerifyError e) {
- if (env._verify_code || env._print_code) {
- env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
- if (env._dump_on_error) {
- e.printStackTrace(env.getErr());
+ if (senv._verify_code || senv._print_code) {
+ senv.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
+ if (senv._dump_on_error) {
+ e.printStackTrace(senv.getErr());
}
} else {
throw e;
@@ -228,11 +456,11 @@
compiler.addClass(className, bytecode);
// should we verify the generated code?
- if (env._verify_code) {
+ if (senv._verify_code) {
compiler.getCodeInstaller().verify(bytecode);
}
- DumpBytecode.dumpBytecode(env, compiler.getLogger(), bytecode, className);
+ DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className);
}
return newFunctionNode;
@@ -240,13 +468,139 @@
@Override
public String toString() {
- return "[Bytecode Generation]";
+ return "'Bytecode Generation'";
}
- };
+ },
+
+ INSTALL_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT,
+ SYMBOLS_ASSIGNED,
+ SCOPE_DEPTHS_COMPUTED,
+ OPTIMISTIC_TYPES_ASSIGNED,
+ LOCAL_VARIABLE_TYPES_CALCULATED,
+ BYTECODE_GENERATED)) {
+
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ final DebugLogger log = compiler.getLogger();
+
+ final Map<String, Class<?>> installedClasses = new LinkedHashMap<>();
+
+ boolean first = true;
+ Class<?> rootClass = null;
+ long length = 0L;
+
+ for (final Entry<String, byte[]> entry : compiler.getBytecode().entrySet()) {
+ final String className = entry.getKey();
+ //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName();
+ final byte[] code = entry.getValue();
+ length += code.length;
+
+ final Class<?> clazz = compiler.getCodeInstaller().install(Compiler.binaryName(className), code);
+ if (first) {
+ rootClass = clazz;
+ first = false;
+ }
+ installedClasses.put(className, clazz);
+ }
+
+ if (rootClass == null) {
+ throw new CompilationException("Internal compiler error: root class not found!");
+ }
+
+ // do these in a loop, to use only one privileged action - this significantly
+ // reduces class installation overhead
+ log.fine("Preparing source and constant fields...");
+ try {
+ final Object[] constants = compiler.getConstantData().toArray();
+ // Need doPrivileged because these fields are private
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ for (final Entry<String, Class<?>> entry : installedClasses.entrySet()) {
+ final Class<?> clazz = entry.getValue();
+ log.fine("Initializing source for ", clazz);
+ //use reflection to write source and constants table to installed classes
+ final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
+ sourceField.setAccessible(true);
+ sourceField.set(null, compiler.getSource());
+ log.fine("Initializing constants for ", clazz);
+ final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
+ constantsField.setAccessible(true);
+ constantsField.set(null, constants);
+ }
+ return null;
+ }
+ });
+ } catch (final PrivilegedActionException e) {
+ throw new RuntimeException(e);
+ }
+ log.fine("Done");
+
+ // index recompilable script function datas in the constant pool
+ final Map<RecompilableScriptFunctionData, RecompilableScriptFunctionData> rfns = new IdentityHashMap<>();
+ for (final Object constant: compiler.getConstantData().getConstants()) {
+ if (constant instanceof RecompilableScriptFunctionData) {
+ final RecompilableScriptFunctionData rfn = (RecompilableScriptFunctionData)constant;
+ rfns.put(rfn, rfn);
+ }
+ }
+
+ // initialize function in the compile units
+ for (final CompileUnit unit : compiler.getCompileUnits()) {
+ unit.setCode(installedClasses.get(unit.getUnitClassName()));
+ unit.initializeFunctionsCode();
+ }
+
+ // remove installed bytecode from table in case compiler is reused
+ for (final String className : installedClasses.keySet()) {
+ log.fine("Removing installed class ", quote(className), " from bytecode table...");
+ compiler.removeClass(className);
+ }
+
+ if (log.isEnabled()) {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append("Installed class '").
+ append(rootClass.getSimpleName()).
+ append('\'').
+ append(" [").
+ append(rootClass.getName()).
+ append(", size=").
+ append(length).
+ append(" bytes, ").
+ append(compiler.getCompileUnits().size()).
+ append(" compile unit(s)]");
+
+ log.info(sb.toString());
+ }
+
+ return setStates(fn.setRootClass(null, rootClass), BYTECODE_INSTALLED);
+ }
+
+ @Override
+ public String toString() {
+ return "'Class Installation'";
+ }
+ };
+
+ /** pre conditions required for function node to which this transform is to be applied */
private final EnumSet<CompilationState> pre;
+
+ /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
private long startTime;
+
+ /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
private long endTime;
+
+ /** boolean that is true upon transform completion */
private boolean isFinished;
private CompilationPhase(final EnumSet<CompilationState> pre) {
@@ -254,36 +608,56 @@
}
boolean isApplicable(final FunctionNode functionNode) {
+ //this means that all in pre are present in state. state can be larger
return functionNode.hasState(pre);
}
+ private static FunctionNode setStates(final FunctionNode functionNode, final CompilationState state) {
+ return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public Node leaveFunctionNode(final FunctionNode fn) {
+ return fn.setState(lc, state);
+ }
+ });
+ }
+
/**
* Start a compilation phase
+ * @param compiler
* @param functionNode function to compile
* @return function node
*/
- protected FunctionNode 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));
- }
- }
+ protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) {
+ compiler.getLogger().indent();
+
+ assert pre != null;
- startTime = System.currentTimeMillis();
- return functionNode;
- }
+ if (!isApplicable(functionNode)) {
+ final StringBuilder sb = new StringBuilder("Compilation phase ");
+ sb.append(this).
+ append(" is not applicable to ").
+ append(quote(functionNode.getName())).
+ append("\n\tFunctionNode state = ").
+ append(functionNode.getState()).
+ append("\n\tRequired state = ").
+ append(this.pre);
+
+ throw new CompilationException(sb.toString());
+ }
+
+ startTime = System.currentTimeMillis();
+
+ return functionNode;
+ }
/**
* End a compilation phase
+ * @param compiler the compiler
* @param functionNode function node to compile
- * @return fucntion node
+ * @return function node
*/
- protected FunctionNode end(final FunctionNode functionNode) {
+ protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) {
+ compiler.getLogger().unindent();
endTime = System.currentTimeMillis();
Timing.accumulateTime(toString(), endTime - startTime);
@@ -303,13 +677,26 @@
return endTime;
}
- abstract FunctionNode transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
+ abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException;
- final FunctionNode apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
- if (!isApplicable(functionNode)) {
- throw new CompilationException("compile phase not applicable: " + this + " to " + functionNode.getName() + " state=" + functionNode.getState());
- }
- return end(transform(compiler, begin(functionNode)));
+ /**
+ * Apply a transform to a function node, returning the transfored function node. If the transform is not
+ * applicable, an exception is thrown. Every transform requires the function to have a certain number of
+ * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor
+ * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states.
+ *
+ * @param compiler compiler
+ * @param phases current complete pipeline of which this phase is one
+ * @param functionNode function node to transform
+ *
+ * @return transformed function node
+ *
+ * @throws CompilationException if function node lacks the state required to run the transform on it
+ */
+ final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException {
+ assert phases.contains(this);
+
+ return end(compiler, transform(compiler, phases, begin(compiler, functionNode)));
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java Mon May 19 15:29:42 2014 +0200
@@ -28,6 +28,8 @@
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
+import java.util.TreeSet;
+
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
@@ -36,7 +38,7 @@
*/
public final class CompileUnit implements Comparable<CompileUnit> {
/** Current class name */
- private final String className;
+ private String className;
/** Current class generator */
private ClassEmitter classEmitter;
@@ -67,24 +69,22 @@
@Override
public boolean equals(final Object obj) {
- if(obj == null || obj.getClass() != FunctionInitializer.class) {
+ if (obj == null || obj.getClass() != FunctionInitializer.class) {
return false;
}
final FunctionInitializer other = (FunctionInitializer)obj;
return data == other.data && functionNode == other.functionNode;
}
-
-
- }
-
- CompileUnit(final String className, final ClassEmitter classEmitter) {
- this(className, classEmitter, 0L);
}
CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) {
this.className = className;
+ this.weight = initialWeight;
this.classEmitter = classEmitter;
- this.weight = initialWeight;
+ }
+
+ static Set<CompileUnit> createCompileUnitSet() {
+ return new TreeSet<>();
}
/**
@@ -126,7 +126,7 @@
}
void initializeFunctionsCode() {
- for(final FunctionInitializer init: functionInitializers) {
+ for(final FunctionInitializer init : functionInitializers) {
init.initializeCode();
}
functionInitializers = Collections.emptySet();
@@ -173,13 +173,25 @@
return className;
}
- @Override
- public String toString() {
- return "[classname=" + className + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']';
+ /**
+ * Reset the class name for this compile unit
+ * @param className new class name
+ */
+ public void setUnitClassName(final String className) {
+ this.className = className;
+ }
+
+ private static String shortName(final String name) {
+ return name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1);
}
@Override
- public int compareTo(CompileUnit o) {
+ public String toString() {
+ return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']';
+ }
+
+ @Override
+ public int compareTo(final CompileUnit o) {
return className.compareTo(o.className);
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Mon May 19 15:29:42 2014 +0200
@@ -27,43 +27,39 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
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 static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import java.io.File;
-import java.lang.reflect.Field;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
-import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
-import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
-import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
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.Optimistic;
import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
+import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.Timing;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
@@ -85,10 +81,13 @@
/** Name of the objects package */
public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
- private Source source;
+ private final Source source;
+
+ private final String sourceName;
- private String sourceName;
- private String sourceURL;
+ private final String sourceURL;
+
+ private final boolean optimistic;
private final Map<String, byte[]> bytecode;
@@ -96,21 +95,184 @@
private final ConstantData constantData;
- private final CompilationEnvironment compilationEnv;
-
- private final ScriptEnvironment scriptEnv;
-
- private String scriptName;
-
private final CodeInstaller<ScriptEnvironment> installer;
/** logger for compiler, trampolines, splits and related code generation events
* that affect classes */
private final DebugLogger log;
+ private final Context context;
+
+ private final TypeMap types;
+
+ // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
+ // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
+ private final TypeEvaluator typeEvaluator;
+
+ private final boolean strict;
+
+ private final boolean onDemand;
+
+ /**
+ * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means
+ * that using whatever was at program point 17 as an int failed.
+ */
+ private final Map<Integer, Type> invalidatedProgramPoints;
+
+ /**
+ * Compile unit name of first compile unit - this prefix will be used for all
+ * classes that a compilation generates.
+ */
+ private final String firstCompileUnitName;
+
+ /**
+ * Contains the program point that should be used as the continuation entry point, as well as all previous
+ * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if
+ * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program
+ * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program
+ * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only
+ * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have
+ * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on.
+ */
+ private final int[] continuationEntryPoints;
+
+ /**
+ * ScriptFunction data for what is being compile, where applicable.
+ * TODO: make this immutable, propagate it through the CompilationPhases
+ */
+ private RecompilableScriptFunctionData compiledFunction;
+
private static boolean initialized = false;
/**
+ * Compilation phases that a compilation goes through
+ */
+ public static class CompilationPhases implements Iterable<CompilationPhase> {
+
+ /** Singleton that describes a standard eager compilation - this includes code installation */
+ public final static CompilationPhases COMPILE_ALL = new CompilationPhases(
+ "Compile all",
+ new CompilationPhase[] {
+ CompilationPhase.CONSTANT_FOLDING_PHASE,
+ CompilationPhase.LOWERING_PHASE,
+ CompilationPhase.PROGRAM_POINT_PHASE,
+ CompilationPhase.TRANSFORM_BUILTINS_PHASE,
+ CompilationPhase.SPLITTING_PHASE,
+ CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
+ CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
+ CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
+ CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
+ CompilationPhase.BYTECODE_GENERATION_PHASE,
+ CompilationPhase.INSTALL_PHASE
+ });
+
+ /** Compile all for a rest of method */
+ public final static CompilationPhases COMPILE_ALL_RESTOF =
+ COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE);
+
+ /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
+ public final static CompilationPhases COMPILE_ALL_NO_INSTALL =
+ COMPILE_ALL.
+ removeLast().
+ setDescription("Compile without install");
+
+ /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */
+ public final static CompilationPhases COMPILE_UPTO_BYTECODE =
+ COMPILE_ALL.
+ removeLast().
+ removeLast().
+ setDescription("Compile upto bytecode");
+
+ /**
+ * Singleton that describes back end of method generation, given that we have generated the normal
+ * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
+ */
+ public final static CompilationPhases COMPILE_FROM_BYTECODE = new CompilationPhases(
+ "Generate bytecode and install",
+ new CompilationPhase[] {
+ CompilationPhase.BYTECODE_GENERATION_PHASE,
+ CompilationPhase.INSTALL_PHASE
+ });
+
+ /**
+ * Singleton that describes restOf method generation, given that we have generated the normal
+ * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
+ */
+ public final static CompilationPhases COMPILE_FROM_BYTECODE_RESTOF =
+ COMPILE_FROM_BYTECODE.
+ addFirst(CompilationPhase.REUSE_COMPILE_UNITS_PHASE).
+ setDescription("Generate bytecode and install - RestOf method");
+
+ private final List<CompilationPhase> phases;
+
+ private final String desc;
+
+ private CompilationPhases(final String desc, final CompilationPhase... phases) {
+ this.desc = desc;
+
+ final List<CompilationPhase> newPhases = new LinkedList<>();
+ newPhases.addAll(Arrays.asList(phases));
+ this.phases = Collections.unmodifiableList(newPhases);
+ }
+
+ @Override
+ public String toString() {
+ return "'" + desc + "' " + phases.toString();
+ }
+
+ private CompilationPhases setDescription(final String desc) {
+ return new CompilationPhases(desc, phases.toArray(new CompilationPhase[phases.size()]));
+ }
+
+ private CompilationPhases removeLast() {
+ final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
+ list.removeLast();
+ return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+ }
+
+ private CompilationPhases addFirst(final CompilationPhase phase) {
+ if (phases.contains(phase)) {
+ return this;
+ }
+ final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
+ list.addFirst(phase);
+ return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+ }
+
+ private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
+ final LinkedList<CompilationPhase> list = new LinkedList<>();
+ for (final CompilationPhase p : phases) {
+ list.add(p);
+ if (p == phase) {
+ list.add(newPhase);
+ }
+ }
+ return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+ }
+
+ boolean contains(final CompilationPhase phase) {
+ return phases.contains(phase);
+ }
+
+ @Override
+ public Iterator<CompilationPhase> iterator() {
+ return phases.iterator();
+ }
+
+ boolean isRestOfCompilation() {
+ return this == COMPILE_ALL_RESTOF || this == COMPILE_FROM_BYTECODE_RESTOF;
+ }
+
+ String toString(final String prefix) {
+ final StringBuilder sb = new StringBuilder();
+ for (final CompilationPhase phase : phases) {
+ sb.append(prefix).append(phase).append('\n');
+ }
+ return sb.toString();
+ }
+ }
+
+ /**
* 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
@@ -125,29 +287,80 @@
ARGUMENTS.symbolName()
};
- private void initCompiler(final String className, final FunctionNode functionNode) {
- this.source = functionNode.getSource();
- this.sourceName = functionNode.getSourceName();
- this.sourceURL = functionNode.getSourceURL();
+ // per instance
+ private final int compilationId = COMPILATION_ID.getAndIncrement();
+
+ // per instance
+ private final AtomicInteger nextCompileUnitId = new AtomicInteger(0);
+
+ private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0);
- if (functionNode.isStrict()) {
- compilationEnv.setIsStrict(true);
- }
-
- final String name = className + '$' + safeSourceName(functionNode.getSource());
- final String uniqueName = functionNode.uniqueName(name);
-
- this.scriptName = uniqueName;
+ /**
+ * Constructor
+ *
+ * @param context context
+ * @param env script environment
+ * @param installer code installer
+ * @param source source to compile
+ * @param sourceURL source URL, or null if not present
+ * @param isStrict is this a strict compilation
+ */
+ public Compiler(
+ final Context context,
+ final ScriptEnvironment env,
+ final CodeInstaller<ScriptEnvironment> installer,
+ final Source source,
+ final String sourceURL,
+ final boolean isStrict) {
+ this(context, env, installer, source, sourceURL, isStrict, false, null, null, null, null, null);
}
- private Compiler(final CompilationEnvironment compilationEnv, final ScriptEnvironment scriptEnv, final CodeInstaller<ScriptEnvironment> installer) {
- this.scriptEnv = scriptEnv;
- this.compilationEnv = compilationEnv;
- this.installer = installer;
- this.constantData = new ConstantData();
- this.compileUnits = new TreeSet<>();
- this.bytecode = new LinkedHashMap<>();
- this.log = initLogger(compilationEnv.getContext());
+ /**
+ * Constructor
+ *
+ * @param context context
+ * @param env script environment
+ * @param installer code installer
+ * @param source source to compile
+ * @param sourceURL source URL, or null if not present
+ * @param isStrict is this a strict compilation
+ * @param isOnDemand is this an on demand compilation
+ * @param compiledFunction compiled function, if any
+ * @param types parameter and return value type information, if any is known
+ * @param invalidatedProgramPoints invalidated program points for recompilation
+ * @param continuationEntryPoints continuation entry points for restof method
+ * @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator}
+ */
+ public Compiler(
+ final Context context,
+ final ScriptEnvironment env,
+ final CodeInstaller<ScriptEnvironment> installer,
+ final Source source,
+ final String sourceURL,
+ final boolean isStrict,
+ final boolean isOnDemand,
+ final RecompilableScriptFunctionData compiledFunction,
+ final TypeMap types,
+ final Map<Integer, Type> invalidatedProgramPoints,
+ final int[] continuationEntryPoints,
+ final ScriptObject runtimeScope) {
+ this.context = context;
+ this.installer = installer;
+ this.constantData = new ConstantData();
+ this.compileUnits = CompileUnit.createCompileUnitSet();
+ this.bytecode = new LinkedHashMap<>();
+ this.log = initLogger(context);
+ this.source = source;
+ this.sourceURL = sourceURL;
+ this.sourceName = FunctionNode.getSourceName(source, sourceURL);
+ this.onDemand = isOnDemand;
+ this.compiledFunction = compiledFunction;
+ this.types = types;
+ this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
+ this.continuationEntryPoints = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
+ this.typeEvaluator = new TypeEvaluator(this, runtimeScope);
+ this.firstCompileUnitName = firstCompileUnitName(context.getEnv());
+ this.strict = isStrict;
if (!initialized) {
initialized = true;
@@ -155,24 +368,53 @@
log.warning("Running without optimistic types. This is a configuration that may be deprecated.");
}
}
+
+ this.optimistic = ScriptEnvironment.globalOptimistic();
+ }
+
+ private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, 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('-', '_');
+ if (!env._loader_per_compile) {
+ baseName = baseName + installer.getUniqueScriptId();
+ }
+
+ final String mangled = NameCodec.encode(baseName);
+ return mangled != null ? mangled : baseName;
}
- /**
- * Constructor - common entry point for generating code.
- * @param env compilation environment
- * @param installer code installer
- */
- public Compiler(final CompilationEnvironment env, final CodeInstaller<ScriptEnvironment> installer) {
- this(env, installer.getOwner(), installer);
+ private String firstCompileUnitName(final ScriptEnvironment env) {
+ final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE).
+ append('/').
+ append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()).
+ append('$');
+
+ if (isOnDemandCompilation()) {
+ sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX);
+ }
+
+ if (compilationId > 0) {
+ sb.append(compilationId).append('$');
+ }
+
+ sb.append(Compiler.safeSourceName(env, installer, source));
+
+ return sb.toString();
}
- /**
- * ScriptEnvironment constructor for compiler. Used only from Shell and --compile-only flag
- * No code installer supplied
- * @param scriptEnv script environment
- */
- public Compiler(final ScriptEnvironment scriptEnv) {
- this(new CompilationEnvironment(Context.getContext(), CompilationPhases.EAGER, scriptEnv._strict), scriptEnv, null);
+ void declareLocalSymbol(final String symbolName) {
+ typeEvaluator.declareLocalSymbol(symbolName);
+ }
+
+ void setData(final RecompilableScriptFunctionData data) {
+ assert this.compiledFunction == null : data;
+ this.compiledFunction = data;
}
@Override
@@ -181,11 +423,268 @@
}
@Override
- public DebugLogger initLogger(final Context context) {
- return context.getLogger(this.getClass());
+ public DebugLogger initLogger(final Context ctxt) {
+ return ctxt.getLogger(this.getClass());
+ }
+
+ boolean isOnDemandCompilation() {
+ return onDemand;
+ }
+
+ boolean useOptimisticTypes() {
+ return optimistic;
+ }
+
+ Context getContext() {
+ return context;
+ }
+
+ Type getOptimisticType(final Optimistic node) {
+ return typeEvaluator.getOptimisticType(node);
+ }
+
+ void addInvalidatedProgramPoint(final int programPoint, final Type type) {
+ invalidatedProgramPoints.put(programPoint, type);
+ }
+
+ TypeMap getTypeMap() {
+ return types;
+ }
+
+ MethodType getCallSiteType(final FunctionNode fn) {
+ if (types == null || !isOnDemandCompilation()) {
+ return null;
+ }
+ return types.getCallSiteType(fn);
+ }
+
+ Type getParamType(final FunctionNode fn, final int pos) {
+ return types == null ? null : types.get(fn, pos);
+ }
+
+ /**
+ * Do a compilation job
+ *
+ * @param functionNode function node to compile
+ * @param phases phases of compilation transforms to apply to function
+
+ * @return transformed function
+ *
+ * @throws CompilationException if error occurs during compilation
+ */
+ public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException {
+
+ log.info("Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", phases);
+ log.indent();
+
+ final String name = DebugLogger.quote(functionNode.getName());
+
+ FunctionNode newFunctionNode = functionNode;
+
+ for (final String reservedName : RESERVED_NAMES) {
+ newFunctionNode.uniqueName(reservedName);
+ }
+
+ final boolean fine = log.levelFinerThanOrEqual(Level.FINE);
+ final boolean info = log.levelFinerThanOrEqual(Level.INFO);
+
+ long time = 0L;
+
+ for (final CompilationPhase phase : phases) {
+ if (fine) {
+ log.fine("Phase ", phase.toString(), " starting for ", name);
+ }
+
+ newFunctionNode = phase.apply(this, phases, newFunctionNode);
+
+ if (getEnv()._print_mem_usage) {
+ printMemoryUsage(functionNode, phase.toString());
+ }
+
+ final long duration = Timing.isEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L;
+ time += duration;
+
+ if (fine) {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append("Phase ").
+ append(phase.toString()).
+ append(" done for function ").
+ append(name);
+
+ if (duration > 0L) {
+ sb.append(" in ").
+ append(duration).
+ append(" ms ");
+ }
+
+ log.fine(sb);
+ }
+ }
+
+ log.unindent();
+
+ if (info) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Compile job for ").
+ append(newFunctionNode.getSource()).
+ append(':').
+ append(DebugLogger.quote(newFunctionNode.getName())).
+ append(" finished");
+
+ if (time > 0L) {
+ sb.append(" in ").
+ append(time).
+ append(" ms");
+ }
+
+ log.info(sb);
+ }
+
+ return newFunctionNode;
+ }
+
+ Source getSource() {
+ return source;
+ }
+
+ Map<String, byte[]> getBytecode() {
+ return Collections.unmodifiableMap(bytecode);
}
- private void printMemoryUsage(final String phaseName, final FunctionNode functionNode) {
+ byte[] getBytecode(final String className) {
+ return bytecode.get(className);
+ }
+
+ CompileUnit getFirstCompileUnit() {
+ assert !compileUnits.isEmpty();
+ return compileUnits.iterator().next();
+ }
+
+ Set<CompileUnit> getCompileUnits() {
+ return compileUnits;
+ }
+
+ ConstantData getConstantData() {
+ return constantData;
+ }
+
+ CodeInstaller<ScriptEnvironment> getCodeInstaller() {
+ return installer;
+ }
+
+ void addClass(final String name, final byte[] code) {
+ bytecode.put(name, code);
+ }
+
+ void removeClass(final String name) {
+ assert bytecode.get(name) != null;
+ bytecode.remove(name);
+ }
+
+ ScriptEnvironment getEnv() {
+ return context.getEnv();
+ }
+
+ String getSourceURL() {
+ return sourceURL;
+ }
+
+ String nextCompileUnitName() {
+ final StringBuilder sb = new StringBuilder(firstCompileUnitName);
+ final int cuid = nextCompileUnitId.getAndIncrement();
+ if (cuid > 0) {
+ sb.append("$cu").append(cuid);
+ }
+
+ return sb.toString();
+ }
+
+ void clearCompileUnits() {
+ compileUnits.clear();
+ }
+
+ CompileUnit addCompileUnit(final long initialWeight) {
+ final CompileUnit compileUnit = createCompileUnit(initialWeight);
+ compileUnits.add(compileUnit);
+ log.fine("Added compile unit ", compileUnit);
+ return compileUnit;
+ }
+
+ CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) {
+ final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict());
+ 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;
+ }
+
+ private CompileUnit createCompileUnit(final long initialWeight) {
+ return createCompileUnit(nextCompileUnitName(), initialWeight);
+ }
+
+ boolean isStrict() {
+ return strict;
+ }
+
+ void replaceCompileUnits(final Set<CompileUnit> newUnits) {
+ compileUnits.clear();
+ compileUnits.addAll(newUnits);
+ }
+
+ 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('/', '.');
+ }
+
+ RecompilableScriptFunctionData getProgram() {
+ if (compiledFunction == null) {
+ return null;
+ }
+ return compiledFunction.getProgram();
+ }
+
+ RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
+ return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
+ }
+
+ boolean isGlobalSymbol(final FunctionNode fn, final String name) {
+ return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name);
+ }
+
+ int[] getContinuationEntryPoints() {
+ return continuationEntryPoints;
+ }
+
+ Type getInvalidatedProgramPointType(final int programPoint) {
+ return invalidatedProgramPoints.get(programPoint);
+ }
+
+ private void printMemoryUsage(final FunctionNode functionNode, final String phaseName) {
if (!log.isEnabled()) {
return;
}
@@ -227,304 +726,4 @@
}
}
}
-
- CompilationEnvironment getCompilationEnvironment() {
- return compilationEnv;
- }
-
- /**
- * Execute the compilation this Compiler was created with with default class name
- * @param functionNode function node to compile from its current state
- * @throws CompilationException if something goes wrong
- * @return function node that results from code transforms
- */
- public FunctionNode compile(final FunctionNode functionNode) throws CompilationException {
- return compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
- }
-
- /**
- * Execute the compilation this Compiler was created with
- * @param className class name for the compile
- * @param functionNode function node to compile from its current state
- * @throws CompilationException if something goes wrong
- * @return function node that results from code transforms
- */
- public FunctionNode compile(final String className, final FunctionNode functionNode) throws CompilationException {
- try {
- return compileInternal(className, functionNode);
- } catch (final AssertionError e) {
- throw new AssertionError("Assertion failure compiling " + functionNode.getSource(), e);
- }
- }
-
- private FunctionNode compileInternal(final String className, final FunctionNode functionNode) throws CompilationException {
- FunctionNode newFunctionNode = functionNode;
-
- initCompiler(className, newFunctionNode); //TODO move this state into functionnode?
-
- for (final String reservedName : RESERVED_NAMES) {
- newFunctionNode.uniqueName(reservedName);
- }
-
- final boolean fine = log.levelFinerThanOrEqual(Level.FINE);
- final boolean info = log.levelFinerThanOrEqual(Level.INFO);
-
- long time = 0L;
-
- for (final CompilationPhase phase : compilationEnv.getPhases()) {
- newFunctionNode = phase.apply(this, newFunctionNode);
-
- if (scriptEnv._print_mem_usage) {
- printMemoryUsage(phase.toString(), newFunctionNode);
- }
-
- final long duration = Timing.isEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L;
- time += duration;
-
- if (fine) {
- final StringBuilder sb = new StringBuilder();
-
- sb.append(phase.toString()).
- append(" done for function '").
- append(newFunctionNode.getName()).
- append('\'');
-
- if (duration > 0L) {
- sb.append(" in ").
- append(duration).
- append(" ms ");
- }
-
- log.fine(sb);
- }
- }
-
- if (info) {
- final StringBuilder sb = new StringBuilder();
- sb.append("Compile job for '").
- append(newFunctionNode.getSource()).
- append(':').
- append(newFunctionNode.getName()).
- append("' finished");
-
- if (time > 0L) {
- sb.append(" in ").
- append(time).
- append(" ms");
- }
-
- log.info(sb);
- }
-
- return newFunctionNode;
- }
-
- private Class<?> install(final String className, final byte[] code) {
- log.fine("Installing class ", className);
-
- final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
-
- try {
- final Object[] constants = getConstantData().toArray();
- // Need doPrivileged because these fields are private
- AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
- @Override
- public Void run() throws Exception {
- //use reflection to write source and constants table to installed classes
- final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
- final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
- sourceField.setAccessible(true);
- constantsField.setAccessible(true);
- sourceField.set(null, source);
- constantsField.set(null, constants);
- return null;
- }
- });
- } catch (final PrivilegedActionException e) {
- throw new RuntimeException(e);
- }
-
- return clazz;
- }
-
- /**
- * Install compiled classes into a given loader
- * @param functionNode function node to install - must be in {@link CompilationState#EMITTED} state
- * @return root script class - if there are several compile units they will also be installed
- */
- public Class<?> install(final FunctionNode functionNode) {
- final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
-
- assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has unexpected compilation state";
-
- final Map<String, Class<?>> installedClasses = new HashMap<>();
-
- final String rootClassName = firstCompileUnitName();
- final byte[] rootByteCode = bytecode.get(rootClassName);
- final Class<?> rootClass = install(rootClassName, rootByteCode);
-
- int length = rootByteCode.length;
-
- installedClasses.put(rootClassName, rootClass);
-
- for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
- final String className = entry.getKey();
- if (className.equals(rootClassName)) {
- continue;
- }
- final byte[] code = entry.getValue();
- length += code.length;
-
- installedClasses.put(className, install(className, code));
- }
-
- final Map<RecompilableScriptFunctionData, RecompilableScriptFunctionData> rfns = new IdentityHashMap<>();
- for(final Object constant: getConstantData().constants) {
- if(constant instanceof RecompilableScriptFunctionData) {
- final RecompilableScriptFunctionData rfn = (RecompilableScriptFunctionData)constant;
- rfns.put(rfn, rfn);
- }
- }
-
- for (final CompileUnit unit : compileUnits) {
- unit.setCode(installedClasses.get(unit.getUnitClassName()));
- unit.initializeFunctionsCode();
- }
-
- final StringBuilder sb;
- if (log.isEnabled()) {
- sb = new StringBuilder();
- sb.append("Installed class '").
- append(rootClass.getSimpleName()).
- append('\'').
- append(" bytes=").
- append(length).
- append('.');
- if (bytecode.size() > 1) {
- sb.append(' ').append(bytecode.size()).append(" compile units.");
- }
- } else {
- sb = null;
- }
-
- if (Timing.isEnabled()) {
- final long duration = System.currentTimeMillis() - t0;
- Timing.accumulateTime("[Code Installation]", duration);
- if (sb != null) {
- sb.append(" Install time: ").append(duration).append(" ms");
- }
- }
-
- if (sb != null) {
- log.fine(sb);
- }
-
- return rootClass;
- }
-
- Set<CompileUnit> getCompileUnits() {
- return compileUnits;
- }
-
- ConstantData getConstantData() {
- return constantData;
- }
-
- CodeInstaller<ScriptEnvironment> getCodeInstaller() {
- return installer;
- }
-
- void addClass(final String name, final byte[] code) {
- bytecode.put(name, code);
- }
-
- ScriptEnvironment getEnv() {
- return this.scriptEnv;
- }
-
- String getSourceURL() {
- return sourceURL;
- }
-
- private String safeSourceName(final Source src) {
- String baseName = new File(src.getName()).getName();
-
- final int index = baseName.lastIndexOf(".js");
- if (index != -1) {
- baseName = baseName.substring(0, index);
- }
-
- baseName = baseName.replace('.', '_').replace('-', '_');
- if (!scriptEnv._loader_per_compile) {
- baseName = baseName + installer.getUniqueScriptId();
- }
-
- 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);
- }
-
- private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
- final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
- compileUnits.add(compileUnit);
- log.fine("Added compile unit ", compileUnit);
- return compileUnit;
- }
-
- private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
- final ClassEmitter classEmitter = new ClassEmitter(compilationEnv.getContext(), sourceName, unitClassName, compilationEnv.isStrict());
- 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('/', '.');
- }
-
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java Mon May 19 15:29:42 2014 +0200
@@ -27,9 +27,12 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
@@ -37,7 +40,7 @@
* Manages constants needed by code generation. Objects are maintained in an
* interning maps to remove duplicates.
*/
-class ConstantData {
+final class ConstantData {
/** Constant table. */
final List<Object> constants;
@@ -206,6 +209,10 @@
return index;
}
+ Collection<Object> getConstants() {
+ return Collections.unmodifiableList(constants);
+ }
+
Object[] toArray() {
return constants.toArray();
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Mon May 19 15:29:42 2014 +0200
@@ -60,7 +60,6 @@
final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Loggable {
private final Compiler compiler;
- private final CompilationEnvironment env;
private final Map<Integer, Map<Integer, RecompilableScriptFunctionData>> fnIdToNestedFunctions = new HashMap<>();
private final Map<Integer, Map<String, Integer>> externalSymbolDepths = new HashMap<>();
private final Map<Integer, Set<String>> internalSymbols = new HashMap<>();
@@ -73,8 +72,7 @@
FindScopeDepths(final Compiler compiler) {
super(new LexicalContext());
this.compiler = compiler;
- this.env = compiler.getCompilationEnvironment();
- this.log = initLogger(compiler.getCompilationEnvironment().getContext());
+ this.log = initLogger(compiler.getContext());
}
@Override
@@ -165,7 +163,7 @@
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
- if (env.isOnDemandCompilation()) {
+ if (compiler.isOnDemandCompilation()) {
return true;
}
@@ -189,8 +187,8 @@
final String name = functionNode.getName();
FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.SCOPE_DEPTHS_COMPUTED);
- if (env.isOnDemandCompilation()) {
- final RecompilableScriptFunctionData data = env.getScriptFunctionData(newFunctionNode.getId());
+ if (compiler.isOnDemandCompilation()) {
+ final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(newFunctionNode.getId());
assert data != null : newFunctionNode.getName() + " lacks data";
if (data.inDynamicContext()) {
log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope.");
@@ -214,7 +212,7 @@
final String allocatorClassName = Compiler.binaryName(getClassName(fieldCount));
final PropertyMap allocatorMap = PropertyMap.newMap(null, 0, fieldCount, 0);
final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData(
- compiler.getCompilationEnvironment().getContext(),
+ compiler.getContext(),
newFunctionNode,
compiler.getCodeInstaller(),
allocatorClassName,
@@ -231,7 +229,7 @@
fnIdToNestedFunctions.get(parentFn.getId()).put(fnId, data);
}
} else {
- env.setData(data);
+ compiler.setData(data);
}
if (isDynamicScopeBoundary(functionNode)) {
@@ -269,7 +267,7 @@
@Override
public boolean enterBlock(final Block block) {
- if (env.isOnDemandCompilation()) {
+ if (compiler.isOnDemandCompilation()) {
return true;
}
@@ -290,7 +288,7 @@
block.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public final boolean enterDefault(final Node node) {
- if (!env.isOnDemandCompilation()) {
+ if (!compiler.isOnDemandCompilation()) {
if (node instanceof IdentNode) {
final Symbol symbol = ((IdentNode)node).getSymbol();
if (symbol != null && symbol.isScope()) {
@@ -351,7 +349,7 @@
@Override
public Node leaveBlock(final Block block) {
- if (env.isOnDemandCompilation()) {
+ if (compiler.isOnDemandCompilation()) {
return block;
}
if (isDynamicScopeBoundary(block)) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Mon May 19 15:29:42 2014 +0200
@@ -60,9 +60,9 @@
private final DebugLogger log;
- FoldConstants(final CompilationEnvironment env) {
+ FoldConstants(final Compiler compiler) {
super(new LexicalContext());
- this.log = initLogger(env.getContext());
+ this.log = initLogger(compiler.getContext());
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/codegen/Label.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Label.java Mon May 19 15:29:42 2014 +0200
@@ -30,6 +30,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
+
import jdk.nashorn.internal.codegen.types.Type;
/**
@@ -93,7 +94,7 @@
}
Type peek(final int n) {
- int pos = sp - 1 - n;
+ final int pos = sp - 1 - n;
return pos < 0 ? null : data[pos];
}
@@ -168,6 +169,7 @@
private void mergeVariableTypes(final Stack joinOrigin, final int toSlot) {
final ListIterator<Type> it1 = localVariableTypes.listIterator();
final Iterator<Type> it2 = joinOrigin.localVariableTypes.iterator();
+
for(int i = 0; i < toSlot; ++i) {
final Type thisType = it1.next();
final Type otherType = it2.next();
@@ -194,11 +196,13 @@
mergeVariableTypes(joinOrigin, firstTemp);
}
- private int getFirstDeadLocal(List<Type> types) {
+ private int getFirstDeadLocal(final List<Type> types) {
int i = types.size();
for(final ListIterator<Type> it = types.listIterator(i);
it.hasPrevious() && it.previous() == Type.UNKNOWN;
- --i); // no body
+ --i) {
+ // no body
+ }
// Respect symbol boundaries; we never chop off half a symbol's storage
while(!symbolBoundary.get(i - 1)) {
@@ -253,7 +257,7 @@
* @return a list of widest local variable slot types.
*/
List<Type> getWidestLiveLocals(final List<Type> lvarTypes) {
- List<Type> widestLiveLocals = new ArrayList<>(lvarTypes);
+ final List<Type> widestLiveLocals = new ArrayList<>(lvarTypes);
boolean keepNextValue = true;
final int size = widestLiveLocals.size();
for(int i = size - 1; i-- > 0;) {
@@ -523,7 +527,6 @@
this.id = label.id;
}
-
jdk.internal.org.objectweb.asm.Label getLabel() {
if (this.label == null) {
this.label = new jdk.internal.org.objectweb.asm.Label();
--- a/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Mon May 19 15:29:42 2014 +0200
@@ -106,7 +106,7 @@
}
private static class JumpTarget {
- private List<JumpOrigin> origins = new LinkedList<>();
+ private final List<JumpOrigin> origins = new LinkedList<>();
private Map<Symbol, LvarType> types = Collections.emptyMap();
void addOrigin(final JoinPredecessor originNode, final Map<Symbol, LvarType> originTypes) {
@@ -143,7 +143,7 @@
private LocalVariableConversion createConversion(final Symbol symbol, final LvarType branchLvarType,
final Map<Symbol, LvarType> joinLvarTypes, final LocalVariableConversion next) {
- LvarType targetType = joinLvarTypes.get(symbol);
+ final LvarType targetType = joinLvarTypes.get(symbol);
assert targetType != null;
if(targetType == branchLvarType) {
return next;
@@ -193,7 +193,8 @@
union = cloneMap(types2);
}
}
- if(!(matches1 || matches2)) {
+ if(!(matches1 || matches2) && union != null) { //remove overly enthusiastic "union can be null" warning
+ assert union != null;
union.put(symbol, widest);
}
}
@@ -344,7 +345,7 @@
// Int64 type anyway, so this loss of precision is actually more conformant to the specification...
return LvarType.values()[Math.max(t1.ordinal(), t2.ordinal())];
}
- private final CompilationEnvironment env;
+ private final Compiler compiler;
private final Map<Label, JumpTarget> jumpTargets = new IdentityHashMap<>();
// Local variable type mapping at the currently evaluated point. No map instance is ever modified; setLvarType() always
// allocates a new map. Immutability of maps allows for cheap snapshots by just keeping the reference to the current
@@ -378,9 +379,9 @@
// variables).
private final Deque<Label> catchLabels = new ArrayDeque<>();
- LocalVariableTypesCalculator(final CompilationEnvironment env) {
+ LocalVariableTypesCalculator(final Compiler compiler) {
super(new LexicalContext());
- this.env = env;
+ this.compiler = compiler;
}
private JumpTarget createJumpTarget(final Label label) {
@@ -449,7 +450,7 @@
@Override
public boolean enterBlock(final Block block) {
- for(Symbol symbol: block.getSymbols()) {
+ for(final Symbol symbol: block.getSymbols()) {
if(symbol.isBytecodeLocal() && getLocalVariableTypeOrNull(symbol) == null) {
setType(symbol, LvarType.UNDEFINED);
}
@@ -569,7 +570,7 @@
// Parameter is not necessarily bytecode local as it can be scoped due to nested context use, but it
// must have a slot if we aren't in a function with vararg signature.
assert symbol.hasSlot();
- final Type callSiteParamType = env.getParamType(functionNode, pos);
+ final Type callSiteParamType = compiler.getParamType(functionNode, pos);
final LvarType paramType = callSiteParamType == null ? LvarType.OBJECT : toLvarType(callSiteParamType);
setType(symbol, paramType);
// Make sure parameter slot for its incoming value is not marked dead. NOTE: this is a heuristic. Right
@@ -1079,7 +1080,7 @@
// If the function is split, the ":return" symbol is used and needs a slot. Note we can't mark the return
// symbol as used in enterSplitNode, as we don't know the final return type of the function earlier than
// here.
- Symbol retSymbol = getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN);
+ final Symbol retSymbol = getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN);
retSymbol.setHasSlotFor(returnType);
retSymbol.setNeedsSlot(true);
}
@@ -1100,10 +1101,10 @@
FunctionNode newFunction = functionNode;
final NodeVisitor<LexicalContext> applyChangesVisitor = new NodeVisitor<LexicalContext>(new LexicalContext()) {
private boolean inOuterFunction = true;
- private Deque<JoinPredecessor> joinPredecessors = new ArrayDeque<>();
+ private final Deque<JoinPredecessor> joinPredecessors = new ArrayDeque<>();
@Override
- protected boolean enterDefault(Node node) {
+ protected boolean enterDefault(final Node node) {
if(!inOuterFunction) {
return false;
}
@@ -1115,7 +1116,7 @@
@Override
public boolean enterFunctionNode(final FunctionNode fn) {
- if(env.isOnDemandCompilation()) {
+ if(compiler.isOnDemandCompilation()) {
// Only calculate nested function local variable types if we're doing eager compilation
return false;
}
@@ -1125,7 +1126,7 @@
@SuppressWarnings("fallthrough")
@Override
- public Node leaveBinaryNode(BinaryNode binaryNode) {
+ public Node leaveBinaryNode(final BinaryNode binaryNode) {
if(binaryNode.isComparison()) {
final Expression lhs = binaryNode.lhs();
final Expression rhs = binaryNode.rhs();
@@ -1173,16 +1174,16 @@
}
@Override
- public Node leaveFunctionNode(FunctionNode nestedFunctionNode) {
+ public Node leaveFunctionNode(final FunctionNode nestedFunctionNode) {
inOuterFunction = true;
final FunctionNode newNestedFunction = (FunctionNode)nestedFunctionNode.accept(
- new LocalVariableTypesCalculator(env));
+ new LocalVariableTypesCalculator(compiler));
lc.replace(nestedFunctionNode, newNestedFunction);
return newNestedFunction;
}
@Override
- public Node leaveIdentNode(IdentNode identNode) {
+ public Node leaveIdentNode(final IdentNode identNode) {
final IdentNode original = (IdentNode)joinPredecessors.pop();
final Symbol symbol = identNode.getSymbol();
if(symbol == null) {
@@ -1205,7 +1206,7 @@
}
@Override
- public Node leaveLiteralNode(LiteralNode<?> literalNode) {
+ public Node leaveLiteralNode(final LiteralNode<?> literalNode) {
if(literalNode instanceof ArrayLiteralNode) {
((ArrayLiteralNode)literalNode).analyze();
}
@@ -1370,9 +1371,9 @@
final Symbol symbol = ((IdentNode)node).getSymbol();
conversion = createConversion(symbol, branchLvarTypes.get(symbol), joinLvarTypes, null);
} else {
- for(Map.Entry<Symbol, LvarType> entry: branchLvarTypes.entrySet()) {
+ for(final Map.Entry<Symbol, LvarType> entry: branchLvarTypes.entrySet()) {
final Symbol symbol = entry.getKey();
- LvarType branchLvarType = entry.getValue();
+ final LvarType branchLvarType = entry.getValue();
conversion = createConversion(symbol, branchLvarType, joinLvarTypes, conversion);
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Mon May 19 15:29:42 2014 +0200
@@ -146,7 +146,7 @@
});
this.installer = compiler.getCodeInstaller();
- this.log = initLogger(compiler.getCompilationEnvironment().getContext());
+ this.log = initLogger(compiler.getContext());
}
@Override
@@ -268,12 +268,12 @@
}
@Override
- public Node leaveIN(BinaryNode binaryNode) {
+ public Node leaveIN(final BinaryNode binaryNode) {
return new RuntimeNode(binaryNode);
}
@Override
- public Node leaveINSTANCEOF(BinaryNode binaryNode) {
+ public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
return new RuntimeNode(binaryNode);
}
@@ -289,7 +289,7 @@
}
@Override
- public Node leaveCaseNode(CaseNode caseNode) {
+ public Node leaveCaseNode(final CaseNode caseNode) {
// Try to represent the case test as an integer
final Node test = caseNode.getTest();
if (test instanceof LiteralNode) {
@@ -526,7 +526,7 @@
return spliceFinally(newTryNode, rethrows, finallyBody);
}
- private TryNode ensureUnconditionalCatch(TryNode tryNode) {
+ private TryNode ensureUnconditionalCatch(final TryNode tryNode) {
final List<CatchNode> catches = tryNode.getCatches();
if(catches == null || catches.isEmpty() || catches.get(catches.size() - 1).getExceptionCondition() == null) {
return tryNode;
--- a/nashorn/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Mon May 19 15:29:42 2014 +0200
@@ -66,16 +66,16 @@
*/
final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
- final CompilationEnvironment env;
+ final Compiler compiler;
// Per-function bit set of program points that must never be optimistic.
final Deque<BitSet> neverOptimistic = new ArrayDeque<>();
// Per-function depth of split nodes
final IntDeque splitDepth = new IntDeque();
- OptimisticTypesCalculator(final CompilationEnvironment env) {
+ OptimisticTypesCalculator(final Compiler compiler) {
super(new LexicalContext());
- this.env = env;
+ this.compiler = compiler;
}
@Override
@@ -85,7 +85,7 @@
}
@Override
- public boolean enterPropertyNode(PropertyNode propertyNode) {
+ public boolean enterPropertyNode(final PropertyNode propertyNode) {
if(propertyNode.getKeyName().equals(ScriptObject.PROTO_PROPERTY_NAME)) {
tagNeverOptimistic(propertyNode.getValue());
}
@@ -149,7 +149,7 @@
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
- if (!neverOptimistic.isEmpty() && env.isOnDemandCompilation()) {
+ if (!neverOptimistic.isEmpty() && compiler.isOnDemandCompilation()) {
// This is a nested function, and we're doing on-demand compilation. In these compilations, we never descend
// into nested functions.
return false;
@@ -190,13 +190,13 @@
}
@Override
- public boolean enterSplitNode(SplitNode splitNode) {
+ public boolean enterSplitNode(final SplitNode splitNode) {
splitDepth.getAndIncrement();
return true;
}
@Override
- public Node leaveSplitNode(SplitNode splitNode) {
+ public Node leaveSplitNode(final SplitNode splitNode) {
final int depth = splitDepth.decrementAndGet();
assert depth >= 0;
return splitNode;
@@ -257,7 +257,7 @@
private Expression leaveOptimistic(final Optimistic opt) {
final int pp = opt.getProgramPoint();
if(isValid(pp) && !inSplitNode() && !neverOptimistic.peek().get(pp)) {
- return (Expression)opt.setType(env.getOptimisticType(opt));
+ return (Expression)opt.setType(compiler.getOptimisticType(opt));
}
return (Expression)opt;
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/ParamTypeMap.java Thu May 15 15:28:51 2014 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2010-2014, 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;
-
-import java.lang.invoke.MethodType;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.FunctionNode;
-
-/**
- * A data structure that maps one or several function nodes (by their unique id:s, not by
- * the FunctionNode object itself, due to copy on write changing it several times through
- * code generation.
- */
-public class ParamTypeMap {
- final Map<Integer, Type[]> map = new HashMap<>();
-
- /**
- * Constructor
- * @param functionNode functionNode
- * @param type method type found at runtime corresponding to parameter guess
- */
- public ParamTypeMap(final FunctionNode functionNode, final MethodType type) {
- this(functionNode.getId(), type);
- }
-
- /**
- * Constructor
- * @param functionNodeId function node id
- * @param type method type found at runtime corresponding to parameter guess
- */
- public ParamTypeMap(final int functionNodeId, final MethodType type) {
- final Type[] types = new Type[type.parameterCount()];
- int pos = 0;
- for (final Class<?> p : type.parameterArray()) {
- types[pos++] = Type.typeFor(p);
- }
- map.put(functionNodeId, types);
- }
-
- ParamTypeMap(final Map<FunctionNode, Type[]> typeMap) {
- for (final Map.Entry<FunctionNode, Type[]> entry : typeMap.entrySet()) {
- map.put(entry.getKey().getId(), entry.getValue());
- }
- }
- /**
- * Get the parameter type for this parameter position, or
- * null if now known
- * @param functionNode functionNode
- * @param pos position
- * @return parameter type for this callsite if known
- */
- Type get(final FunctionNode functionNode, final int pos) {
- final Type[] types = map.get(functionNode.getId());
- assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this;
- if (types != null && pos < types.length) {
- return types[pos];
- }
- return null;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
- sb.append("\n[ParamTypeMap]\n");
- if (map.isEmpty()) {
- sb.append("\t{}");
- } else {
- for (final Map.Entry<Integer, Type[]> entry : map.entrySet()) {
- sb.append('\t').append(entry.getKey() + "=>" + ((entry.getValue() == null) ? "[]" : Arrays.toString(entry.getValue()))).append('\n');
- }
- }
- return sb.toString();
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Mon May 19 15:29:42 2014 +0200
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2010, 2014, 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;
+
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.IndexNode;
+import jdk.nashorn.internal.ir.Optimistic;
+import jdk.nashorn.internal.objects.NativeArray;
+import jdk.nashorn.internal.runtime.FindProperty;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+
+/**
+ * Functionality for using a runtime scope to look up value types.
+ * Used during recompilation.
+ */
+final class TypeEvaluator {
+ final Compiler compiler;
+ final ScriptObject runtimeScope;
+
+ TypeEvaluator(final Compiler compiler, final ScriptObject runtimeScope) {
+ this.compiler = compiler;
+ this.runtimeScope = runtimeScope;
+ }
+
+ Type getOptimisticType(final Optimistic node) {
+ assert compiler.useOptimisticTypes();
+
+ final int programPoint = node.getProgramPoint();
+ final Type validType = compiler.getInvalidatedProgramPointType(programPoint);
+
+ if (validType != null) {
+ return validType;
+ }
+
+ final Type mostOptimisticType = node.getMostOptimisticType();
+ final Type evaluatedType = getEvaluatedType(node);
+
+ if (evaluatedType != null) {
+ if (evaluatedType.widerThan(mostOptimisticType)) {
+ final Type newValidType = evaluatedType.isObject() || evaluatedType.isBoolean() ? Type.OBJECT : evaluatedType;
+ // Update invalidatedProgramPoints so we don't re-evaluate the expression next time. This is a heuristic
+ // as we're doing a tradeoff. Re-evaluating expressions on each recompile takes time, but it might
+ // notice a widening in the type of the expression and thus prevent an unnecessary deoptimization later.
+ // We'll presume though that the types of expressions are mostly stable, so if we evaluated it in one
+ // compilation, we'll keep to that and risk a low-probability deoptimization if its type gets widened
+ // in the future.
+ compiler.addInvalidatedProgramPoint(node.getProgramPoint(), newValidType);
+ }
+ return evaluatedType;
+ }
+ return mostOptimisticType;
+ }
+
+ private static Type getPropertyType(final ScriptObject sobj, final String name) {
+ final FindProperty find = sobj.findProperty(name, true);
+ if (find == null) {
+ return null;
+ }
+
+ final Property property = find.getProperty();
+ final Class<?> propertyClass = property.getCurrentType();
+ if (propertyClass == null) {
+ // propertyClass == null means its value is Undefined. It is probably not initialized yet, so we won't make
+ // a type assumption yet.
+ return null;
+ } else if (propertyClass.isPrimitive()) {
+ return Type.typeFor(propertyClass);
+ }
+
+ final ScriptObject owner = find.getOwner();
+ if (property.hasGetterFunction(owner)) {
+ // Can have side effects, so we can't safely evaluate it; since !propertyClass.isPrimitive(), it's Object.
+ return Type.OBJECT;
+ }
+
+ // Safely evaluate the property, and return the narrowest type for the actual value (e.g. Type.INT for a boxed
+ // integer).
+ final Object value = property.getObjectValue(owner, owner);
+ if (value == ScriptRuntime.UNDEFINED) {
+ return null;
+ }
+ return Type.typeFor(JSType.unboxedFieldType(value));
+ }
+
+ void declareLocalSymbol(final String symbolName) {
+ assert
+ compiler.useOptimisticTypes() &&
+ compiler.isOnDemandCompilation() &&
+ runtimeScope != null :
+ "useOptimistic=" +
+ compiler.useOptimisticTypes() +
+ " isOnDemand=" +
+ compiler.isOnDemandCompilation() +
+ " scope="+runtimeScope;
+
+ if (runtimeScope.findProperty(symbolName, false) == null) {
+ runtimeScope.set(symbolName, ScriptRuntime.UNDEFINED, true);
+ }
+ }
+
+ private Object evaluateSafely(final Expression expr) {
+ if (expr instanceof IdentNode) {
+ return runtimeScope == null ? null : evaluatePropertySafely(runtimeScope, ((IdentNode)expr).getName());
+ }
+
+ if (expr instanceof AccessNode) {
+ final AccessNode accessNode = (AccessNode)expr;
+ final Object base = evaluateSafely(accessNode.getBase());
+ if (!(base instanceof ScriptObject)) {
+ return null;
+ }
+ return evaluatePropertySafely((ScriptObject)base, accessNode.getProperty());
+ }
+
+ return null;
+ }
+
+ private static Object evaluatePropertySafely(final ScriptObject sobj, final String name) {
+ final FindProperty find = sobj.findProperty(name, true);
+ if (find == null) {
+ return null;
+ }
+ final Property property = find.getProperty();
+ final ScriptObject owner = find.getOwner();
+ if (property.hasGetterFunction(owner)) {
+ // Possible side effects; can't evaluate safely
+ return null;
+ }
+ return property.getObjectValue(owner, owner);
+ }
+
+
+ private Type getEvaluatedType(final Optimistic expr) {
+ if (expr instanceof IdentNode) {
+ if (runtimeScope == null) {
+ return null;
+ }
+ return getPropertyType(runtimeScope, ((IdentNode)expr).getName());
+ }
+
+ if (expr instanceof AccessNode) {
+ final AccessNode accessNode = (AccessNode)expr;
+ final Object base = evaluateSafely(accessNode.getBase());
+ if (!(base instanceof ScriptObject)) {
+ return null;
+ }
+ return getPropertyType((ScriptObject)base, accessNode.getProperty());
+ }
+
+ if (expr instanceof IndexNode) {
+ final IndexNode indexNode = (IndexNode)expr;
+ final Object base = evaluateSafely(indexNode.getBase());
+ if(!(base instanceof NativeArray)) {
+ // We only know how to deal with NativeArray. TODO: maybe manage buffers too
+ return null;
+ }
+ // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying
+ // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every
+ // optimistic int linkage attempt, even if the long value being returned in the first invocation would be
+ // representable as int. That way, we can presume that the array's optimistic type is the most optimistic
+ // type for which an element getter has a chance of executing successfully.
+ return ((NativeArray)base).getArray().getOptimisticType();
+ }
+
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/TypeMap.java Mon May 19 15:29:42 2014 +0200
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2010-2014, 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;
+
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.runtime.ScriptFunction;
+
+/**
+ * A data structure that maps one or several function nodes (by their unique id:s, not by
+ * the FunctionNode object itself, due to copy on write changing it several times through
+ * code generation.
+ */
+public class TypeMap {
+ private final Map<Integer, Type[]> paramTypeMap = new HashMap<>();
+ private final Map<Integer, Type> returnTypeMap = new HashMap<>();
+ private final boolean needsCallee;
+
+ /**
+ * Constructor
+ * @param functionNodeId function node id
+ * @param type method type found at runtime corresponding to parameter guess
+ * @param needsCallee does the function using this type map need a callee
+ */
+ public TypeMap(final int functionNodeId, final MethodType type, final boolean needsCallee) {
+ final Type[] types = new Type[type.parameterCount()];
+ int pos = 0;
+ for (final Class<?> p : type.parameterArray()) {
+ types[pos++] = Type.typeFor(p);
+ }
+ paramTypeMap.put(functionNodeId, types);
+ returnTypeMap.put(functionNodeId, Type.typeFor(type.returnType()));
+
+ this.needsCallee = needsCallee;
+ }
+
+ MethodType getCallSiteType(final FunctionNode functionNode) {
+ final Type[] types = paramTypeMap.get(functionNode.getId());
+ if (types == null) {
+ return null;
+ }
+
+ MethodType mt = MethodType.methodType(returnTypeMap.get(functionNode.getId()).getTypeClass());
+ if (needsCallee) {
+ mt = mt.appendParameterTypes(ScriptFunction.class);
+ }
+
+ mt = mt.appendParameterTypes(Object.class); //this
+
+ for (final Type type : types) {
+ if (type == null) {
+ return null; // not all parameter information is supplied
+ }
+ mt = mt.appendParameterTypes(type.getTypeClass());
+ }
+
+ return mt;
+ }
+
+ /**
+ * Does the function using this TypeMap need a callee argument. This is used
+ * to compute correct param index offsets in {@link jdk.nashorn.internal.codegen.ApplySpecialization}
+ * @return true if a callee is needed, false otherwise
+ */
+ public boolean needsCallee() {
+ return needsCallee;
+ }
+
+ /**
+ * Get the parameter type for this parameter position, or
+ * null if now known
+ * @param functionNode functionNode
+ * @param pos position
+ * @return parameter type for this callsite if known
+ */
+ Type get(final FunctionNode functionNode, final int pos) {
+ final Type[] types = paramTypeMap.get(functionNode.getId());
+ assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this;
+ if (types != null && pos < types.length) {
+ return types[pos];
+ }
+ return null;
+ }
+
+ boolean has(final FunctionNode functionNode) {
+ final int id = functionNode.getId();
+ final Type[] paramTypes = paramTypeMap.get(id);
+ assert (paramTypes == null) == (returnTypeMap.get(id) == null) : "inconsistent param and return types in param map";
+ return paramTypes != null;
+ }
+
+ @Override
+ public String toString() {
+ return toString("");
+ }
+
+ String toString(final String prefix) {
+ final StringBuilder sb = new StringBuilder();
+
+ if (paramTypeMap.isEmpty()) {
+ sb.append(prefix).append("\t<empty>");
+ return sb.toString();
+ }
+
+ for (final Map.Entry<Integer, Type[]> entry : paramTypeMap.entrySet()) {
+ final int id = entry.getKey();
+ sb.append(prefix).append('\t');
+ sb.append("function ").append(id).append('\n');
+ sb.append(prefix).append("\t\tparamTypes=");
+ if (entry.getValue() == null) {
+ sb.append("[]");
+ } else {
+ sb.append(Arrays.toString(entry.getValue()));
+ }
+ sb.append('\n');
+ sb.append(prefix).append("\t\treturnType=");
+ final Type ret = returnTypeMap.get(id);
+ sb.append(ret == null ? "N/A" : ret);
+ sb.append('\n');
+ }
+
+ return sb.toString();
+ }
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java Mon May 19 15:29:42 2014 +0200
@@ -69,16 +69,18 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
- optimisticTypeToString(sb);
+ if (printType) {
+ optimisticTypeToString(sb);
+ }
if (needsParen) {
sb.append('(');
}
- base.toString(sb);
+ base.toString(sb, printType);
if (needsParen) {
sb.append(')');
@@ -113,7 +115,7 @@
}
@Override
- public AccessNode setProgramPoint(int programPoint) {
+ public AccessNode setProgramPoint(final int programPoint) {
if (this.programPoint == programPoint) {
return this;
}
--- a/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java Mon May 19 15:29:42 2014 +0200
@@ -97,7 +97,7 @@
}
@Override
- public Type getType(Function<Symbol, Type> localVariableTypes) {
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
return type == null ? getMostPessimisticType() : type;
}
--- a/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java Mon May 19 15:29:42 2014 +0200
@@ -143,7 +143,7 @@
private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
@Override
- public Type apply(Symbol t) {
+ public Type apply(final Symbol t) {
return null;
}
};
@@ -250,7 +250,7 @@
}
}
- private static Type booleanToInt(Type type) {
+ private static Type booleanToInt(final Type type) {
return type == Type.BOOLEAN ? Type.INT : type;
}
@@ -291,7 +291,7 @@
}
@Override
- public BinaryNode setAssignmentDest(Expression n) {
+ public BinaryNode setAssignmentDest(final Expression n) {
return setLHS(n);
}
@@ -377,7 +377,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
final TokenType tokenType = tokenType();
final boolean lhsParen = tokenType.needsParens(lhs().tokenType(), true);
@@ -387,7 +387,7 @@
sb.append('(');
}
- lhs().toString(sb);
+ lhs().toString(sb, printType);
if (lhsParen) {
sb.append(')');
@@ -420,7 +420,7 @@
if (rhsParen) {
sb.append('(');
}
- rhs().toString(sb);
+ rhs().toString(sb, printType);
if (rhsParen) {
sb.append(')');
}
@@ -531,7 +531,7 @@
}
@Override
- public BinaryNode setType(Type type) {
+ public BinaryNode setType(final Type type) {
if (this.type == type) {
return this;
}
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java Mon May 19 15:29:42 2014 +0200
@@ -107,7 +107,7 @@
this(token, finish, statements.toArray(new Statement[statements.size()]));
}
- private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols, LocalVariableConversion conversion) {
+ private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols, final LocalVariableConversion conversion) {
super(block);
this.statements = statements;
this.flags = flags;
@@ -184,9 +184,9 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
for (final Node statement : statements) {
- statement.toString(sb);
+ statement.toString(sb, printType);
sb.append(';');
}
}
@@ -378,7 +378,7 @@
@Override
public List<Label> getLabels() {
- return Collections.singletonList(breakLabel);
+ return Collections.unmodifiableList(Arrays.asList(entryLabel, breakLabel));
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/ir/BlockStatement.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BlockStatement.java Mon May 19 15:29:42 2014 +0200
@@ -70,7 +70,7 @@
* @return a block statement with the new statements. It will have the line number, and token of the
* original statement.
*/
- public static BlockStatement createReplacement(final Statement stmt, int finish, final List<Statement> newStmts) {
+ public static BlockStatement createReplacement(final Statement stmt, final int finish, final List<Statement> newStmts) {
return new BlockStatement(stmt.getLineNumber(), new Block(stmt.getToken(), finish, newStmts));
}
@@ -89,8 +89,8 @@
}
@Override
- public void toString(final StringBuilder sb) {
- block.toString(sb);
+ public void toString(final StringBuilder sb, final boolean printType) {
+ block.toString(sb, printType);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/ir/BreakableNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BreakableNode.java Mon May 19 15:29:42 2014 +0200
@@ -25,14 +25,13 @@
package jdk.nashorn.internal.ir;
-import java.util.List;
import jdk.nashorn.internal.codegen.Label;
/**
* This class represents a node from which control flow can execute
* a {@code break} statement
*/
-public interface BreakableNode extends LexicalContextNode, JoinPredecessor {
+public interface BreakableNode extends LexicalContextNode, JoinPredecessor, Labels {
/**
* Ensure that any labels in this breakable node are unique so
* that new jumps won't go to old parts of the tree. Used for
@@ -56,11 +55,4 @@
*/
public Label getBreakLabel();
- /**
- * Return the labels associated with this node. Breakable nodes that
- * aren't LoopNodes only have a break label - the location immediately
- * afterwards the node in code
- * @return list of labels representing locations around this node
- */
- public List<Label> getLabels();
}
--- a/nashorn/src/jdk/nashorn/internal/ir/BreakableStatement.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BreakableStatement.java Mon May 19 15:29:42 2014 +0200
@@ -91,11 +91,11 @@
*/
@Override
public List<Label> getLabels() {
- return Collections.singletonList(breakLabel);
+ return Collections.unmodifiableList(Collections.singletonList(breakLabel));
}
@Override
- public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, LocalVariableConversion conversion) {
+ public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == conversion) {
return this;
}
--- a/nashorn/src/jdk/nashorn/internal/ir/CallNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CallNode.java Mon May 19 15:29:42 2014 +0200
@@ -185,7 +185,7 @@
}
@Override
- public Type getType(Function<Symbol, Type> localVariableTypes) {
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
return optimisticType == null ? Type.OBJECT : optimisticType;
}
@@ -225,10 +225,14 @@
}
@Override
- public void toString(final StringBuilder sb) {
- optimisticTypeToString(sb);
+ public void toString(final StringBuilder sb, final boolean printType) {
+ if (printType) {
+ optimisticTypeToString(sb);
+ }
+
final StringBuilder fsb = new StringBuilder();
- function.toString(fsb);
+ function.toString(fsb, printType);
+
if (isApplyToCall()) {
sb.append(fsb.toString().replace("apply", "[apply => call]"));
} else {
@@ -246,7 +250,7 @@
first = false;
}
- arg.toString(sb);
+ arg.toString(sb, printType);
}
sb.append(')');
--- a/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java Mon May 19 15:29:42 2014 +0200
@@ -25,6 +25,9 @@
package jdk.nashorn.internal.ir;
+import java.util.Collections;
+import java.util.List;
+
import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -34,7 +37,7 @@
* Case nodes are not BreakableNodes, but the SwitchNode is
*/
@Immutable
-public final class CaseNode extends Node implements JoinPredecessor {
+public final class CaseNode extends Node implements JoinPredecessor, Labels {
/** Test expression. */
private final Expression test;
@@ -97,10 +100,10 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printTypes) {
if (test != null) {
sb.append("case ");
- test.toString(sb);
+ test.toString(sb, printTypes);
sb.append(':');
} else {
sb.append("default:");
@@ -162,4 +165,9 @@
}
return new CaseNode(this, test, body, conversion);
}
+
+ @Override
+ public List<Label> getLabels() {
+ return Collections.unmodifiableList(Collections.singletonList(entry));
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java Mon May 19 15:29:42 2014 +0200
@@ -95,13 +95,13 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printTypes) {
sb.append(" catch (");
- exception.toString(sb);
+ exception.toString(sb, printTypes);
if (exceptionCondition != null) {
sb.append(" if ");
- exceptionCondition.toString(sb);
+ exceptionCondition.toString(sb, printTypes);
}
sb.append(')');
}
--- a/nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java Mon May 19 15:29:42 2014 +0200
@@ -64,7 +64,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printTypes) {
sb.append(';');
}
--- a/nashorn/src/jdk/nashorn/internal/ir/Expression.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Expression.java Mon May 19 15:29:42 2014 +0200
@@ -26,7 +26,9 @@
package jdk.nashorn.internal.ir;
import java.util.function.Function;
+
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
/**
* Common superclass for all expression nodes. Expression nodes can have
@@ -34,9 +36,11 @@
*
*/
public abstract class Expression extends Node {
+ static final String OPT_IDENTIFIER = "%";
+
private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
@Override
- public Type apply(Symbol t) {
+ public Type apply(final Symbol t) {
return null;
}
};
@@ -109,22 +113,22 @@
return getType().narrowerThan(getWidestOperationType());
}
- static final String OPT_IDENTIFIER = "%";
-
void optimisticTypeToString(final StringBuilder sb) {
optimisticTypeToString(sb, isOptimistic());
}
- void optimisticTypeToString(final StringBuilder sb, boolean optimistic) {
+ void optimisticTypeToString(final StringBuilder sb, final boolean optimistic) {
sb.append('{');
final Type type = getType();
final String desc = type == Type.UNDEFINED ? "U" : type.getDescriptor();
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : desc);
- if(isOptimistic() && optimistic) {
+ if (isOptimistic() && optimistic) {
sb.append(OPT_IDENTIFIER);
- sb.append('_');
- sb.append(((Optimistic)this).getProgramPoint());
+ final int pp = ((Optimistic)this).getProgramPoint();
+ if (UnwarrantedOptimismException.isValid(pp)) {
+ sb.append('_').append(pp);
+ }
}
sb.append('}');
}
@@ -152,7 +156,7 @@
* @param test a test expression used as a predicate of a branch or a loop.
* @return true if the expression is not null and {@link #isAlwaysFalse()}.
*/
- public static boolean isAlwaysFalse(Expression test) {
+ public static boolean isAlwaysFalse(final Expression test) {
return test != null && test.isAlwaysFalse();
}
@@ -163,7 +167,7 @@
* @param test a test expression used as a predicate of a branch or a loop.
* @return true if the expression is null or {@link #isAlwaysFalse()}.
*/
- public static boolean isAlwaysTrue(Expression test) {
+ public static boolean isAlwaysTrue(final Expression test) {
return test == null || test.isAlwaysTrue();
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/ExpressionStatement.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ExpressionStatement.java Mon May 19 15:29:42 2014 +0200
@@ -71,8 +71,8 @@
}
@Override
- public void toString(final StringBuilder sb) {
- expression.toString(sb);
+ public void toString(final StringBuilder sb, final boolean printTypes) {
+ expression.toString(sb, printTypes);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/ir/ForNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ForNode.java Mon May 19 15:29:42 2014 +0200
@@ -71,7 +71,7 @@
}
private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
- final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, LocalVariableConversion conversion) {
+ final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, final LocalVariableConversion conversion) {
super(forNode, test, body, controlFlowEscapes, conversion);
this.init = init;
this.modify = modify;
@@ -82,7 +82,7 @@
}
@Override
- public Node ensureUniqueLabels(LexicalContext lc) {
+ public Node ensureUniqueLabels(final LexicalContext lc) {
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
@@ -100,25 +100,25 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printTypes) {
sb.append("for");
LocalVariableConversion.toString(conversion, sb).append(' ');
if (isForIn()) {
- init.toString(sb);
+ init.toString(sb, printTypes);
sb.append(" in ");
- modify.toString(sb);
+ modify.toString(sb, printTypes);
} else {
if (init != null) {
- init.toString(sb);
+ init.toString(sb, printTypes);
}
sb.append("; ");
if (test != null) {
- test.toString(sb);
+ test.toString(sb, printTypes);
}
sb.append("; ");
if (modify != null) {
- modify.toString(sb);
+ modify.toString(sb, printTypes);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Mon May 19 15:29:42 2014 +0200
@@ -33,6 +33,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
+
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -79,6 +80,10 @@
CONSTANT_FOLDED,
/** method has been lowered */
LOWERED,
+ /** program points have been assigned to unique locations */
+ PROGRAM_POINTS_ASSIGNED,
+ /** any transformations of builtins have taken place, e.g. apply=>call */
+ BUILTINS_TRANSFORMED,
/** method has been split */
SPLIT,
/** method has had symbols assigned */
@@ -87,11 +92,16 @@
SCOPE_DEPTHS_COMPUTED,
/** method has had types calculated*/
OPTIMISTIC_TYPES_ASSIGNED,
- /** method has had types calculated*/
+ /** method has had types calculated */
LOCAL_VARIABLE_TYPES_CALCULATED,
+ /** compile units reused (optional) */
+ COMPILE_UNITS_REUSED,
/** method has been emitted to bytecode */
- EMITTED
- }
+ BYTECODE_GENERATED,
+ /** method has been installed */
+ BYTECODE_INSTALLED
+ };
+
/** Source of entity. */
private final Source source;
@@ -147,6 +157,9 @@
/** Line number of function start */
private final int lineNumber;
+ /** Root class for function */
+ private final Class<?> rootClass;
+
/** Is anonymous function flag. */
public static final int IS_ANONYMOUS = 1 << 0;
@@ -292,6 +305,7 @@
this.compileUnit = null;
this.body = null;
this.thisProperties = 0;
+ this.rootClass = null;
}
private FunctionNode(
@@ -305,8 +319,10 @@
final EnumSet<CompilationState> compilationState,
final Block body,
final List<IdentNode> parameters,
- final int thisProperties) {
+ final int thisProperties,
+ final Class<?> rootClass) {
super(functionNode);
+
this.lineNumber = functionNode.lineNumber;
this.flags = flags;
this.sourceURL = sourceURL;
@@ -318,6 +334,7 @@
this.body = body;
this.parameters = parameters;
this.thisProperties = thisProperties;
+ this.rootClass = rootClass;
// the fields below never change - they are final and assigned in constructor
this.source = functionNode.source;
@@ -368,7 +385,17 @@
* @return name for the script source
*/
public String getSourceName() {
- return sourceURL != null? sourceURL : source.getName();
+ return getSourceName(source, sourceURL);
+ }
+
+ /**
+ * static source name getter
+ * @param source
+ * @param sourceURL
+ * @return
+ */
+ public static String getSourceName(final Source source, final String sourceURL) {
+ return sourceURL != null ? sourceURL : source.getName();
}
/**
@@ -391,7 +418,22 @@
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ newSourceURL,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
}
/**
@@ -417,11 +459,17 @@
* @return true of the node is in the given state
*/
public boolean hasState(final EnumSet<CompilationState> state) {
- return compilationState.equals(state);
+ //this.compilationState >= state, or we fail
+ for (final CompilationState cs : state) {
+ if (!hasState(cs)) {
+ return false;
+ }
+ }
+ return true;
}
/**
- * Check whether the state of this FunctionNode contains a given compilation
+ * 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.
@@ -449,7 +497,22 @@
}
final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
newState.add(state);
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters, thisProperties));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ sourceURL,
+ name,
+ returnType,
+ compileUnit,
+ newState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
}
/**
@@ -462,7 +525,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printTypes) {
sb.append('[').
append(returnType).
append(']').
@@ -472,7 +535,7 @@
if (ident != null) {
sb.append(' ');
- ident.toString(sb);
+ ident.toString(sb, printTypes);
}
sb.append('(');
@@ -482,7 +545,7 @@
if (parameter.getSymbol() != null) {
sb.append('[').append(parameter.getType()).append(']').append(' ');
}
- parameter.toString(sb);
+ parameter.toString(sb, printTypes);
if (iter.hasNext()) {
sb.append(", ");
}
@@ -506,7 +569,22 @@
if (this.flags == flags) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ sourceURL,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
}
@Override
@@ -642,10 +720,28 @@
* @return new function node if body changed, same if not
*/
public FunctionNode setBody(final LexicalContext lc, final Block body) {
- if(this.body == body) {
+ if (this.body == body) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags |
+ (body.needsScope() ?
+ FunctionNode.HAS_SCOPE_BLOCK :
+ 0),
+ sourceURL,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
}
/**
@@ -726,7 +822,22 @@
if (this.thisProperties == thisProperties) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ sourceURL,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
}
/**
@@ -772,7 +883,22 @@
if (this.lastToken == lastToken) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ sourceURL,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
}
/**
@@ -793,7 +919,22 @@
if (this.name.equals(name)) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ sourceURL,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
}
/**
@@ -844,7 +985,22 @@
if (this.parameters == parameters) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ sourceURL,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
}
/**
@@ -874,7 +1030,7 @@
}
@Override
- public Type getType(Function<Symbol, Type> localVariableTypes) {
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
return FUNCTION_TYPE;
}
@@ -922,7 +1078,8 @@
compilationState,
body,
parameters,
- thisProperties
+ thisProperties,
+ rootClass
));
}
@@ -954,7 +1111,22 @@
if (this.compileUnit == compileUnit) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ sourceURL,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
}
/**
@@ -975,4 +1147,41 @@
public Symbol compilerConstant(final CompilerConstants cc) {
return body.getExistingSymbol(cc.symbolName());
}
+
+ /**
+ * Get the root class that this function node compiles to
+ * @return root class
+ */
+ public Class<?> getRootClass() {
+ return rootClass;
+ }
+
+ /**
+ * Reset the root class that this function is compiled to
+ * @see Compiler
+ * @param lc lexical context
+ * @param rootClass root class
+ * @return function node or a new one if state was changed
+ */
+ public FunctionNode setRootClass(final LexicalContext lc, final Class<?> rootClass) {
+ if (this.rootClass == rootClass) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ sourceURL,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass));
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java Mon May 19 15:29:42 2014 +0200
@@ -114,7 +114,7 @@
}
@Override
- public Type getType(Function<Symbol, Type> localVariableTypes) {
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
if(type != null) {
return type;
} else if(symbol != null && symbol.isScope()) {
@@ -139,8 +139,10 @@
}
@Override
- public void toString(final StringBuilder sb) {
- optimisticTypeToString(sb, symbol == null || !symbol.hasSlot());
+ public void toString(final StringBuilder sb, final boolean printType) {
+ if (printType) {
+ optimisticTypeToString(sb, symbol == null || !symbol.hasSlot());
+ }
sb.append(name);
}
--- a/nashorn/src/jdk/nashorn/internal/ir/IfNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IfNode.java Mon May 19 15:29:42 2014 +0200
@@ -92,9 +92,9 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printTypes) {
sb.append("if (");
- test.toString(sb);
+ test.toString(sb, printTypes);
sb.append(')');
}
--- a/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java Mon May 19 15:29:42 2014 +0200
@@ -65,23 +65,25 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
final boolean needsParen = tokenType().needsParens(base.tokenType(), true);
if (needsParen) {
sb.append('(');
}
- optimisticTypeToString(sb);
+ if (printType) {
+ optimisticTypeToString(sb);
+ }
- base.toString(sb);
+ base.toString(sb, printType);
if (needsParen) {
sb.append(')');
}
sb.append('[');
- index.toString(sb);
+ index.toString(sb, printType);
sb.append(']');
}
@@ -105,7 +107,7 @@
* @param index new index expression
* @return a node equivalent to this one except for the requested change.
*/
- public IndexNode setIndex(Expression index) {
+ public IndexNode setIndex(final Expression index) {
if(this.index == index) {
return this;
}
@@ -129,7 +131,7 @@
}
@Override
- public IndexNode setProgramPoint(int programPoint) {
+ public IndexNode setProgramPoint(final int programPoint) {
if (this.programPoint == programPoint) {
return this;
}
--- a/nashorn/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java Mon May 19 15:29:42 2014 +0200
@@ -70,7 +70,7 @@
}
@Override
- public Type getType(Function<Symbol, Type> localVariableTypes) {
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
return expression.getType(localVariableTypes);
}
@@ -110,7 +110,7 @@
}
@Override
- public Node accept(NodeVisitor<? extends LexicalContext> visitor) {
+ public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if(visitor.enterJoinPredecessorExpression(this)) {
final Expression expr = getExpression();
return visitor.leaveJoinPredecessorExpression(expr == null ? this : setExpression((Expression)expr.accept(visitor)));
@@ -119,9 +119,9 @@
}
@Override
- public void toString(StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
if(expression != null) {
- expression.toString(sb);
+ expression.toString(sb, printType);
}
if(conversion != null) {
conversion.toString(sb);
--- a/nashorn/src/jdk/nashorn/internal/ir/JumpStatement.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/JumpStatement.java Mon May 19 15:29:42 2014 +0200
@@ -72,7 +72,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append(getStatementName());
if (labelName != null) {
--- a/nashorn/src/jdk/nashorn/internal/ir/LabelNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/LabelNode.java Mon May 19 15:29:42 2014 +0200
@@ -82,7 +82,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append(labelName).append(':');
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/Labels.java Mon May 19 15:29:42 2014 +0200
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.ir;
+
+import java.util.List;
+
+import jdk.nashorn.internal.codegen.Label;
+
+/**
+ * Interface that can be used to get a list of all labels in a node
+ */
+public interface Labels {
+
+ /**
+ * Return the labels associated with this node. Breakable nodes that
+ * aren't LoopNodes only have a break label - the location immediately
+ * afterwards the node in code
+ * @return list of labels representing locations around this node
+ */
+ public List<Label> getLabels();
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java Mon May 19 15:29:42 2014 +0200
@@ -29,6 +29,7 @@
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
+
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
@@ -210,7 +211,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
if (value == null) {
sb.append("null");
} else {
@@ -448,7 +449,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append('\"');
sb.append(value);
sb.append('\"');
@@ -496,7 +497,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append(value.toString());
}
}
@@ -880,7 +881,7 @@
}
@Override
- public Node accept(LexicalContext lc, NodeVisitor<? extends LexicalContext> visitor) {
+ public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterLiteralNode(this)) {
final List<Expression> oldValue = Arrays.asList(value);
final List<Expression> newValue = Node.accept(visitor, Expression.class, oldValue);
@@ -894,7 +895,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append('[');
boolean first = true;
for (final Node node : value) {
@@ -905,7 +906,7 @@
if (node == null) {
sb.append("undefined");
} else {
- node.toString(sb);
+ node.toString(sb, printType);
}
first = false;
}
--- a/nashorn/src/jdk/nashorn/internal/ir/LoopNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/LoopNode.java Mon May 19 15:29:42 2014 +0200
@@ -26,7 +26,9 @@
package jdk.nashorn.internal.ir;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+
import jdk.nashorn.internal.codegen.Label;
/**
@@ -126,7 +128,7 @@
@Override
public List<Label> getLabels() {
- return Arrays.asList(breakLabel, continueLabel);
+ return Collections.unmodifiableList(Arrays.asList(breakLabel, continueLabel));
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/ir/Node.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Node.java Mon May 19 15:29:42 2014 +0200
@@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.List;
+
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
@@ -129,7 +130,19 @@
*
* @param sb a StringBuilder
*/
- public abstract void toString(StringBuilder sb);
+ public void toString(final StringBuilder sb) {
+ toString(sb, true);
+ }
+
+ /**
+ * Print logic that decides whether to show the optimistic type
+ * or not - for example it should not be printed after just parse,
+ * when it hasn't been computed, or has been set to a trivially provable
+ * value
+ * @param sb string builder
+ * @param printType print type?
+ */
+ public abstract void toString(final StringBuilder sb, final boolean printType);
/**
* Check if this node has terminal flags, i.e. ends or breaks control flow
--- a/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java Mon May 19 15:29:42 2014 +0200
@@ -28,6 +28,7 @@
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
+
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -73,7 +74,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append('{');
if (!elements.isEmpty()) {
@@ -86,7 +87,7 @@
}
first = false;
- element.toString(sb);
+ element.toString(sb, printType);
}
sb.append(' ');
}
--- a/nashorn/src/jdk/nashorn/internal/ir/Optimistic.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Optimistic.java Mon May 19 15:29:42 2014 +0200
@@ -37,9 +37,6 @@
* @see BinaryNode (local calculations to strongly typed bytecode)
* @see UnaryNode (local calculations to strongly typed bytecode)
* @see CallNode (dynamicCall)
- *
- * TODO : to be implemented are
- *
* @see AccessNode (dynamicGet)
* @see IdentNode (dynamicGet)
*/
--- a/nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java Mon May 19 15:29:42 2014 +0200
@@ -94,25 +94,25 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
if (value instanceof FunctionNode && ((FunctionNode)value).getIdent() != null) {
value.toString(sb);
}
if (value != null) {
- ((Node)key).toString(sb);
+ ((Node)key).toString(sb, printType);
sb.append(": ");
- value.toString(sb);
+ value.toString(sb, printType);
}
if (getter != null) {
sb.append(' ');
- getter.toString(sb);
+ getter.toString(sb, printType);
}
if (setter != null) {
sb.append(' ');
- setter.toString(sb);
+ setter.toString(sb, printType);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java Mon May 19 15:29:42 2014 +0200
@@ -27,7 +27,6 @@
import static jdk.nashorn.internal.parser.TokenType.RETURN;
import static jdk.nashorn.internal.parser.TokenType.YIELD;
-
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -100,11 +99,11 @@
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append(isYield() ? "yield" : "return");
if (expression != null) {
sb.append(' ');
- expression.toString(sb);
+ expression.toString(sb, printType);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java Mon May 19 15:29:42 2014 +0200
@@ -460,7 +460,7 @@
* Return type for the ReferenceNode
*/
@Override
- public Type getType(Function<Symbol, Type> localVariableTypes) {
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
return request.getReturnType();
}
@@ -478,7 +478,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append("ScriptRuntime.");
sb.append(request);
sb.append('(');
@@ -492,7 +492,7 @@
first = false;
}
- arg.toString(sb);
+ arg.toString(sb, printType);
}
sb.append(')');
--- a/nashorn/src/jdk/nashorn/internal/ir/SplitNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/SplitNode.java Mon May 19 15:29:42 2014 +0200
@@ -25,8 +25,12 @@
package jdk.nashorn.internal.ir;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
@@ -36,7 +40,7 @@
* Node indicating code is split across classes.
*/
@Immutable
-public class SplitNode extends LexicalContextStatement {
+public class SplitNode extends LexicalContextStatement implements Labels {
/** Split node method name. */
private final String name;
@@ -62,12 +66,12 @@
this.compileUnit = compileUnit;
}
- private SplitNode(final SplitNode splitNode, final Block body) {
+ private SplitNode(final SplitNode splitNode, final Block body, final CompileUnit compileUnit, final Map<Label, JoinPredecessor> jumps) {
super(splitNode);
this.name = splitNode.name;
this.body = body;
- this.compileUnit = splitNode.compileUnit;
- this.jumps = splitNode.jumps;
+ this.compileUnit = compileUnit;
+ this.jumps = jumps;
}
/**
@@ -82,7 +86,7 @@
if (this.body == body) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body));
+ return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps));
}
@Override
@@ -94,11 +98,11 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append("<split>(");
sb.append(compileUnit.getClass().getSimpleName());
sb.append(") ");
- body.toString(sb);
+ body.toString(sb, printType);
}
/**
@@ -118,13 +122,26 @@
}
/**
+ * Set the compile unit for this split node
+ * @param lc lexical context
+ * @param compileUnit compile unit
+ * @return new node if changed, otherwise same node
+ */
+ public SplitNode setCompileUnit(final LexicalContext lc, final CompileUnit compileUnit) {
+ if (this.compileUnit == compileUnit) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps));
+ }
+
+ /**
* Adds a jump that crosses this split node's boundary (it originates within the split node, and goes to a target
* outside of it).
* @param jumpOrigin the join predecessor that's the origin of the jump
* @param targetLabel the label that's the target of the jump.
*/
public void addJump(final JoinPredecessor jumpOrigin, final Label targetLabel) {
- if(jumps == null) {
+ if (jumps == null) {
jumps = new HashMap<>();
}
jumps.put(targetLabel, jumpOrigin);
@@ -138,4 +155,9 @@
public JoinPredecessor getJumpOrigin(final Label targetLabel) {
return jumps == null ? null : jumps.get(targetLabel);
}
+
+ @Override
+ public List<Label> getLabels() {
+ return Collections.unmodifiableList(new ArrayList<>(jumps.keySet()));
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java Mon May 19 15:29:42 2014 +0200
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -111,9 +112,9 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append("switch (");
- expression.toString(sb);
+ expression.toString(sb, printType);
sb.append(')');
}
--- a/nashorn/src/jdk/nashorn/internal/ir/Symbol.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Symbol.java Mon May 19 15:29:42 2014 +0200
@@ -138,7 +138,7 @@
protected Symbol(final String name, final int flags, final int slot) {
this.name = name;
this.flags = flags;
- this.firstSlot = slot;
+ this.firstSlot = slot;
this.fieldIndex = -1;
if(shouldTrace()) {
trace("CREATE SYMBOL " + name);
--- a/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java Mon May 19 15:29:42 2014 +0200
@@ -26,9 +26,11 @@
package jdk.nashorn.internal.ir;
import java.util.function.Function;
+
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.parser.TokenType;
/**
* TernaryNode represent the ternary operator {@code ?:}. Note that for control-flow calculation reasons its branch
@@ -76,15 +78,16 @@
}
@Override
- public void toString(final StringBuilder sb) {
- final boolean testParen = tokenType().needsParens(getTest().tokenType(), true);
- final boolean trueParen = tokenType().needsParens(getTrueExpression().tokenType(), false);
- final boolean falseParen = tokenType().needsParens(getFalseExpression().tokenType(), false);
+ public void toString(final StringBuilder sb, final boolean printType) {
+ final TokenType tokenType = tokenType();
+ final boolean testParen = tokenType.needsParens(getTest().tokenType(), true);
+ final boolean trueParen = tokenType.needsParens(getTrueExpression().tokenType(), false);
+ final boolean falseParen = tokenType.needsParens(getFalseExpression().tokenType(), false);
if (testParen) {
sb.append('(');
}
- getTest().toString(sb);
+ getTest().toString(sb, printType);
if (testParen) {
sb.append(')');
}
@@ -94,7 +97,7 @@
if (trueParen) {
sb.append('(');
}
- getTrueExpression().toString(sb);
+ getTrueExpression().toString(sb, printType);
if (trueParen) {
sb.append(')');
}
@@ -104,7 +107,7 @@
if (falseParen) {
sb.append('(');
}
- getFalseExpression().toString(sb);
+ getFalseExpression().toString(sb, printType);
if (falseParen) {
sb.append(')');
}
@@ -118,7 +121,7 @@
}
@Override
- public Type getType(Function<Symbol, Type> localVariableTypes) {
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
return Type.widestReturnType(getTrueExpression().getType(localVariableTypes), getFalseExpression().getType(localVariableTypes));
}
--- a/nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java Mon May 19 15:29:42 2014 +0200
@@ -83,13 +83,13 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append("throw ");
if (expression != null) {
- expression.toString(sb);
+ expression.toString(sb, printType);
}
- if(conversion != null) {
+ if (conversion != null) {
conversion.toString(sb);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/TryNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/TryNode.java Mon May 19 15:29:42 2014 +0200
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -71,7 +72,7 @@
this.conversion = null;
}
- private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, LocalVariableConversion conversion) {
+ private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion) {
super(tryNode);
this.body = body;
this.catchBlocks = catchBlocks;
@@ -120,7 +121,7 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append("try ");
}
--- a/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java Mon May 19 15:29:42 2014 +0200
@@ -34,6 +34,7 @@
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
+
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable;
@@ -123,7 +124,7 @@
private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
@Override
- public Type apply(Symbol t) {
+ public Type apply(final Symbol t) {
return null;
}
};
@@ -166,7 +167,7 @@
}
@Override
- public UnaryNode setAssignmentDest(Expression n) {
+ public UnaryNode setAssignmentDest(final Expression n) {
return setExpression(n);
}
@@ -209,13 +210,15 @@
}
@Override
- public void toString(final StringBuilder sb) {
- toString(sb, new Runnable() {
- @Override
- public void run() {
- sb.append(getExpression().toString());
- }
- });
+ public void toString(final StringBuilder sb, final boolean printType) {
+ toString(sb,
+ new Runnable() {
+ @Override
+ public void run() {
+ getExpression().toString(sb, printType);
+ }
+ },
+ printType);
}
/**
@@ -223,9 +226,10 @@
* operand to a specified runnable.
* @param sb the string builder to use
* @param rhsStringBuilder the runnable that appends the string representation of the operand to the string builder
+ * @param printType should we print type
* when invoked.
*/
- public void toString(final StringBuilder sb, final Runnable rhsStringBuilder) {
+ public void toString(final StringBuilder sb, final Runnable rhsStringBuilder, final boolean printType) {
final TokenType tokenType = tokenType();
final String name = tokenType.getName();
final boolean isPostfix = tokenType == DECPOSTFIX || tokenType == INCPOSTFIX;
@@ -233,7 +237,7 @@
if (isOptimistic()) {
sb.append(Expression.OPT_IDENTIFIER);
}
- boolean rhsParen = tokenType.needsParens(getExpression().tokenType(), false);
+ boolean rhsParen = tokenType.needsParens(getExpression().tokenType(), false);
if (!isPostfix) {
if (name == null) {
@@ -321,7 +325,7 @@
}
@Override
- public Type getType(Function<Symbol, Type> localVariableTypes) {
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
final Type widest = getWidestOperationType(localVariableTypes);
if(type == null) {
return widest;
@@ -330,7 +334,7 @@
}
@Override
- public UnaryNode setType(Type type) {
+ public UnaryNode setType(final Type type) {
if (this.type == type) {
return this;
}
--- a/nashorn/src/jdk/nashorn/internal/ir/VarNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/VarNode.java Mon May 19 15:29:42 2014 +0200
@@ -138,13 +138,13 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append("var ");
- name.toString(sb);
+ name.toString(sb, printType);
if (init != null) {
sb.append(" = ");
- init.toString(sb);
+ init.toString(sb, printType);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/WhileNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/WhileNode.java Mon May 19 15:29:42 2014 +0200
@@ -60,7 +60,7 @@
* @param controlFlowEscapes control flow escapes?
* @param conversion TODO
*/
- private WhileNode(final WhileNode whileNode, final JoinPredecessorExpression test, final Block body, final boolean controlFlowEscapes, LocalVariableConversion conversion) {
+ private WhileNode(final WhileNode whileNode, final JoinPredecessorExpression test, final Block body, final boolean controlFlowEscapes, final LocalVariableConversion conversion) {
super(whileNode, test, body, controlFlowEscapes, conversion);
this.isDoWhile = whileNode.isDoWhile;
}
@@ -133,9 +133,9 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append("while (");
- test.toString(sb);
+ test.toString(sb, printType);
sb.append(')');
}
--- a/nashorn/src/jdk/nashorn/internal/ir/WithNode.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/WithNode.java Mon May 19 15:29:42 2014 +0200
@@ -79,9 +79,9 @@
}
@Override
- public void toString(final StringBuilder sb) {
+ public void toString(final StringBuilder sb, final boolean printType) {
sb.append("with (");
- expression.toString(sb);
+ expression.toString(sb, printType);
sb.append(')');
}
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Mon May 19 15:29:42 2014 +0200
@@ -28,7 +28,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
@@ -92,7 +91,7 @@
final Parser parser = new Parser(context.getEnv(), new Source(name, code), new Context.ThrowErrorManager(), context.getEnv()._strict, context.getLogger(Parser.class));
final JSONWriter jsonWriter = new JSONWriter(includeLoc);
try {
- final FunctionNode functionNode = parser.parse(CompilerConstants.PROGRAM.symbolName());
+ final FunctionNode functionNode = parser.parse(); //symbol name is ":program", default
functionNode.accept(jsonWriter);
return jsonWriter.getString();
} catch (final ParserException e) {
@@ -102,7 +101,7 @@
}
@Override
- public boolean enterJoinPredecessorExpression(JoinPredecessorExpression joinPredecessorExpression) {
+ public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinPredecessorExpression) {
final Expression expr = joinPredecessorExpression.getExpression();
if(expr != null) {
expr.accept(this);
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java Mon May 19 15:29:42 2014 +0200
@@ -52,8 +52,6 @@
* this fact and will report incorrect sizes, as it will presume the default JVM
* behavior.
*/
-
-@SuppressWarnings("StaticNonFinalUsedInInitialization")
public class ObjectSizeCalculator {
/**
@@ -307,7 +305,7 @@
public ClassSizeInfo(final Class<?> clazz) {
long newFieldsSize = 0;
final List<Field> newReferenceFields = new LinkedList<>();
- for (Field f : clazz.getDeclaredFields()) {
+ for (final Field f : clazz.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())) {
continue;
}
@@ -338,10 +336,10 @@
}
public void enqueueReferencedObjects(final Object obj, final ObjectSizeCalculator calc) {
- for (Field f : referenceFields) {
+ for (final Field f : referenceFields) {
try {
calc.enqueue(f.get(obj));
- } catch (IllegalAccessException e) {
+ } catch (final IllegalAccessException e) {
final AssertionError ae = new AssertionError(
"Unexpected denial of access to " + f);
ae.initCause(e);
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Mon May 19 15:29:42 2014 +0200
@@ -77,25 +77,30 @@
/** Print line numbers */
private final boolean printLineNumbers;
+ /** Print inferred and optimistic types */
+ private final boolean printTypes;
+
private int lastLineNumber = -1;
/**
* Constructor.
*/
public PrintVisitor() {
- this(true);
+ this(true, true);
}
/**
* Constructor
*
* @param printLineNumbers should line number nodes be included in the output?
+ * @param printTypes should we print optimistic and inferred types?
*/
- public PrintVisitor(final boolean printLineNumbers) {
+ public PrintVisitor(final boolean printLineNumbers, final boolean printTypes) {
super(new LexicalContext());
this.EOLN = System.lineSeparator();
this.sb = new StringBuilder();
this.printLineNumbers = printLineNumbers;
+ this.printTypes = printTypes;
}
/**
@@ -104,7 +109,7 @@
* @param root a node from which to start printing code
*/
public PrintVisitor(final Node root) {
- this(root, true);
+ this(root, true, true);
}
/**
@@ -112,9 +117,10 @@
*
* @param root a node from which to start printing code
* @param printLineNumbers should line numbers nodes be included in the output?
+ * @param printTypes should we print optimistic and inferred types?
*/
- public PrintVisitor(final Node root, final boolean printLineNumbers) {
- this(printLineNumbers);
+ public PrintVisitor(final Node root, final boolean printLineNumbers, final boolean printTypes) {
+ this(printLineNumbers, printTypes);
visit(root);
}
@@ -142,27 +148,27 @@
@Override
public boolean enterDefault(final Node node) {
- node.toString(sb);
+ node.toString(sb, printTypes);
return false;
}
@Override
public boolean enterContinueNode(final ContinueNode node) {
- node.toString(sb);
+ node.toString(sb, printTypes);
printLocalVariableConversion(node);
return false;
}
@Override
public boolean enterBreakNode(final BreakNode node) {
- node.toString(sb);
+ node.toString(sb, printTypes);
printLocalVariableConversion(node);
return false;
}
@Override
public boolean enterThrowNode(final ThrowNode node) {
- node.toString(sb);
+ node.toString(sb, printTypes);
printLocalVariableConversion(node);
return false;
}
@@ -240,15 +246,15 @@
}
@Override
- public boolean enterJoinPredecessorExpression(JoinPredecessorExpression expr) {
+ public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression expr) {
expr.getExpression().accept(this);
printLocalVariableConversion(expr);
return false;
}
@Override
- public boolean enterIdentNode(IdentNode identNode) {
- identNode.toString(sb);
+ public boolean enterIdentNode(final IdentNode identNode) {
+ identNode.toString(sb, printTypes);
printLocalVariableConversion(identNode);
return true;
}
@@ -264,7 +270,7 @@
public void run() {
unaryNode.getExpression().accept(PrintVisitor.this);
}
- });
+ }, printTypes);
return false;
}
@@ -276,21 +282,21 @@
@Override
public boolean enterForNode(final ForNode forNode) {
- forNode.toString(sb);
+ forNode.toString(sb, printTypes);
forNode.getBody().accept(this);
return false;
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
- functionNode.toString(sb);
+ functionNode.toString(sb, printTypes);
enterBlock(functionNode.getBody());
return false;
}
@Override
public boolean enterIfNode(final IfNode ifNode) {
- ifNode.toString(sb);
+ ifNode.toString(sb, printTypes);
ifNode.getPass().accept(this);
final Block fail = ifNode.getFail();
@@ -313,7 +319,7 @@
indent -= TABWIDTH;
indent();
indent += TABWIDTH;
- labeledNode.toString(sb);
+ labeledNode.toString(sb, printTypes);
labeledNode.getBody().accept(this);
printLocalVariableConversion(labeledNode);
return false;
@@ -321,7 +327,7 @@
@Override
public boolean enterSplitNode(final SplitNode splitNode) {
- splitNode.toString(sb);
+ splitNode.toString(sb, printTypes);
sb.append(EOLN);
indent += TABWIDTH;
indent();
@@ -339,7 +345,7 @@
@Override
public boolean enterSwitchNode(final SwitchNode switchNode) {
- switchNode.toString(sb);
+ switchNode.toString(sb, printTypes);
sb.append(" {");
final List<CaseNode> cases = switchNode.getCases();
@@ -347,7 +353,7 @@
for (final CaseNode caseNode : cases) {
sb.append(EOLN);
indent();
- caseNode.toString(sb);
+ caseNode.toString(sb, printTypes);
printLocalVariableConversion(caseNode);
indent += TABWIDTH;
caseNode.getBody().accept(this);
@@ -370,7 +376,7 @@
@Override
public boolean enterTryNode(final TryNode tryNode) {
- tryNode.toString(sb);
+ tryNode.toString(sb, printTypes);
printLocalVariableConversion(tryNode);
tryNode.getBody().accept(this);
@@ -378,7 +384,7 @@
for (final Block catchBlock : catchBlocks) {
final CatchNode catchNode = (CatchNode)catchBlock.getStatements().get(0);
- catchNode.toString(sb);
+ catchNode.toString(sb, printTypes);
catchNode.getBody().accept(this);
}
@@ -395,7 +401,7 @@
@Override
public boolean enterVarNode(final VarNode varNode) {
sb.append("var ");
- varNode.getName().toString(sb);
+ varNode.getName().toString(sb, printTypes);
printLocalVariableConversion(varNode.getName());
final Node init = varNode.getInit();
if (init != null) {
@@ -413,9 +419,9 @@
sb.append("do");
whileNode.getBody().accept(this);
sb.append(' ');
- whileNode.toString(sb);
+ whileNode.toString(sb, printTypes);
} else {
- whileNode.toString(sb);
+ whileNode.toString(sb, printTypes);
whileNode.getBody().accept(this);
}
@@ -424,7 +430,7 @@
@Override
public boolean enterWithNode(final WithNode withNode) {
- withNode.toString(sb);
+ withNode.toString(sb, printTypes);
withNode.getBody().accept(this);
return false;
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java Mon May 19 15:29:42 2014 +0200
@@ -713,14 +713,15 @@
}
private static void concatToList(final ArrayList<Object> list, final Object obj) {
- final boolean isScriptArray = isArray(obj);
+ final boolean isScriptArray = isArray(obj);
final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject;
- if (isScriptArray || obj instanceof Iterable || obj != null && obj.getClass().isArray()) {
+ if (isScriptArray || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) {
final Iterator<Object> iter = arrayLikeIterator(obj, true);
if (iter.hasNext()) {
for (int i = 0; iter.hasNext(); ++i) {
final Object value = iter.next();
- if (value == ScriptRuntime.UNDEFINED && isScriptObject && !((ScriptObject)obj).has(i)) {
+ final boolean lacksIndex = obj != null && !((ScriptObject)obj).has(i);
+ if (value == ScriptRuntime.UNDEFINED && isScriptObject && lacksIndex) {
// TODO: eventually rewrite arrayLikeIterator to use a three-state enum for handling
// UNDEFINED instead of an "includeUndefined" boolean with states SKIP, INCLUDE,
// RETURN_EMPTY. Until then, this is how we'll make sure that empty elements don't make it
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java Mon May 19 15:29:42 2014 +0200
@@ -234,20 +234,7 @@
* @return function node resulting from successful parse
*/
public FunctionNode parse() {
- return parse(PROGRAM.symbolName());
- }
-
- /**
- * 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) {
- return parse(scriptName, 0, source.getLength(), false);
+ return parse(PROGRAM.symbolName(), 0, source.getLength(), false);
}
/**
@@ -3184,7 +3171,7 @@
@Override
public String toString() {
- return "[JavaScript Parsing]";
+ return "'JavaScript Parsing'";
}
private static void markEval(final LexicalContext lc) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Mon May 19 15:29:42 2014 +0200
@@ -34,9 +34,13 @@
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.SwitchPoint;
+import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
+
+import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -556,6 +560,43 @@
}
/**
+ * Debug function for printing out all invalidated program points and their
+ * invalidation mapping to next type
+ * @param ipp
+ * @return string describing the ipp map
+ */
+ private static String toStringInvalidations(final Map<Integer, Type> ipp) {
+ if (ipp == null) {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+
+ for (final Iterator<Map.Entry<Integer, Type>> iter = ipp.entrySet().iterator(); iter.hasNext(); ) {
+ final Map.Entry<Integer, Type> entry = iter.next();
+ final char bct = entry.getValue().getBytecodeStackType();
+
+ sb.append('[').
+ append(entry.getKey()).
+ append("->").
+ append(bct == 'A' ? 'O' : bct).
+ append(']');
+
+ if (iter.hasNext()) {
+ sb.append(' ');
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private void logRecompile(final String reason, final FunctionNode fn, final MethodType callSiteType, final Map<Integer, Type> ipp) {
+ if (log.isEnabled()) {
+ log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", callSiteType, " ", toStringInvalidations(ipp));
+ }
+ }
+
+ /**
* Handles a {@link RewriteException} raised during the execution of this function by recompiling (if needed) the
* function with an optimistic assumption invalidated at the program point indicated by the exception, and then
* executing a rest-of method to complete the execution with the deoptimized version.
@@ -567,45 +608,90 @@
*/
private MethodHandle handleRewriteException(final OptimismInfo oldOptimismInfo, final RewriteException re) {
if (log.isEnabled()) {
- log.info(new RecompilationEvent(Level.INFO, re, re.getReturnValueNonDestructive()), "\tRewriteException ", re.getMessageShort());
+ log.info(new RecompilationEvent(Level.INFO, re, re.getReturnValueNonDestructive()), "RewriteException ", re.getMessageShort());
}
final MethodType type = type();
+
// Compiler needs a call site type as its input, which always has a callee parameter, so we must add it if
// this function doesn't have a callee parameter.
- final MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ? type : type.insertParameterTypes(0, ScriptFunction.class);
-
- final FunctionNode fn = oldOptimismInfo.recompile(callSiteType, re);
+ final MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ?
+ type :
+ type.insertParameterTypes(0, ScriptFunction.class);
+ final boolean shouldRecompile = oldOptimismInfo.requestRecompile(re);
+ final boolean canBeDeoptimized;
- final boolean canBeDeoptimized;
- if (fn != null) {
- //is recompiled
- assert optimismInfo == oldOptimismInfo;
- canBeDeoptimized = fn.canBeDeoptimized();
- if (log.isEnabled()) {
- log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ")", canBeDeoptimized ? " can still be deoptimized." : " is completely deoptimized.");
- }
- final MethodHandle newInvoker = oldOptimismInfo.data.lookup(fn);
- invoker = newInvoker.asType(type.changeReturnType(newInvoker.type().returnType()));
- constructor = null; // Will be regenerated when needed
- // Note that we only adjust the switch point after we set the invoker/constructor. This is important.
- if (canBeDeoptimized) {
- // Otherwise, set a new switch point.
- oldOptimismInfo.newOptimisticAssumptions();
- } else {
- // If we got to a point where we no longer have optimistic assumptions, let the optimism info go.
- optimismInfo = null;
- }
- } else {
+ FunctionNode fn = oldOptimismInfo.reparse();
+ final Compiler compiler = oldOptimismInfo.getCompiler(fn, callSiteType, re); //set to non rest-of
+
+ if (!shouldRecompile) {
// It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already
// recompiled a deoptimized version for an inner invocation.
+ // We still need to do the rest of from the beginning
canBeDeoptimized = canBeDeoptimized();
assert !canBeDeoptimized || optimismInfo == oldOptimismInfo;
+ logRecompile("Rest-of compilation [STANDALONE] ", fn, callSiteType, oldOptimismInfo.invalidatedProgramPoints);
+ return restOfHandle(oldOptimismInfo, compiler.compile(fn, CompilationPhases.COMPILE_ALL_RESTOF), canBeDeoptimized);
+ }
+
+ logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, callSiteType, oldOptimismInfo.invalidatedProgramPoints);
+ fn = compiler.compile(fn, CompilationPhases.COMPILE_UPTO_BYTECODE);
+ log.info("Reusable IR generated");
+
+ assert optimismInfo == oldOptimismInfo;
+
+ // compile the rest of the function, and install it
+ log.info("Generating and installing bytecode from reusable IR...");
+ logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, oldOptimismInfo.invalidatedProgramPoints);
+ final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE);
+
+ FunctionNode fn2 = oldOptimismInfo.reparse();
+ fn2 = compiler.compile(fn2, CompilationPhases.COMPILE_UPTO_BYTECODE);
+ log.info("Done.");
+
+ canBeDeoptimized = normalFn.canBeDeoptimized();
+
+ if (log.isEnabled()) {
+ log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ") ", canBeDeoptimized ? " can still be deoptimized." : " is completely deoptimized.");
}
- final MethodHandle restOf = changeReturnType(oldOptimismInfo.compileRestOfMethod(callSiteType, re), Object.class);
+ log.info("Looking up invoker...");
+
+ final MethodHandle newInvoker = oldOptimismInfo.data.lookup(fn);
+ invoker = newInvoker.asType(type.changeReturnType(newInvoker.type().returnType()));
+ constructor = null; // Will be regenerated when needed
+
+ log.info("Done: ", invoker);
+ final MethodHandle restOf = restOfHandle(oldOptimismInfo, compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE_RESTOF), canBeDeoptimized);
+
+ // Note that we only adjust the switch point after we set the invoker/constructor. This is important.
+ if (canBeDeoptimized) {
+ oldOptimismInfo.newOptimisticAssumptions(); // Otherwise, set a new switch point.
+ } else {
+ optimismInfo = null; // If we got to a point where we no longer have optimistic assumptions, let the optimism info go.
+ }
+
+ return restOf;
+ }
+
+ private MethodHandle restOfHandle(final OptimismInfo info, final FunctionNode restOfFunction, final boolean canBeDeoptimized) {
+ assert info != null;
+ assert restOfFunction.getCompileUnit().getUnitClassName().indexOf("restOf") != -1;
+ final MethodHandle restOf =
+ changeReturnType(
+ info.data.lookupWithExplicitType(
+ restOfFunction,
+ MH.type(restOfFunction.getReturnType().getTypeClass(),
+ RewriteException.class)),
+ Object.class);
+
+ if (!canBeDeoptimized) {
+ return restOf;
+ }
+
// If rest-of is itself optimistic, we must make sure that we can repeat a deoptimization if it, too hits an exception.
- return canBeDeoptimized ? MH.catchException(restOf, RewriteException.class, createRewriteExceptionHandler()) : restOf;
+ return MH.catchException(restOf, RewriteException.class, createRewriteExceptionHandler());
+
}
private static class OptimismInfo {
@@ -625,23 +711,34 @@
optimisticAssumptions = new SwitchPoint();
}
- FunctionNode recompile(final MethodType callSiteType, final RewriteException e) {
- final Type retType = e.getReturnType();
+ boolean requestRecompile(final RewriteException e) {
+ final Type retType = e.getReturnType();
final Type previousFailedType = invalidatedProgramPoints.put(e.getProgramPoint(), retType);
+
if (previousFailedType != null && !previousFailedType.narrowerThan(retType)) {
- final StackTraceElement[] stack = e.getStackTrace();
- final String functionId = stack.length == 0 ? data.getName() : stack[0].getClassName() + "." + stack[0].getMethodName();
+ final StackTraceElement[] stack = e.getStackTrace();
+ final String functionId = stack.length == 0 ?
+ data.getName() :
+ stack[0].getClassName() + "." + stack[0].getMethodName();
+
log.info("RewriteException for an already invalidated program point ", e.getProgramPoint(), " in ", functionId, ". This is okay for a recursive function invocation, but a bug otherwise.");
- return null;
+
+ return false;
}
+
SwitchPoint.invalidateAll(new SwitchPoint[] { optimisticAssumptions });
- return data.compile(callSiteType, invalidatedProgramPoints, e.getRuntimeScope(), "Deoptimizing recompilation", data.getDefaultTransform(callSiteType));
+
+ return true;
}
- MethodHandle compileRestOfMethod(final MethodType callSiteType, final RewriteException e) {
+ Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final RewriteException e) {
+ return data.getCompiler(fn, actualCallSiteType, e.getRuntimeScope(), invalidatedProgramPoints, getEntryPoints(e));
+ }
+
+ private static int[] getEntryPoints(final RewriteException e) {
final int[] prevEntryPoints = e.getPreviousContinuationEntryPoints();
final int[] entryPoints;
- if(prevEntryPoints == null) {
+ if (prevEntryPoints == null) {
entryPoints = new int[1];
} else {
final int l = prevEntryPoints.length;
@@ -649,7 +746,11 @@
System.arraycopy(prevEntryPoints, 0, entryPoints, 1, l);
}
entryPoints[0] = e.getProgramPoint();
- return data.compileRestOfMethod(callSiteType, invalidatedProgramPoints, entryPoints, e.getRuntimeScope(), data.getDefaultTransform(callSiteType));
+ return entryPoints;
+ }
+
+ FunctionNode reparse() {
+ return data.reparse();
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Mon May 19 15:29:42 2014 +0200
@@ -63,6 +63,14 @@
return '\'' + name + "' code=" + functions;
}
+ private static MethodType widen(final MethodType cftype) {
+ final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()];
+ for (int i = 0; i < cftype.parameterCount(); i++) {
+ paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
+ }
+ return MH.type(cftype.returnType(), paramTypes);
+ }
+
/**
* Used to find an apply to call version that fits this callsite.
* We cannot just, as in the normal matcher case, return e.g. (Object, Object, int)
@@ -82,15 +90,10 @@
continue;
}
- final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()];
- for (int i = 0; i < cftype.parameterCount(); i++) {
- paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
- }
-
- if (MH.type(cftype.returnType(), paramTypes).equals(type)) {
+ if (widen(cftype).equals(widen(type))) {
return cf;
}
- }
+ }
return null;
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java Mon May 19 15:29:42 2014 +0200
@@ -57,10 +57,8 @@
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
-import jdk.nashorn.internal.codegen.CompilationEnvironment;
-import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
import jdk.nashorn.internal.codegen.Compiler;
-import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.debug.ASTWriter;
@@ -944,7 +942,7 @@
}
if (env._print_parse) {
- getErr().println(new PrintVisitor(functionNode));
+ getErr().println(new PrintVisitor(functionNode, true, false));
}
if (env._parse_only) {
@@ -956,18 +954,17 @@
final CodeSource cs = new CodeSource(url, (CodeSigner[])null);
final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
- final CompilationPhases phases = CompilationEnvironment.CompilationPhases.EAGER;
+ final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
+
final Compiler compiler = new Compiler(
- new CompilationEnvironment(
- this,
- phases.
- makeOptimistic(
- ScriptEnvironment.globalOptimistic()),
- strict),
- installer);
+ this,
+ env,
+ installer,
+ source,
+ functionNode.getSourceURL(),
+ strict | functionNode.isStrict());
- final FunctionNode newFunctionNode = compiler.compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
- script = compiler.install(newFunctionNode);
+ script = compiler.compile(functionNode, phases).getRootClass();
cacheClass(source, script);
return script;
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Mon May 19 15:29:42 2014 +0200
@@ -32,19 +32,16 @@
import java.lang.invoke.MethodType;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
+
import jdk.internal.dynalink.support.NameCodec;
-import jdk.nashorn.internal.codegen.ApplySpecialization;
-import jdk.nashorn.internal.codegen.CompilationEnvironment;
-import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.FunctionSignature;
-import jdk.nashorn.internal.codegen.ParamTypeMap;
+import jdk.nashorn.internal.codegen.TypeMap;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.LexicalContext;
@@ -70,7 +67,7 @@
public static final boolean LAZY_COMPILATION = Options.getBooleanProperty("nashorn.lazy");
/** Prefix used for all recompiled script classes */
- public static final String RECOMPILATION_PREFIX = "Script$Recompilation$";
+ public static final String RECOMPILATION_PREFIX = "Recompilation$";
/** Unique function node id for this function node */
private final int functionNodeId;
@@ -113,9 +110,6 @@
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
- /** Unique id for classes needed to wrap recompiled script functions */
- private static final AtomicInteger RECOMPILE_ID = new AtomicInteger(0);
-
private final DebugLogger log;
private final Map<String, Integer> externalScopeDepths;
@@ -338,7 +332,7 @@
}
}
- private FunctionNode reparse(final String scriptName) {
+ FunctionNode reparse() {
final boolean isProgram = functionNodeId == FunctionNode.FIRST_FUNCTION_ID;
// NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node
final int descPosition = Token.descPosition(token);
@@ -355,145 +349,80 @@
parser.setFunctionName(functionName);
}
- final FunctionNode program = parser.parse(scriptName, descPosition, Token.descLength(token), true);
+ final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true);
// Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a
// single function, extract it from the program.
return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName).setSourceURL(null, sourceURL);
}
- private static String stringifyInvalidations(final Map<Integer, Type> ipp) {
- if (ipp == null) {
- return "";
+ TypeMap typeMap(final MethodType fnCallSiteType) {
+ if (fnCallSiteType == null) {
+ return null;
}
- final StringBuilder sb = new StringBuilder();
- final Iterator<Map.Entry<Integer, Type>> iter = ipp.entrySet().iterator();
- while (iter.hasNext()) {
- final Map.Entry<Integer, Type> entry = iter.next();
- final char bct = entry.getValue().getBytecodeStackType();
- sb.append('[').
- append(entry.getKey()).
- append("->").
- append(bct == 'A' ? 'O' : bct).
- append(']');
- if (iter.hasNext()) {
- sb.append(' ');
- }
+
+ if (CompiledFunction.isVarArgsType(fnCallSiteType)) {
+ return null;
}
- return sb.toString();
+
+ return new TypeMap(functionNodeId, explicitParams(fnCallSiteType), needsCallee());
}
- private FunctionNodeTransform getNopTransform() {
- return new FunctionNodeTransform() {
- @Override
- FunctionNode apply(final FunctionNode functionNode) {
- return functionNode;
- }
-
- @Override
- int getArity() {
- return RecompilableScriptFunctionData.this.getArity();
- }
-
- @Override
- public String toString() {
- return "[NopTransform]";
- }
- };
+ private static ScriptObject newLocals(final ScriptObject runtimeScope) {
+ final ScriptObject locals = Global.newEmptyInstance();
+ locals.setProto(runtimeScope);
+ return locals;
}
- FunctionNodeTransform getDefaultTransform(final MethodType callSiteType) {
- return new ApplyToCallTransform(this, callSiteType);
- }
-
- private ParamTypeMap typeMap(final MethodType fnCallSiteType, final FunctionNodeTransform tr) {
- if (isVariableArity() && !tr.wasTransformed()) {
- return null;
- }
- return new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType, tr.getArity()));
+ private Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
+ return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
}
- MethodHandle compileRestOfMethod(final MethodType fnCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final int[] continuationEntryPoints, final ScriptObject runtimeScope, final FunctionNodeTransform tr) {
- if (log.isEnabled()) {
- log.info("Rest-of compilation of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
- }
-
- final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet() + "$restOf";
-
- FunctionNode fn = tr.apply(reparse(scriptName));
- final ParamTypeMap ptm = typeMap(fnCallSiteType, tr);
-
- final Compiler compiler = new Compiler(
- new CompilationEnvironment(
- context,
- CompilationPhases.EAGER.makeOptimistic(),
- isStrict(),
- this,
- runtimeScope,
- ptm,
- invalidatedProgramPoints,
- continuationEntryPoints,
- true
- ),
- installer);
-
- fn = compiler.compile(scriptName, fn);
- compiler.install(fn);
-
- // look up the rest of method
- return lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
+ Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType, final ScriptObject runtimeScope, final Map<Integer, Type> ipp, final int[] cep) {
+ return new Compiler(
+ context,
+ context.getEnv(),
+ installer,
+ functionNode.getSource(), // source
+ functionNode.getSourceURL(),
+ isStrict() | functionNode.isStrict(), // is strict
+ true, // is on demand
+ this, // compiledFunction, i.e. this RecompilableScriptFunctionData
+ typeMap(actualCallSiteType), // type map
+ ipp, // invalidated program points
+ cep, // continuation entry points
+ runtimeScope); // runtime scope
}
- private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final FunctionNodeTransform tr) {
+ private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
// We're creating an empty script object for holding local variables. AssignSymbols will populate it with
// explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and
// CompilationEnvironment#declareLocalSymbol()).
- final ScriptObject locals = Global.newEmptyInstance();
- locals.setProto(runtimeScope);
-
- return compile(actualCallSiteType, null, locals, "Type specialized compilation", tr);
- }
-
- FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final ScriptObject runtimeScope, final String reason, final FunctionNodeTransform tr) {
- final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet();
- final MethodType fnCallSiteType = actualCallSiteType == null ? null : actualCallSiteType.changeParameterType(0, ScriptFunction.class);
if (log.isEnabled()) {
- log.info(reason, " of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
+ log.info("Type specialization of '", functionName, "' signature: ", actualCallSiteType);
}
- FunctionNode fn = tr.apply(reparse(scriptName));
-
- final ParamTypeMap ptm = fnCallSiteType == null ? null : typeMap(fnCallSiteType, tr);
-
- final CompilationPhases phases = CompilationPhases.EAGER;
- final Compiler compiler = new Compiler(
- new CompilationEnvironment(
- context,
- phases.makeOptimistic(ScriptEnvironment.globalOptimistic()),
- isStrict(),
- this,
- runtimeScope,
- ptm,
- invalidatedProgramPoints,
- true),
- installer);
-
- fn = compiler.compile(scriptName, fn);
- compiler.install(fn);
-
- return fn;
+ final FunctionNode fn = reparse();
+ return getCompiler(fn, actualCallSiteType, runtimeScope).compile(fn, CompilationPhases.COMPILE_ALL);
}
- private static MethodType explicitParams(final MethodType callSiteType, final int arity) {
+ Context getContext() {
+ return context;
+ }
+
+ private MethodType explicitParams(final MethodType callSiteType) {
+ if (CompiledFunction.isVarArgsType(callSiteType)) {
+ return null;
+ }
+
final MethodType noCalleeThisType = callSiteType.dropParameterTypes(0, 2); // (callee, this) is always in call site type
final int callSiteParamCount = noCalleeThisType.parameterCount();
// Widen parameters of reference types to Object as we currently don't care for specialization among reference
// types. E.g. call site saying (ScriptFunction, Object, String) should still link to (ScriptFunction, Object, Object)
- final int minParams = Math.min(callSiteParamCount, arity);
final Class<?>[] paramTypes = noCalleeThisType.parameterArray();
boolean changed = false;
- for (int i = 0; i < minParams; ++i) {
+ for (int i = 0; i < paramTypes.length; ++i) {
final Class<?> paramType = paramTypes[i];
if (!(paramType.isPrimitive() || paramType == Object.class)) {
paramTypes[i] = Object.class;
@@ -502,14 +431,10 @@
}
final MethodType generalized = changed ? MethodType.methodType(noCalleeThisType.returnType(), paramTypes) : noCalleeThisType;
- // Match arity
- if (callSiteParamCount < arity) {
- return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(arity - callSiteParamCount, Object.class));
- } else if (callSiteParamCount > arity) {
- return generalized.dropParameterTypes(arity, callSiteParamCount);
- } else {
- return generalized;
+ if (callSiteParamCount < getArity()) {
+ return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(getArity() - callSiteParamCount, Object.class));
}
+ return generalized;
}
private FunctionNode extractFunctionFromScript(final FunctionNode script) {
@@ -531,10 +456,12 @@
}
MethodHandle lookup(final FunctionNode fn) {
+ final MethodType type = new FunctionSignature(fn).getMethodType();
+ log.info("Looking up ", DebugLogger.quote(fn.getName()), " type=", type);
return lookupWithExplicitType(fn, new FunctionSignature(fn).getMethodType());
}
- private MethodHandle lookupWithExplicitType(final FunctionNode fn, final MethodType targetType) {
+ MethodHandle lookupWithExplicitType(final FunctionNode fn, final MethodType targetType) {
return lookupCodeMethod(fn.getCompileUnit(), targetType);
}
@@ -615,10 +542,11 @@
synchronized (code) {
CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
if (existingBest == null) {
- existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, getNopTransform()), callSiteType);
+ existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
}
assert existingBest != null;
+ //we are calling a vararg method with real args
boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
//if the best one is an apply to call, it has to match the callsite exactly
@@ -632,7 +560,7 @@
}
if (applyToCall) {
- final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope, new ApplyToCallTransform(this, callSiteType));
+ final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope);
if (fn.hasOptimisticApplyToCall()) { //did the specialization work
existingBest = addCode(fn, callSiteType);
}
@@ -725,66 +653,4 @@
return true;
}
-
- private static abstract class FunctionNodeTransform {
-
- abstract int getArity();
-
- boolean wasTransformed() {
- return false;
- }
-
- abstract FunctionNode apply(final FunctionNode functionNode);
- }
-
- /**
- * Helper class for transforming apply calls to calls
- */
- private static class ApplyToCallTransform extends FunctionNodeTransform {
- private final RecompilableScriptFunctionData data;
- private final MethodType actualCallSiteType;
- private int arity;
- private FunctionNode initialFunctionNode;
- private FunctionNode transformedFunctionNode;
-
- ApplyToCallTransform(final RecompilableScriptFunctionData data, final MethodType actualCallSiteType) {
- this.data = data;
- this.actualCallSiteType = actualCallSiteType;
- this.arity = data.getArity();
- }
-
- @Override
- public FunctionNode apply(final FunctionNode functionNode) {
- this.initialFunctionNode = functionNode;
- if (data.isVariableArity() && !CompiledFunction.isVarArgsType(actualCallSiteType)) {
- final ApplySpecialization spec = new ApplySpecialization(data.context, data, functionNode, actualCallSiteType);
- if (spec.transform()) {
- setTransformedFunctionNode(spec.getFunctionNode());
- return transformedFunctionNode;
- }
- }
- return functionNode;
- }
-
- private void setTransformedFunctionNode(final FunctionNode transformedFunctionNode) {
- this.transformedFunctionNode = transformedFunctionNode;
- assert !transformedFunctionNode.isVarArg();
- this.arity = transformedFunctionNode.getParameters().size();
- }
-
- @Override
- public int getArity() {
- return arity;
- }
-
- @Override
- public boolean wasTransformed() {
- return initialFunctionNode != transformedFunctionNode;
- }
-
- @Override
- public String toString() {
- return "[ApplyToCallTransform]";
- }
- }
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/RewriteException.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RewriteException.java Mon May 19 15:29:42 2014 +0200
@@ -230,7 +230,7 @@
return (Object[])obj;
}
- assert obj instanceof int[] || obj instanceof long[] || obj instanceof double[] : obj.getClass().getName();
+ assert obj instanceof int[] || obj instanceof long[] || obj instanceof double[] : obj + " is " + obj.getClass().getName();
final int l = Array.getLength(obj);
final Object[] out = new Object[l];
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Mon May 19 15:29:42 2014 +0200
@@ -505,11 +505,12 @@
final String name = getName();
final boolean isUnstable = request.isCallSiteUnstable();
final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
-
final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name);
final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name);
- if (isUnstable && !(isApply || isCall)) {
+ final boolean isApplyOrCall = isCall | isApply;
+
+ if (isUnstable && !isApplyOrCall) {
//megamorphic - replace call with apply
final MethodHandle handle;
//ensure that the callsite is vararg so apply can consume it
@@ -534,7 +535,7 @@
MethodHandle guard = null;
// Special handling of Function.apply and Function.call. Note we must be invoking
- if ((isApply || isCall) && !isUnstable) {
+ if (isApplyOrCall && !isUnstable) {
final Object[] args = request.getArguments();
if (Bootstrap.isCallable(args[1])) {
return createApplyOrCallCall(isApply, desc, request, args);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptLoader.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptLoader.java Mon May 19 15:29:42 2014 +0200
@@ -49,7 +49,7 @@
}
@Override
- protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
checkPackageAccess(name);
if (name.startsWith(NASHORN_PKG_PREFIX)) {
return context.getSharedLoader().loadClass(name);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Mon May 19 15:29:42 2014 +0200
@@ -2260,21 +2260,22 @@
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
if (find != null) {
- final Object value = find.getObjectValue();
- ScriptFunction func = null;
- MethodHandle methodHandle = null;
+ final Object value = find.getObjectValue();
+ ScriptFunction func = null;
+ MethodHandle mh = null;
if (value instanceof ScriptFunction) {
func = (ScriptFunction)value;
- methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
+ mh = getCallMethodHandle(func, desc.getMethodType(), name);
}
- if (methodHandle != null) {
- if (scopeAccess && func.isStrict()) {
- methodHandle = bindTo(methodHandle, UNDEFINED);
+ if (mh != null) {
+ assert func != null;
+ if (scopeAccess && func != null && func.isStrict()) {
+ mh = bindTo(mh, UNDEFINED);
}
return new GuardedInvocation(
- methodHandle,
+ mh,
//TODO this always does a scriptobject check
getKnownFunctionPropertyGuard(
getMap(),
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java Mon May 19 15:29:42 2014 +0200
@@ -39,7 +39,7 @@
* It can be invoked repeatedly to create multiple adapter classes from the same bytecode; adapter classes that have
* class-level overrides must be re-created for every set of such overrides. Note that while this class is named
* "class loader", it does not, in fact, extend {@code ClassLoader}, but rather uses them internally. Instances of this
- * class are normally created by {@link JavaAdapterBytecodeGenerator}.
+ * class are normally created by {@code JavaAdapterBytecodeGenerator}.
*/
final class JavaAdapterClassLoader {
private static final AccessControlContext CREATE_LOADER_ACC_CTXT = ClassAndLoader.createPermAccCtxt("createClassLoader");
--- a/nashorn/src/jdk/nashorn/tools/Shell.java Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/src/jdk/nashorn/tools/Shell.java Mon May 19 15:29:42 2014 +0200
@@ -39,7 +39,7 @@
import java.util.ResourceBundle;
import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.codegen.Compiler;
-import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
@@ -259,8 +259,14 @@
}
//null - pass no code installer - this is compile only
- new Compiler(env).
- compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
+ new Compiler(
+ context,
+ env,
+ null,
+ functionNode.getSource(),
+ functionNode.getSourceURL(),
+ env._strict | functionNode.isStrict()).
+ compile(functionNode, CompilationPhases.COMPILE_ALL_NO_INSTALL);
}
} finally {
env.getOut().flush();
--- a/nashorn/test/script/trusted/JDK-8006529.js Thu May 15 15:28:51 2014 +0200
+++ b/nashorn/test/script/trusted/JDK-8006529.js Mon May 19 15:29:42 2014 +0200
@@ -42,7 +42,9 @@
var forName = java.lang.Class["forName(String)"];
var Parser = forName("jdk.nashorn.internal.parser.Parser").static
var Compiler = forName("jdk.nashorn.internal.codegen.Compiler").static
+var CompilationPhases = forName("jdk.nashorn.internal.codegen.Compiler$CompilationPhases").static;
var Context = forName("jdk.nashorn.internal.runtime.Context").static
+var CodeInstaller = forName("jdk.nashorn.internal.runtime.CodeInstaller").static
var ScriptEnvironment = forName("jdk.nashorn.internal.runtime.ScriptEnvironment").static
var Source = forName("jdk.nashorn.internal.runtime.Source").static
var FunctionNode = forName("jdk.nashorn.internal.ir.FunctionNode").static
@@ -54,9 +56,11 @@
var ThrowErrorManager = forName("jdk.nashorn.internal.runtime.Context$ThrowErrorManager").static
var ErrorManager = forName("jdk.nashorn.internal.runtime.ErrorManager").static
var Debug = forName("jdk.nashorn.internal.runtime.Debug").static
+var String = forName("java.lang.String").static
+var boolean = Java.type("boolean");
var parseMethod = Parser.class.getMethod("parse");
-var compileMethod = Compiler.class.getMethod("compile", FunctionNode.class);
+var compileMethod = Compiler.class.getMethod("compile", FunctionNode.class, CompilationPhases.class);
var getBodyMethod = FunctionNode.class.getMethod("getBody");
var getStatementsMethod = Block.class.getMethod("getStatements");
var getInitMethod = VarNode.class.getMethod("getInit");
@@ -65,6 +69,7 @@
var lhsMethod = BinaryNode.class.getMethod("lhs")
var binaryRhsMethod = BinaryNode.class.getMethod("rhs")
var debugIdMethod = Debug.class.getMethod("id", java.lang.Object.class)
+var compilePhases = CompilationPhases.class.getField("COMPILE_UPTO_BYTECODE").get(null);
// These are method names of methods in FunctionNode class
var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'usesSelfSymbol', 'isSplit', 'hasEval', 'allVarsInScope', 'isStrict']
@@ -115,22 +120,23 @@
var SourceConstructor = Source.class.getConstructor(java.lang.String.class, java.lang.String.class)
var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
-var CompilerConstructor = Compiler.class.getConstructor(ScriptEnvironment.class)
+var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, String.class, boolean.class);
// compile(script) -- compiles a script specified as a string with its
// source code, returns a jdk.nashorn.internal.ir.FunctionNode object
// representing it.
-function compile(source) {
+function compile(source, phases) {
var source = SourceConstructor.newInstance("<no name>", source);
- var env = getEnvMethod.invoke(getContextMethod.invoke(null))
+ var ctxt = getContextMethod.invoke(null);
+ var env = getEnvMethod.invoke(ctxt);
var parser = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance());
var func = parseMethod.invoke(parser);
- var compiler = CompilerConstructor.newInstance(env);
+ var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, null, false);
- return compileMethod.invoke(compiler, func);
+ return compileMethod.invoke(compiler, func, phases);
};
var allAssertions = (function() {
@@ -166,8 +172,8 @@
// assertions are true in the first function in the given script; "script"
// is a string with the source text of the script.
function testFirstFn(script) {
- arguments[0] = getFirstFunction(compile(script))
- test.apply(null, arguments)
+ arguments[0] = getFirstFunction(compile(script, compilePhases));
+ test.apply(null, arguments);
}
// ---------------------------------- ACTUAL TESTS START HERE --------------