8068901: Surprising behavior with more than one functional interface on a class

8068903: Can't invoke vararg @FunctionalInterface methods

Reviewed-by: attila, hannesw
This commit is contained in:
Athijegannathan Sundararajan 2015-09-01 18:28:11 +05:30
parent 9404b65ba8
commit 5c04be3588
6 changed files with 158 additions and 22 deletions

View File

@ -189,7 +189,7 @@ public final class Bootstrap {
* @return true if the obj is an instance of @FunctionalInterface interface
*/
public static boolean isFunctionalInterfaceObject(final Object obj) {
return !JSType.isPrimitive(obj) && (NashornBeansLinker.getFunctionalInterfaceMethod(obj.getClass()) != null);
return !JSType.isPrimitive(obj) && (NashornBeansLinker.getFunctionalInterfaceMethodName(obj.getClass()) != null);
}
/**

View File

@ -79,10 +79,10 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
}
// cache of @FunctionalInterface method of implementor classes
private static final ClassValue<Method> FUNCTIONAL_IFACE_METHOD = new ClassValue<Method>() {
private static final ClassValue<String> FUNCTIONAL_IFACE_METHOD_NAME = new ClassValue<String>() {
@Override
protected Method computeValue(final Class<?> type) {
return findFunctionalInterfaceMethod(type);
protected String computeValue(final Class<?> type) {
return findFunctionalInterfaceMethodName(type);
}
};
@ -107,19 +107,21 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
// annotated interface. This way Java method, constructor references or
// implementations of java.util.function.* interfaces can be called as though
// those are script functions.
final Method m = getFunctionalInterfaceMethod(self.getClass());
if (m != null) {
final String name = getFunctionalInterfaceMethodName(self.getClass());
if (name != null) {
final MethodType callType = desc.getMethodType();
// 'callee' and 'thiz' passed from script + actual arguments
if (callType.parameterCount() != m.getParameterCount() + 2) {
throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
}
return new GuardedInvocation(
// drop 'thiz' passed from the script.
MH.dropArguments(linkerServices.filterInternalObjects(desc.getLookup().unreflect(m)), 1,
callType.parameterType(1)), Guards.getInstanceOfGuard(
m.getDeclaringClass())).asTypeSafeReturn(
new NashornBeansLinkerServices(linkerServices), callType);
// drop callee (Undefined ScriptFunction) and change the request to be dyn:callMethod:<name>
final NashornCallSiteDescriptor newDesc = NashornCallSiteDescriptor.get(desc.getLookup(),
"dyn:callMethod:" + name, desc.getMethodType().dropParameterTypes(1, 2),
NashornCallSiteDescriptor.getFlags(desc));
final GuardedInvocation gi = getGuardedInvocation(beansLinker,
linkRequest.replaceArguments(newDesc, linkRequest.getArguments()),
new NashornBeansLinkerServices(linkerServices));
// drop 'thiz' passed from the script.
return gi.replaceMethods(
MH.dropArguments(linkerServices.filterInternalObjects(gi.getInvocation()), 1, callType.parameterType(1)),
gi.getGuard());
}
}
return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
@ -163,7 +165,7 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
return arg instanceof ConsString ? arg.toString() : arg;
}
private static Method findFunctionalInterfaceMethod(final Class<?> clazz) {
private static String findFunctionalInterfaceMethodName(final Class<?> clazz) {
if (clazz == null) {
return null;
}
@ -179,20 +181,20 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
// return the first abstract method
for (final Method m : iface.getMethods()) {
if (Modifier.isAbstract(m.getModifiers())) {
return m;
return m.getName();
}
}
}
}
// did not find here, try super class
return findFunctionalInterfaceMethod(clazz.getSuperclass());
return findFunctionalInterfaceMethodName(clazz.getSuperclass());
}
// Returns @FunctionalInterface annotated interface's single abstract
// method. If not found, returns null.
static Method getFunctionalInterfaceMethod(final Class<?> clazz) {
return FUNCTIONAL_IFACE_METHOD.get(clazz);
// method name. If not found, returns null.
static String getFunctionalInterfaceMethodName(final Class<?> clazz) {
return FUNCTIONAL_IFACE_METHOD_NAME.get(clazz);
}
static MethodHandleTransformer createHiddenObjectFilter() {

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2015, 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-8068901: Surprising behavior with more than one functional interface on a class
*
* @test
* @run
*/
var Consumer = java.util.function.Consumer;
var JFunction = java.util.function.Function;
var fc = new (Java.extend(JFunction, Consumer))({
apply: function(x) { print("fc invoked as a function") },
accept: function(x) { print("fc invoked as a consumer") }
});
var c = new Consumer(function(x) { print("c invoked as a consumer") });
var cf = new (Java.extend(Consumer, JFunction))({
apply: function(x) { print("cf invoked as a function") },
accept: function(x) { print("cf invoked as a consumer") }
});
var f = new JFunction(function(x) { print("f invoked as a function") });
for each(x in [fc, c, fc, cf, f, cf, c, fc, f, cf]) { x(null); }

View File

@ -0,0 +1,10 @@
fc invoked as a function
c invoked as a consumer
fc invoked as a function
cf invoked as a consumer
f invoked as a function
cf invoked as a consumer
c invoked as a consumer
fc invoked as a function
f invoked as a function
cf invoked as a consumer

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2015, 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-8068903: Can't invoke vararg @FunctionalInterface methods
*
* @test
* @run
*/
var vc = new (Java.type("jdk.nashorn.test.models.VarArgConsumer"))(
function(x) {
Assert.assertTrue(x.length == 3);
Assert.assertTrue(x[0] == 1);
Assert.assertTrue(x[1] == 2);
Assert.assertTrue(x[2] == 3);
}
);
vc(1, 2, 3);

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2015, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.nashorn.test.models;
/**
* Simple function interface with a varargs SAM method.
*/
@FunctionalInterface
public interface VarArgConsumer {
public void apply(Object... o);
}