提交 0c3406ad 编写于 作者: H hannesw

8027301: Optimizations for Function.prototype.apply

Reviewed-by: jlaskey
上级 36547cff
......@@ -24,6 +24,7 @@
*/
package jdk.nashorn.internal.runtime;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.Iterator;
import java.util.TreeSet;
......@@ -35,6 +36,8 @@ import java.util.TreeSet;
@SuppressWarnings("serial")
final class CompiledFunctions extends TreeSet<CompiledFunction> {
private CompiledFunction generic;
CompiledFunction best(final MethodType type) {
final Iterator<CompiledFunction> iter = iterator();
while (iter.hasNext()) {
......@@ -43,13 +46,10 @@ final class CompiledFunctions extends TreeSet<CompiledFunction> {
return next;
}
}
return mostGeneric();
return generic();
}
boolean needsCallee() {
for (final CompiledFunction inv : this) {
assert ScriptFunctionData.needsCallee(inv.getInvoker()) == ScriptFunctionData.needsCallee(mostGeneric().getInvoker());
}
return ScriptFunctionData.needsCallee(mostGeneric().getInvoker());
}
......@@ -57,6 +57,48 @@ final class CompiledFunctions extends TreeSet<CompiledFunction> {
return last();
}
CompiledFunction generic() {
CompiledFunction gen = this.generic;
if (gen == null) {
gen = this.generic = makeGeneric(mostGeneric());
}
return gen;
}
private static CompiledFunction makeGeneric(final CompiledFunction func) {
final MethodHandle invoker = composeGenericMethod(func.getInvoker());
final MethodHandle constructor = func.hasConstructor() ? composeGenericMethod(func.getConstructor()) : null;
return new CompiledFunction(invoker.type(), invoker, constructor);
}
/**
* Takes a method handle, and returns a potentially different method handle that can be used in
* {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
* The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
* {@code Object} as well, except for the following ones:
* <ul>
* <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
* <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
* (callee) as an argument.</li>
* </ul>
*
* @param mh the original method handle
*
* @return the new handle, conforming to the rules above.
*/
private static MethodHandle composeGenericMethod(final MethodHandle mh) {
final MethodType type = mh.type();
final boolean isVarArg = ScriptFunctionData.isVarArg(mh);
final int paramCount = isVarArg ? type.parameterCount() - 1 : type.parameterCount();
MethodType newType = MethodType.genericMethodType(paramCount, isVarArg);
if (ScriptFunctionData.needsCallee(mh)) {
newType = newType.changeParameterType(0, ScriptFunction.class);
}
return type.equals(newType) ? mh : mh.asType(newType);
}
/**
* Is the given type even more specific than this entire list? That means
* we have an opportunity for more specific versions of the method
......
......@@ -40,7 +40,7 @@ final class FinalScriptFunctionData extends ScriptFunctionData {
*
* @param name name
* @param arity arity
* @param list precompiled code
* @param functions precompiled code
* @param isStrict strict
* @param isBuiltin builtin
* @param isConstructor constructor
......@@ -73,12 +73,13 @@ final class FinalScriptFunctionData extends ScriptFunctionData {
}
private void addInvoker(final MethodHandle mh) {
boolean needsCallee = needsCallee(mh);
if (isConstructor(mh)) {
//only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
//is too conservative a check. However, isConstructor(mh) always implies isConstructor param
// only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
// is too conservative a check. However, isConstructor(mh) always implies isConstructor param
assert isConstructor();
code.add(new CompiledFunction(mh.type(), MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor
final MethodHandle invoker = MH.insertArguments(mh, 0, false);
final MethodHandle constructor = composeConstructor(MH.insertArguments(mh, 0, true));
code.add(new CompiledFunction(mh.type(), invoker, constructor));
} else {
code.add(new CompiledFunction(mh.type(), mh));
}
......
......@@ -213,13 +213,13 @@ public abstract class ScriptFunctionData {
*/
public final MethodHandle getGenericInvoker() {
ensureCodeGenerated();
return composeGenericMethod(code.mostGeneric().getInvoker());
return code.generic().getInvoker();
}
final MethodHandle getGenericConstructor() {
ensureCodeGenerated();
ensureConstructor(code.mostGeneric());
return composeGenericMethod(code.mostGeneric().getConstructor());
ensureConstructor(code.generic());
return code.generic().getConstructor();
}
private CompiledFunction getBest(final MethodType callSiteType) {
......@@ -267,18 +267,17 @@ public abstract class ScriptFunctionData {
}
/**
* Compose a constructor given a primordial constructor handle
*
* @param ctor primordial constructor handle
* @param needsCallee do we need to pass a callee
* Compose a constructor given a primordial constructor handle.
*
* @param ctor primordial constructor handle
* @return the composed constructor
*/
protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) {
protected MethodHandle composeConstructor(final MethodHandle ctor) {
// If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
// "this" in the first argument position is what allows the elegant folded composition of
// (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
// always returns Object.
final boolean needsCallee = needsCallee(ctor);
MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor;
composedCtor = changeReturnTypeToObject(composedCtor);
......@@ -471,33 +470,6 @@ public abstract class ScriptFunctionData {
return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
}
/**
* Takes a method handle, and returns a potentially different method handle that can be used in
* {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
* The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
* {@code Object} as well, except for the following ones:
* <ul>
* <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
* <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
* (callee) as an argument.</li>
* </ul>
*
* @param mh the original method handle
*
* @return the new handle, conforming to the rules above.
*/
protected MethodHandle composeGenericMethod(final MethodHandle mh) {
final MethodType type = mh.type();
MethodType newType = type.generic();
if (isVarArg(mh)) {
newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
}
if (needsCallee(mh)) {
newType = newType.changeParameterType(0, ScriptFunction.class);
}
return type.equals(newType) ? mh : mh.asType(newType);
}
/**
* Execute this script function.
*
......@@ -508,10 +480,9 @@ 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 Object selfObj = convertThisObject(self);
final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
final MethodHandle mh = getGenericInvoker();
final Object selfObj = convertThisObject(self);
final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
if (isVarArg(mh)) {
if (needsCallee(mh)) {
......@@ -531,6 +502,12 @@ public abstract class ScriptFunctionData {
return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
case 5:
return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
case 6:
return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
case 7:
return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
case 8:
return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
default:
return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
}
......@@ -545,15 +522,20 @@ public abstract class ScriptFunctionData {
return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
case 4:
return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
case 5:
return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
case 6:
return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
case 7:
return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
default:
return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
}
}
Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
final MethodHandle mh = getGenericConstructor();
final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
final MethodHandle mh = getGenericConstructor();
final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
if (isVarArg(mh)) {
if (needsCallee(mh)) {
......@@ -573,6 +555,12 @@ public abstract class ScriptFunctionData {
return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1));
case 4:
return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2));
case 5:
return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
case 6:
return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
case 7:
return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
default:
return mh.invokeWithArguments(withArguments(fn, paramCount, args));
}
......@@ -587,6 +575,12 @@ public abstract class ScriptFunctionData {
return mh.invokeExact(getArg(args, 0), getArg(args, 1));
case 3:
return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2));
case 4:
return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
case 5:
return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
case 6:
return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
default:
return mh.invokeWithArguments(withArguments(null, paramCount, args));
}
......@@ -664,20 +658,21 @@ public abstract class ScriptFunctionData {
* @return the adapted handle
*/
private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
return MH.asType(mh, mh.type().changeReturnType(Object.class));
final MethodType type = mh.type();
return (type.returnType() == Object.class) ? mh : MH.asType(mh, type.changeReturnType(Object.class));
}
private void ensureConstructor(final CompiledFunction inv) {
if (!inv.hasConstructor()) {
inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker())));
inv.setConstructor(composeConstructor(inv.getInvoker()));
}
}
/**
* Heuristic to figure out if the method handle has a callee argument. If it's type is either
* {@code (boolean, ScriptFunction, ...)} or {@code (ScriptFunction, ...)}, then we'll assume it has
* a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
* assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
* Heuristic to figure out if the method handle has a callee argument. If it's type is
* {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as
* the constructor above is not passed this information, and can't just blindly assume it's false
* (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
* they also always receive a callee).
*
* @param mh the examined method handle
......@@ -685,18 +680,8 @@ public abstract class ScriptFunctionData {
* @return true if the method handle expects a callee, false otherwise
*/
protected static boolean needsCallee(final MethodHandle mh) {
final MethodType type = mh.type();
final int length = type.parameterCount();
if (length == 0) {
return false;
}
if (type.parameterType(0) == ScriptFunction.class) {
return true;
}
return length > 1 && type.parameterType(0) == boolean.class && type.parameterType(1) == ScriptFunction.class;
final MethodType type = mh.type();
return (type.parameterCount() > 0 && type.parameterType(0) == ScriptFunction.class);
}
/**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册