8046905: apply on apply is broken
Reviewed-by: hannesw, lagergren
This commit is contained in:
parent
f4c68a9cfc
commit
7aa808a993
@ -35,6 +35,7 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.SwitchPoint;
|
||||
import java.util.Collections;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
@ -593,6 +594,12 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
|
||||
final MethodType descType = desc.getMethodType();
|
||||
final int paramCount = descType.parameterCount();
|
||||
if(descType.parameterType(paramCount - 1).isArray()) {
|
||||
// This is vararg invocation of apply or call. This can normally only happen when we do a recursive
|
||||
// invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate
|
||||
// linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader.
|
||||
return createVarArgApplyOrCallCall(isApply, desc, request, args);
|
||||
}
|
||||
|
||||
final boolean passesThis = paramCount > 2;
|
||||
final boolean passesArgs = paramCount > 3;
|
||||
@ -647,6 +654,7 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
System.arraycopy(args, 3, tmp, 0, tmp.length);
|
||||
appliedArgs[2] = NativeFunction.toApplyArgs(tmp);
|
||||
} else {
|
||||
assert !isApply;
|
||||
System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
|
||||
}
|
||||
} else if (isFailedApplyToCall) {
|
||||
@ -705,8 +713,7 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
}
|
||||
final MethodType guardType = guard.type();
|
||||
|
||||
// Original function guard will expect the invoked function in parameter position 0, but we're passing it in
|
||||
// position 1.
|
||||
// We need to account for the dropped (apply|call) function argument.
|
||||
guard = MH.dropArguments(guard, 0, descType.parameterType(0));
|
||||
// Take the "isApplyFunction" guard, and bind it to this function.
|
||||
MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this);
|
||||
@ -718,7 +725,72 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
return appliedInvocation.replaceMethods(inv, guard);
|
||||
}
|
||||
|
||||
private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
|
||||
/*
|
||||
* This method is used for linking nested apply. Specialized apply and call linking will create a variable arity
|
||||
* call site for an apply call; when createApplyOrCallCall sees a linking request for apply or call with
|
||||
* Nashorn-style variable arity call site (last argument type is Object[]) it'll delegate to this method.
|
||||
* This method converts the link request from a vararg to a non-vararg one (unpacks the array), then delegates back
|
||||
* to createApplyOrCallCall (with which it is thus mutually recursive), and adds appropriate argument spreaders to
|
||||
* invocation and the guard of whatever createApplyOrCallCall returned to adapt it back into a variable arity
|
||||
* invocation. It basically reduces the problem of vararg call site linking of apply and call back to the (already
|
||||
* solved by createApplyOrCallCall) non-vararg call site linking.
|
||||
*/
|
||||
private GuardedInvocation createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc,
|
||||
final LinkRequest request, final Object[] args) {
|
||||
final MethodType descType = desc.getMethodType();
|
||||
final int paramCount = descType.parameterCount();
|
||||
final Object[] varArgs = (Object[])args[paramCount - 1];
|
||||
// -1 'cause we're not passing the vararg array itself
|
||||
final int copiedArgCount = args.length - 1;
|
||||
int varArgCount = varArgs.length;
|
||||
|
||||
// Spread arguments for the delegate createApplyOrCallCall invocation.
|
||||
final Object[] spreadArgs = new Object[copiedArgCount + varArgCount];
|
||||
System.arraycopy(args, 0, spreadArgs, 0, copiedArgCount);
|
||||
System.arraycopy(varArgs, 0, spreadArgs, copiedArgCount, varArgCount);
|
||||
|
||||
// Spread call site descriptor for the delegate createApplyOrCallCall invocation. We drop vararg array and
|
||||
// replace it with a list of Object.class.
|
||||
final MethodType spreadType = descType.dropParameterTypes(paramCount - 1, paramCount).appendParameterTypes(
|
||||
Collections.<Class<?>>nCopies(varArgCount, Object.class));
|
||||
final CallSiteDescriptor spreadDesc = desc.changeMethodType(spreadType);
|
||||
|
||||
// Delegate back to createApplyOrCallCall with the spread (that is, reverted to non-vararg) request/
|
||||
final LinkRequest spreadRequest = request.replaceArguments(spreadDesc, spreadArgs);
|
||||
final GuardedInvocation spreadInvocation = createApplyOrCallCall(isApply, spreadDesc, spreadRequest, spreadArgs);
|
||||
|
||||
// Add spreader combinators to returned invocation and guard.
|
||||
return spreadInvocation.replaceMethods(
|
||||
// Use standard ScriptObject.pairArguments on the invocation
|
||||
pairArguments(spreadInvocation.getInvocation(), descType),
|
||||
// Use our specialized spreadGuardArguments on the guard (see below).
|
||||
spreadGuardArguments(spreadInvocation.getGuard(), descType));
|
||||
}
|
||||
|
||||
private static MethodHandle spreadGuardArguments(final MethodHandle guard, final MethodType descType) {
|
||||
final MethodType guardType = guard.type();
|
||||
final int guardParamCount = guardType.parameterCount();
|
||||
final int descParamCount = descType.parameterCount();
|
||||
final int spreadCount = guardParamCount - descParamCount + 1;
|
||||
if (spreadCount <= 0) {
|
||||
// Guard doesn't dip into the varargs
|
||||
return guard;
|
||||
}
|
||||
|
||||
final MethodHandle arrayConvertingGuard;
|
||||
// If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply
|
||||
// invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail
|
||||
// with ClassCastException of NativeArray to Object[].
|
||||
if(guardType.parameterType(guardParamCount - 1).isArray()) {
|
||||
arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS);
|
||||
} else {
|
||||
arrayConvertingGuard = guard;
|
||||
}
|
||||
|
||||
return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount);
|
||||
}
|
||||
|
||||
private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
|
||||
final MethodHandle bound;
|
||||
if(fn instanceof ScriptFunction && ((ScriptFunction)fn).needsWrappedThis()) {
|
||||
bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER);
|
||||
|
@ -2499,18 +2499,7 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
}
|
||||
|
||||
if (isCallerVarArg) {
|
||||
final int spreadArgs = parameterCount - callCount + 1;
|
||||
return MH.filterArguments(
|
||||
MH.asSpreader(
|
||||
methodHandle,
|
||||
Object[].class,
|
||||
spreadArgs),
|
||||
callCount - 1,
|
||||
MH.insertArguments(
|
||||
TRUNCATINGFILTER,
|
||||
0,
|
||||
spreadArgs)
|
||||
);
|
||||
return adaptHandleToVarArgCallSite(methodHandle, callCount);
|
||||
}
|
||||
|
||||
if (callCount < parameterCount) {
|
||||
@ -2541,6 +2530,21 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
return methodHandle;
|
||||
}
|
||||
|
||||
static MethodHandle adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount) {
|
||||
final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1;
|
||||
return MH.filterArguments(
|
||||
MH.asSpreader(
|
||||
mh,
|
||||
Object[].class,
|
||||
spreadArgs),
|
||||
callSiteParamCount - 1,
|
||||
MH.insertArguments(
|
||||
TRUNCATINGFILTER,
|
||||
0,
|
||||
spreadArgs)
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static Object[] truncatingFilter(final int n, final Object[] array) {
|
||||
final int length = array == null ? 0 : array.length;
|
||||
|
91
nashorn/test/script/basic/JDK-8046905.js
Normal file
91
nashorn/test/script/basic/JDK-8046905.js
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8046905: apply on apply is broken
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
var apply = Function.prototype.apply;
|
||||
var call = Function.prototype.call;
|
||||
var sort = Array.prototype.sort;
|
||||
var join = Array.prototype.join;
|
||||
|
||||
// Running three times so that we test an already linked call site too:
|
||||
// i==0: linking initially with assumed optimistic returned type int.
|
||||
// i==1: linking after deoptimization with returned type Object.
|
||||
// i==2: re-running code linked in previous iteration. This will
|
||||
// properly exercise the guards too.
|
||||
print("1 level of apply")
|
||||
for(i = 0; i < 3; ++i) {
|
||||
print(sort.apply([4,3,2,1]))
|
||||
}
|
||||
print("2 levels of apply")
|
||||
for(i = 0; i < 3; ++i) {
|
||||
print(apply.apply(sort,[[4,3,2,1]]))
|
||||
}
|
||||
print("3 levels of apply")
|
||||
for(i = 0; i < 3; ++i) {
|
||||
print(apply.apply(apply,[sort,[[4,3,2,1]]]))
|
||||
}
|
||||
print("4 levels of apply")
|
||||
for(i = 0; i < 3; ++i) {
|
||||
print(apply.apply(apply,[apply,[sort,[[4,3,2,1]]]]))
|
||||
}
|
||||
print("5 levels of apply")
|
||||
for(i = 0; i < 3; ++i) {
|
||||
print(apply.apply(apply,[apply,[apply,[sort,[[4,3,2,1]]]]]))
|
||||
}
|
||||
print("Many levels of apply!")
|
||||
for(i = 0; i < 3; ++i) {
|
||||
print(apply.apply(apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[sort,[[4,3,2,1]]]]]]]]]]]]]]]]]]]]]]))
|
||||
}
|
||||
|
||||
print("different invocations that'll trigger relinking")
|
||||
var invocation = [sort,[[4,3,2,1]]];
|
||||
for(i = 0; i < 4; ++i) {
|
||||
print(apply.apply(apply,[apply,invocation]))
|
||||
// First change after i==1, so it relinks an otherwise stable linkage
|
||||
if(i == 1) {
|
||||
invocation = [sort,[[8,7,6,5]]];
|
||||
} else if(i == 2) {
|
||||
invocation = [join,[[8,7,6,5],["-"]]];
|
||||
}
|
||||
}
|
||||
|
||||
print("Many levels of call!")
|
||||
for(i = 0; i < 3; ++i) {
|
||||
print(call.call(call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,sort,[4,3,2,1]))
|
||||
}
|
||||
|
||||
print("call apply call apply call... a lot");
|
||||
for(i = 0; i < 3; ++i) {
|
||||
print(apply.call(call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [sort, [4,3,2,1]]]]]]]))
|
||||
}
|
||||
|
||||
print("apply call apply call apply... a lot");
|
||||
for(i = 0; i < 3; ++i) {
|
||||
print(call.apply(apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, sort, [[4,3,2,1]]]]]]]]]))
|
||||
}
|
41
nashorn/test/script/basic/JDK-8046905.js.EXPECTED
Normal file
41
nashorn/test/script/basic/JDK-8046905.js.EXPECTED
Normal file
@ -0,0 +1,41 @@
|
||||
1 level of apply
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
2 levels of apply
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
3 levels of apply
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
4 levels of apply
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
5 levels of apply
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
Many levels of apply!
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
different invocations that'll trigger relinking
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
5,6,7,8
|
||||
8-7-6-5
|
||||
Many levels of call!
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
call apply call apply call... a lot
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
apply call apply call apply... a lot
|
||||
1,2,3,4
|
||||
1,2,3,4
|
||||
1,2,3,4
|
@ -67,7 +67,7 @@ makeFuncExpectError("eval(\"x4\", x3);", ReferenceError);
|
||||
makeFuncAndCall("with({5.0000000000000000000000: String()}){(false); }");
|
||||
makeFuncAndCall("try { var x = undefined, x = 5.0000000000000000000000; } catch(x) { x = undefined; }");
|
||||
makeFuncAndCall("(function (x){ x %= this}(false))");
|
||||
// makeFuncAndCall("eval.apply.apply(function(){ eval('') })");
|
||||
makeFuncAndCall("eval.apply.apply(function(){ eval('') })");
|
||||
makeFuncAndCall("(false % !this) && 0");
|
||||
makeFuncAndCall("with({8: 'fafafa'.replace()}){ }");
|
||||
makeFuncAndCall("(function (x) '' )(true)");
|
||||
|
Loading…
Reference in New Issue
Block a user