8166186: ClassCastException with arguments usage

Reviewed-by: attila, sundar
This commit is contained in:
Hannes Wallnöfer 2017-01-25 09:49:02 +01:00
parent b11a5c2059
commit a043135fe4
6 changed files with 109 additions and 27 deletions

View File

@ -18,7 +18,7 @@ Information about the forest extension can be found at
http://mercurial.selenic.com/wiki/ForestExtension http://mercurial.selenic.com/wiki/ForestExtension
and downlaoded using and downloaded using
hg clone https://bitbucket.org/gxti/hgforest hg clone https://bitbucket.org/gxti/hgforest

View File

@ -395,39 +395,45 @@ public class GuardedInvocation {
} }
/** /**
* Applies argument filters to both the invocation and the guard (if there * Applies argument filters to both the invocation and the guard
* is one) with {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)}. * (if it exists and has at least {@code pos + 1} parameters) with
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)}.
* @param pos the position of the first argument being filtered * @param pos the position of the first argument being filtered
* @param filters the argument filters * @param filters the argument filters
* @return a filtered invocation * @return a filtered invocation
*/ */
public GuardedInvocation filterArguments(final int pos, final MethodHandle... filters) { public GuardedInvocation filterArguments(final int pos, final MethodHandle... filters) {
return replaceMethods(MethodHandles.filterArguments(invocation, pos, filters), guard == null ? null : return replaceMethods(MethodHandles.filterArguments(invocation, pos, filters),
MethodHandles.filterArguments(guard, pos, filters)); guard == null || pos >= guard.type().parameterCount() ?
guard : MethodHandles.filterArguments(guard, pos, filters));
} }
/** /**
* Makes an invocation that drops arguments in both the invocation and the * Makes an invocation that drops arguments in both the invocation and the
* guard (if there is one) with {@link MethodHandles#dropArguments(MethodHandle, int, List)}. * guard (if it exists and has at least {@code pos} parameters) with
* {@link MethodHandles#dropArguments(MethodHandle, int, List)}.
* @param pos the position of the first argument being dropped * @param pos the position of the first argument being dropped
* @param valueTypes the types of the values being dropped * @param valueTypes the types of the values being dropped
* @return an invocation that drops arguments * @return an invocation that drops arguments
*/ */
public GuardedInvocation dropArguments(final int pos, final List<Class<?>> valueTypes) { public GuardedInvocation dropArguments(final int pos, final List<Class<?>> valueTypes) {
return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes), guard == null ? null : return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes),
MethodHandles.dropArguments(guard, pos, valueTypes)); guard == null || pos > guard.type().parameterCount() ?
guard : MethodHandles.dropArguments(guard, pos, valueTypes));
} }
/** /**
* Makes an invocation that drops arguments in both the invocation and the * Makes an invocation that drops arguments in both the invocation and the
* guard (if there is one) with {@link MethodHandles#dropArguments(MethodHandle, int, Class...)}. * guard (if it exists and has at least {@code pos} parameters) with
* {@link MethodHandles#dropArguments(MethodHandle, int, Class...)}.
* @param pos the position of the first argument being dropped * @param pos the position of the first argument being dropped
* @param valueTypes the types of the values being dropped * @param valueTypes the types of the values being dropped
* @return an invocation that drops arguments * @return an invocation that drops arguments
*/ */
public GuardedInvocation dropArguments(final int pos, final Class<?>... valueTypes) { public GuardedInvocation dropArguments(final int pos, final Class<?>... valueTypes) {
return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes), guard == null ? null : return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes),
MethodHandles.dropArguments(guard, pos, valueTypes)); guard == null || pos > guard.type().parameterCount() ?
guard : MethodHandles.dropArguments(guard, pos, valueTypes));
} }

View File

@ -38,7 +38,7 @@ import jdk.nashorn.internal.runtime.linker.Bootstrap;
/** /**
* This linker exporter is a service provider that exports Nashorn Dynalink * This linker exporter is a service provider that exports Nashorn Dynalink
* linkers to external users. Other languague runtimes that use Dynalink * linkers to external users. Other language runtimes that use Dynalink
* can use the linkers exported by this provider to support tight integration * can use the linkers exported by this provider to support tight integration
* of Nashorn objects. * of Nashorn objects.
*/ */

View File

