8068901: Surprising behavior with more than one functional interface on a class
8068903: Can't invoke vararg @FunctionalInterface methods
Reviewed-by: attila, hannesw
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Mon Aug 31 15:18:59 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Tue Sep 01 18:28:11 2015 +0530
@@ -189,7 +189,7 @@
* @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);
}
/**
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Mon Aug 31 15:18:59 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Tue Sep 01 18:28:11 2015 +0530
@@ -79,10 +79,10 @@
}
// 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 @@
// 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 @@
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 @@
// 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() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8068901.js Tue Sep 01 18:28:11 2015 +0530
@@ -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); }
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8068901.js.EXPECTED Tue Sep 01 18:28:11 2015 +0530
@@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8068903.js Tue Sep 01 18:28:11 2015 +0530
@@ -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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/test/models/VarArgConsumer.java Tue Sep 01 18:28:11 2015 +0530
@@ -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);
+}
+