8162839: JavaAdapters do not work with ScriptObjectMirror objects
Reviewed-by: sundar, jlaskey
--- a/nashorn/make/build.xml Wed Nov 16 12:35:19 2016 -0800
+++ b/nashorn/make/build.xml Thu Nov 17 13:39:30 2016 +0100
@@ -524,17 +524,19 @@
<!-- only to be invoked as dependency of "test" target -->
<target name="-test-classes-all" depends="jar" unless="test.class">
<fileset id="test.classes" dir="${build.test.classes.dir}">
+ <include name="**/dynalink/beans/test/*Test.class"/>
+ <include name="**/dynalink/linker/support/test/*Test.class"/>
+ <include name="**/dynalink/support/test/*Test.class"/>
<include name="**/dynalink/test/*Test.class"/>
- <include name="**/dynalink/beans/test/*Test.class"/>
<include name="**/api/javaaccess/test/*Test.class"/>
<include name="**/api/scripting/test/*Test.class"/>
<include name="**/api/tree/test/*Test.class"/>
<include name="**/codegen/test/*Test.class"/>
<include name="**/parser/test/*Test.class"/>
<include name="**/runtime/test/*Test.class"/>
+ <include name="**/runtime/doubleconv/test/*Test.class"/>
<include name="**/runtime/regexp/test/*Test.class"/>
<include name="**/runtime/regexp/joni/test/*Test.class"/>
- <include name="**/runtime/doubleconv/test/*Test.class"/>
<include name="**/framework/*Test.class"/>
</fileset>
</target>
@@ -550,6 +552,7 @@
<target name="-test-nosecurity" unless="test.class">
<fileset id="test.nosecurity.classes" dir="${build.test.classes.dir}">
<include name="**/framework/ScriptTest.class"/>
+ <include name="**/runtime/linker/test/*Test.class"/>
</fileset>
<testng outputdir="${build.nosecurity.test.results.dir}/${testResultsSubDir}" classfilesetref="test.nosecurity.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/ClassString.java Wed Nov 16 12:35:19 2016 -0800
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/ClassString.java Thu Nov 17 13:39:30 2016 +0100
@@ -88,6 +88,7 @@
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import jdk.dynalink.internal.AccessControlContextFactory;
@@ -149,6 +150,11 @@
return hashCode;
}
+ @Override
+ public String toString() {
+ return "ClassString[" + Arrays.toString(classes) + "]";
+ }
+
boolean isVisibleFrom(final ClassLoader classLoader) {
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Wed Nov 16 12:35:19 2016 -0800
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Thu Nov 17 13:39:30 2016 +0100
@@ -31,24 +31,13 @@
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
-import static jdk.internal.org.objectweb.asm.Opcodes.AALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
-import static jdk.internal.org.objectweb.asm.Opcodes.ARRAYLENGTH;
import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.D2F;
-import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
-import static jdk.internal.org.objectweb.asm.Opcodes.GOTO;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
-import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
-import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGE;
-import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
-import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
-import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.I2B;
import static jdk.internal.org.objectweb.asm.Opcodes.I2S;
-import static jdk.internal.org.objectweb.asm.Opcodes.POP;
-import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
@@ -66,6 +55,7 @@
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -73,20 +63,14 @@
import java.util.List;
import java.util.Set;
import jdk.internal.org.objectweb.asm.ClassWriter;
-import jdk.internal.org.objectweb.asm.FieldVisitor;
-import jdk.internal.org.objectweb.asm.Handle;
-import jdk.internal.org.objectweb.asm.Label;
-import jdk.internal.org.objectweb.asm.MethodVisitor;
-import jdk.internal.org.objectweb.asm.Opcodes;
-import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.Label;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.api.scripting.ScriptUtils;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
-import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome;
@@ -139,7 +123,8 @@
* to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>.
* </p><p>
* It is possible to create two different adapter classes: those that can have class-level overrides, and those that can
- * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked
+ * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject, ProtectionDomain)}
+ * or {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject, Lookup)} is invoked
* with non-null {@code classOverrides} parameter, an adapter class is created that can have class-level overrides, and
* the passed script object will be used as the implementations for its methods, just as in the above case of the
* constructor taking a script object. Note that in the case of class-level overrides, a new adapter class is created on
@@ -168,6 +153,7 @@
private static final Type OBJECT_TYPE = Type.getType(Object.class);
private static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class);
private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class);
+ private static final Type SCRIPT_OBJECT_MIRROR_TYPE = Type.getType(ScriptObjectMirror.class);
// JavaAdapterServices methods used in generated bytecode
private static final Call CHECK_FUNCTION = lookupServiceMethod("checkFunction", ScriptFunction.class, Object.class, String.class);
@@ -182,6 +168,7 @@
private static final Call TO_CHAR_PRIMITIVE = lookupServiceMethod("toCharPrimitive", char.class, Object.class);
private static final Call UNSUPPORTED = lookupServiceMethod("unsupported", UnsupportedOperationException.class);
private static final Call WRAP_THROWABLE = lookupServiceMethod("wrapThrowable", RuntimeException.class, Throwable.class);
+ private static final Call UNWRAP_MIRROR = lookupServiceMethod("unwrapMirror", ScriptObject.class, Object.class, boolean.class);
// Other methods invoked by the generated bytecode
private static final Call UNWRAP = staticCallNoLookup(ScriptUtils.class, "unwrap", Object.class, Object.class);
@@ -216,8 +203,7 @@
private static final String GET_METHOD_PROPERTY_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, SCRIPT_OBJECT_TYPE);
private static final String VOID_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE);
- static final String ADAPTER_PACKAGE_INTERNAL = "jdk/nashorn/javaadapters/";
- static final String ADAPTER_PACKAGE = "jdk.nashorn.javaadapters";
+ private static final String ADAPTER_PACKAGE_INTERNAL = "jdk/nashorn/javaadapters/";
private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 255;
// Method name prefix for invoking super-methods
@@ -265,7 +251,7 @@
* @throws AdaptationException if the adapter can not be generated for some reason.
*/
JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces,
- final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException {
+ final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException {
assert superClass != null && !superClass.isInterface();
assert interfaces != null;
@@ -369,7 +355,7 @@
// If the class is a SAM, allow having ScriptFunction passed as class overrides
mv.dup();
mv.instanceOf(SCRIPT_FUNCTION_TYPE);
- mv.dup();
+ mv.dup();
mv.putstatic(generatedClassName, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR);
final Label notFunction = new Label();
mv.ifeq(notFunction);
@@ -389,9 +375,9 @@
private void emitInitCallThis(final InstructionAdapter mv) {
loadField(mv, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
GET_CALL_THIS.invoke(mv);
- if(classOverride) {
+ if(classOverride) {
mv.putstatic(generatedClassName, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR);
- } else {
+ } else {
// It is presumed ALOAD 0 was already executed
mv.putfield(generatedClassName, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR);
}
@@ -427,10 +413,10 @@
if (samName == null) {
return false;
- }
- // If all our abstract methods have a single name, generate an additional constructor, one that takes a
- // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods.
- generateOverridingConstructor(ctor, true);
+ }
+ // If all our abstract methods have a single name, generate an additional constructor, one that takes a
+ // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods.
+ generateOverridingConstructor(ctor, true);
// If the original type only has a single abstract method name, as well as a default ctor, then it can
// be automatically converted from JS function.
return ctor.getParameterTypes().length == 0;
@@ -456,14 +442,9 @@
* constructor passed as the argument here, and delegate to it. However, it will take an additional argument of
* either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize
* all the method handle fields of the adapter instance with functions from the script object (or the script
- * function itself, if that's what's passed). There is one method handle field in the adapter class for every method
- * that can be implemented or overridden; the name of every field is same as the name of the method, with a number
- * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke
- * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType,
- * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity
- * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}.
- * The constructor that takes a script function will only initialize the methods with the same name as the single
- * abstract method. The constructor will also store the Nashorn global that was current at the constructor
+ * function itself, if that's what's passed). Additionally, it will create another constructor with an additional
+ * Object type parameter that can be used for ScriptObjectMirror objects.
+ * The constructor will also store the Nashorn global that was current at the constructor
* invocation time in a field named "global". The generated constructor will be public, regardless of whether the
* supertype constructor was public or protected. The generated constructor will not be variable arity, even if the
* supertype constructor was.
@@ -524,14 +505,59 @@
}
}
- // Object additional param accepting constructor - generated to handle null and undefined value
- // for script adapters. This is effectively to throw TypeError on such script adapters. See
- // JavaAdapterServices.getHandle as well.
+ // Object additional param accepting constructor for handling ScriptObjectMirror objects, which are
+ // unwrapped to work as ScriptObjects or ScriptFunctions. This also handles null and undefined values for
+ // script adapters by throwing TypeError on such script adapters.
private void generateOverridingConstructorWithObjectParam(final InstructionAdapter mv, final String ctorDescriptor) {
mv.visitCode();
final int extraArgOffset = emitSuperConstructorCall(mv, ctorDescriptor);
+
+ // Check for ScriptObjectMirror
+ mv.visitVarInsn(ALOAD, extraArgOffset);
+ mv.instanceOf(SCRIPT_OBJECT_MIRROR_TYPE);
+ final Label notMirror = new Label();
+ mv.ifeq(notMirror);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, extraArgOffset);
+ mv.iconst(0);
+ UNWRAP_MIRROR.invoke(mv);
+ mv.putfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, extraArgOffset);
+ mv.iconst(1);
+ UNWRAP_MIRROR.invoke(mv);
+ mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+
+ final Label done = new Label();
+
+ if (samName != null) {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.getfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+ mv.instanceOf(SCRIPT_FUNCTION_TYPE);
+ mv.ifeq(done);
+
+ // Assign "isFunction = true"
+ mv.visitVarInsn(ALOAD, 0);
+ mv.iconst(1);
+ mv.putfield(generatedClassName, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.dup();
+ mv.getfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+ mv.checkcast(SCRIPT_FUNCTION_TYPE);
+ emitInitCallThis(mv);
+ mv.goTo(done);
+ }
+
+ mv.visitLabel(notMirror);
+
+ // Throw error if not a ScriptObject
mv.visitVarInsn(ALOAD, extraArgOffset);
NOT_AN_OBJECT.invoke(mv);
+
+ mv.visitLabel(done);
endInitMethod(mv);
}
@@ -678,7 +704,7 @@
// stack: [callThis, delegate]
mv.goTo(callCallee);
mv.visitLabel(notFunction);
- } else {
+ } else {
// If it's not a SAM method, and the delegate is a function,
// it'll fall back to default behavior
mv.ifne(defaultBehavior);
@@ -818,7 +844,7 @@
if (isVarArgCall) {
// Variable arity calls are always (Object callee, Object this, Object[] params)
callParamTypes = new Class<?>[] { Object.class, Object.class, Object[].class };
- } else {
+ } else {
// Adjust invocation type signature for conversions we instituted in
// convertParam; also, byte and short get passed as ints.
final Class<?>[] origParamTypes = type.parameterArray();
@@ -868,13 +894,13 @@
private void loadField(final InstructionAdapter mv, final String name, final String desc) {
- if(classOverride) {
+ if(classOverride) {
mv.getstatic(generatedClassName, name, desc);
- } else {
- mv.visitVarInsn(ALOAD, 0);
+ } else {
+ mv.visitVarInsn(ALOAD, 0);
mv.getfield(generatedClassName, name, desc);
- }
- }
+ }
+ }
private static void convertReturnValue(final InstructionAdapter mv, final Class<?> origReturnType) {
if (origReturnType == void.class) {
@@ -948,6 +974,7 @@
}
return len;
}
+
/**
* Emit code to restore the previous Nashorn Context when needed.
* @param mv the instruction adapter
@@ -999,9 +1026,9 @@
}
for (final Class<?> iface : interfaces) {
- if (cl.isAssignableFrom(iface)) {
- return iface;
- }
+ if (cl.isAssignableFrom(iface)) {
+ return iface;
+ }
}
// we better that interface that extends the given interface!
@@ -1122,8 +1149,8 @@
if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) {
finalMethods.add(mi);
} else if (!finalMethods.contains(mi) && methodInfos.add(mi) && Modifier.isAbstract(m)) {
- abstractMethodNames.add(mi.getName());
- }
+ abstractMethodNames.add(mi.getName());
+ }
}
}
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java Wed Nov 16 12:35:19 2016 -0800
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java Thu Nov 17 13:39:30 2016 +0100
@@ -234,7 +234,7 @@
/**
* For a given class, create its adapter class and associated info.
*
- * @param type the class for which the adapter is created
+ * @param types the class and interfaces for which the adapter is created
*
* @return the adapter info for the class.
*/
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Wed Nov 16 12:35:19 2016 -0800
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Thu Nov 17 13:39:30 2016 +0100
@@ -39,6 +39,7 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.CodeSigner;
import java.security.CodeSource;
@@ -51,6 +52,7 @@
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ECMAException;
@@ -176,6 +178,23 @@
}
/**
+ * Returns the ScriptObject or Global field value from a ScriptObjectMirror using reflection.
+ *
+ * @param mirror the mirror object
+ * @param getGlobal true if we want the global object, false to return the script object
+ * @return the script object or global object
+ */
+ public static ScriptObject unwrapMirror(final Object mirror, final boolean getGlobal) {
+ assert mirror instanceof ScriptObjectMirror;
+ try {
+ final Field field = getGlobal ? MirrorFieldHolder.GLOBAL_FIELD : MirrorFieldHolder.SOBJ_FIELD;
+ return (ScriptObject) field.get(mirror);
+ } catch (final IllegalAccessException x) {
+ throw new RuntimeException(x);
+ }
+ }
+
+ /**
* Delegate to {@link Bootstrap#bootstrap(Lookup, String, MethodType, int)}.
* @param lookup MethodHandle lookup.
* @param opDesc Dynalink dynamic operation descriptor.
@@ -291,4 +310,24 @@
.asCollector(Object[].class, type.parameterCount())
.asType(type));
}
+
+ // Initialization on demand holder for accessible ScriptObjectMirror fields
+ private static class MirrorFieldHolder {
+
+ private static final Field SOBJ_FIELD = getMirrorField("sobj");
+ private static final Field GLOBAL_FIELD = getMirrorField("global");
+
+ private static Field getMirrorField(final String fieldName) {
+ try {
+ final Field field = ScriptObjectMirror.class.getDeclaredField(fieldName);
+ AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+ field.setAccessible(true);
+ return null;
+ });
+ return field;
+ } catch (final NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornLinker.java Wed Nov 16 12:35:19 2016 -0800
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornLinker.java Thu Nov 17 13:39:30 2016 +0100
@@ -157,12 +157,17 @@
*/
private static GuardedInvocation getSamTypeConverter(final Class<?> sourceType, final Class<?> targetType, final Supplier<MethodHandles.Lookup> lookupSupplier) throws Exception {
// If source type is more generic than ScriptFunction class, we'll need to use a guard
- final boolean isSourceTypeGeneric = sourceType.isAssignableFrom(ScriptFunction.class);
+ final boolean isSourceTypeGeneric = sourceType.isAssignableFrom(ScriptObject.class);
if ((isSourceTypeGeneric || ScriptFunction.class.isAssignableFrom(sourceType)) && isAutoConvertibleFromFunction(targetType)) {
- final MethodHandle ctor = JavaAdapterFactory.getConstructor(ScriptFunction.class, targetType, getCurrentLookup(lookupSupplier));
+ final Class<?> paramType = isSourceTypeGeneric ? Object.class : ScriptFunction.class;
+ // Using Object.class as constructor source type means we're getting an overloaded constructor handle,
+ // which is safe but slower than a single constructor handle. If the actual argument is a ScriptFunction it
+ // would be nice if we could change the formal parameter to ScriptFunction.class and add a guard for it
+ // in the main invocation.
+ final MethodHandle ctor = JavaAdapterFactory.getConstructor(paramType, targetType, getCurrentLookup(lookupSupplier));
assert ctor != null; // if isAutoConvertibleFromFunction() returned true, then ctor must exist.
- return new GuardedInvocation(ctor, isSourceTypeGeneric ? IS_SCRIPT_FUNCTION : null);
+ return new GuardedInvocation(ctor, isSourceTypeGeneric ? IS_FUNCTION : null);
}
return null;
}
@@ -315,7 +320,7 @@
}
private static final MethodHandle IS_SCRIPT_OBJECT = Guards.isInstance(ScriptObject.class, MH.type(Boolean.TYPE, Object.class));
- private static final MethodHandle IS_SCRIPT_FUNCTION = Guards.isInstance(ScriptFunction.class, MH.type(Boolean.TYPE, Object.class));
+ private static final MethodHandle IS_FUNCTION = findOwnMH("isFunction", boolean.class, Object.class);
private static final MethodHandle IS_NATIVE_ARRAY = Guards.isOfClass(NativeArray.class, MH.type(Boolean.TYPE, Object.class));
private static final MethodHandle IS_NASHORN_OR_UNDEFINED_TYPE = findOwnMH("isNashornTypeOrUndefined", Boolean.TYPE, Object.class);
@@ -348,6 +353,11 @@
return obj instanceof ScriptObject? ScriptUtils.wrap((ScriptObject)obj) : obj;
}
+ @SuppressWarnings("unused")
+ private static boolean isFunction(final Object obj) {
+ return obj instanceof ScriptFunction || obj instanceof ScriptObjectMirror && ((ScriptObjectMirror) obj).isFunction();
+ }
+
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(MethodHandles.lookup(), NashornLinker.class, name, MH.type(rtype, types));
}
--- a/nashorn/test/src/jdk/nashorn/internal/runtime/linker/test/JavaAdapterTest.java Wed Nov 16 12:35:19 2016 -0800
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/linker/test/JavaAdapterTest.java Thu Nov 17 13:39:30 2016 +0100
@@ -29,11 +29,14 @@
import java.util.List;
import java.util.Map;
import java.util.Queue;
+import java.util.function.Function;
import java.util.function.Supplier;
import javax.script.Bindings;
+import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import jdk.nashorn.api.scripting.JSObject;
+import jdk.nashorn.api.scripting.NashornScriptEngine;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.runtime.Context;
@@ -277,4 +280,37 @@
Assert.assertEquals(tla.getQueue().peek(), "Queue");
Assert.assertEquals(tla.getDequeue().peek(), "Dequeue");
}
+
+ @Test
+ public static void testMirrorAdapter() throws ScriptException {
+ final NashornScriptEngine e = (NashornScriptEngine) createEngine();
+ e.setBindings(e.createBindings(), ScriptContext.GLOBAL_SCOPE); // Null by default
+
+ // Referencing functions from across scopes causes them to be wrapped in ScriptObjectMirrors
+ e.eval("function convertObjectFromEngineScope(){ return new java.util.concurrent.Callable(o).call(); }", e.getBindings(ScriptContext.ENGINE_SCOPE));
+ e.eval("function convertObjectFromGlobalScope(){ return new java.util.concurrent.Callable(o).call(); }", e.getBindings(ScriptContext.GLOBAL_SCOPE));
+ e.eval("function convertFuncFromEngineScope(){ return new java.util.concurrent.Callable(g).call(); }", e.getBindings(ScriptContext.ENGINE_SCOPE));
+ e.eval("function convertFuncFromGlobalScope(){ return new java.util.concurrent.Callable(g).call(); }", e.getBindings(ScriptContext.GLOBAL_SCOPE));
+ e.eval("function convertParamFromEngineScope(){ return Java.type('jdk.nashorn.internal.runtime.linker.test.JavaAdapterTest').m(f);}", e.getBindings(ScriptContext.ENGINE_SCOPE));
+ e.eval("function convertParamFromGlobalScope(){ return Java.type('jdk.nashorn.internal.runtime.linker.test.JavaAdapterTest').m(f);}", e.getBindings(ScriptContext.GLOBAL_SCOPE));
+
+ e.eval("var o = { call: function () { return 'ok from o'; } }", e.getBindings(ScriptContext.ENGINE_SCOPE));
+ e.eval("function g() { return 'ok from g'; }", e.getBindings(ScriptContext.ENGINE_SCOPE));
+ e.eval("function f(a) { return a.toUpperCase(); }", e.getBindings(ScriptContext.ENGINE_SCOPE));
+
+ try {
+ Assert.assertEquals(e.invokeFunction("convertObjectFromEngineScope"), "ok from o");
+ Assert.assertEquals(e.invokeFunction("convertObjectFromGlobalScope"), "ok from o");
+ Assert.assertEquals(e.invokeFunction("convertFuncFromEngineScope"), "ok from g");
+ Assert.assertEquals(e.invokeFunction("convertFuncFromGlobalScope"), "ok from g");
+ Assert.assertEquals(e.invokeFunction("convertParamFromEngineScope"), "OK");
+ Assert.assertEquals(e.invokeFunction("convertParamFromGlobalScope"), "OK");
+ } catch (final NoSuchMethodException x) {
+ throw new RuntimeException(x);
+ }
+ }
+
+ public static String m(final Function<String, String> f){
+ return f.apply("ok");
+ }
}