# HG changeset patch # User sundar # Date 1383623021 -19800 # Node ID b0d4ef6fb2db3acc1dece6feb7faac2a4c6be6d5 # Parent 144861e242604c061fb462a5ced5fff55e219a50# Parent dcedc753fd09bccec997a1a561d39f414c22e7af Merge diff -r 144861e24260 -r b0d4ef6fb2db src/jdk/nashorn/api/scripting/ScriptObjectMirror.java --- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Mon Nov 04 09:29:12 2013 +0530 +++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Tue Nov 05 09:13:41 2013 +0530 @@ -601,9 +601,9 @@ * @param homeGlobal global to which this object belongs. Not used for ConsStrings. * @return wrapped/converted object */ - public static Object wrap(final Object obj, final ScriptObject homeGlobal) { + public static Object wrap(final Object obj, final Object homeGlobal) { if(obj instanceof ScriptObject) { - return homeGlobal != null ? new ScriptObjectMirror((ScriptObject)obj, homeGlobal) : obj; + return homeGlobal instanceof ScriptObject ? new ScriptObjectMirror((ScriptObject)obj, (ScriptObject)homeGlobal) : obj; } if(obj instanceof ConsString) { return obj.toString(); @@ -618,7 +618,7 @@ * @param homeGlobal global to which this object belongs * @return unwrapped object */ - public static Object unwrap(final Object obj, final ScriptObject homeGlobal) { + public static Object unwrap(final Object obj, final Object homeGlobal) { if (obj instanceof ScriptObjectMirror) { final ScriptObjectMirror mirror = (ScriptObjectMirror)obj; return (mirror.global == homeGlobal)? mirror.sobj : obj; @@ -634,7 +634,7 @@ * @param homeGlobal global to which this object belongs * @return wrapped array */ - public static Object[] wrapArray(final Object[] args, final ScriptObject homeGlobal) { + public static Object[] wrapArray(final Object[] args, final Object homeGlobal) { if (args == null || args.length == 0) { return args; } @@ -655,7 +655,7 @@ * @param homeGlobal global to which this object belongs * @return unwrapped array */ - public static Object[] unwrapArray(final Object[] args, final ScriptObject homeGlobal) { + public static Object[] unwrapArray(final Object[] args, final Object homeGlobal) { if (args == null || args.length == 0) { return args; } diff -r 144861e24260 -r b0d4ef6fb2db src/jdk/nashorn/api/scripting/ScriptUtils.java --- a/src/jdk/nashorn/api/scripting/ScriptUtils.java Mon Nov 04 09:29:12 2013 +0530 +++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java Tue Nov 05 09:13:41 2013 +0530 @@ -25,7 +25,9 @@ package jdk.nashorn.api.scripting; +import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; +import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; /** @@ -71,4 +73,59 @@ return func.makeSynchronizedFunction(sync); } + /** + * Make a script object mirror on given object if needed. + * + * @param obj object to be wrapped + * @return wrapped object + */ + public static Object wrap(final Object obj) { + if (obj instanceof ScriptObject) { + return ScriptObjectMirror.wrap(obj, Context.getGlobal()); + } + + return obj; + } + + /** + * Unwrap a script object mirror if needed. + * + * @param obj object to be unwrapped + * @return unwrapped object + */ + public static Object unwrap(final Object obj) { + if (obj instanceof ScriptObjectMirror) { + return ScriptObjectMirror.unwrap(obj, Context.getGlobal()); + } + + return obj; + } + + /** + * Wrap an array of object to script object mirrors if needed. + * + * @param args array to be unwrapped + * @return wrapped array + */ + public static Object[] wrapArray(final Object[] args) { + if (args == null || args.length == 0) { + return args; + } + + return ScriptObjectMirror.wrapArray(args, Context.getGlobal()); + } + + /** + * Unwrap an array of script object mirrors if needed. + * + * @param args array to be unwrapped + * @return unwrapped array + */ + public static Object[] unwrapArray(final Object[] args) { + if (args == null || args.length == 0) { + return args; + } + + return ScriptObjectMirror.unwrapArray(args, Context.getGlobal()); + } } diff -r 144861e24260 -r b0d4ef6fb2db src/jdk/nashorn/internal/runtime/linker/NashornLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java Mon Nov 04 09:29:12 2013 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java Tue Nov 05 09:13:41 2013 +0530 @@ -32,6 +32,8 @@ import java.lang.reflect.Modifier; import java.util.Deque; import java.util.List; +import java.util.Map; +import javax.script.Bindings; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.ConversionComparator; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -40,7 +42,11 @@ import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; import jdk.internal.dynalink.support.Guards; +import jdk.nashorn.api.scripting.JSObject; +import jdk.nashorn.api.scripting.ScriptObjectMirror; +import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.internal.objects.NativeArray; +import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; @@ -115,9 +121,14 @@ return new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : IS_NASHORN_OR_UNDEFINED_TYPE); } - GuardedInvocation inv = getArrayConverter(sourceType, targetType); - if(inv != null) { - return inv; + final GuardedInvocation arrayConverter = getArrayConverter(sourceType, targetType); + if(arrayConverter != null) { + return arrayConverter; + } + + final GuardedInvocation mirrorConverter = getMirrorConverter(sourceType, targetType); + if(mirrorConverter != null) { + return mirrorConverter; } return getSamTypeConverter(sourceType, targetType); @@ -181,6 +192,18 @@ return MH.asType(converter, converter.type().changeReturnType(type)); } + private static GuardedInvocation getMirrorConverter(Class sourceType, Class targetType) { + // Could've also used (targetType.isAssignableFrom(ScriptObjectMirror.class) && targetType != Object.class) but + // it's probably better to explicitly spell out the supported target types + if (targetType == Map.class || targetType == Bindings.class || targetType == JSObject.class || targetType == ScriptObjectMirror.class) { + if(ScriptObject.class.isAssignableFrom(sourceType)) { + return new GuardedInvocation(CREATE_MIRROR, null); + } + return new GuardedInvocation(CREATE_MIRROR, IS_SCRIPT_OBJECT); + } + return null; + } + private static boolean isAutoConvertibleFromFunction(final Class clazz) { return isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) && JavaAdapterFactory.isAutoConvertibleFromFunction(clazz); @@ -235,17 +258,23 @@ return clazz == List.class || clazz == Deque.class; } + 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_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); + private static final MethodHandle IS_NASHORN_OR_UNDEFINED_TYPE = findOwnMH("isNashornTypeOrUndefined", Boolean.TYPE, Object.class); + private static final MethodHandle CREATE_MIRROR = findOwnMH("createMirror", Object.class, Object.class); @SuppressWarnings("unused") private static boolean isNashornTypeOrUndefined(final Object obj) { return obj instanceof ScriptObject || obj instanceof Undefined; } + @SuppressWarnings("unused") + private static Object createMirror(final Object obj) { + return ScriptUtils.wrap(obj); + } + 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)); } diff -r 144861e24260 -r b0d4ef6fb2db test/script/basic/JDK-8027753.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8027753.js Tue Nov 05 09:13:41 2013 +0530 @@ -0,0 +1,50 @@ +/* + * 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-8027753: Support ScriptObject to JSObject, ScriptObjectMirror, Map, Bindings auto-conversion as well as explicit wrap, unwrap + * + * @test + * @run + */ + +var ScriptUtils = Java.type("jdk.nashorn.api.scripting.ScriptUtils"); +var ScriptObjectMirror = Java.type("jdk.nashorn.api.scripting.ScriptObjectMirror"); + +var obj = { foo: 34, bar: 'hello' }; + +var wrapped = ScriptUtils.wrap(obj); +if (! (wrapped instanceof ScriptObjectMirror)) { + fail("ScriptUtils.wrap does not return a ScriptObjectMirror"); +} + +print("wrapped.foo = " + wrapped.foo); +print("wrapped.bar = " + wrapped.bar); + +var unwrapped = ScriptUtils.unwrap(wrapped); +if (! (unwrapped instanceof Object)) { + fail("ScriptUtils.unwrap does not return a ScriptObject"); +} + +// same object unwrapped? +print(unwrapped === obj); diff -r 144861e24260 -r b0d4ef6fb2db test/script/basic/JDK-8027753.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8027753.js.EXPECTED Tue Nov 05 09:13:41 2013 +0530 @@ -0,0 +1,3 @@ +wrapped.foo = 34 +wrapped.bar = hello +true diff -r 144861e24260 -r b0d4ef6fb2db test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java --- a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java Mon Nov 04 09:29:12 2013 +0530 +++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java Tue Nov 05 09:13:41 2013 +0530 @@ -523,6 +523,18 @@ assertEquals(sw.toString(), println("34 true hello")); } + @Test + public void scriptObjectAutoConversionTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + e.eval("obj = { foo: 'hello' }"); + e.put("Window", e.eval("Packages.jdk.nashorn.api.scripting.Window")); + assertEquals(e.eval("Window.funcJSObject(obj)"), "hello"); + assertEquals(e.eval("Window.funcScriptObjectMirror(obj)"), "hello"); + assertEquals(e.eval("Window.funcMap(obj)"), "hello"); + assertEquals(e.eval("Window.funcJSObject(obj)"), "hello"); + } + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); // Returns String that would be the result of calling PrintWriter.println diff -r 144861e24260 -r b0d4ef6fb2db test/src/jdk/nashorn/api/scripting/Window.java --- a/test/src/jdk/nashorn/api/scripting/Window.java Mon Nov 04 09:29:12 2013 +0530 +++ b/test/src/jdk/nashorn/api/scripting/Window.java Tue Nov 05 09:13:41 2013 +0530 @@ -25,6 +25,9 @@ package jdk.nashorn.api.scripting; +import java.util.Map; +import javax.script.Bindings; + public class Window { private String location = "http://localhost:8080/window"; @@ -63,4 +66,20 @@ System.out.println("window.setTimeout: " + delay + ", code: " + code); return 0; } + + public static Object funcJSObject(final JSObject jsobj) { + return jsobj.getMember("foo"); + } + + public static Object funcScriptObjectMirror(final ScriptObjectMirror sobj) { + return sobj.get("foo"); + } + + public static Object funcMap(final Map map) { + return map.get("foo"); + } + + public static Object funcBindings(final Bindings bindings) { + return bindings.get("foo"); + } }