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
@ -39,7 +39,7 @@ To learn about Mercurial in detail, please visit http://hgbook.red-bean.com.
- How to build? - How to build?
To build Nashorn, you need to install JDK 9. You may use the Nashorn To build Nashorn, you need to install JDK 9. You may use the Nashorn
forest build (recommended) or down load from java.net. You will need to forest build (recommended) or download from java.net. You will need to
set JAVA_HOME environmental variable to point to your JDK installation set JAVA_HOME environmental variable to point to your JDK installation
directory. directory.

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