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:
Marcus Lagergren 2014-05-19 15:29:42 +02:00
parent b0873269dc
commit 26308e1cd6
74 changed files with 2554 additions and 1850 deletions

View File

@ -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 \

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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)));
}
}

View File

@ -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);
}
}

View File

@ -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('/', '.');
}
}

View File

@ -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();
}

View File

@ -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)) {

View File

@ -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

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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();
}
}

View 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;
}
}

View 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();
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}
/**

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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(')');

View File

@ -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));
}
}

View File

@ -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(')');
}

View File

@ -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(';');
}

View File

@ -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();
}
}

View File

@ -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);
}
/**

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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);
}

View File

@ -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(')');
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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) {

View File

@ -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(':');
}

View 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();
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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(' ');
}

View File

@ -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)
*/

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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(')');

View File

@ -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()));
}
}

View File

@ -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(')');
}

View File

@ -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);

View File

@ -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));
}

View File

@ -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);
}
}

View File

@ -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 ");
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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(')');
}

View File

@ -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(')');
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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) {

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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]";
}
}
}

View File

@ -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];

View File

@ -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);

View File

@ -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);

View File

@ -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(),

View File

@ -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");

View File

@ -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();

View File

@ -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 --------------