8037534: Use scope types to determine optimistic types

Reviewed-by: hannesw, lagergren
This commit is contained in:
Attila Szegedi 2014-03-24 18:41:06 +01:00
parent a7394f09e5
commit 0cbec476dd
12 changed files with 265 additions and 92 deletions

View File

@ -172,6 +172,13 @@ final class Attr extends NodeOperatorVisitor<OptimisticLexicalContext> {
return end(node);
}
@Override
public boolean enterAccessNode(AccessNode accessNode) {
tagNeverOptimistic(accessNode.getBase());
tagNeverOptimistic(accessNode.getProperty());
return true;
};
@Override
public Node leaveAccessNode(final AccessNode accessNode) {
return end(ensureSymbolTypeOverride(accessNode, Type.OBJECT));
@ -355,6 +362,7 @@ final class Attr extends NodeOperatorVisitor<OptimisticLexicalContext> {
for (final Expression arg : callNode.getArgs()) {
tagOptimistic(arg);
}
tagNeverOptimistic(callNode.getFunction());
return true;
}
@ -669,7 +677,7 @@ final class Attr extends NodeOperatorVisitor<OptimisticLexicalContext> {
if (!identNode.isInitializedHere()) {
symbol.increaseUseCount();
}
addLocalUse(identNode.getName());
addLocalUse(name);
IdentNode node = (IdentNode)identNode.setSymbol(lc, symbol);
if (isTaggedOptimistic(identNode) && symbol.isScope()) {
node = ensureSymbolTypeOverride(node, symbol.getSymbolType());
@ -793,6 +801,12 @@ final class Attr extends NodeOperatorVisitor<OptimisticLexicalContext> {
return null;
}
@Override
public boolean enterIndexNode(IndexNode indexNode) {
tagNeverOptimistic(indexNode.getBase());
return true;
}
@Override
public Node leaveIndexNode(final IndexNode indexNode) {
// return end(ensureSymbolOptimistic(Type.OBJECT, indexNode));

View File

@ -49,14 +49,13 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
@ -285,7 +284,7 @@ public class ClassEmitter implements Emitter {
}
/**
* Constructs a primitive specific method for getting the ith entry from the constants table and cast.
* Constructs a primitive specific method for getting the ith entry from the constants table as an array.
* @param clazz Array class.
*/
private void defineGetArrayMethod(final Class<?> clazz) {
@ -299,9 +298,8 @@ public class ClassEmitter implements Emitter {
.load(Type.INT, 0)
.arrayload()
.checkcast(clazz)
.dup()
.arraylength()
.invoke(staticCallNoLookup(Arrays.class, "copyOf", clazz, clazz, int.class))
.invoke(virtualCallNoLookup(clazz, "clone", Object.class))
.checkcast(clazz)
._return();
getArrayMethod.end();
}

View File

@ -178,9 +178,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private static final Type SCRIPTFUNCTION_IMPL_TYPE = Type.typeFor(ScriptFunction.class);
private static final Call INIT_REWRITE_EXCEPTION = CompilerConstants.specialCallNoLookup(RewriteException.class,
"<init>", void.class, UnwarrantedOptimismException.class, Object[].class);
"<init>", void.class, UnwarrantedOptimismException.class, Object[].class, String[].class, ScriptObject.class);
private static final Call INIT_REWRITE_EXCEPTION_REST_OF = CompilerConstants.specialCallNoLookup(RewriteException.class,
"<init>", void.class, UnwarrantedOptimismException.class, Object[].class, int[].class);
"<init>", void.class, UnwarrantedOptimismException.class, Object[].class, String[].class, ScriptObject.class, int[].class);
private static final Call ENSURE_INT = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
"ensureInt", int.class, Object.class, int.class);
@ -1535,7 +1535,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
try {
final boolean markOptimistic;
if (emittedMethods.add(functionNode.getName())) {
markOptimistic = generateUnwarrantedOptimismExceptionHandlers();
markOptimistic = generateUnwarrantedOptimismExceptionHandlers(functionNode);
generateContinuationHandler();
method.end(); // wrap up this method
unit = lc.popCompileUnit(functionNode.getCompileUnit());
@ -4133,7 +4133,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* entry to its immediately preceding one for longest matching prefix.
* @return true if there is at least one exception handler
*/
private boolean generateUnwarrantedOptimismExceptionHandlers() {
private boolean generateUnwarrantedOptimismExceptionHandlers(final FunctionNode fn) {
if(!useOptimisticTypes()) {
return false;
}
@ -4274,8 +4274,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
method.dup(2);
method.dup(2);
method.pop();
loadConstant(getByteCodeSymbolNames(fn));
if (fn.compilerConstant(SCOPE).hasSlot()) {
method.loadCompilerConstant(SCOPE);
} else {
method.loadNull();
}
final CompilationEnvironment env = compiler.getCompilationEnvironment();
if(env.isCompileRestOf()) {
if (env.isCompileRestOf()) {
loadConstant(env.getContinuationEntryPoints());
method.invoke(INIT_REWRITE_EXCEPTION_REST_OF);
} else {
@ -4287,6 +4293,26 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return true;
}
private static String[] getByteCodeSymbolNames(final FunctionNode fn) {
// Only names of local variables on the function level are captured. This information is used to reduce
// deoptimizations, so as much as we can capture will help. We rely on the fact that function wide variables are
// all live all the time, so the array passed to rewrite exception contains one element for every slotted symbol
// here.
final List<String> names = new ArrayList<>();
for (final Symbol symbol: fn.getBody().getSymbols()) {
if (symbol.hasSlot()) {
if (symbol.isScope()) {
// slot + scope can only be true for parameters
assert symbol.isParam();
names.add(null);
} else {
names.add(symbol.getName());
}
}
}
return names.toArray(new String[names.size()]);
}
private static String commonPrefix(final String s1, final String s2) {
final int l1 = s1.length();
final int l = Math.min(l1, s2.length());

View File

@ -35,9 +35,17 @@ 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.FindProperty;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
* Class for managing metadata during a compilation, e.g. which phases
@ -52,6 +60,10 @@ public final class CompilationEnvironment {
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;
@ -169,14 +181,18 @@ public final class CompilationEnvironment {
public CompilationEnvironment(
final CompilationPhases phases,
final boolean strict) {
this(phases, null, null, null, null, strict, false);
this(phases, null, null, null, null, null, strict, false);
}
/**
* Constructor for compilation environment of the rest-of method
* @param phases compilation phases
* @param strict strict mode
* @param compiledFunction recompiled function
* @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
@ -186,11 +202,12 @@ public final class CompilationEnvironment {
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(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, continuationEntryPoint, strict, onDemand);
this(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, runtimeScope, continuationEntryPoint, strict, onDemand);
}
/**
@ -198,6 +215,10 @@ public final class CompilationEnvironment {
* @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
@ -206,10 +227,11 @@ public final class CompilationEnvironment {
final CompilationPhases phases,
final boolean strict,
final RecompilableScriptFunctionData compiledFunction,
final ScriptObject runtimeScope,
final ParamTypeMap paramTypeMap,
final Map<Integer, Type> invalidatedProgramPoints,
final boolean onDemand) {
this(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, null, strict, onDemand);
this(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, runtimeScope, null, strict, onDemand);
}
private CompilationEnvironment(
@ -217,17 +239,16 @@ public final class CompilationEnvironment {
final ParamTypeMap paramTypes,
final Map<Integer, Type> invalidatedProgramPoints,
final RecompilableScriptFunctionData compiledFunction,
final ScriptObject runtimeScope,
final int[] continuationEntryPoints,
final boolean strict,
final boolean onDemand) {
this.phases = phases;
this.paramTypes = paramTypes;
this.continuationEntryPoints = continuationEntryPoints;
this.invalidatedProgramPoints =
invalidatedProgramPoints == null ?
Collections.unmodifiableMap(new HashMap<Integer, Type>()) :
invalidatedProgramPoints;
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;
@ -352,13 +373,94 @@ public final class CompilationEnvironment {
*/
Type getOptimisticType(final Optimistic node) {
assert useOptimisticTypes();
final Type invalidType = invalidatedProgramPoints.get(node.getProgramPoint());
if (invalidType != null) {
return invalidType;//.nextWider();
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 node.getMostOptimisticType();
}
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().getName());
} 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 Class<?> clazz = find.getProperty().getCurrentType();
return clazz == null ? null : Type.typeFor(clazz);
}
private Object evaluateSafely(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().getName());
}
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

View File

@ -63,7 +63,7 @@ class ConstantData {
private int calcHashCode() {
final Class<?> cls = array.getClass();
if (cls == Object[].class) {
if (!cls.getComponentType().isPrimitive()) {
return Arrays.hashCode((Object[])array);
} else if (cls == double[].class) {
return Arrays.hashCode((double[])array);
@ -91,7 +91,7 @@ class ConstantData {
final Class<?> cls = array.getClass();
if (cls == otherArray.getClass()) {
if (cls == Object[].class) {
if (!cls.getComponentType().isPrimitive()) {
return Arrays.equals((Object[])array, (Object[])otherArray);
} else if (cls == double[].class) {
return Arrays.equals((double[])array, (double[])otherArray);

View File

@ -620,7 +620,7 @@ final class CompiledFunction {
return null;
}
SwitchPoint.invalidateAll(new SwitchPoint[] { optimisticAssumptions });
return data.compile(callSiteType, invalidatedProgramPoints, "Deoptimizing recompilation");
return data.compile(callSiteType, invalidatedProgramPoints, e.getRuntimeScope(), "Deoptimizing recompilation");
}
MethodHandle compileRestOfMethod(final MethodType callSiteType, final RewriteException e) {
@ -634,7 +634,7 @@ final class CompiledFunction {
System.arraycopy(prevEntryPoints, 0, entryPoints, 1, l);
}
entryPoints[0] = e.getProgramPoint();
return data.compileRestOfMethod(callSiteType, invalidatedProgramPoints, entryPoints);
return data.compileRestOfMethod(callSiteType, invalidatedProgramPoints, entryPoints, e.getRuntimeScope());
}
}

View File

@ -320,7 +320,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
return sb.toString();
}
MethodHandle compileRestOfMethod(final MethodType fnCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final int[] continuationEntryPoints) {
MethodHandle compileRestOfMethod(final MethodType fnCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final int[] continuationEntryPoints, final ScriptObject runtimeScope) {
LOG.info("Rest-of compilation of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet() + "$restOf";
@ -331,6 +331,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
CompilationPhases.EAGER.makeOptimistic(),
isStrict(),
this,
runtimeScope,
isVariableArity() ? null : new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType)),
invalidatedProgramPoints,
continuationEntryPoints,
@ -345,11 +346,11 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
return lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
}
private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType) {
return compile(actualCallSiteType, null, "Type specialized compilation");
private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
return compile(actualCallSiteType, null, runtimeScope, "Type specialized compilation");
}
FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final String reason) {
FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final ScriptObject runtimeScope, final String reason) {
final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet();
final MethodType fnCallSiteType = actualCallSiteType == null ? null : actualCallSiteType.changeParameterType(0, ScriptFunction.class);
LOG.info(reason, " of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
@ -361,6 +362,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
phases.makeOptimistic(ScriptEnvironment.globalOptimistic()),
isStrict(),
this,
runtimeScope,
fnCallSiteType == null || isVariableArity() ?
null :
new ParamTypeMap(
@ -506,11 +508,11 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
}
@Override
CompiledFunction getBest(final MethodType callSiteType) {
CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
synchronized(code) {
final CompiledFunction existingBest = super.getBest(callSiteType);
final CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
// TODO: what if callSiteType is vararg?
return existingBest != null ? existingBest : addCode(compileTypeSpecialization(callSiteType), callSiteType);
return existingBest != null ? existingBest : addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
}
}

View File

@ -39,6 +39,7 @@ import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
import jdk.nashorn.internal.objects.Global;
/**
* Used to signal to the linker to relink the callee
@ -47,6 +48,9 @@ import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
public class RewriteException extends Exception {
private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
// 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 ScriptObject runtimeScope;
//contents of bytecode slots
private Object[] byteCodeSlots;
private final int[] previousContinuationEntryPoints;
@ -84,8 +88,8 @@ public class RewriteException extends Exception {
* @param e the {@link UnwarrantedOptimismException} that triggered this exception.
* @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point
*/
public RewriteException(final UnwarrantedOptimismException e, final Object[] byteCodeSlots) {
this(e, byteCodeSlots, null);
public RewriteException(final UnwarrantedOptimismException e, final Object[] byteCodeSlots, final String[] byteCodeSymbolNames, final ScriptObject runtimeScope) {
this(e, byteCodeSlots, byteCodeSymbolNames, runtimeScope, null);
}
/**
@ -95,12 +99,28 @@ public class RewriteException extends Exception {
* @param previousContinuationEntryPoints an array of continuation entry points that were already executed during
* one logical invocation of the function (a rest-of triggering a rest-of triggering a...)
*/
public RewriteException(final UnwarrantedOptimismException e, final Object[] byteCodeSlots, final int[] previousContinuationEntryPoints) {
public RewriteException(final UnwarrantedOptimismException e, final Object[] byteCodeSlots, final String[] byteCodeSymbolNames, final ScriptObject runtimeScope, final int[] previousContinuationEntryPoints) {
super("", e, false, Context.DEBUG);
this.byteCodeSlots = byteCodeSlots;
this.runtimeScope = mergeSlotsWithScope(byteCodeSlots, byteCodeSymbolNames, runtimeScope);
this.previousContinuationEntryPoints = previousContinuationEntryPoints;
}
private static ScriptObject mergeSlotsWithScope(final Object[] byteCodeSlots, final String[] byteCodeSymbolNames,
final ScriptObject runtimeScope) {
final ScriptObject locals = Global.newEmptyInstance();
final int l = Math.min(byteCodeSlots.length, byteCodeSymbolNames.length);
for(int i = 0; i < l; ++i) {
final String name = byteCodeSymbolNames[i];
final Object value = byteCodeSlots[i];
if(name != null) {
locals.set(name, value, true);
}
}
locals.setProto(runtimeScope);
return locals;
}
/**
* Array populator used for saving the local variable state into the array contained in the
* RewriteException
@ -127,6 +147,7 @@ public class RewriteException extends Exception {
public Object getReturnValueDestructive() {
assert byteCodeSlots != null;
byteCodeSlots = null;
runtimeScope = null;
return getUOE().getReturnValueDestructive();
}
@ -165,6 +186,14 @@ public class RewriteException extends Exception {
return previousContinuationEntryPoints;
}
/**
* Returns the runtime scope that was in effect when the exception was thrown.
* @return the runtime scope.
*/
public ScriptObject getRuntimeScope() {
return runtimeScope;
}
private static String stringify(final Object returnValue) {
if (returnValue == null) {
return "null";

View File

@ -331,7 +331,7 @@ public abstract class ScriptFunction extends ScriptObject {
* assumptions.
*/
private GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint) {
return data.getBestInvoker(callSiteType, callerProgramPoint);
return data.getBestInvoker(callSiteType, callerProgramPoint, scope);
}
/**
@ -342,7 +342,7 @@ public abstract class ScriptFunction extends ScriptObject {
* @return bound invoke handle
*/
public final MethodHandle getBoundInvokeHandle(final Object self) {
return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker()), self);
return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), self);
}
/**
@ -471,7 +471,7 @@ public abstract class ScriptFunction extends ScriptObject {
protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
final MethodType type = desc.getMethodType();
assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
final GuardedInvocation bestCtorInv = data.getBestConstructor(type);
final GuardedInvocation bestCtorInv = data.getBestConstructor(type, scope);
//TODO - ClassCastException
return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this), bestCtorInv.getSwitchPoint());
}
@ -697,7 +697,7 @@ public abstract class ScriptFunction extends ScriptObject {
* These don't want a callee parameter, so bind that. Name binding is optional.
*/
MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker()), bindName), type);
return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type);
}
private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {

View File

@ -38,7 +38,6 @@ import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
/**
@ -203,18 +202,18 @@ public abstract class ScriptFunctionData {
* @return guarded invocation with method handle to best invoker and potentially a switch point guarding optimistic
* assumptions.
*/
final GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint) {
final CompiledFunction cf = getBest(callSiteType);
final GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint, final ScriptObject runtimeScope) {
final CompiledFunction cf = getBest(callSiteType, runtimeScope);
assert cf != null;
return new GuardedInvocation(cf.createInvoker(callSiteType.returnType(), callerProgramPoint), cf.getOptimisticAssumptionsSwitchPoint());
}
final GuardedInvocation getBestConstructor(final MethodType callSiteType) {
final GuardedInvocation getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope) {
if (!isConstructor()) {
throw typeError("not.a.constructor", toSource());
}
// Constructor call sites don't have a "this", but getBest is meant to operate on "callee, this, ..." style
final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class));
final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope);
return new GuardedInvocation(cf.getConstructor(), cf.getOptimisticAssumptionsSwitchPoint());
}
@ -232,12 +231,12 @@ public abstract class ScriptFunctionData {
* is generated, get the most generic of all versions of this function and adapt it
* to Objects.
*
* TODO this is only public because {@link JavaAdapterFactory} can't supply us with
* a MethodType that we can use for lookup due to boostrapping problems. Can be fixed
*
* @param runtimeScope the runtime scope. 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 deoptimization passes.
* @return generic invoker of this script function
*/
public final MethodHandle getGenericInvoker() {
final MethodHandle getGenericInvoker(final ScriptObject runtimeScope) {
MethodHandle invoker;
final Reference<MethodHandle> ref = GENERIC_INVOKERS.get(this);
if(ref != null) {
@ -246,16 +245,16 @@ public abstract class ScriptFunctionData {
return invoker;
}
}
invoker = createGenericInvoker();
invoker = createGenericInvoker(runtimeScope);
GENERIC_INVOKERS.put(this, new WeakReference<>(invoker));
return invoker;
}
private MethodHandle createGenericInvoker() {
return makeGenericMethod(getGeneric().createComposableInvoker());
private MethodHandle createGenericInvoker(final ScriptObject runtimeScope) {
return makeGenericMethod(getGeneric(runtimeScope).createComposableInvoker());
}
final MethodHandle getGenericConstructor() {
final MethodHandle getGenericConstructor(final ScriptObject runtimeScope) {
MethodHandle constructor;
final Reference<MethodHandle> ref = GENERIC_CONSTRUCTORS.get(this);
if(ref != null) {
@ -264,29 +263,32 @@ public abstract class ScriptFunctionData {
return constructor;
}
}
constructor = createGenericConstructor();
constructor = createGenericConstructor(runtimeScope);
GENERIC_CONSTRUCTORS.put(this, new WeakReference<>(constructor));
return constructor;
}
private MethodHandle createGenericConstructor() {
return makeGenericMethod(getGeneric().createComposableConstructor());
private MethodHandle createGenericConstructor(final ScriptObject runtimeScope) {
return makeGenericMethod(getGeneric(runtimeScope).createComposableConstructor());
}
/**
* Returns the best function for the specified call site type.
* @param callSiteType The call site type. Call site types are expected to have the form
* {@code (callee, this[, args...])}.
* @param runtimeScope the runtime scope. 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 deoptimization passes.
* @return the best function for the specified call site type.
*/
CompiledFunction getBest(final MethodType callSiteType) {
CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
return code.best(callSiteType, isRecompilable());
}
abstract boolean isRecompilable();
CompiledFunction getGeneric() {
return getBest(getGenericType());
CompiledFunction getGeneric(final ScriptObject runtimeScope) {
return getBest(getGenericType(), runtimeScope);
}
@ -326,7 +328,8 @@ public abstract class ScriptFunctionData {
final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS;
final CompiledFunctions boundList = new CompiledFunctions(fn.getName());
final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(), getGenericConstructor());
final ScriptObject runtimeScope = fn.getScope();
final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope));
boundList.add(bind(bindTarget, fn, self, allArgs));
return new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, boundFlags);
@ -518,7 +521,7 @@ public abstract class ScriptFunctionData {
* @throws Throwable if there is an exception/error with the invocation or thrown from it
*/
Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
final MethodHandle mh = getGenericInvoker();
final MethodHandle mh = getGenericInvoker(fn.getScope());
final Object selfObj = convertThisObject(self);
final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
@ -572,7 +575,7 @@ public abstract class ScriptFunctionData {
}
Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
final MethodHandle mh = getGenericConstructor();
final MethodHandle mh = getGenericConstructor(fn.getScope());
final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
if (isVarArg(mh)) {

View File

@ -3024,7 +3024,7 @@ public abstract class ScriptObject implements PropertyAccess {
assert sobj != null : "no parent global object in scope";
}
//this will unbox any Number object to its primitive type in case the
//property supporst primitive types, so it doesn't matter that it comes
//property supports primitive types, so it doesn't matter that it comes
//in as an Object.
sobj.addSpillProperty(key, 0, value, true);
}

View File

@ -24,18 +24,19 @@
*/
package jdk.nashorn.internal.runtime;
import static org.testng.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.testng.Assert.fail;
import org.testng.annotations.Test;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
/**
* @test
@ -102,56 +103,54 @@ public class OptimisticRecompilationTest {
@Test
public void divisionByZeroTest() {
//Check that two Deoptimizing recompilations and RewriteExceptions happened
runTest("function f() {var x = { a: 2, b:1 }; x.a = Number.POSITIVE_INFINITY;"
+ " x.b = 0; print(x.a/x.b);} f()",
getRecompilationPattern("double", "Infinity"), 2);
//Check that one Deoptimizing recompilation and RewriteExceptions happened
runTest("function f() {var x1 = { a: 2, b:1 }; x1.a = Number.POSITIVE_INFINITY;"
+ " x1.b = 0; print(x1.a/x1.b);} f()",
getRecompilationPattern("double", "Infinity"), 1);
}
@Test
public void divisionWithRemainderTest() {
//Check that one Deoptimizing recompilation and RewriteException happened
runTest("function f() {var x = { a: 7, b:2 }; print(x.a/x.b);} f()",
runTest("function f() {var x2 = { a: 7, b:2 }; print(x2.a/x2.b);} f()",
getRecompilationPattern("double", "3.5"), 1);
}
@Test
public void infinityMultiplicationTest() {
//Check that three Deoptimizing recompilations and RewriteExceptions happened
runTest("function f() {var x = { a: Number.POSITIVE_INFINITY, "
+ "b: Number.POSITIVE_INFINITY}; print(x.a*x.b);} f()",
getRecompilationPattern("double", "Infinity"), 3);
//Check that one deoptimizing recompilation and RewriteExceptions happened
runTest("function f() {var x3 = { a: Number.POSITIVE_INFINITY, "
+ "b: Number.POSITIVE_INFINITY}; print(x3.a*x3.b);} f()",
getRecompilationPattern("double", "Infinity"), 1);
}
@Test
public void maxValueMultiplicationTest() {
runTest("function f() {var x = { a: Number.MAX_VALUE, b: Number.MAX_VALUE};"
+ " print(x.a*x.b);} f()",
getRecompilationPattern("double", "1.7976931348623157E308"), 3);
runTest("function f() {var x4 = { a: Number.MAX_VALUE, b: Number.MAX_VALUE};"
+ " print(x4.a*x4.b);} f()",
getRecompilationPattern("double", "1.7976931348623157E308"), 1);
}
@Test
public void divisionByInfinityTest() {
//Check that two Deoptimizing recompilations and RewriteExceptions happened
runTest("function f() {var x = { a: -1, b: Number.POSITIVE_INFINITY};"
+ " print(x.a/x.b);} f()",
getRecompilationPattern("double", "Infinity"), 2);
//Check that one Deoptimizing recompilation and RewriteExceptions happened
runTest("function f() {var x5 = { a: -1, b: Number.POSITIVE_INFINITY};"
+ " print(x5.a/x5.b);} f()",
getRecompilationPattern("double", "Infinity"), 1);
}
@Test
public void divisionByStringTest() {
//Check that three Deoptimizing recompilations and RewriteExceptions happened
String str1 = getRecompilationPattern("double", "Infinity");
String str2 = getRecompilationPattern("object", "Hello");
runTest("function f() {var x = { a: Number.POSITIVE_INFINITY, b: 'Hello'};"
+ " print(x.a/x.b);} f()", String.format("(?s)%s.*%1$s.*%s", str1, str2), 1);
//Check that one deoptimizing recompilations and RewriteExceptions happened
runTest("function f() {var x6 = { a: Number.POSITIVE_INFINITY, b: 'Hello'};"
+ " print(x6.a/x6.b);} f()", getRecompilationPattern("double", "Infinity"), 1);
}
@Test
public void nestedFunctionTest() {
//Check that one Deoptimizing recompilations and RewriteExceptions happened
runTest("var a=3,b,c; function f() {var x = 2, y =1; function g(){ "
+ "var y = x; var z = a; z = x*y; print(a*b); } g() } f()",
runTest("var a=3,b,c; function f() {var x7 = 2, y =1; function g(){ "
+ "var y = x7; var z = a; z = x7*y; print(a*b); } g() } f()",
getRecompilationPattern("object", "undefined"), 1);
}
@ -165,7 +164,7 @@ public class OptimisticRecompilationTest {
@Test
public void functionTest() {
//Check that one Deoptimizing recompilations and RewriteExceptions happened
runTest("function f(a,b,c) { d = (a + b) * c; print(d);} f()",
runTest("function f(a,b,c) { h = (a + b) * c; print(h);} f()",
getRecompilationPattern("double", "NaN"), 1);
}
}