@ -735,7 +735,7 @@ public class ScriptFunction extends ScriptObject {
/** /**
* Name getter - ECMA Function.name * Name getter - ECMA Function.name
* *
* @param self self refence * @param self self reference
* @return the name, or undefined if none * @return the name, or undefined if none
*/ */
public static Object G$name(final Object self) { public static Object G$name(final Object self) {
@ -1120,12 +1120,19 @@ public class ScriptFunction extends ScriptObject {
assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage. assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage.
final Class<?> applyFnType = descType.parameterType(0); final Class<?> applyFnType = descType.parameterType(0);
MethodHandle inv = appliedInvocation.getInvocation(); //method handle from apply invocation. the applied function invocation // Invocation and guard handles from apply invocation.
MethodHandle inv = appliedInvocation.getInvocation();
MethodHandle guard = appliedInvocation.getGuard();
if (isApply && !isFailedApplyToCall) { if (isApply && !isFailedApplyToCall) {
if (passesArgs) { if (passesArgs) {
// Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it. // Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it.
inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS); inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS);
// Some guards (non-strict functions with non-primitive this) have a this-object parameter, so we
// need to apply this transformations to them as well.
if (guard.type().parameterCount() > 2) {
guard = MH.filterArguments(guard, 2, NativeFunction.TO_APPLY_ARGS);
}
} else { } else {
// If the original call site doesn't pass argArray, pass in an empty array // If the original call site doesn't pass argArray, pass in an empty array
inv = MH.insertArguments(inv, 2, (Object) ScriptRuntime.EMPTY_ARRAY); inv = MH.insertArguments(inv, 2, (Object) ScriptRuntime.EMPTY_ARRAY);
@ -1144,12 +1151,25 @@ public class ScriptFunction extends ScriptObject {
if (!passesThis) { if (!passesThis) {
// If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed // If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed
inv = bindImplicitThis(appliedFn, inv); inv = bindImplicitThis(appliedFnNeedsWrappedThis, inv);
// guard may have this-parameter that needs to be inserted
if (guard.type().parameterCount() > 1) {
guard = bindImplicitThis(appliedFnNeedsWrappedThis, guard);
}
} else if (appliedFnNeedsWrappedThis) { } else if (appliedFnNeedsWrappedThis) {
// target function needs a wrapped this, so make sure we filter for that // target function needs a wrapped this, so make sure we filter for that
inv = MH.filterArguments(inv, 1, WRAP_THIS); inv = MH.filterArguments(inv, 1, WRAP_THIS);
// guard may have this-parameter that needs to be wrapped
if (guard.type().parameterCount() > 1) {
guard = MH.filterArguments(guard, 1, WRAP_THIS);
} }
}
final MethodType guardType = guard.type(); // Needed for combining guards below
// We need to account for the dropped (apply|call) function argument.
inv = MH.dropArguments(inv, 0, applyFnType); inv = MH.dropArguments(inv, 0, applyFnType);
guard = MH.dropArguments(guard, 0, applyFnType);
/* /*
* Dropargs can only be non-()V in the case of isApply && !isFailedApplyToCall, which * Dropargs can only be non-()V in the case of isApply && !isFailedApplyToCall, which
@ -1160,15 +1180,6 @@ public class ScriptFunction extends ScriptObject {
inv = MH.dropArguments(inv, 4 + i, dropArgs.parameterType(i)); inv = MH.dropArguments(inv, 4 + i, dropArgs.parameterType(i));
} }
MethodHandle guard = appliedInvocation.getGuard();
// If the guard checks the value of "this" but we aren't passing thisArg, insert the default one
if (!passesThis && guard.type().parameterCount() > 1) {
guard = bindImplicitThis(appliedFn, guard);
}
final MethodType guardType = guard.type();
// 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. // Take the "isApplyFunction" guard, and bind it to this function.
MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this); //TODO replace this with switchpoint MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this); //TODO replace this with switchpoint
// Adapt the guard to receive all the arguments that the original guard does. // Adapt the guard to receive all the arguments that the original guard does.
@ -1244,9 +1255,9 @@ public class ScriptFunction extends ScriptObject {
return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount); return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount);
} }
private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) { private static MethodHandle bindImplicitThis(final boolean needsWrappedThis, final MethodHandle mh) {
final MethodHandle bound; final MethodHandle bound;
if (fn instanceof ScriptFunction && ((ScriptFunction) fn).needsWrappedThis()) { if (needsWrappedThis) {
bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER); bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER);
} else { } else {
bound = mh; bound = mh;

View File

@ -0,0 +1,45 @@
/*
* 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.
*/
/**
* 8166186: ClassCastException with arguments usage
*
* @test
* @run
*/
function F (arg) {
print("F called");
Function.prototype.call.apply(G, arguments);
}
function G (){
print("G called");
print(this);
if (C++ > 5) return;
F("argument " + C);
}
var C = 0;
G();

View File

@ -0,0 +1,20 @@
G called
[object global]
F called
G called
argument 1
F called
G called
argument 2
F called
G called
argument 3
F called
G called
argument 4
F called
G called
argument 5
F called
G called
argument 6