8027828: ClassCastException when converting return value of a Java method to boolean
Reviewed-by: jlaskey, attila
This commit is contained in:
parent
d3e3eead18
commit
3b4737a6ba
@ -594,6 +594,21 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin
|
|||||||
return obj == ScriptRuntime.UNDEFINED;
|
return obj == ScriptRuntime.UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilitity to convert this script object to the given type.
|
||||||
|
*
|
||||||
|
* @param type destination type to convert to
|
||||||
|
* @return converted object
|
||||||
|
*/
|
||||||
|
public <T> T to(final Class<T> type) {
|
||||||
|
return inGlobal(new Callable<T>() {
|
||||||
|
@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.
|
* Make a script object mirror on given object if needed. Also converts ConsString instances to Strings.
|
||||||
*
|
*
|
||||||
|
@ -25,13 +25,17 @@
|
|||||||
|
|
||||||
package jdk.nashorn.api.scripting;
|
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.Context;
|
||||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
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 {
|
public final class ScriptUtils {
|
||||||
private ScriptUtils() {}
|
private ScriptUtils() {}
|
||||||
@ -128,4 +132,41 @@ public final class ScriptUtils {
|
|||||||
|
|
||||||
return ScriptObjectMirror.unwrapArray(args, Context.getGlobal());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,9 @@ public enum JSType {
|
|||||||
/** JavaScript compliant conversion function from Object to number */
|
/** JavaScript compliant conversion function from Object to number */
|
||||||
public static final Call TO_NUMBER = staticCall(myLookup, JSType.class, "toNumber", double.class, Object.class);
|
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 */
|
/** JavaScript compliant conversion function from Object to int32 */
|
||||||
public static final Call TO_INT32 = staticCall(myLookup, JSType.class, "toInt32", int.class, Object.class);
|
public static final Call TO_INT32 = staticCall(myLookup, JSType.class, "toInt32", int.class, Object.class);
|
||||||
|
|
||||||
|
@ -33,14 +33,18 @@ import java.lang.invoke.MethodHandle;
|
|||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||||
import jdk.internal.dynalink.beans.BeansLinker;
|
import jdk.internal.dynalink.beans.BeansLinker;
|
||||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||||
import jdk.internal.dynalink.linker.GuardingDynamicLinker;
|
import jdk.internal.dynalink.linker.GuardingDynamicLinker;
|
||||||
|
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
|
||||||
import jdk.internal.dynalink.linker.LinkRequest;
|
import jdk.internal.dynalink.linker.LinkRequest;
|
||||||
import jdk.internal.dynalink.linker.LinkerServices;
|
import jdk.internal.dynalink.linker.LinkerServices;
|
||||||
import jdk.internal.dynalink.support.Guards;
|
import jdk.internal.dynalink.support.Guards;
|
||||||
import jdk.nashorn.internal.runtime.Context;
|
import jdk.nashorn.internal.runtime.Context;
|
||||||
|
import jdk.nashorn.internal.runtime.JSType;
|
||||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +54,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
|
|||||||
* setters for Java objects that couldn't be linked by any other linker, and throw appropriate ECMAScript errors for
|
* 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.
|
* attempts to invoke arbitrary Java objects as functions or constructors.
|
||||||
*/
|
*/
|
||||||
final class NashornBottomLinker implements GuardingDynamicLinker {
|
final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeConverterFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
|
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
|
||||||
@ -129,6 +133,29 @@ final class NashornBottomLinker implements GuardingDynamicLinker {
|
|||||||
throw new AssertionError("unknown call type " + desc);
|
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) {
|
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);
|
return Bootstrap.asType(new GuardedInvocation(handle, Guards.getClassGuard(self.getClass())), linkerServices, desc);
|
||||||
}
|
}
|
||||||
@ -161,6 +188,15 @@ final class NashornBottomLinker implements GuardingDynamicLinker {
|
|||||||
throw new AssertionError("unknown call type " + desc);
|
throw new AssertionError("unknown call type " + desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Map<Class<?>, 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) {
|
private static String getArgument(final LinkRequest linkRequest) {
|
||||||
final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
|
final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
|
||||||
if (desc.getNameTokenCount() > 2) {
|
if (desc.getNameTokenCount() > 2) {
|
||||||
|
35
nashorn/test/script/basic/JDK-8027828.js
Normal file
35
nashorn/test/script/basic/JDK-8027828.js
Normal file
@ -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!')
|
||||||
|
}
|
1
nashorn/test/script/basic/JDK-8027828.js.EXPECTED
Normal file
1
nashorn/test/script/basic/JDK-8027828.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
|||||||
|
Found!
|
61
nashorn/test/script/basic/convert.js
Normal file
61
nashorn/test/script/basic/convert.js
Normal file
@ -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]);
|
||||||
|
}
|
||||||
|
|
14
nashorn/test/script/basic/convert.js.EXPECTED
Normal file
14
nashorn/test/script/basic/convert.js.EXPECTED
Normal file
@ -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
|
@ -26,6 +26,7 @@
|
|||||||
package jdk.nashorn.api.scripting;
|
package jdk.nashorn.api.scripting;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngine;
|
||||||
import javax.script.ScriptEngineManager;
|
import javax.script.ScriptEngineManager;
|
||||||
@ -227,4 +228,28 @@ public class ScriptObjectMirrorTest {
|
|||||||
final Object newObj = ((ScriptObjectMirror)e2obj.getMember("foo")).newObject();
|
final Object newObj = ((ScriptObjectMirror)e2obj.getMember("foo")).newObject();
|
||||||
assertTrue(newObj instanceof ScriptObjectMirror);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user