# HG changeset patch # User sundar # Date 1407343272 -19800 # Node ID 41d18e9e45a4edf92c2ee69bf08c09445bdff5db # Parent cd6f4557e7fea5799ff3762ed7a80a743e75d5fd 8053910: ScriptObjectMirror causing havoc with Invocation interface Reviewed-by: jlaskey, attila, hannesw diff -r cd6f4557e7fe -r 41d18e9e45a4 nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java --- a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Wed Jul 05 19:53:51 2017 +0200 +++ b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Wed Aug 06 22:11:12 2014 +0530 @@ -715,6 +715,23 @@ return newArgs; } + /** + * Are the given objects mirrors to same underlying object? + * + * @param obj1 first object + * @param obj2 second object + * @return true if obj1 and obj2 are identical script objects or mirrors of it. + */ + public static boolean identical(final Object obj1, final Object obj2) { + final Object o1 = (obj1 instanceof ScriptObjectMirror)? + ((ScriptObjectMirror)obj1).sobj : obj1; + + final Object o2 = (obj2 instanceof ScriptObjectMirror)? + ((ScriptObjectMirror)obj2).sobj : obj2; + + return o1 == o2; + } + // package-privates below this. ScriptObjectMirror(final ScriptObject sobj, final Global global) { diff -r cd6f4557e7fe -r 41d18e9e45a4 nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java --- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Wed Jul 05 19:53:51 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Wed Aug 06 22:11:12 2014 +0530 @@ -702,6 +702,9 @@ if (x instanceof ScriptObject && y instanceof ScriptObject) { return x == y; } + if (x instanceof ScriptObjectMirror || y instanceof ScriptObjectMirror) { + return ScriptObjectMirror.identical(x, y); + } return equalValues(x, y); } diff -r cd6f4557e7fe -r 41d18e9e45a4 nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java --- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Wed Jul 05 19:53:51 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Wed Aug 06 22:11:12 2014 +0530 @@ -60,6 +60,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import jdk.nashorn.api.scripting.ScriptUtils; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.Label; @@ -134,10 +135,12 @@ * implemented securely. */ final class JavaAdapterBytecodeGenerator { + private static final Type SCRIPTUTILS_TYPE = Type.getType(ScriptUtils.class); private static final Type OBJECT_TYPE = Type.getType(Object.class); private static final Type CLASS_TYPE = Type.getType(Class.class); static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName(); + static final String SCRIPTUTILS_TYPE_NAME = SCRIPTUTILS_TYPE.getInternalName(); static final String INIT = ""; @@ -172,6 +175,7 @@ private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE); private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(CLASS_TYPE); private static final String EXPORT_RETURN_VALUE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE); + private static final String UNWRAP_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE); private static final String GET_CONVERTER_METHOD_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, CLASS_TYPE); private static final String TO_CHAR_PRIMITIVE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.CHAR_TYPE, OBJECT_TYPE); private static final String TO_STRING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(STRING_TYPE, OBJECT_TYPE); @@ -927,10 +931,14 @@ invokeValueOf(mv, "Double", 'D'); break; case Type.ARRAY: - case Type.OBJECT: case Type.METHOD: // Already boxed break; + case Type.OBJECT: + if(t.equals(OBJECT_TYPE)) { + mv.invokestatic(SCRIPTUTILS_TYPE_NAME, "unwrap", UNWRAP_METHOD_DESCRIPTOR, false); + } + break; default: // Not expecting anything else (e.g. VOID) assert false; diff -r cd6f4557e7fe -r 41d18e9e45a4 nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java --- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Wed Jul 05 19:53:51 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Wed Aug 06 22:11:12 2014 +0530 @@ -47,6 +47,8 @@ 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.ScriptUtils; +import jdk.nashorn.api.scripting.ScriptObjectMirror; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; @@ -214,12 +216,12 @@ /** * Invoked when returning Object from an adapted method to filter out internal Nashorn objects that must not be seen - * by the callers. Currently only transforms {@code ConsString} into {@code String}. + * by the callers. Currently only transforms {@code ConsString} into {@code String} and transforms {@code ScriptObject} into {@code ScriptObjectMirror}. * @param obj the return value * @return the filtered return value. */ public static Object exportReturnValue(final Object obj) { - return NashornBeansLinker.exportArgument(obj); + return ScriptUtils.wrap(NashornBeansLinker.exportArgument(obj)); } /** diff -r cd6f4557e7fe -r 41d18e9e45a4 nashorn/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java --- a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java Wed Jul 05 19:53:51 2017 +0200 +++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java Wed Aug 06 22:11:12 2014 +0530 @@ -31,10 +31,12 @@ import static org.testng.Assert.fail; import java.nio.ByteBuffer; +import java.util.function.Function; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.script.Bindings; +import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -306,4 +308,57 @@ // getMember("obj.foo") - thereby getting null instead of undefined assertEquals("undefined", engine.eval(TEST_SCRIPT, newGlobal)); } + + public interface MirrorCheckExample { + Object test1(Object arg); + Object test2(Object arg); + boolean compare(Object o1, Object o2); + } + + // @bug 8053910: ScriptObjectMirror causing havoc with Invocation interface + @Test + public void checkMirrorToObject() throws Exception { + final ScriptEngineManager engineManager = new ScriptEngineManager(); + final ScriptEngine engine = engineManager.getEngineByName("nashorn"); + final Invocable invocable = (Invocable)engine; + + engine.eval("function test1(arg) { return { arg: arg }; }"); + engine.eval("function test2(arg) { return arg; }"); + engine.eval("function compare(arg1, arg2) { return arg1 == arg2; }"); + + final Map map = new HashMap<>(); + map.put("option", true); + + final MirrorCheckExample example = invocable.getInterface(MirrorCheckExample.class); + + final Object value1 = invocable.invokeFunction("test1", map); + final Object value2 = example.test1(map); + final Object value3 = invocable.invokeFunction("test2", value2); + final Object value4 = example.test2(value2); + + // check that Object type argument receives a ScriptObjectMirror + // when ScriptObject is passed + assertEquals(ScriptObjectMirror.class, value1.getClass()); + assertEquals(ScriptObjectMirror.class, value2.getClass()); + assertEquals(ScriptObjectMirror.class, value3.getClass()); + assertEquals(ScriptObjectMirror.class, value4.getClass()); + assertTrue((boolean)invocable.invokeFunction("compare", value1, value1)); + assertTrue((boolean)example.compare(value1, value1)); + assertTrue((boolean)invocable.invokeFunction("compare", value3, value4)); + assertTrue((boolean)example.compare(value3, value4)); + } + + // @bug 8053910: ScriptObjectMirror causing havoc with Invocation interface + @Test + @SuppressWarnings("unchecked") + public void mirrorUnwrapInterfaceMethod() throws Exception { + final ScriptEngineManager engineManager = new ScriptEngineManager(); + final ScriptEngine engine = engineManager.getEngineByName("nashorn"); + final Invocable invocable = (Invocable)engine; + engine.eval("function apply(obj) { " + + " return obj instanceof Packages.jdk.nashorn.api.scripting.ScriptObjectMirror; " + + "}"); + Function func = invocable.getInterface(Function.class); + assertFalse((boolean)func.apply(engine.eval("({ x: 2 })"))); + } }