8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
authorsundar
Wed, 16 Jan 2013 21:26:55 +0530
changeset 16178 2704dd3b2691
parent 16177 e6464f96bdb2
child 16179 5b8bcfd712d3
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException Reviewed-by: attila, hannesw, jlaskey
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
nashorn/test/script/basic/JDK-8006424.js
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java	Wed Jan 16 17:58:51 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java	Wed Jan 16 21:26:55 2013 +0530
@@ -81,6 +81,7 @@
 import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.Undefined;
 import org.dynalang.dynalink.beans.StaticClass;
 import org.dynalang.dynalink.support.LinkRequestImpl;
@@ -144,7 +145,7 @@
     private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class);
     private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
     private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
-            SCRIPT_OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
+            OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
     private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
             SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
     private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class);
@@ -570,7 +571,7 @@
      * function itself, if that's what's passed). There is one method handle field in the adapter class for every method
      * that can be implemented or overridden; the name of every field is same as the name of the method, with a number
      * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke
-     * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(ScriptObject, String, MethodType,
+     * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType,
      * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity
      * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}.
      * The constructor that takes a script function will only initialize the methods with the same name as the single
@@ -589,8 +590,8 @@
         final int argLen = originalArgTypes.length;
         final Type[] newArgTypes = new Type[argLen + 1];
 
-        // Insert ScriptFunction|ScriptObject as the last argument to the constructor
-        final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : SCRIPT_OBJECT_TYPE;
+        // Insert ScriptFunction|Object as the last argument to the constructor
+        final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE;
         newArgTypes[argLen] = extraArgumentType;
         System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
 
@@ -675,7 +676,7 @@
     /**
      * Given a JS script object, retrieves a function from it by name, binds it to the script object as its "this", and
      * adapts its parameter types, return types, and arity to the specified type and arity. This method is public mainly
-     * for implementation reasons, so the adapter classes can invoke it from their constructors that take a ScriptObject
+     * for implementation reasons, so the adapter classes can invoke it from their constructors that take a Object
      * in its first argument to obtain the method handles for their method implementations.
      * @param obj the script obj
      * @param name the name of the property that contains the function
@@ -685,15 +686,21 @@
      * property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly
      * define it but just inherits it through prototype.
      */
-    public static MethodHandle getHandle(final ScriptObject obj, final String name, final MethodType type, final boolean varArg) {
+    public static MethodHandle getHandle(final Object obj, final String name, final MethodType type, final boolean varArg) {
+        if (! (obj instanceof ScriptObject)) {
+            typeError(Context.getGlobal(), "not.an.object", ScriptRuntime.safeToString(obj));
+            throw new AssertionError();
+        }
+
+        final ScriptObject sobj = (ScriptObject)obj;
         // Since every JS Object has a toString, we only override "String toString()" it if it's explicitly specified
-        if ("toString".equals(name) && !obj.hasOwnProperty("toString")) {
+        if ("toString".equals(name) && !sobj.hasOwnProperty("toString")) {
             return null;
         }
 
-        final Object fnObj = obj.get(name);
+        final Object fnObj = sobj.get(name);
         if (fnObj instanceof ScriptFunction) {
-            return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(obj), type, varArg);
+            return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type, varArg);
         } else if(fnObj == null || fnObj instanceof Undefined) {
             return null;
         } else {
@@ -773,7 +780,7 @@
      * exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime
      * exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of
      * the method; this is guaranteed by the way constructors of the adapter class obtain them using
-     * {@link #getHandle(ScriptObject, String, MethodType, boolean)}.
+     * {@link #getHandle(Object, String, MethodType, boolean)}.
      * @param mi the method info describing the method to be generated.
      */
     private void generateMethod(final MethodInfo mi) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8006424.js	Wed Jan 16 21:26:55 2013 +0530
@@ -0,0 +1,22 @@
+/**
+ * JDK-8006424 : Passing null or undefined to adapter class constructors results in NPE or ClassCastException
+ *
+ * @test
+ * @run
+ */
+
+function check(callback) {
+    try {
+        callback();
+        fail("should have thrown exception");
+    } catch (e) {
+        if (! (e instanceof TypeError)) {
+            fail("TypeError expected, but got " + e);
+        }
+    }
+}
+
+check(function() { new java.lang.ClassLoader(null) });
+check(function() { new java.lang.ClassLoader(undefined) });
+check(function() { new java.lang.Runnable(null) });
+check(function() { new java.lang.Runnable(undefined) });