8069338: Implement sharedScopeCall for optimistic types
Reviewed-by: attila, sundar
This commit is contained in:
parent
ac4e5933a6
commit
225ec213e4
@ -346,28 +346,30 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
|||||||
|
|
||||||
assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
|
assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
|
||||||
final int flags = getScopeCallSiteFlags(symbol);
|
final int flags = getScopeCallSiteFlags(symbol);
|
||||||
if (isFastScope(symbol)) {
|
if (!isFastScope(symbol)) {
|
||||||
// Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
|
// slow scope load, prototype chain must be inspected at runtime
|
||||||
if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD && !identNode.isOptimistic()) {
|
|
||||||
// As shared scope vars are only used with non-optimistic identifiers, we switch from using TypeBounds to
|
|
||||||
// just a single definitive type, resultBounds.widest.
|
|
||||||
new OptimisticOperation(identNode, TypeBounds.OBJECT) {
|
|
||||||
@Override
|
|
||||||
void loadStack() {
|
|
||||||
method.loadCompilerConstant(SCOPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void consumeStack() {
|
|
||||||
loadSharedScopeVar(resultBounds.widest, symbol, flags);
|
|
||||||
}
|
|
||||||
}.emit();
|
|
||||||
} else {
|
|
||||||
new LoadFastScopeVar(identNode, resultBounds, flags).emit();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//slow scope load, we have no proto depth
|
|
||||||
new LoadScopeVar(identNode, resultBounds, flags).emit();
|
new LoadScopeVar(identNode, resultBounds, flags).emit();
|
||||||
|
} else if (identNode.isCompileTimePropertyName() || symbol.getUseCount() < SharedScopeCall.SHARED_GET_THRESHOLD) {
|
||||||
|
// fast scope load with known prototype depth
|
||||||
|
new LoadFastScopeVar(identNode, resultBounds, flags).emit();
|
||||||
|
} else {
|
||||||
|
// Only generate shared scope getter for often used fast-scope symbols.
|
||||||
|
new OptimisticOperation(identNode, resultBounds) {
|
||||||
|
@Override
|
||||||
|
void loadStack() {
|
||||||
|
method.loadCompilerConstant(SCOPE);
|
||||||
|
final int depth = getScopeProtoDepth(lc.getCurrentBlock(), symbol);
|
||||||
|
assert depth >= 0;
|
||||||
|
method.load(depth);
|
||||||
|
method.load(getProgramPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void consumeStack() {
|
||||||
|
final Type resultType = isOptimistic ? getOptimisticCoercedType() : resultBounds.widest;
|
||||||
|
lc.getScopeGet(unit, symbol, resultType, flags, isOptimistic).generateInvoke(method);
|
||||||
|
}
|
||||||
|
}.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
return method;
|
return method;
|
||||||
@ -467,12 +469,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
|||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
|
|
||||||
assert isFastScope(symbol);
|
|
||||||
method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol));
|
|
||||||
return lc.getScopeGet(unit, symbol, valueType, flags).generateInvoke(method);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LoadScopeVar extends OptimisticOperation {
|
private class LoadScopeVar extends OptimisticOperation {
|
||||||
final IdentNode identNode;
|
final IdentNode identNode;
|
||||||
private final int flags;
|
private final int flags;
|
||||||
@ -551,18 +547,23 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
|||||||
if (swap) {
|
if (swap) {
|
||||||
method.swap();
|
method.swap();
|
||||||
}
|
}
|
||||||
if (depth > 1) {
|
invokeGetProto(depth);
|
||||||
method.load(depth);
|
|
||||||
method.invoke(ScriptObject.GET_PROTO_DEPTH);
|
|
||||||
} else {
|
|
||||||
method.invoke(ScriptObject.GET_PROTO);
|
|
||||||
}
|
|
||||||
if (swap) {
|
if (swap) {
|
||||||
method.swap();
|
method.swap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void invokeGetProto(final int depth) {
|
||||||
|
assert depth > 0;
|
||||||
|
if (depth > 1) {
|
||||||
|
method.load(depth);
|
||||||
|
method.invoke(ScriptObject.GET_PROTO_DEPTH);
|
||||||
|
} else {
|
||||||
|
method.invoke(ScriptObject.GET_PROTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate code that loads this node to the stack, not constraining its type
|
* Generate code that loads this node to the stack, not constraining its type
|
||||||
*
|
*
|
||||||
@ -1386,12 +1387,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
method.loadCompilerConstant(SCOPE);
|
method.loadCompilerConstant(SCOPE);
|
||||||
if (count > 1) {
|
invokeGetProto(count);
|
||||||
method.load(count);
|
|
||||||
method.invoke(ScriptObject.GET_PROTO_DEPTH);
|
|
||||||
} else {
|
|
||||||
method.invoke(ScriptObject.GET_PROTO);
|
|
||||||
}
|
|
||||||
method.storeCompilerConstant(SCOPE);
|
method.storeCompilerConstant(SCOPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1444,20 +1440,22 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
|||||||
final CodeGeneratorLexicalContext codegenLexicalContext = lc;
|
final CodeGeneratorLexicalContext codegenLexicalContext = lc;
|
||||||
|
|
||||||
function.accept(new SimpleNodeVisitor() {
|
function.accept(new SimpleNodeVisitor() {
|
||||||
|
|
||||||
private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) {
|
private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) {
|
||||||
final Symbol symbol = identNode.getSymbol();
|
final Symbol symbol = identNode.getSymbol();
|
||||||
final boolean isFastScope = isFastScope(symbol);
|
assert isFastScope(symbol);
|
||||||
|
|
||||||
new OptimisticOperation(callNode, resultBounds) {
|
new OptimisticOperation(callNode, resultBounds) {
|
||||||
@Override
|
@Override
|
||||||
void loadStack() {
|
void loadStack() {
|
||||||
method.loadCompilerConstant(SCOPE);
|
method.loadCompilerConstant(SCOPE);
|
||||||
if (isFastScope) {
|
final int depth = getScopeProtoDepth(currentBlock, symbol);
|
||||||
method.load(getScopeProtoDepth(currentBlock, symbol));
|
assert depth >= 0;
|
||||||
} else {
|
method.load(depth);
|
||||||
method.load(-1); // Bypass fast-scope code in shared callsite
|
method.load(getProgramPoint());
|
||||||
}
|
|
||||||
loadArgs(args);
|
loadArgs(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void consumeStack() {
|
void consumeStack() {
|
||||||
final Type[] paramTypes = method.getTypesFromStack(args.size());
|
final Type[] paramTypes = method.getTypesFromStack(args.size());
|
||||||
@ -1466,13 +1464,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
|||||||
for(int i = 0; i < paramTypes.length; ++i) {
|
for(int i = 0; i < paramTypes.length; ++i) {
|
||||||
paramTypes[i] = Type.generic(paramTypes[i]);
|
paramTypes[i] = Type.generic(paramTypes[i]);
|
||||||
}
|
}
|
||||||
// As shared scope calls are only used in non-optimistic compilation, we switch from using
|
|
||||||
// TypeBounds to just a single definitive type, resultBounds.widest.
|
final Type resultType = isOptimistic ? getOptimisticCoercedType() : resultBounds.widest;
|
||||||
final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol,
|
final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol,
|
||||||
identNode.getType(), resultBounds.widest, paramTypes, flags);
|
identNode.getType(), resultType, paramTypes, flags, isOptimistic);
|
||||||
scopeCall.generateInvoke(method);
|
scopeCall.generateInvoke(method);
|
||||||
}
|
}
|
||||||
}.emit();
|
}.emit();
|
||||||
|
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1573,15 +1572,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
|||||||
final int flags = getScopeCallSiteFlags(symbol);
|
final int flags = getScopeCallSiteFlags(symbol);
|
||||||
final int useCount = symbol.getUseCount();
|
final int useCount = symbol.getUseCount();
|
||||||
|
|
||||||
// Threshold for generating shared scope callsite is lower for fast scope symbols because we know
|
// We only use shared scope calls for fast scopes
|
||||||
// we can dial in the correct scope. However, we also need to enable it for non-fast scopes to
|
|
||||||
// support huge scripts like mandreel.js.
|
|
||||||
if (callNode.isEval()) {
|
if (callNode.isEval()) {
|
||||||
evalCall(node, flags);
|
evalCall(node, flags);
|
||||||
} else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
|
} else if (!isFastScope(symbol) || symbol.getUseCount() < SharedScopeCall.SHARED_CALL_THRESHOLD) {
|
||||||
|| !isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD
|
|
||||||
|| CodeGenerator.this.lc.inDynamicScope()
|
|
||||||
|| callNode.isOptimistic()) {
|
|
||||||
scopeCall(node, flags);
|
scopeCall(node, flags);
|
||||||
} else {
|
} else {
|
||||||
sharedScopeCall(node, flags);
|
sharedScopeCall(node, flags);
|
||||||
@ -4650,7 +4644,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
|||||||
}
|
}
|
||||||
|
|
||||||
private abstract class OptimisticOperation {
|
private abstract class OptimisticOperation {
|
||||||
private final boolean isOptimistic;
|
final boolean isOptimistic;
|
||||||
// expression and optimistic are the same reference
|
// expression and optimistic are the same reference
|
||||||
private final Expression expression;
|
private final Expression expression;
|
||||||
private final Optimistic optimistic;
|
private final Optimistic optimistic;
|
||||||
@ -4966,7 +4960,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
|||||||
* affect it.
|
* affect it.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private Type getOptimisticCoercedType() {
|
Type getOptimisticCoercedType() {
|
||||||
final Type optimisticType = expression.getType();
|
final Type optimisticType = expression.getType();
|
||||||
assert resultBounds.widest.widerThan(optimisticType);
|
assert resultBounds.widest.widerThan(optimisticType);
|
||||||
final Type narrowest = resultBounds.narrowest;
|
final Type narrowest = resultBounds.narrowest;
|
||||||
|
@ -184,10 +184,14 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
* @param returnType the return type
|
* @param returnType the return type
|
||||||
* @param paramTypes the parameter types
|
* @param paramTypes the parameter types
|
||||||
* @param flags the callsite flags
|
* @param flags the callsite flags
|
||||||
|
* @param isOptimistic is this an optimistic call
|
||||||
* @return an object representing a shared scope call
|
* @return an object representing a shared scope call
|
||||||
*/
|
*/
|
||||||
SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) {
|
SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType,
|
||||||
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags);
|
final Type returnType, final Type[] paramTypes, final int flags,
|
||||||
|
final boolean isOptimistic) {
|
||||||
|
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags,
|
||||||
|
isOptimistic);
|
||||||
if (scopeCalls.containsKey(scopeCall)) {
|
if (scopeCalls.containsKey(scopeCall)) {
|
||||||
return scopeCalls.get(scopeCall);
|
return scopeCalls.get(scopeCall);
|
||||||
}
|
}
|
||||||
@ -203,10 +207,12 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
* @param symbol the symbol
|
* @param symbol the symbol
|
||||||
* @param valueType the type of the variable
|
* @param valueType the type of the variable
|
||||||
* @param flags the callsite flags
|
* @param flags the callsite flags
|
||||||
* @return an object representing a shared scope call
|
* @param isOptimistic is this an optimistic get
|
||||||
|
* @return an object representing a shared scope get
|
||||||
*/
|
*/
|
||||||
SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags) {
|
SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags,
|
||||||
return getScopeCall(unit, symbol, valueType, valueType, null, flags);
|
final boolean isOptimistic) {
|
||||||
|
return getScopeCall(unit, symbol, valueType, valueType, null, flags, isOptimistic);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onEnterBlock(final Block block) {
|
void onEnterBlock(final Block block) {
|
||||||
|
@ -702,19 +702,21 @@ public class MethodEmitter {
|
|||||||
}
|
}
|
||||||
pushType(Type.typeFor(Throwable.class));
|
pushType(Type.typeFor(Throwable.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a try/catch block.
|
* Start a try/catch block.
|
||||||
*
|
*
|
||||||
* @param entry start label for try
|
* @param entry start label for try
|
||||||
* @param exit end label for try
|
* @param exit end label for try
|
||||||
* @param recovery start label for catch
|
* @param recovery start label for catch
|
||||||
* @param typeDescriptor type descriptor for exception
|
* @param clazz exception class or null for any Throwable
|
||||||
* @param isOptimismHandler true if this is a hander for {@code UnwarrantedOptimismException}. Normally joining on a
|
* @param isOptimismHandler true if this is a hander for {@code UnwarrantedOptimismException}. Normally joining on a
|
||||||
* catch handler kills temporary variables, but optimism handlers are an exception, as they need to capture
|
* catch handler kills temporary variables, but optimism handlers are an exception, as they need to capture
|
||||||
* temporaries as well, so they must remain live.
|
* temporaries as well, so they must remain live.
|
||||||
*/
|
*/
|
||||||
private void _try(final Label entry, final Label exit, final Label recovery, final String typeDescriptor, final boolean isOptimismHandler) {
|
void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz, final boolean isOptimismHandler) {
|
||||||
recovery.joinFromTry(entry.getStack(), isOptimismHandler);
|
recovery.joinFromTry(entry.getStack(), isOptimismHandler);
|
||||||
|
final String typeDescriptor = clazz == null ? null : CompilerConstants.className(clazz);
|
||||||
method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), typeDescriptor);
|
method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), typeDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -727,7 +729,7 @@ public class MethodEmitter {
|
|||||||
* @param clazz exception class
|
* @param clazz exception class
|
||||||
*/
|
*/
|
||||||
void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz) {
|
void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz) {
|
||||||
_try(entry, exit, recovery, CompilerConstants.className(clazz), clazz == UnwarrantedOptimismException.class);
|
_try(entry, exit, recovery, clazz, clazz == UnwarrantedOptimismException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.codegen;
|
package jdk.nashorn.internal.codegen;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
|
||||||
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
|
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -32,39 +33,52 @@ import java.util.EnumSet;
|
|||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.Symbol;
|
import jdk.nashorn.internal.ir.Symbol;
|
||||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||||
|
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
|
||||||
|
import jdk.nashorn.internal.runtime.options.Options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A scope call or get operation that can be shared by several callsites. This generates a static
|
* A scope call or get operation that can be shared by several call sites. This generates a static
|
||||||
* method that wraps the invokedynamic instructions to get or call scope variables.
|
* method that wraps the invokedynamic instructions to get or call scope variables.
|
||||||
* The rationale for this is that initial linking of invokedynamic callsites is expensive,
|
* The reason for this is to reduce memory footprint and initial linking overhead of huge scripts.
|
||||||
* so by sharing them we can reduce startup overhead and allow very large scripts to run that otherwise wouldn't.
|
|
||||||
*
|
*
|
||||||
* <p>Static methods generated by this class expect two parameters in addition to the parameters of the
|
* <p>Static methods generated by this class expect three parameters in addition to the parameters of the
|
||||||
* function call: The current scope object and the depth of the target scope relative to the scope argument
|
* function call: The current scope object, the depth of the target scope relative to the scope argument,
|
||||||
* for when this is known at compile-time (fast-scope access).</p>
|
* and the program point in case the target operation is optimistic.</p>
|
||||||
*
|
*
|
||||||
* <p>The second argument may be -1 for non-fast-scope symbols, in which case the scope chain is checked
|
* <p>Optimistic operations are called with program point <code>0</code>. If an <code>UnwarrentedOptimismException</code>
|
||||||
* for each call. This may cause callsite invalidation when the shared method is used from different
|
* is thrown, it is caught by the shared call method and rethrown with the program point of the invoking call site.</p>
|
||||||
* scopes, but such sharing of non-fast scope calls may still be necessary for very large scripts.</p>
|
|
||||||
*
|
*
|
||||||
* <p>Scope calls must not be shared between normal callsites and callsites contained in a <tt>with</tt>
|
* <p>Shared scope calls are not used if the scope contains a <code>with</code> statement or a call to
|
||||||
* statement as this condition is not handled by current guards and will cause a runtime error.</p>
|
* <code>eval</code>.</p>
|
||||||
*/
|
*/
|
||||||
class SharedScopeCall {
|
class SharedScopeCall {
|
||||||
|
|
||||||
/** Threshold for using shared scope calls with fast scope access. */
|
/**
|
||||||
public static final int FAST_SCOPE_CALL_THRESHOLD = 4;
|
* Threshold for using shared scope function calls.
|
||||||
/** Threshold for using shared scope calls with slow scope access. */
|
*/
|
||||||
public static final int SLOW_SCOPE_CALL_THRESHOLD = 500;
|
public static final int SHARED_CALL_THRESHOLD =
|
||||||
/** Threshold for using shared scope gets with fast scope access. */
|
Options.getIntProperty("nashorn.shared.scope.call.threshold", 5);
|
||||||
public static final int FAST_SCOPE_GET_THRESHOLD = 200;
|
/**
|
||||||
|
* Threshold for using shared scope variable getter. This is higher than for calls as lower values
|
||||||
|
* degrade performance on many scripts.
|
||||||
|
*/
|
||||||
|
public static final int SHARED_GET_THRESHOLD =
|
||||||
|
Options.getIntProperty("nashorn.shared.scope.get.threshold", 100);
|
||||||
|
|
||||||
final Type valueType;
|
private static final CompilerConstants.Call REPLACE_PROGRAM_POINT = virtualCallNoLookup(
|
||||||
final Symbol symbol;
|
UnwarrantedOptimismException.class, "replaceProgramPoint",
|
||||||
final Type returnType;
|
UnwarrantedOptimismException.class, int.class);
|
||||||
final Type[] paramTypes;
|
|
||||||
final int flags;
|
/** Number of fixed parameters */
|
||||||
final boolean isCall;
|
private static final int FIXED_PARAM_COUNT = 3;
|
||||||
|
|
||||||
|
private final Type valueType;
|
||||||
|
private final Symbol symbol;
|
||||||
|
private final Type returnType;
|
||||||
|
private final Type[] paramTypes;
|
||||||
|
private final int flags;
|
||||||
|
private final boolean isCall;
|
||||||
|
private final boolean isOptimistic;
|
||||||
private CompileUnit compileUnit;
|
private CompileUnit compileUnit;
|
||||||
private String methodName;
|
private String methodName;
|
||||||
private String staticSignature;
|
private String staticSignature;
|
||||||
@ -77,21 +91,22 @@ class SharedScopeCall {
|
|||||||
* @param returnType the return type
|
* @param returnType the return type
|
||||||
* @param paramTypes the function parameter types
|
* @param paramTypes the function parameter types
|
||||||
* @param flags the callsite flags
|
* @param flags the callsite flags
|
||||||
|
* @param isOptimistic whether target call is optimistic and we need to handle UnwarrentedOptimismException
|
||||||
*/
|
*/
|
||||||
SharedScopeCall(final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) {
|
SharedScopeCall(final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes,
|
||||||
|
final int flags, final boolean isOptimistic) {
|
||||||
this.symbol = symbol;
|
this.symbol = symbol;
|
||||||
this.valueType = valueType;
|
this.valueType = valueType;
|
||||||
this.returnType = returnType;
|
this.returnType = returnType;
|
||||||
this.paramTypes = paramTypes;
|
this.paramTypes = paramTypes;
|
||||||
assert (flags & CALLSITE_OPTIMISTIC) == 0;
|
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
// If paramTypes is not null this is a call, otherwise it's just a get.
|
this.isCall = paramTypes != null; // If paramTypes is not null this is a call, otherwise it's just a get.
|
||||||
this.isCall = paramTypes != null;
|
this.isOptimistic = isOptimistic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return symbol.hashCode() ^ returnType.hashCode() ^ Arrays.hashCode(paramTypes) ^ flags;
|
return symbol.hashCode() ^ returnType.hashCode() ^ Arrays.hashCode(paramTypes) ^ flags ^ Boolean.hashCode(isOptimistic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -101,7 +116,8 @@ class SharedScopeCall {
|
|||||||
return symbol.equals(c.symbol)
|
return symbol.equals(c.symbol)
|
||||||
&& flags == c.flags
|
&& flags == c.flags
|
||||||
&& returnType.equals(c.returnType)
|
&& returnType.equals(c.returnType)
|
||||||
&& Arrays.equals(paramTypes, c.paramTypes);
|
&& Arrays.equals(paramTypes, c.paramTypes)
|
||||||
|
&& isOptimistic == c.isOptimistic;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -119,10 +135,9 @@ class SharedScopeCall {
|
|||||||
/**
|
/**
|
||||||
* Generate the invoke instruction for this shared scope call.
|
* Generate the invoke instruction for this shared scope call.
|
||||||
* @param method the method emitter
|
* @param method the method emitter
|
||||||
* @return the method emitter
|
|
||||||
*/
|
*/
|
||||||
public MethodEmitter generateInvoke(final MethodEmitter method) {
|
public void generateInvoke(final MethodEmitter method) {
|
||||||
return method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature());
|
method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,51 +155,79 @@ class SharedScopeCall {
|
|||||||
final MethodEmitter method = classEmitter.method(methodFlags, methodName, getStaticSignature());
|
final MethodEmitter method = classEmitter.method(methodFlags, methodName, getStaticSignature());
|
||||||
method.begin();
|
method.begin();
|
||||||
|
|
||||||
// Load correct scope by calling getProto() on the scope argument as often as specified
|
// Load correct scope by calling getProto(int) on the scope argument with the supplied depth argument
|
||||||
// by the second argument.
|
|
||||||
final Label parentLoopStart = new Label("parent_loop_start");
|
|
||||||
final Label parentLoopDone = new Label("parent_loop_done");
|
|
||||||
method.load(Type.OBJECT, 0);
|
method.load(Type.OBJECT, 0);
|
||||||
method.label(parentLoopStart);
|
|
||||||
method.load(Type.INT, 1);
|
method.load(Type.INT, 1);
|
||||||
method.iinc(1, -1);
|
method.invoke(ScriptObject.GET_PROTO_DEPTH);
|
||||||
method.ifle(parentLoopDone);
|
|
||||||
method.invoke(ScriptObject.GET_PROTO);
|
|
||||||
method._goto(parentLoopStart);
|
|
||||||
method.label(parentLoopDone);
|
|
||||||
|
|
||||||
assert !isCall || valueType.isObject(); // Callables are always objects
|
assert !isCall || valueType.isObject(); // Callables are always loaded as object
|
||||||
// If flags are optimistic, but we're doing a call, remove optimistic flags from the getter, as they obviously
|
|
||||||
// only apply to the call.
|
// Labels for catch of UnsupportedOptimismException
|
||||||
method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall, false);
|
final Label beginTry;
|
||||||
|
final Label endTry;
|
||||||
|
final Label catchLabel;
|
||||||
|
|
||||||
|
if(isOptimistic) {
|
||||||
|
beginTry = new Label("begin_try");
|
||||||
|
endTry = new Label("end_try");
|
||||||
|
catchLabel = new Label("catch_label");
|
||||||
|
method.label(beginTry);
|
||||||
|
method._try(beginTry, endTry, catchLabel, UnwarrantedOptimismException.class, false);
|
||||||
|
} else {
|
||||||
|
beginTry = endTry = catchLabel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is an optimistic get we set the optimistic flag but don't set the program point,
|
||||||
|
// which implies a program point of 0. If optimism fails we'll replace it with the actual
|
||||||
|
// program point which caller supplied as third argument.
|
||||||
|
final int getFlags = isOptimistic && !isCall ? flags | CALLSITE_OPTIMISTIC : flags;
|
||||||
|
method.dynamicGet(valueType, symbol.getName(), getFlags, isCall, false);
|
||||||
|
|
||||||
// If this is a get we're done, otherwise call the value as function.
|
// If this is a get we're done, otherwise call the value as function.
|
||||||
if (isCall) {
|
if (isCall) {
|
||||||
method.convert(Type.OBJECT);
|
method.convert(Type.OBJECT);
|
||||||
// ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
|
// ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
|
||||||
method.loadUndefined(Type.OBJECT);
|
method.loadUndefined(Type.OBJECT);
|
||||||
int slot = 2;
|
int slot = FIXED_PARAM_COUNT;
|
||||||
for (final Type type : paramTypes) {
|
for (final Type type : paramTypes) {
|
||||||
method.load(type, slot);
|
method.load(type, slot);
|
||||||
slot += type.getSlots();
|
slot += type.getSlots();
|
||||||
}
|
}
|
||||||
// Shared scope calls disabled in optimistic world. TODO is this right?
|
|
||||||
method.dynamicCall(returnType, 2 + paramTypes.length, flags, symbol.getName());
|
// Same as above, set optimistic flag but leave program point as 0.
|
||||||
|
final int callFlags = isOptimistic ? flags | CALLSITE_OPTIMISTIC : flags;
|
||||||
|
|
||||||
|
method.dynamicCall(returnType, 2 + paramTypes.length, callFlags, symbol.getName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOptimistic) {
|
||||||
|
method.label(endTry);
|
||||||
}
|
}
|
||||||
|
|
||||||
method._return(returnType);
|
method._return(returnType);
|
||||||
|
|
||||||
|
if (isOptimistic) {
|
||||||
|
// We caught a UnwarrantedOptimismException, replace 0 program point with actual program point
|
||||||
|
method._catch(catchLabel);
|
||||||
|
method.load(Type.INT, 2);
|
||||||
|
method.invoke(REPLACE_PROGRAM_POINT);
|
||||||
|
method.athrow();
|
||||||
|
}
|
||||||
|
|
||||||
method.end();
|
method.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getStaticSignature() {
|
private String getStaticSignature() {
|
||||||
if (staticSignature == null) {
|
if (staticSignature == null) {
|
||||||
if (paramTypes == null) {
|
if (paramTypes == null) {
|
||||||
staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT);
|
staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT, Type.INT);
|
||||||
} else {
|
} else {
|
||||||
final Type[] params = new Type[paramTypes.length + 2];
|
final Type[] params = new Type[paramTypes.length + FIXED_PARAM_COUNT];
|
||||||
params[0] = Type.typeFor(ScriptObject.class);
|
params[0] = Type.typeFor(ScriptObject.class);
|
||||||
params[1] = Type.INT;
|
params[1] = Type.INT;
|
||||||
System.arraycopy(paramTypes, 0, params, 2, paramTypes.length);
|
params[2] = Type.INT;
|
||||||
|
System.arraycopy(paramTypes, 0, params, FIXED_PARAM_COUNT, paramTypes.length);
|
||||||
staticSignature = Type.getMethodDescriptor(returnType, params);
|
staticSignature = Type.getMethodDescriptor(returnType, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1229,9 +1229,8 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
|
|||||||
* @return proto at given depth
|
* @return proto at given depth
|
||||||
*/
|
*/
|
||||||
public final ScriptObject getProto(final int n) {
|
public final ScriptObject getProto(final int n) {
|
||||||
assert n > 0;
|
ScriptObject p = this;
|
||||||
ScriptObject p = getProto();
|
for (int i = n; i > 0; i--) {
|
||||||
for (int i = n; --i > 0;) {
|
|
||||||
p = p.getProto();
|
p = p.getProto();
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
|
@ -153,12 +153,15 @@ public final class UnwarrantedOptimismException extends RuntimeException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we ended up with a primitive return value (even though it may be
|
* Return a new {@code UnwarrantedOptimismException} with the same return value and the
|
||||||
* too wide for what we tried to do, e.g. double instead of int)
|
* new program point.
|
||||||
* @return true if return value is primitive
|
*
|
||||||
|
* @param newProgramPoint new new program point
|
||||||
|
* @return the new exception instance
|
||||||
*/
|
*/
|
||||||
public boolean hasPrimitiveReturnValue() {
|
public UnwarrantedOptimismException replaceProgramPoint(final int newProgramPoint) {
|
||||||
return returnValue instanceof Number || returnValue instanceof Boolean;
|
assert isValid(newProgramPoint);
|
||||||
|
return new UnwarrantedOptimismException(returnValue, newProgramPoint, returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
85
test/nashorn/script/basic/JDK-8069338.js
Normal file
85
test/nashorn/script/basic/JDK-8069338.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JDK-8069338: Implement sharedScopeCall for optimistic types
|
||||||
|
*
|
||||||
|
* @test
|
||||||
|
* @run
|
||||||
|
*/
|
||||||
|
|
||||||
|
var c = 0;
|
||||||
|
var n = 1;
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
return c++ > 10 ? 'f' : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function h() {
|
||||||
|
var x = 0;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
n = 'b';
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
x += n;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() {
|
||||||
|
var x = 0;
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
x += f();
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertEquals(h(), '11bbbbbb');
|
||||||
|
Assert.assertEquals(g(), '11ffffff');
|
||||||
|
|
Loading…
Reference in New Issue
Block a user