Mercurial > hg > openjdk > aarch64-port > nashorn
changeset 460:0cfa27ed82fe
8021122: Not all callables are handled for toString and other function valued properties
Reviewed-by: attila, hannesw, jlaskey
line wrap: on
line diff
--- a/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Tue Jul 23 18:17:25 2013 +0530 @@ -102,7 +102,7 @@ preorder.add(node); } - final boolean isReference = field != null && field.getAnnotation(Reference.class) != null; + final boolean isReference = field != null && field.isAnnotationPresent(Reference.class); Class<?> clazz = node.getClass(); String type = clazz.getName(); @@ -183,7 +183,7 @@ append('\n'); for (final Field child : children) { - if (child.getAnnotation(Ignore.class) != null) { + if (child.isAnnotationPresent(Ignore.class)) { continue; }
--- a/src/jdk/nashorn/internal/objects/Global.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/objects/Global.java Tue Jul 23 18:17:25 2013 +0530 @@ -63,6 +63,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.ScriptingFunctions; import jdk.nashorn.internal.runtime.Source; +import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; import jdk.nashorn.internal.scripts.JO; @@ -548,7 +549,8 @@ if (hint == String.class) { final Object toString = TO_STRING.getGetter().invokeExact(sobj); - if (toString instanceof ScriptFunction) { + + if (Bootstrap.isCallable(toString)) { final Object value = TO_STRING.getInvoker().invokeExact(toString, sobj); if (JSType.isPrimitive(value)) { return value; @@ -556,7 +558,7 @@ } final Object valueOf = VALUE_OF.getGetter().invokeExact(sobj); - if (valueOf instanceof ScriptFunction) { + if (Bootstrap.isCallable(valueOf)) { final Object value = VALUE_OF.getInvoker().invokeExact(valueOf, sobj); if (JSType.isPrimitive(value)) { return value; @@ -567,7 +569,7 @@ if (hint == Number.class) { final Object valueOf = VALUE_OF.getGetter().invokeExact(sobj); - if (valueOf instanceof ScriptFunction) { + if (Bootstrap.isCallable(valueOf)) { final Object value = VALUE_OF.getInvoker().invokeExact(valueOf, sobj); if (JSType.isPrimitive(value)) { return value; @@ -575,7 +577,7 @@ } final Object toString = TO_STRING.getGetter().invokeExact(sobj); - if (toString instanceof ScriptFunction) { + if (Bootstrap.isCallable(toString)) { final Object value = TO_STRING.getInvoker().invokeExact(toString, sobj); if (JSType.isPrimitive(value)) { return value;
--- a/src/jdk/nashorn/internal/objects/NativeArray.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/objects/NativeArray.java Tue Jul 23 18:17:25 2013 +0530 @@ -360,7 +360,7 @@ final ScriptObject sobj = (ScriptObject)obj; try { final Object join = JOIN.getGetter().invokeExact(sobj); - if (join instanceof ScriptFunction) { + if (Bootstrap.isCallable(join)) { return JOIN.getInvoker().invokeExact(join, sobj); } } catch (final RuntimeException | Error e) { @@ -396,7 +396,7 @@ final ScriptObject sobj = (ScriptObject)val; final Object toLocaleString = TO_LOCALE_STRING.getGetter().invokeExact(sobj); - if (toLocaleString instanceof ScriptFunction) { + if (Bootstrap.isCallable(toLocaleString)) { sb.append((String)TO_LOCALE_STRING.getInvoker().invokeExact(toLocaleString, sobj)); } else { throw typeError("not.a.function", "toLocaleString");
--- a/src/jdk/nashorn/internal/objects/NativeDate.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/objects/NativeDate.java Tue Jul 23 18:17:25 2013 +0530 @@ -47,6 +47,7 @@ import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; +import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; /** @@ -862,7 +863,7 @@ try { final Object func = TO_ISO_STRING.getGetter().invokeExact(sobj); - if (func instanceof ScriptFunction) { + if (Bootstrap.isCallable(func)) { return TO_ISO_STRING.getInvoker().invokeExact(func, sobj, key); } throw typeError("not.a.function", ScriptRuntime.safeToString(func));
--- a/src/jdk/nashorn/internal/objects/NativeJSON.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/objects/NativeJSON.java Tue Jul 23 18:17:25 2013 +0530 @@ -189,7 +189,7 @@ if (value instanceof ScriptObject) { final ScriptObject svalue = (ScriptObject)value; final Object toJSON = TO_JSON.getGetter().invokeExact(svalue); - if (toJSON instanceof ScriptFunction) { + if (Bootstrap.isCallable(toJSON)) { value = TO_JSON.getInvoker().invokeExact(toJSON, svalue, key); } }
--- a/src/jdk/nashorn/internal/objects/NativeObject.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/objects/NativeObject.java Tue Jul 23 18:17:25 2013 +0530 @@ -407,7 +407,7 @@ try { final Object toString = TO_STRING.getGetter().invokeExact(sobj); - if (toString instanceof ScriptFunction) { + if (Bootstrap.isCallable(toString)) { return TO_STRING.getInvoker().invokeExact(toString, sobj); } } catch (final RuntimeException | Error e) {
--- a/src/jdk/nashorn/internal/runtime/Context.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/Context.java Tue Jul 23 18:17:25 2013 +0530 @@ -36,6 +36,7 @@ import java.io.PrintWriter; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.reflect.Modifier; import java.util.concurrent.atomic.AtomicLong; import java.net.MalformedURLException; import java.net.URL; @@ -555,6 +556,7 @@ * Checks that the given package can be accessed from current call stack. * * @param fullName fully qualified package name + * @throw SecurityException if not accessible */ public static void checkPackageAccess(final String fullName) { final int index = fullName.lastIndexOf('.'); @@ -567,6 +569,31 @@ } /** + * Checks that the given package can be accessed from current call stack. + * + * @param fullName fully qualified package name + * @return true if package is accessible, false otherwise + */ + public static boolean isAccessiblePackage(final String fullName) { + try { + checkPackageAccess(fullName); + return true; + } catch (final SecurityException se) { + return false; + } + } + + /** + * Checks that the given Class can be accessed from current call stack and is public. + * + * @param clazz Class object to check + * @return true if Class is accessible, false otherwise + */ + public static boolean isAccessibleClass(final Class<?> clazz) { + return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz.getName()); + } + + /** * Lookup a Java class. This is used for JSR-223 stuff linking in from * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage} *
--- a/src/jdk/nashorn/internal/runtime/ListAdapter.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/ListAdapter.java Tue Jul 23 18:17:25 2013 +0530 @@ -31,6 +31,7 @@ import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.RandomAccess; +import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; /** @@ -157,7 +158,7 @@ } } private static void checkFunction(Object fn, InvokeByName invoke) { - if(!(fn instanceof ScriptFunction)) { + if(!(Bootstrap.isCallable(fn))) { throw new UnsupportedOperationException("The script object doesn't have a function named " + invoke.getName()); } }
--- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Tue Jul 23 18:17:25 2013 +0530 @@ -381,9 +381,7 @@ */ public static Object checkAndConstruct(final ScriptFunction target, final Object... args) { final ScriptObject global = Context.getGlobalTrusted(); - if (! (global instanceof GlobalObject)) { - throw new IllegalStateException("No current global set"); - } + assert (global instanceof GlobalObject): "No current global set"; if (target.getContext() != global.getContext()) { throw new IllegalArgumentException("'target' function is not from current Context");
--- a/src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java Tue Jul 23 18:17:25 2013 +0530 @@ -31,6 +31,7 @@ import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptRuntime; +import jdk.nashorn.internal.runtime.linker.Bootstrap; /** * Helper class for the various map/apply functions in {@link jdk.nashorn.internal.objects.NativeArray}. @@ -103,6 +104,8 @@ } else if (callbackfn instanceof ScriptObjectMirror && ((ScriptObjectMirror)callbackfn).isFunction()) { strict = ((ScriptObjectMirror)callbackfn).isStrictFunction(); + } else if (Bootstrap.isDynamicMethod(callbackfn) || Bootstrap.isFunctionalInterfaceObject(callbackfn)) { + strict = false; } else { throw typeError("not.a.function", ScriptRuntime.safeToString(callbackfn)); }
--- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Tue Jul 23 18:17:25 2013 +0530 @@ -38,8 +38,11 @@ import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkerServices; +import jdk.nashorn.api.scripting.ScriptObjectMirror; import jdk.nashorn.internal.codegen.CompilerConstants.Call; import jdk.nashorn.internal.codegen.RuntimeCallSite; +import jdk.nashorn.internal.runtime.ScriptFunction; +import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.options.Options; /** @@ -68,6 +71,41 @@ } /** + * Returns if the given object is a "callable" + * @param obj object to be checked for callability + * @return true if the obj is callable + */ + public static boolean isCallable(final Object obj) { + if (obj == ScriptRuntime.UNDEFINED || obj == null) { + return false; + } + + return obj instanceof ScriptFunction || + ((obj instanceof ScriptObjectMirror) && ((ScriptObjectMirror)obj).isFunction()) || + isDynamicMethod(obj) || + isFunctionalInterfaceObject(obj); + } + + /** + * Returns if the given object is a dynalink Dynamic method + * @param obj object to be checked + * @return true if the obj is a dynamic method + */ + public static boolean isDynamicMethod(final Object obj) { + return obj instanceof BoundDynamicMethod || BeansLinker.isDynamicMethod(obj); + } + + /** + * Returns if the given object is an instance of an interface annotated with + * java.lang.FunctionalInterface + * @param obj object to be checked + * @return true if the obj is an instance of @FunctionalInterface interface + */ + public static boolean isFunctionalInterfaceObject(final Object obj) { + return obj != null && (NashornBottomLinker.getFunctionalInterfaceMethod(obj.getClass()) != null); + } + + /** * Create a call site and link it for Nashorn. This version of the method conforms to the invokedynamic bootstrap * method expected signature and is referenced from Nashorn generated bytecode as the bootstrap method for all * invokedynamic instructions.
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Tue Jul 23 18:17:25 2013 +0530 @@ -924,6 +924,6 @@ } private static boolean isCallerSensitive(final AccessibleObject e) { - return e.getAnnotation(CallerSensitive.class) != null; + return e.isAnnotationPresent(CallerSensitive.class); } }
--- a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Thu Jul 18 16:47:45 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Tue Jul 23 18:17:25 2013 +0530 @@ -30,6 +30,9 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -37,6 +40,7 @@ import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.support.Guards; +import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptRuntime; /** @@ -73,7 +77,7 @@ private static final MethodHandle EMPTY_ELEM_SETTER = MH.dropArguments(EMPTY_PROP_SETTER, 0, Object.class); - private static GuardedInvocation linkBean(final LinkRequest linkRequest, final LinkerServices linkerServices) { + private static GuardedInvocation linkBean(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor)linkRequest.getCallSiteDescriptor(); final Object self = linkRequest.getReceiver(); final String operator = desc.getFirstOperator(); @@ -84,6 +88,22 @@ } throw typeError("not.a.function", ScriptRuntime.safeToString(self)); case "call": + // Support dyn:call on any object that supports some @FunctionalInterface + // 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 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(desc.getLookup().unreflect(m), 1, callType.parameterType(1)), + Guards.getInstanceOfGuard(m.getDeclaringClass())).asType(callType); + } if(BeansLinker.isDynamicMethod(self)) { throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self)); } @@ -148,4 +168,32 @@ } return ScriptRuntime.safeToString(linkRequest.getArguments()[1]); } + + // Returns @FunctionalInterface annotated interface's single abstract method. + // If not found, returns null + static Method getFunctionalInterfaceMethod(final Class<?> clazz) { + if (clazz == null) { + return null; + } + + for (Class<?> iface : clazz.getInterfaces()) { + // check accessiblity up-front + if (! Context.isAccessibleClass(iface)) { + continue; + } + + // check for @FunctionalInterface + if (iface.isAnnotationPresent(FunctionalInterface.class)) { + // return the first abstract method + for (final Method m : iface.getMethods()) { + if (Modifier.isAbstract(m.getModifiers())) { + return m; + } + } + } + } + + // did not find here, try super class + return getFunctionalInterfaceMethod(clazz.getSuperclass()); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8021122.js Tue Jul 23 18:17:25 2013 +0530 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2010, 2013, 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-8021122: Not all callables are handled for toString and other function valued properties + * + * @test + * @run + */ + +var a = {} +var obj = new java.util.HashMap(); +Object.bindProperties(a, obj); +try { + print(a); +} catch (e) { + print(e); +} + +var a = {} +var global = loadWithNewGlobal({ name:"xx", script: "this" }); +var obj = global.eval("({ toString: function() { return 'hello'; } })"); +Object.bindProperties(a, obj); +try { + print(a); +} catch (e) { + print(e); +} + +function runLambdaTests() { + var r = new java.lang.Runnable() { + run: function() { print("I am runnable"); } + }; + + // call any @FunctionalInterface object as though it is a function + r(); + + var twice = new java.util.function.Function() { + apply: function(x) 2*x + }; + + print(twice(34)); + + var sum = new java.util.function.BiFunction() { + apply: function(x, y) x + y + }; + + print(sum(32, 12)) + + // make toString to be a @FunctionalInterface object + var a = {}; + a.toString = new java.util.function.Supplier() { + get: function() { return "MyString"; } + }; + + try { + print(a); + } catch (e) { + print(e); + } +} + +try { + // check for java.util.function.Function class + Java.type("java.util.function.Function"); + runLambdaTests(); +} catch (e) { + // fake output to match .EXPECTED values + print("I am runnable"); + print("68"); + print("44"); + print("MyString"); +}