# HG changeset patch # User sundar # Date 1383828072 -19800 # Node ID 3b794f364c77ea2dbed7bc001b5648a81a57bdee # Parent bda654c6d59ced41ad68c42e3f132afb29ab1f8c# Parent 2f07b4234451e007b2ce9efd256d82c8c0c70f00 Merge diff -r bda654c6d59c -r 3b794f364c77 src/jdk/nashorn/api/scripting/ScriptObjectMirror.java --- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Tue Nov 05 13:09:40 2013 +0400 +++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Thu Nov 07 18:11:12 2013 +0530 @@ -595,6 +595,21 @@ } /** + * Utilitity to convert this script object to the given type. + * + * @param type destination type to convert to + * @return converted object + */ + public T to(final Class type) { + return inGlobal(new Callable() { + @Override + public T call() { + return type.cast(ScriptUtils.convert(sobj, type)); + } + }); + } + + /** * Make a script object mirror on given object if needed. Also converts ConsString instances to Strings. * * @param obj object to be wrapped/converted diff -r bda654c6d59c -r 3b794f364c77 src/jdk/nashorn/api/scripting/ScriptUtils.java --- a/src/jdk/nashorn/api/scripting/ScriptUtils.java Tue Nov 05 13:09:40 2013 +0400 +++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java Thu Nov 07 18:11:12 2013 +0530 @@ -25,13 +25,17 @@ package jdk.nashorn.api.scripting; +import java.lang.invoke.MethodHandle; +import jdk.internal.dynalink.beans.StaticClass; +import jdk.internal.dynalink.linker.LinkerServices; +import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; /** - * Utilities that are to be called from script code + * Utilities that are to be called from script code. */ public final class ScriptUtils { private ScriptUtils() {} @@ -128,4 +132,41 @@ return ScriptObjectMirror.unwrapArray(args, Context.getGlobal()); } + + /** + * Convert the given object to the given type. + * + * @param obj object to be converted + * @param type destination type to convert to + * @return converted object + */ + public static Object convert(final Object obj, final Object type) { + if (obj == null) { + return null; + } + + final Class clazz; + if (type instanceof Class) { + clazz = (Class)type; + } else if (type instanceof StaticClass) { + clazz = ((StaticClass)type).getRepresentedClass(); + } else { + throw new IllegalArgumentException("type expected"); + } + + final LinkerServices linker = Bootstrap.getLinkerServices(); + final MethodHandle converter = linker.getTypeConverter(obj.getClass(), clazz); + if (converter == null) { + // no supported conversion! + throw new UnsupportedOperationException("conversion not supported"); + } + + try { + return converter.invoke(obj); + } catch (final RuntimeException | Error e) { + throw e; + } catch (final Throwable t) { + throw new RuntimeException(t); + } + } } diff -r bda654c6d59c -r 3b794f364c77 src/jdk/nashorn/internal/runtime/JSType.java --- a/src/jdk/nashorn/internal/runtime/JSType.java Tue Nov 05 13:09:40 2013 +0400 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Thu Nov 07 18:11:12 2013 +0530 @@ -88,6 +88,9 @@ /** JavaScript compliant conversion function from Object to number */ public static final Call TO_NUMBER = staticCall(myLookup, JSType.class, "toNumber", double.class, Object.class); + /** JavaScript compliant conversion function from Object to String */ + public static final Call TO_STRING = staticCall(myLookup, JSType.class, "toString", String.class, Object.class); + /** JavaScript compliant conversion function from Object to int32 */ public static final Call TO_INT32 = staticCall(myLookup, JSType.class, "toInt32", int.class, Object.class); diff -r bda654c6d59c -r 3b794f364c77 src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Tue Nov 05 13:09:40 2013 +0400 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Thu Nov 07 18:11:12 2013 +0530 @@ -33,14 +33,18 @@ import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.HashMap; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.GuardingDynamicLinker; +import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; 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.JSType; import jdk.nashorn.internal.runtime.ScriptRuntime; /** @@ -50,7 +54,7 @@ * setters for Java objects that couldn't be linked by any other linker, and throw appropriate ECMAScript errors for * attempts to invoke arbitrary Java objects as functions or constructors. */ -final class NashornBottomLinker implements GuardingDynamicLinker { +final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeConverterFactory { @Override public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) @@ -129,6 +133,29 @@ throw new AssertionError("unknown call type " + desc); } + @Override + public GuardedInvocation convertToType(final Class sourceType, final Class targetType) throws Exception { + final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType); + return gi == null ? null : gi.asType(MH.type(targetType, sourceType)); + } + + /** + * Main part of the implementation of {@link GuardingTypeConverterFactory#convertToType(Class, Class)} that doesn't + * care about adapting the method signature; that's done by the invoking method. Returns conversion from Object to String/number/boolean (JS primitive types). + * @param sourceType the source type + * @param targetType the target type + * @return a guarded invocation that converts from the source type to the target type. + * @throws Exception if something goes wrong + */ + private static GuardedInvocation convertToTypeNoCast(final Class sourceType, final Class targetType) throws Exception { + final MethodHandle mh = CONVERTERS.get(targetType); + if (mh != null) { + return new GuardedInvocation(mh, null); + } + + return null; + } + private static GuardedInvocation getInvocation(final MethodHandle handle, final Object self, final LinkerServices linkerServices, final CallSiteDescriptor desc) { return Bootstrap.asType(new GuardedInvocation(handle, Guards.getClassGuard(self.getClass())), linkerServices, desc); } @@ -161,6 +188,15 @@ throw new AssertionError("unknown call type " + desc); } + private static final Map, MethodHandle> CONVERTERS = new HashMap<>(); + static { + CONVERTERS.put(boolean.class, JSType.TO_BOOLEAN.methodHandle()); + CONVERTERS.put(double.class, JSType.TO_NUMBER.methodHandle()); + CONVERTERS.put(int.class, JSType.TO_INTEGER.methodHandle()); + CONVERTERS.put(long.class, JSType.TO_LONG.methodHandle()); + CONVERTERS.put(String.class, JSType.TO_STRING.methodHandle()); + } + private static String getArgument(final LinkRequest linkRequest) { final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor(); if (desc.getNameTokenCount() > 2) { diff -r bda654c6d59c -r 3b794f364c77 test/script/basic/JDK-8027828.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8027828.js Thu Nov 07 18:11:12 2013 +0530 @@ -0,0 +1,35 @@ +/* + * 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-8027828: ClassCastException when converting return value of a Java method to boolean + * + * @test + * @run + */ + +var x = new java.util.HashMap() +x.put('test', new java.io.File('test')) +if (x.get("test")) { + print('Found!') +} diff -r bda654c6d59c -r 3b794f364c77 test/script/basic/JDK-8027828.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8027828.js.EXPECTED Thu Nov 07 18:11:12 2013 +0530 @@ -0,0 +1,1 @@ +Found! diff -r bda654c6d59c -r 3b794f364c77 test/script/basic/convert.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/convert.js Thu Nov 07 18:11:12 2013 +0530 @@ -0,0 +1,61 @@ +/* + * 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. + */ + +/** + * Tests for convert method of ScriptUtils. + * + * @test + * @run + */ + +var ScriptUtils = Java.type("jdk.nashorn.api.scripting.ScriptUtils"); +obj = { valueOf: function() { print("hello"); return 43.3; } }; + +// object to double +print(ScriptUtils.convert(obj, java.lang.Number.class)); + +// array to List +var arr = [3, 44, 23, 33]; +var list = ScriptUtils.convert(arr, java.util.List.class); +print(list instanceof java.util.List) +print(list); + +// object to Map +obj = { foo: 333, bar: 'hello'}; +var map = ScriptUtils.convert(obj, java.util.Map.class); +print(map instanceof java.util.Map); +for (m in map) { + print(m + " " + map[m]); +} + +// object to String +obj = { toString: function() { print("in toString"); return "foo" } }; +print(ScriptUtils.convert(obj, java.lang.String.class)); + +// array to Java array +var jarr = ScriptUtils.convert(arr, Java.type("int[]")); +print(jarr instanceof Java.type("int[]")); +for (i in jarr) { + print(jarr[i]); +} + diff -r bda654c6d59c -r 3b794f364c77 test/script/basic/convert.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/convert.js.EXPECTED Thu Nov 07 18:11:12 2013 +0530 @@ -0,0 +1,14 @@ +hello +43.3 +true +[3, 44, 23, 33] +true +foo 333 +bar hello +in toString +foo +true +3 +44 +23 +33 diff -r bda654c6d59c -r 3b794f364c77 test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java --- a/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java Tue Nov 05 13:09:40 2013 +0400 +++ b/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java Thu Nov 07 18:11:12 2013 +0530 @@ -26,6 +26,7 @@ package jdk.nashorn.api.scripting; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -227,4 +228,28 @@ final Object newObj = ((ScriptObjectMirror)e2obj.getMember("foo")).newObject(); assertTrue(newObj instanceof ScriptObjectMirror); } + + @Test + public void conversionTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final ScriptObjectMirror arr = (ScriptObjectMirror)e.eval("[33, 45, 23]"); + final int[] intArr = arr.to(int[].class); + assertEquals(intArr[0], 33); + assertEquals(intArr[1], 45); + assertEquals(intArr[2], 23); + + final List list = arr.to(List.class); + assertEquals(list.get(0), 33); + assertEquals(list.get(1), 45); + assertEquals(list.get(2), 23); + + ScriptObjectMirror obj = (ScriptObjectMirror)e.eval( + "({ valueOf: function() { return 42 } })"); + assertEquals(Double.valueOf(42.0), obj.to(Double.class)); + + obj = (ScriptObjectMirror)e.eval( + "({ toString: function() { return 'foo' } })"); + assertEquals("foo", obj.to(String.class)); + } }