mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-28 11:53:09 +00:00
8020356: ClassCastException Undefined->Scope on spiltter class generated for a large switch statement
Reviewed-by: jlaskey, attila
This commit is contained in:
parent
2c90f36ca0
commit
c4bea48ac1
@ -1143,22 +1143,23 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
final Type elementType = arrayType.getElementType();
|
||||
|
||||
if (units != null) {
|
||||
final MethodEmitter savedMethod = method;
|
||||
final MethodEmitter savedMethod = method;
|
||||
final FunctionNode currentFunction = lc.getCurrentFunction();
|
||||
|
||||
for (final ArrayUnit arrayUnit : units) {
|
||||
unit = lc.pushCompileUnit(arrayUnit.getCompileUnit());
|
||||
|
||||
final String className = unit.getUnitClassName();
|
||||
final String name = lc.getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName());
|
||||
final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
|
||||
final String name = currentFunction.uniqueName(SPLIT_PREFIX.symbolName());
|
||||
final String signature = methodDescriptor(type, ScriptFunction.class, Object.class, ScriptObject.class, type);
|
||||
|
||||
final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
|
||||
method = lc.pushMethodEmitter(me);
|
||||
|
||||
method.setFunctionNode(lc.getCurrentFunction());
|
||||
method.setFunctionNode(currentFunction);
|
||||
method.begin();
|
||||
|
||||
fixScopeSlot();
|
||||
fixScopeSlot(currentFunction);
|
||||
|
||||
method.load(arrayType, SPLIT_ARRAY_ARG.slot());
|
||||
|
||||
@ -1171,10 +1172,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
method = lc.popMethodEmitter(me);
|
||||
|
||||
assert method == savedMethod;
|
||||
method.loadCompilerConstant(THIS);
|
||||
method.swap();
|
||||
method.loadCompilerConstant(CALLEE);
|
||||
method.swap();
|
||||
method.loadCompilerConstant(THIS);
|
||||
method.swap();
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
method.swap();
|
||||
method.invokestatic(className, name, signature);
|
||||
@ -1680,11 +1681,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
method = lc.pushMethodEmitter(splitEmitter);
|
||||
method.setFunctionNode(fn);
|
||||
|
||||
if (fn.needsCallee()) {
|
||||
caller.loadCompilerConstant(CALLEE);
|
||||
} else {
|
||||
caller.loadNull();
|
||||
}
|
||||
assert fn.needsCallee() : "split function should require callee";
|
||||
caller.loadCompilerConstant(CALLEE);
|
||||
caller.loadCompilerConstant(THIS);
|
||||
caller.loadCompilerConstant(SCOPE);
|
||||
if (needsArguments) {
|
||||
@ -1694,18 +1692,18 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
caller.storeCompilerConstant(RETURN);
|
||||
|
||||
method.begin();
|
||||
// Copy scope to its target slot as first thing because the original slot could be used by return symbol.
|
||||
fixScopeSlot(fn);
|
||||
|
||||
method.loadUndefined(fn.getReturnType());
|
||||
method.storeCompilerConstant(RETURN);
|
||||
|
||||
fixScopeSlot();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void fixScopeSlot() {
|
||||
if (lc.getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) {
|
||||
// TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method)
|
||||
private void fixScopeSlot(final FunctionNode functionNode) {
|
||||
// TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method)
|
||||
if (functionNode.compilerConstant(SCOPE).getSlot() != SCOPE.slot()) {
|
||||
method.load(Type.typeFor(ScriptObject.class), SCOPE.slot());
|
||||
method.storeCompilerConstant(SCOPE);
|
||||
}
|
||||
|
||||
@ -105,6 +105,18 @@ public final class Label {
|
||||
Stack copy() {
|
||||
return new Stack(data, sp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder("[");
|
||||
for (int i = 0; i < sp; i++) {
|
||||
builder.append(data[i]);
|
||||
if (i < sp - 1) {
|
||||
builder.append(", ");
|
||||
}
|
||||
}
|
||||
return builder.append("]").toString();
|
||||
}
|
||||
}
|
||||
|
||||
/** Name of this label */
|
||||
|
||||
@ -96,6 +96,10 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
||||
long weight = WeighNodes.weigh(functionNode);
|
||||
final boolean top = fn.isProgram(); //compiler.getFunctionNode() == outermost;
|
||||
|
||||
// We know that our LexicalContext is empty outside the call to functionNode.accept(this) below,
|
||||
// so we can pass null to all methods expecting a LexicalContext parameter.
|
||||
assert lc.isEmpty() : "LexicalContext not empty";
|
||||
|
||||
if (weight >= SPLIT_THRESHOLD) {
|
||||
LOG.finest("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
|
||||
functionNode = (FunctionNode)functionNode.accept(this);
|
||||
@ -103,11 +107,12 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
||||
if (functionNode.isSplit()) {
|
||||
// Weight has changed so weigh again, this time using block weight cache
|
||||
weight = WeighNodes.weigh(functionNode, weightCache);
|
||||
functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(lc));
|
||||
functionNode = functionNode.setBody(null, functionNode.getBody().setNeedsScope(null));
|
||||
}
|
||||
|
||||
if (weight >= SPLIT_THRESHOLD) {
|
||||
functionNode = functionNode.setBody(lc, splitBlock(functionNode.getBody(), functionNode));
|
||||
functionNode = functionNode.setBody(null, splitBlock(functionNode.getBody(), functionNode));
|
||||
functionNode = functionNode.setFlag(null, FunctionNode.IS_SPLIT);
|
||||
weight = WeighNodes.weigh(functionNode.getBody(), weightCache);
|
||||
}
|
||||
}
|
||||
@ -116,10 +121,10 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
||||
|
||||
if (top) {
|
||||
assert outermostCompileUnit != null : "outermost compile unit is null";
|
||||
functionNode = functionNode.setCompileUnit(lc, outermostCompileUnit);
|
||||
functionNode = functionNode.setCompileUnit(null, outermostCompileUnit);
|
||||
outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
|
||||
} else {
|
||||
functionNode = functionNode.setCompileUnit(lc, findUnit(weight));
|
||||
functionNode = functionNode.setCompileUnit(null, findUnit(weight));
|
||||
}
|
||||
|
||||
final Block body = functionNode.getBody();
|
||||
@ -138,11 +143,11 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
||||
return split;
|
||||
}
|
||||
});
|
||||
functionNode = functionNode.setBody(lc, newBody);
|
||||
functionNode = functionNode.setBody(null, newBody);
|
||||
|
||||
assert functionNode.getCompileUnit() != null;
|
||||
|
||||
return functionNode.setState(lc, CompilationState.SPLIT);
|
||||
return functionNode.setState(null, CompilationState.SPLIT);
|
||||
}
|
||||
|
||||
private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
|
||||
@ -179,7 +184,6 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
||||
* @return new weight for the resulting block.
|
||||
*/
|
||||
private Block splitBlock(final Block block, final FunctionNode function) {
|
||||
lc.setFlag(lc.getCurrentFunction(), FunctionNode.IS_SPLIT);
|
||||
|
||||
final List<Statement> splits = new ArrayList<>();
|
||||
List<Statement> statements = new ArrayList<>();
|
||||
@ -255,8 +259,10 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
||||
// been split already, so weigh again before splitting.
|
||||
long weight = WeighNodes.weigh(block, weightCache);
|
||||
if (weight >= SPLIT_THRESHOLD) {
|
||||
newBlock = splitBlock(block, lc.getFunction(block));
|
||||
final FunctionNode currentFunction = lc.getCurrentFunction();
|
||||
newBlock = splitBlock(block, currentFunction);
|
||||
weight = WeighNodes.weigh(newBlock, weightCache);
|
||||
lc.setFlag(currentFunction, FunctionNode.IS_SPLIT);
|
||||
}
|
||||
weightCache.put(newBlock, weight);
|
||||
return newBlock;
|
||||
@ -289,7 +295,7 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
||||
final Node element = value[postset];
|
||||
|
||||
weight = WeighNodes.weigh(element);
|
||||
totalWeight += weight;
|
||||
totalWeight += WeighNodes.AASTORE_WEIGHT + weight;
|
||||
|
||||
if (totalWeight >= SPLIT_THRESHOLD) {
|
||||
final CompileUnit unit = compiler.findUnit(totalWeight - weight);
|
||||
|
||||
@ -68,24 +68,25 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
||||
/*
|
||||
* Weight constants.
|
||||
*/
|
||||
static final long FUNCTION_WEIGHT = 40;
|
||||
private static final long ACCESS_WEIGHT = 4;
|
||||
private static final long ADD_WEIGHT = 10;
|
||||
private static final long BREAK_WEIGHT = 1;
|
||||
private static final long CALL_WEIGHT = 10;
|
||||
private static final long CATCH_WEIGHT = 10;
|
||||
private static final long CONTINUE_WEIGHT = 1;
|
||||
private static final long IF_WEIGHT = 2;
|
||||
private static final long LITERAL_WEIGHT = 10;
|
||||
private static final long LOOP_WEIGHT = 4;
|
||||
private static final long NEW_WEIGHT = 6;
|
||||
private static final long FUNC_EXPR_WEIGHT = 20;
|
||||
private static final long RETURN_WEIGHT = 2;
|
||||
private static final long SPLIT_WEIGHT = 40;
|
||||
private static final long SWITCH_WEIGHT = 8;
|
||||
private static final long THROW_WEIGHT = 2;
|
||||
private static final long VAR_WEIGHT = 40;
|
||||
private static final long WITH_WEIGHT = 8;
|
||||
static final long FUNCTION_WEIGHT = 40;
|
||||
static final long AASTORE_WEIGHT = 2;
|
||||
static final long ACCESS_WEIGHT = 4;
|
||||
static final long ADD_WEIGHT = 10;
|
||||
static final long BREAK_WEIGHT = 1;
|
||||
static final long CALL_WEIGHT = 10;
|
||||
static final long CATCH_WEIGHT = 10;
|
||||
static final long CONTINUE_WEIGHT = 1;
|
||||
static final long IF_WEIGHT = 2;
|
||||
static final long LITERAL_WEIGHT = 10;
|
||||
static final long LOOP_WEIGHT = 4;
|
||||
static final long NEW_WEIGHT = 6;
|
||||
static final long FUNC_EXPR_WEIGHT = 20;
|
||||
static final long RETURN_WEIGHT = 2;
|
||||
static final long SPLIT_WEIGHT = 40;
|
||||
static final long SWITCH_WEIGHT = 8;
|
||||
static final long THROW_WEIGHT = 2;
|
||||
static final long VAR_WEIGHT = 40;
|
||||
static final long WITH_WEIGHT = 8;
|
||||
|
||||
/** Accumulated weight. */
|
||||
private long weight;
|
||||
@ -210,6 +211,7 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
||||
|
||||
if (units == null) {
|
||||
for (final int postset : postsets) {
|
||||
weight += AASTORE_WEIGHT;
|
||||
final Node element = value[postset];
|
||||
|
||||
if (element != null) {
|
||||
|
||||
@ -95,7 +95,6 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param lineNumber line number
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param statements statements
|
||||
|
||||
@ -540,12 +540,13 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
/**
|
||||
* Check if this function's generated Java method needs a {@code callee} parameter. Functions that need access to
|
||||
* their parent scope, functions that reference themselves, and non-strict functions that need an Arguments object
|
||||
* (since it exposes {@code arguments.callee} property) will need to have a callee parameter.
|
||||
* (since it exposes {@code arguments.callee} property) will need to have a callee parameter. We also return true
|
||||
* for split functions to make sure symbols slots are the same in the main and split methods.
|
||||
*
|
||||
* @return true if the function's generated Java method needs a {@code callee} parameter.
|
||||
*/
|
||||
public boolean needsCallee() {
|
||||
return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrict());
|
||||
return needsParentScope() || needsSelfSymbol() || isSplit() || (needsArguments() && !isStrict());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -350,10 +350,12 @@ public class LexicalContext {
|
||||
* @return the innermost function in the context.
|
||||
*/
|
||||
public FunctionNode getCurrentFunction() {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
for (int i = sp - 1; i >= 0; i--) {
|
||||
if (stack[i] instanceof FunctionNode) {
|
||||
return (FunctionNode) stack[i];
|
||||
}
|
||||
}
|
||||
return new NodeIterator<>(FunctionNode.class).next();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
10435
nashorn/test/script/basic/JDK-8020356.js
Normal file
10435
nashorn/test/script/basic/JDK-8020356.js
Normal file
File diff suppressed because it is too large
Load Diff
2
nashorn/test/script/basic/JDK-8020356.js.EXPECTED
Normal file
2
nashorn/test/script/basic/JDK-8020356.js.EXPECTED
Normal file
@ -0,0 +1,2 @@
|
||||
20
|
||||
10
|
||||
Loading…
x
Reference in New Issue
Block a user