mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-02 06:28:23 +00:00
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation
Reviewed-by: hannesw, attila
This commit is contained in:
parent
b0873269dc
commit
26308e1cd6
@ -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 @@ $FLAGS \
|
||||
-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 \
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ import java.util.Deque;
|
||||
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.Node;
|
||||
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 @@ import jdk.nashorn.internal.runtime.options.Options;
|
||||
*/
|
||||
|
||||
@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 final Compiler compiler;
|
||||
|
||||
private final Set<Integer> changed = new HashSet<>();
|
||||
|
||||
private final Deque<List<IdentNode>> explodedArguments = new ArrayDeque<>();
|
||||
|
||||
private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
|
||||
|
||||
private boolean changed;
|
||||
|
||||
private boolean finished;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -127,14 +118,6 @@ public final class ApplySpecialization implements Loggable {
|
||||
return context.getLogger(this.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -143,12 +126,12 @@ public final class ApplySpecialization implements Loggable {
|
||||
*
|
||||
* @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 @@ public final class ApplySpecialization implements Loggable {
|
||||
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) {
|
||||
return false;
|
||||
@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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
return callNode;
|
||||
}
|
||||
|
||||
private boolean pushExplodedArgs(final FunctionNode functionNode) {
|
||||
int start = 0;
|
||||
|
||||
if (data.needsCallee()) {
|
||||
final MethodType actualCallSiteType = compiler.getCallSiteType(functionNode);
|
||||
if (actualCallSiteType == null) {
|
||||
return false;
|
||||
}
|
||||
assert actualCallSiteType.parameterType(actualCallSiteType.parameterCount() - 1) != Object[].class : "error vararg callsite passed to apply2call " + functionNode.getName() + " " + actualCallSiteType;
|
||||
|
||||
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 @@ public final class ApplySpecialization implements Loggable {
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
if (!USE_APPLY2CALL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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 (argumentsEscape(functionNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
functionNode = functionNode.clearFlag(null, FunctionNode.USES_ARGUMENTS).
|
||||
setFlag(null, FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION).
|
||||
setParameters(null, newParams);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
explodedArguments.pop();
|
||||
|
||||
return finish();
|
||||
return newFunctionNode;
|
||||
}
|
||||
|
||||
private static boolean isApply(final CallNode callNode) {
|
||||
|
||||
@ -152,12 +152,12 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
|
||||
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 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
});
|
||||
}
|
||||
|
||||
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 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
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 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveFunctionNode(FunctionNode functionNode) {
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
|
||||
return markProgramBlock(
|
||||
removeUnusedSlots(
|
||||
@ -842,8 +842,8 @@ final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -253,16 +253,20 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
// 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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
void getProto() {
|
||||
//empty
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -443,7 +476,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
//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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterSUB(UnaryNode unaryNode) {
|
||||
public boolean enterSUB(final UnaryNode unaryNode) {
|
||||
loadSUB(unaryNode, resultBounds);
|
||||
return false;
|
||||
}
|
||||
@ -877,19 +910,19 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
@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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterVOID(UnaryNode unaryNode) {
|
||||
public boolean enterVOID(final UnaryNode unaryNode) {
|
||||
loadVOID(unaryNode, resultBounds);
|
||||
return false;
|
||||
}
|
||||
@ -1041,7 +1074,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
private boolean useOptimisticTypes() {
|
||||
return !lc.inSplitNode() && compiler.getCompilationEnvironment().useOptimisticTypes();
|
||||
return !lc.inSplitNode() && compiler.useOptimisticTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1438,7 +1471,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* @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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
} 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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* 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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* 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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
method.begin();
|
||||
|
||||
if (isRestOf) {
|
||||
if (isRestOf()) {
|
||||
final ContinuationInfo ci = new ContinuationInfo();
|
||||
fnIdToContinuationInfo.put(fnId, ci);
|
||||
method.gotoLoopStart(ci.getHandlerLabel());
|
||||
@ -1868,8 +1905,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
final List<MapTuple<Expression>> tuples = new ArrayList<>();
|
||||
final List<PropertyNode> gettersSetters = new ArrayList<>();
|
||||
Expression protoNode = null;
|
||||
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 class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* @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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return false;
|
||||
}
|
||||
|
||||
final CaseNode defaultCase = switchNode.getDefaultCase();
|
||||
final Label breakLabel = switchNode.getBreakLabel();
|
||||
final int liveLocalsOnBreak = method.getUsedSlotsWithLiveTemporaries();
|
||||
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) {
|
||||
if (defaultCase != null && cases.size() == 1) {
|
||||
// default case only
|
||||
assert cases.get(0) == defaultCase;
|
||||
loadAndDiscard(expression);
|
||||
@ -2822,7 +2858,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
// 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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
method.ifne(caseNode.getEntry());
|
||||
}
|
||||
}
|
||||
if(hasDefault) {
|
||||
|
||||
if (defaultCase != null) {
|
||||
method._goto(defaultLabel);
|
||||
} else {
|
||||
method.beforeJoinPoint(switchNode);
|
||||
@ -3244,9 +3281,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
method._try(tryLabel, endLabel, catchLabel);
|
||||
}
|
||||
|
||||
boolean reachable = method.isReachable();
|
||||
final boolean reachable = method.isReachable();
|
||||
if(reachable) {
|
||||
popScope();
|
||||
if(bodyCanThrow) {
|
||||
@ -3955,13 +3995,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
@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 class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
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 class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
private void generateContinuationHandler() {
|
||||
if (!compiler.getCompilationEnvironment().isCompileRestOf()) {
|
||||
if (!isRestOf()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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.PARSED;
|
||||
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 @@ enum CompilationPhase {
|
||||
* 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 @@ enum CompilationPhase {
|
||||
* 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 @@ enum CompilationPhase {
|
||||
* 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 @@ enum CompilationPhase {
|
||||
* 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 @@ enum CompilationPhase {
|
||||
|
||||
@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]";
|
||||
}
|
||||
},
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Scope Depth Computation]";
|
||||
}
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Optimistic Type Assignment]";
|
||||
}
|
||||
},
|
||||
|
||||
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]";
|
||||
return "'Local Variable Type Calculation'";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
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;
|
||||
|
||||
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 "'Reuse Compile Units'";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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)) {
|
||||
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)) {
|
||||
|
||||
@Override
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
final ScriptEnvironment env = compiler.getEnv();
|
||||
|
||||
if (env._print_lower_ast) {
|
||||
env.getErr().println(new ASTWriter(fn));
|
||||
}
|
||||
|
||||
if (env._print_lower_parse) {
|
||||
env.getErr().println(new PrintVisitor(fn));
|
||||
}
|
||||
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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
|
||||
@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 @@ enum CompilationPhase {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
startTime = System.currentTimeMillis();
|
||||
return functionNode;
|
||||
}
|
||||
assert pre != null;
|
||||
|
||||
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 @@ enum CompilationPhase {
|
||||
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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -28,6 +28,8 @@ package jdk.nashorn.internal.codegen;
|
||||
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 @@ import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
|
||||
*/
|
||||
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 @@ public final class CompileUnit implements Comparable<CompileUnit> {
|
||||
|
||||
@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.classEmitter = classEmitter;
|
||||
this.weight = initialWeight;
|
||||
this.classEmitter = classEmitter;
|
||||
}
|
||||
|
||||
static Set<CompileUnit> createCompileUnitSet() {
|
||||
return new TreeSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -126,7 +126,7 @@ public final class CompileUnit implements Comparable<CompileUnit> {
|
||||
}
|
||||
|
||||
void initializeFunctionsCode() {
|
||||
for(final FunctionInitializer init: functionInitializers) {
|
||||
for(final FunctionInitializer init : functionInitializers) {
|
||||
init.initializeCode();
|
||||
}
|
||||
functionInitializers = Collections.emptySet();
|
||||
@ -173,13 +173,25 @@ public final class CompileUnit implements Comparable<CompileUnit> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,43 +27,39 @@ package jdk.nashorn.internal.codegen;
|
||||
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
/** Name of the objects package */
|
||||
public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
|
||||
|
||||
private Source source;
|
||||
private final Source source;
|
||||
|
||||
private String sourceName;
|
||||
private String sourceURL;
|
||||
private final String sourceName;
|
||||
|
||||
private final String sourceURL;
|
||||
|
||||
private final boolean optimistic;
|
||||
|
||||
private final Map<String, byte[]> bytecode;
|
||||
|
||||
@ -96,20 +95,183 @@ public final class Compiler implements Loggable {
|
||||
|
||||
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.
|
||||
@ -125,29 +287,80 @@ public final class Compiler implements Loggable {
|
||||
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();
|
||||
|
||||
if (functionNode.isStrict()) {
|
||||
compilationEnv.setIsStrict(true);
|
||||
}
|
||||
// per instance
|
||||
private final AtomicInteger nextCompileUnitId = new AtomicInteger(0);
|
||||
|
||||
final String name = className + '$' + safeSourceName(functionNode.getSource());
|
||||
final String uniqueName = functionNode.uniqueName(name);
|
||||
private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0);
|
||||
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
log.warning("Running without optimistic types. This is a configuration that may be deprecated.");
|
||||
}
|
||||
}
|
||||
|
||||
this.optimistic = ScriptEnvironment.globalOptimistic();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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();
|
||||
}
|
||||
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugLogger initLogger(final Context context) {
|
||||
return context.getLogger(this.getClass());
|
||||
public DebugLogger initLogger(final Context ctxt) {
|
||||
return ctxt.getLogger(this.getClass());
|
||||
}
|
||||
|
||||
private void printMemoryUsage(final String phaseName, final FunctionNode functionNode) {
|
||||
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);
|
||||
}
|
||||
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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('/', '.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -27,9 +27,12 @@ package jdk.nashorn.internal.codegen;
|
||||
|
||||
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 @@ import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
* 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 @@ class ConstantData {
|
||||
return index;
|
||||
}
|
||||
|
||||
Collection<Object> getConstants() {
|
||||
return Collections.unmodifiableList(constants);
|
||||
}
|
||||
|
||||
Object[] toArray() {
|
||||
return constants.toArray();
|
||||
}
|
||||
|
||||
@ -60,7 +60,6 @@ import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
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 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
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 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
if (env.isOnDemandCompilation()) {
|
||||
if (compiler.isOnDemandCompilation()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -189,8 +187,8 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
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 class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
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 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
fnIdToNestedFunctions.get(parentFn.getId()).put(fnId, data);
|
||||
}
|
||||
} else {
|
||||
env.setData(data);
|
||||
compiler.setData(data);
|
||||
}
|
||||
|
||||
if (isDynamicScopeBoundary(functionNode)) {
|
||||
@ -269,7 +267,7 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
|
||||
@Override
|
||||
public boolean enterBlock(final Block block) {
|
||||
if (env.isOnDemandCompilation()) {
|
||||
if (compiler.isOnDemandCompilation()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -290,7 +288,7 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
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 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
|
||||
@Override
|
||||
public Node leaveBlock(final Block block) {
|
||||
if (env.isOnDemandCompilation()) {
|
||||
if (compiler.isOnDemandCompilation()) {
|
||||
return block;
|
||||
}
|
||||
if (isDynamicScopeBoundary(block)) {
|
||||
|
||||
@ -60,9 +60,9 @@ final class FoldConstants extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
|
||||
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
|
||||
|
||||
@ -30,6 +30,7 @@ import java.util.BitSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
|
||||
/**
|
||||
@ -93,7 +94,7 @@ public final class Label {
|
||||
}
|
||||
|
||||
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 @@ public final class Label {
|
||||
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 @@ public final class Label {
|
||||
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 @@ public final class Label {
|
||||
* @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 @@ public final class Label {
|
||||
this.id = label.id;
|
||||
}
|
||||
|
||||
|
||||
jdk.internal.org.objectweb.asm.Label getLabel() {
|
||||
if (this.label == null) {
|
||||
this.label = new jdk.internal.org.objectweb.asm.Label();
|
||||
|
||||
@ -106,7 +106,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
}
|
||||
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
// 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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
// 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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
@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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
// 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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
// 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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
@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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
@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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
}
|
||||
|
||||
@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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveLiteralNode(LiteralNode<?> literalNode) {
|
||||
public Node leaveLiteralNode(final LiteralNode<?> literalNode) {
|
||||
if(literalNode instanceof ArrayLiteralNode) {
|
||||
((ArrayLiteralNode)literalNode).analyze();
|
||||
}
|
||||
@ -1370,9 +1371,9 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
});
|
||||
|
||||
this.installer = compiler.getCodeInstaller();
|
||||
this.log = initLogger(compiler.getCompilationEnvironment().getContext());
|
||||
this.log = initLogger(compiler.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -268,12 +268,12 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
}
|
||||
|
||||
@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 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
}
|
||||
|
||||
@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 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
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;
|
||||
|
||||
@ -66,16 +66,16 @@ import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
*/
|
||||
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 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
}
|
||||
|
||||
@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 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
|
||||
@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 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
}
|
||||
|
||||
@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 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
196
nashorn/src/jdk/nashorn/internal/codegen/TypeEvaluator.java
Normal file
196
nashorn/src/jdk/nashorn/internal/codegen/TypeEvaluator.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
152
nashorn/src/jdk/nashorn/internal/codegen/TypeMap.java
Normal file
152
nashorn/src/jdk/nashorn/internal/codegen/TypeMap.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
@ -69,16 +69,18 @@ public final class AccessNode extends BaseNode {
|
||||
}
|
||||
|
||||
@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 @@ public final class AccessNode extends BaseNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessNode setProgramPoint(int programPoint) {
|
||||
public AccessNode setProgramPoint(final int programPoint) {
|
||||
if (this.programPoint == programPoint) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ public abstract class BaseNode extends Expression implements FunctionCall, Optim
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(Function<Symbol, Type> localVariableTypes) {
|
||||
public Type getType(final Function<Symbol, Type> localVariableTypes) {
|
||||
return type == null ? getMostPessimisticType() : type;
|
||||
}
|
||||
|
||||
|
||||
@ -143,7 +143,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
||||
|
||||
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 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
||||
}
|
||||
}
|
||||
|
||||
private static Type booleanToInt(Type type) {
|
||||
private static Type booleanToInt(final Type type) {
|
||||
return type == Type.BOOLEAN ? Type.INT : type;
|
||||
}
|
||||
|
||||
@ -291,7 +291,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryNode setAssignmentDest(Expression n) {
|
||||
public BinaryNode setAssignmentDest(final Expression n) {
|
||||
return setLHS(n);
|
||||
}
|
||||
|
||||
@ -377,7 +377,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
||||
}
|
||||
|
||||
@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 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
||||
sb.append('(');
|
||||
}
|
||||
|
||||
lhs().toString(sb);
|
||||
lhs().toString(sb, printType);
|
||||
|
||||
if (lhsParen) {
|
||||
sb.append(')');
|
||||
@ -420,7 +420,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
||||
if (rhsParen) {
|
||||
sb.append('(');
|
||||
}
|
||||
rhs().toString(sb);
|
||||
rhs().toString(sb, printType);
|
||||
if (rhsParen) {
|
||||
sb.append(')');
|
||||
}
|
||||
@ -531,7 +531,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryNode setType(Type type) {
|
||||
public BinaryNode setType(final Type type) {
|
||||
if (this.type == type) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
|
||||
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 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
|
||||
}
|
||||
|
||||
@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 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
|
||||
|
||||
@Override
|
||||
public List<Label> getLabels() {
|
||||
return Collections.singletonList(breakLabel);
|
||||
return Collections.unmodifiableList(Arrays.asList(entryLabel, breakLabel));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -70,7 +70,7 @@ public class BlockStatement extends Statement {
|
||||
* @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 @@ public class BlockStatement extends Statement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
block.toString(sb);
|
||||
public void toString(final StringBuilder sb, final boolean printType) {
|
||||
block.toString(sb, printType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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 interface BreakableNode extends LexicalContextNode, JoinPredecessor {
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
@ -91,11 +91,11 @@ abstract class BreakableStatement extends LexicalContextStatement implements Bre
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ public final class CallNode extends LexicalContextExpression implements Optimist
|
||||
}
|
||||
|
||||
@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 @@ public final class CallNode extends LexicalContextExpression implements Optimist
|
||||
}
|
||||
|
||||
@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 @@ public final class CallNode extends LexicalContextExpression implements Optimist
|
||||
first = false;
|
||||
}
|
||||
|
||||
arg.toString(sb);
|
||||
arg.toString(sb, printType);
|
||||
}
|
||||
|
||||
sb.append(')');
|
||||
|
||||
@ -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 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* 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 @@ public final class CaseNode extends Node implements JoinPredecessor {
|
||||
}
|
||||
|
||||
@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 @@ public final class CaseNode extends Node implements JoinPredecessor {
|
||||
}
|
||||
return new CaseNode(this, test, body, conversion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Label> getLabels() {
|
||||
return Collections.unmodifiableList(Collections.singletonList(entry));
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,13 +95,13 @@ public final class CatchNode extends Statement {
|
||||
}
|
||||
|
||||
@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(')');
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ public final class EmptyNode extends Statement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
public void toString(final StringBuilder sb, final boolean printTypes) {
|
||||
sb.append(';');
|
||||
}
|
||||
|
||||
|
||||
@ -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 @@ import jdk.nashorn.internal.codegen.types.Type;
|
||||
*
|
||||
*/
|
||||
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 @@ public abstract class Expression extends Node {
|
||||
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 @@ public abstract class Expression extends Node {
|
||||
* @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 @@ public abstract class Expression extends Node {
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,8 +71,8 @@ public final class ExpressionStatement extends Statement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
expression.toString(sb);
|
||||
public void toString(final StringBuilder sb, final boolean printTypes) {
|
||||
expression.toString(sb, printTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -71,7 +71,7 @@ public final class ForNode extends LoopNode {
|
||||
}
|
||||
|
||||
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 @@ public final class ForNode extends LoopNode {
|
||||
}
|
||||
|
||||
@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 @@ public final class ForNode extends LoopNode {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ import java.util.List;
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
/** 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
this.compileUnit = null;
|
||||
this.body = null;
|
||||
this.thisProperties = 0;
|
||||
this.rootClass = null;
|
||||
}
|
||||
|
||||
private FunctionNode(
|
||||
@ -305,8 +319,10 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
* @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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
* @return true of the node is in the given state
|
||||
*/
|
||||
public boolean hasState(final EnumSet<CompilationState> state) {
|
||||
return compilationState.equals(state);
|
||||
//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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
}
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
}
|
||||
|
||||
@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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
|
||||
if (ident != null) {
|
||||
sb.append(' ');
|
||||
ident.toString(sb);
|
||||
ident.toString(sb, printTypes);
|
||||
}
|
||||
|
||||
sb.append('(');
|
||||
@ -482,7 +545,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
* @return new function node if body changed, same if not
|
||||
*/
|
||||
public FunctionNode setBody(final LexicalContext lc, final Block body) {
|
||||
if(this.body == body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters, 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
if (this.lastToken == lastToken) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
|
||||
return Node.replaceInLexicalContext(
|
||||
lc,
|
||||
this,
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
flags,
|
||||
sourceURL,
|
||||
name,
|
||||
returnType,
|
||||
compileUnit,
|
||||
compilationState,
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -793,7 +919,22 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
if (this.name.equals(name)) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
|
||||
return Node.replaceInLexicalContext(
|
||||
lc,
|
||||
this,
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
flags,
|
||||
sourceURL,
|
||||
name,
|
||||
returnType,
|
||||
compileUnit,
|
||||
compilationState,
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -844,7 +985,22 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
if (this.parameters == parameters) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, thisProperties));
|
||||
return Node.replaceInLexicalContext(
|
||||
lc,
|
||||
this,
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
flags,
|
||||
sourceURL,
|
||||
name,
|
||||
returnType,
|
||||
compileUnit,
|
||||
compilationState,
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -874,7 +1030,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(Function<Symbol, Type> localVariableTypes) {
|
||||
public Type getType(final Function<Symbol, Type> localVariableTypes) {
|
||||
return FUNCTION_TYPE;
|
||||
}
|
||||
|
||||
@ -922,7 +1078,8 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
compilationState,
|
||||
body,
|
||||
parameters,
|
||||
thisProperties
|
||||
thisProperties,
|
||||
rootClass
|
||||
));
|
||||
}
|
||||
|
||||
@ -954,7 +1111,22 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
if (this.compileUnit == compileUnit) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, 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 final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
public Symbol compilerConstant(final CompilerConstants cc) {
|
||||
return body.getExistingSymbol(cc.symbolName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root class that this function node compiles to
|
||||
* @return root class
|
||||
*/
|
||||
public Class<?> getRootClass() {
|
||||
return rootClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the root class that this function is compiled to
|
||||
* @see Compiler
|
||||
* @param lc lexical context
|
||||
* @param rootClass root class
|
||||
* @return function node or a new one if state was changed
|
||||
*/
|
||||
public FunctionNode setRootClass(final LexicalContext lc, final Class<?> rootClass) {
|
||||
if (this.rootClass == rootClass) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(
|
||||
lc,
|
||||
this,
|
||||
new FunctionNode(
|
||||
this,
|
||||
lastToken,
|
||||
flags,
|
||||
sourceURL,
|
||||
name,
|
||||
returnType,
|
||||
compileUnit,
|
||||
compilationState,
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
}
|
||||
|
||||
@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 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
@ -92,9 +92,9 @@ public final class IfNode extends Statement implements JoinPredecessor {
|
||||
}
|
||||
|
||||
@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(')');
|
||||
}
|
||||
|
||||
|
||||
@ -65,23 +65,25 @@ public final class IndexNode extends BaseNode {
|
||||
}
|
||||
|
||||
@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 @@ public final class IndexNode extends BaseNode {
|
||||
* @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 @@ public final class IndexNode extends BaseNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexNode setProgramPoint(int programPoint) {
|
||||
public IndexNode setProgramPoint(final int programPoint) {
|
||||
if (this.programPoint == programPoint) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ public class JoinPredecessorExpression extends Expression implements JoinPredece
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(Function<Symbol, Type> localVariableTypes) {
|
||||
public Type getType(final Function<Symbol, Type> localVariableTypes) {
|
||||
return expression.getType(localVariableTypes);
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@ public class JoinPredecessorExpression extends Expression implements JoinPredece
|
||||
}
|
||||
|
||||
@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 @@ public class JoinPredecessorExpression extends Expression implements JoinPredece
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
@ -72,7 +72,7 @@ public abstract class JumpStatement extends Statement implements JoinPredecessor
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
public void toString(final StringBuilder sb, final boolean printType) {
|
||||
sb.append(getStatementName());
|
||||
|
||||
if (labelName != null) {
|
||||
|
||||
@ -82,7 +82,7 @@ public final class LabelNode extends LexicalContextStatement implements JoinPred
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
public void toString(final StringBuilder sb, final boolean printType) {
|
||||
sb.append(labelName).append(':');
|
||||
}
|
||||
|
||||
|
||||
44
nashorn/src/jdk/nashorn/internal/ir/Labels.java
Normal file
44
nashorn/src/jdk/nashorn/internal/ir/Labels.java
Normal file
@ -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();
|
||||
}
|
||||
@ -29,6 +29,7 @@ import java.util.Arrays;
|
||||
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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
public void toString(final StringBuilder sb, final boolean printType) {
|
||||
sb.append(value.toString());
|
||||
}
|
||||
}
|
||||
@ -880,7 +881,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
if (node == null) {
|
||||
sb.append("undefined");
|
||||
} else {
|
||||
node.toString(sb);
|
||||
node.toString(sb, printType);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
@ -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 @@ public abstract class LoopNode extends BreakableStatement {
|
||||
|
||||
@Override
|
||||
public List<Label> getLabels() {
|
||||
return Arrays.asList(breakLabel, continueLabel);
|
||||
return Collections.unmodifiableList(Arrays.asList(breakLabel, continueLabel));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
|
||||
|
||||
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 @@ public abstract class Node implements Cloneable {
|
||||
*
|
||||
* @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
|
||||
|
||||
@ -28,6 +28,7 @@ package jdk.nashorn.internal.ir;
|
||||
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 @@ public final class ObjectNode extends Expression {
|
||||
}
|
||||
|
||||
@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 @@ public final class ObjectNode extends Expression {
|
||||
}
|
||||
first = false;
|
||||
|
||||
element.toString(sb);
|
||||
element.toString(sb, printType);
|
||||
}
|
||||
sb.append(' ');
|
||||
}
|
||||
|
||||
@ -37,9 +37,6 @@ import jdk.nashorn.internal.codegen.types.Type;
|
||||
* @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)
|
||||
*/
|
||||
|
||||
@ -94,25 +94,25 @@ public final class PropertyNode extends Node {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,6 @@ package jdk.nashorn.internal.ir;
|
||||
|
||||
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 @@ public class ReturnNode extends Statement {
|
||||
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -460,7 +460,7 @@ public class RuntimeNode extends Expression implements Optimistic {
|
||||
* 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 @@ public class RuntimeNode extends Expression implements Optimistic {
|
||||
}
|
||||
|
||||
@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 @@ public class RuntimeNode extends Expression implements Optimistic {
|
||||
first = false;
|
||||
}
|
||||
|
||||
arg.toString(sb);
|
||||
arg.toString(sb, printType);
|
||||
}
|
||||
|
||||
sb.append(')');
|
||||
|
||||
@ -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 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* 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 @@ public class SplitNode extends LexicalContextStatement {
|
||||
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 @@ public class SplitNode extends LexicalContextStatement {
|
||||
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 @@ public class SplitNode extends LexicalContextStatement {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,6 +121,19 @@ public class SplitNode extends LexicalContextStatement {
|
||||
return compileUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
@ -124,7 +141,7 @@ public class SplitNode extends LexicalContextStatement {
|
||||
* @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 class SplitNode extends LexicalContextStatement {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ package jdk.nashorn.internal.ir;
|
||||
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 @@ public final class SwitchNode extends BreakableStatement {
|
||||
}
|
||||
|
||||
@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(')');
|
||||
}
|
||||
|
||||
|
||||
@ -138,7 +138,7 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
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);
|
||||
|
||||
@ -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 @@ public final class TernaryNode extends Expression {
|
||||
}
|
||||
|
||||
@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 @@ public final class TernaryNode extends Expression {
|
||||
if (trueParen) {
|
||||
sb.append('(');
|
||||
}
|
||||
getTrueExpression().toString(sb);
|
||||
getTrueExpression().toString(sb, printType);
|
||||
if (trueParen) {
|
||||
sb.append(')');
|
||||
}
|
||||
@ -104,7 +107,7 @@ public final class TernaryNode extends Expression {
|
||||
if (falseParen) {
|
||||
sb.append('(');
|
||||
}
|
||||
getFalseExpression().toString(sb);
|
||||
getFalseExpression().toString(sb, printType);
|
||||
if (falseParen) {
|
||||
sb.append(')');
|
||||
}
|
||||
@ -118,7 +121,7 @@ public final class TernaryNode extends Expression {
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
|
||||
@ -83,13 +83,13 @@ public final class ThrowNode extends Statement implements JoinPredecessor {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ package jdk.nashorn.internal.ir;
|
||||
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 @@ public final class TryNode extends Statement implements JoinPredecessor {
|
||||
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 @@ public final class TryNode extends Statement implements JoinPredecessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
public void toString(final StringBuilder sb, final boolean printType) {
|
||||
sb.append("try ");
|
||||
}
|
||||
|
||||
|
||||
@ -34,6 +34,7 @@ import java.util.Arrays;
|
||||
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 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
||||
|
||||
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 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnaryNode setAssignmentDest(Expression n) {
|
||||
public UnaryNode setAssignmentDest(final Expression n) {
|
||||
return setExpression(n);
|
||||
}
|
||||
|
||||
@ -209,13 +210,15 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
||||
}
|
||||
|
||||
@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 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
||||
* 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 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
||||
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 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
||||
}
|
||||
|
||||
@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 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnaryNode setType(Type type) {
|
||||
public UnaryNode setType(final Type type) {
|
||||
if (this.type == type) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -138,13 +138,13 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ public final class WhileNode extends LoopNode {
|
||||
* @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 @@ public final class WhileNode extends LoopNode {
|
||||
}
|
||||
|
||||
@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(')');
|
||||
}
|
||||
|
||||
|
||||
@ -79,9 +79,9 @@ public final class WithNode extends LexicalContextStatement {
|
||||
}
|
||||
|
||||
@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(')');
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,6 @@ package jdk.nashorn.internal.ir.debug;
|
||||
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 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
|
||||
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 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterJoinPredecessorExpression(JoinPredecessorExpression joinPredecessorExpression) {
|
||||
public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinPredecessorExpression) {
|
||||
final Expression expr = joinPredecessorExpression.getExpression();
|
||||
if(expr != null) {
|
||||
expr.accept(this);
|
||||
|
||||
@ -52,8 +52,6 @@ import java.util.Map;
|
||||
* 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 class ObjectSizeCalculator {
|
||||
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 class ObjectSizeCalculator {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -77,25 +77,30 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
/** 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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
* @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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
*
|
||||
* @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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
|
||||
@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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
}
|
||||
|
||||
@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 final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
public void run() {
|
||||
unaryNode.getExpression().accept(PrintVisitor.this);
|
||||
}
|
||||
});
|
||||
}, printTypes);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -276,21 +282,21 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
|
||||
@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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
indent -= TABWIDTH;
|
||||
indent();
|
||||
indent += TABWIDTH;
|
||||
labeledNode.toString(sb);
|
||||
labeledNode.toString(sb, printTypes);
|
||||
labeledNode.getBody().accept(this);
|
||||
printLocalVariableConversion(labeledNode);
|
||||
return false;
|
||||
@ -321,7 +327,7 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
|
||||
@Override
|
||||
public boolean enterSplitNode(final SplitNode splitNode) {
|
||||
splitNode.toString(sb);
|
||||
splitNode.toString(sb, printTypes);
|
||||
sb.append(EOLN);
|
||||
indent += TABWIDTH;
|
||||
indent();
|
||||
@ -339,7 +345,7 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
|
||||
@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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
|
||||
@Override
|
||||
public boolean enterTryNode(final TryNode tryNode) {
|
||||
tryNode.toString(sb);
|
||||
tryNode.toString(sb, printTypes);
|
||||
printLocalVariableConversion(tryNode);
|
||||
tryNode.getBody().accept(this);
|
||||
|
||||
@ -378,7 +384,7 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
|
||||
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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
@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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
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 @@ public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
|
||||
@Override
|
||||
public boolean enterWithNode(final WithNode withNode) {
|
||||
withNode.toString(sb);
|
||||
withNode.toString(sb, printTypes);
|
||||
withNode.getBody().accept(this);
|
||||
|
||||
return false;
|
||||
|
||||
@ -713,14 +713,15 @@ public final class NativeArray extends ScriptObject {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -234,20 +234,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
* @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 @@ loop:
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[JavaScript Parsing]";
|
||||
return "'JavaScript Parsing'";
|
||||
}
|
||||
|
||||
private static void markEval(final LexicalContext lc) {
|
||||
|
||||
@ -34,9 +34,13 @@ import java.lang.invoke.MethodHandles;
|
||||
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;
|
||||
@ -555,6 +559,43 @@ final class CompiledFunction {
|
||||
return function.handleRewriteException(oldOptimismInfo, re);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -567,45 +608,90 @@ final class CompiledFunction {
|
||||
*/
|
||||
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 MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ?
|
||||
type :
|
||||
type.insertParameterTypes(0, ScriptFunction.class);
|
||||
final boolean shouldRecompile = oldOptimismInfo.requestRecompile(re);
|
||||
final boolean canBeDeoptimized;
|
||||
|
||||
final FunctionNode fn = oldOptimismInfo.recompile(callSiteType, re);
|
||||
FunctionNode fn = oldOptimismInfo.reparse();
|
||||
final Compiler compiler = oldOptimismInfo.getCompiler(fn, callSiteType, re); //set to non rest-of
|
||||
|
||||
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 {
|
||||
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.");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
final MethodHandle restOf = changeReturnType(oldOptimismInfo.compileRestOfMethod(callSiteType, re), Object.class);
|
||||
// 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 @@ final class CompiledFunction {
|
||||
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 @@ final class CompiledFunction {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -63,6 +63,14 @@ final class CompiledFunctions {
|
||||
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 @@ final class CompiledFunctions {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -57,10 +57,8 @@ import java.util.logging.Level;
|
||||
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 @@ public final class Context {
|
||||
}
|
||||
|
||||
if (env._print_parse) {
|
||||
getErr().println(new PrintVisitor(functionNode));
|
||||
getErr().println(new PrintVisitor(functionNode, true, false));
|
||||
}
|
||||
|
||||
if (env._parse_only) {
|
||||
@ -956,18 +954,17 @@ public final class Context {
|
||||
final CodeSource cs = new CodeSource(url, (CodeSigner[])null);
|
||||
final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
|
||||
|
||||
final CompilationPhases phases = CompilationEnvironment.CompilationPhases.EAGER;
|
||||
final Compiler compiler = new Compiler(
|
||||
new CompilationEnvironment(
|
||||
this,
|
||||
phases.
|
||||
makeOptimistic(
|
||||
ScriptEnvironment.globalOptimistic()),
|
||||
strict),
|
||||
installer);
|
||||
final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
|
||||
|
||||
final FunctionNode newFunctionNode = compiler.compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
|
||||
script = compiler.install(newFunctionNode);
|
||||
final Compiler compiler = new Compiler(
|
||||
this,
|
||||
env,
|
||||
installer,
|
||||
source,
|
||||
functionNode.getSourceURL(),
|
||||
strict | functionNode.isStrict());
|
||||
|
||||
script = compiler.compile(functionNode, phases).getRootClass();
|
||||
cacheClass(source, script);
|
||||
|
||||
return script;
|
||||
|
||||
@ -32,19 +32,16 @@ import java.lang.invoke.MethodHandles;
|
||||
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 final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 "";
|
||||
}
|
||||
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(' ');
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
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]";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
FunctionNodeTransform getDefaultTransform(final MethodType callSiteType) {
|
||||
return new ApplyToCallTransform(this, callSiteType);
|
||||
}
|
||||
|
||||
private ParamTypeMap typeMap(final MethodType fnCallSiteType, final FunctionNodeTransform tr) {
|
||||
if (isVariableArity() && !tr.wasTransformed()) {
|
||||
TypeMap typeMap(final MethodType fnCallSiteType) {
|
||||
if (fnCallSiteType == null) {
|
||||
return null;
|
||||
}
|
||||
return new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType, tr.getArity()));
|
||||
}
|
||||
|
||||
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));
|
||||
if (CompiledFunction.isVarArgsType(fnCallSiteType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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));
|
||||
return new TypeMap(functionNodeId, explicitParams(fnCallSiteType), needsCallee());
|
||||
}
|
||||
|
||||
private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final FunctionNodeTransform tr) {
|
||||
private static ScriptObject newLocals(final ScriptObject runtimeScope) {
|
||||
final ScriptObject locals = Global.newEmptyInstance();
|
||||
locals.setProto(runtimeScope);
|
||||
return locals;
|
||||
}
|
||||
|
||||
private Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
|
||||
return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
}
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
}
|
||||
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
}
|
||||
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
|
||||
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]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +230,7 @@ public class RewriteException extends Exception {
|
||||
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];
|
||||
|
||||
@ -505,11 +505,12 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
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 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
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);
|
||||
|
||||
@ -49,7 +49,7 @@ final class ScriptLoader extends NashornLoader {
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
@ -2260,21 +2260,22 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
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(),
|
||||
|
||||
@ -39,7 +39,7 @@ import jdk.nashorn.internal.runtime.Context;
|
||||
* 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");
|
||||
|
||||
@ -39,7 +39,7 @@ import java.util.Locale;
|
||||
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 @@ public class Shell {
|
||||
}
|
||||
|
||||
//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();
|
||||
|
||||
@ -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 BinaryNode = forName("jdk.nashorn.internal.ir.BinaryNode").static
|
||||
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 rhsMethod = UnaryNode.class.getMethod("getExpression")
|
||||
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 getEnvMethod = Context.class.getMethod("getEnv")
|
||||
|
||||
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 @@ function test(f) {
|
||||
// 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 --------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user