8053910: ScriptObjectMirror causing havoc with Invocation interface
Reviewed-by: jlaskey, attila, hannesw
--- 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) {
--- 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);
}
--- 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 = "<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;
--- 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));
}
/**
--- 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<String, Object> 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<Object,Object> func = invocable.getInterface(Function.class);
+ assertFalse((boolean)func.apply(engine.eval("({ x: 2 })")));
+ }
}