8015959: Can't call foreign constructor

Mon, 24 Jun 2013 19:06:01 +0530

author
sundar
date
Mon, 24 Jun 2013 19:06:01 +0530
changeset 376
51a5ee93d6bc
parent 375
2ded2fc08c94
child 377
26a345c26e62

8015959: Can't call foreign constructor
Reviewed-by: jlaskey, hannesw

src/jdk/nashorn/api/scripting/JSObject.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/api/scripting/ScriptObjectMirror.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptFunction.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptFunctionData.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptRuntime.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java file | annotate | diff | comparison | revisions
test/script/basic/JDK-8015959.js file | annotate | diff | comparison | revisions
test/script/basic/JDK-8015959.js.EXPECTED file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/api/scripting/JSObject.java	Sat Jun 22 10:12:19 2013 -0300
     1.2 +++ b/src/jdk/nashorn/api/scripting/JSObject.java	Mon Jun 24 19:06:01 2013 +0530
     1.3 @@ -30,13 +30,23 @@
     1.4   */
     1.5  public abstract class JSObject {
     1.6      /**
     1.7 -     * Call a JavaScript method
     1.8 +     * Call a JavaScript function
     1.9       *
    1.10 -     * @param methodName name of method
    1.11 +     * @param functionName name of function
    1.12       * @param args arguments to method
    1.13       * @return result of call
    1.14       */
    1.15 -    public abstract Object call(String methodName, Object args[]);
    1.16 +    public abstract Object call(String functionName, Object... args);
    1.17 +
    1.18 +    /**
    1.19 +     * Call a JavaScript method as a constructor. This is equivalent to
    1.20 +     * calling new obj.Method(arg1, arg2...) in JavaScript.
    1.21 +     *
    1.22 +     * @param functionName name of function
    1.23 +     * @param args arguments to method
    1.24 +     * @return result of constructor call
    1.25 +     */
    1.26 +    public abstract Object newObject(String functionName, Object... args);
    1.27  
    1.28      /**
    1.29       * Evaluate a JavaScript expression
     2.1 --- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Sat Jun 22 10:12:19 2013 -0300
     2.2 +++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Mon Jun 24 19:06:01 2013 +0530
     2.3 @@ -102,7 +102,7 @@
     2.4  
     2.5      // JSObject methods
     2.6      @Override
     2.7 -    public Object call(final String methodName, final Object args[]) {
     2.8 +    public Object call(final String functionName, final Object... args) {
     2.9          final ScriptObject oldGlobal = NashornScriptEngine.getNashornGlobal();
    2.10          final boolean globalChanged = (oldGlobal != global);
    2.11  
    2.12 @@ -111,9 +111,9 @@
    2.13                  NashornScriptEngine.setNashornGlobal(global);
    2.14              }
    2.15  
    2.16 -            final Object val = sobj.get(methodName);
    2.17 +            final Object val = functionName == null? sobj : sobj.get(functionName);
    2.18              if (! (val instanceof ScriptFunction)) {
    2.19 -                throw new RuntimeException("No such method: " + methodName);
    2.20 +                throw new RuntimeException("No such function " + ((functionName != null)? functionName : ""));
    2.21              }
    2.22  
    2.23              final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
    2.24 @@ -130,6 +130,34 @@
    2.25      }
    2.26  
    2.27      @Override
    2.28 +    public Object newObject(final String functionName, final Object... args) {
    2.29 +        final ScriptObject oldGlobal = NashornScriptEngine.getNashornGlobal();
    2.30 +        final boolean globalChanged = (oldGlobal != global);
    2.31 +
    2.32 +        try {
    2.33 +            if (globalChanged) {
    2.34 +                NashornScriptEngine.setNashornGlobal(global);
    2.35 +            }
    2.36 +
    2.37 +            final Object val = functionName == null? sobj : sobj.get(functionName);
    2.38 +            if (! (val instanceof ScriptFunction)) {
    2.39 +                throw new RuntimeException("not a constructor " + ((functionName != null)? functionName : ""));
    2.40 +            }
    2.41 +
    2.42 +            final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
    2.43 +            return wrap(ScriptRuntime.checkAndConstruct((ScriptFunction)val, unwrapArray(modArgs, global)), global);
    2.44 +        } catch (final RuntimeException | Error e) {
    2.45 +            throw e;
    2.46 +        } catch (final Throwable t) {
    2.47 +            throw new RuntimeException(t);
    2.48 +        } finally {
    2.49 +            if (globalChanged) {
    2.50 +                NashornScriptEngine.setNashornGlobal(oldGlobal);
    2.51 +            }
    2.52 +        }
    2.53 +    }
    2.54 +
    2.55 +    @Override
    2.56      public Object eval(final String s) {
    2.57          return inGlobal(new Callable<Object>() {
    2.58              @Override
     3.1 --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Sat Jun 22 10:12:19 2013 -0300
     3.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Mon Jun 24 19:06:01 2013 +0530
     3.3 @@ -203,6 +203,16 @@
     3.4      }
     3.5  
     3.6      /**
     3.7 +     * Execute this script function as a constructor.
     3.8 +     * @param arguments  Call arguments.
     3.9 +     * @return Newly constructed result.
    3.10 +     * @throws Throwable if there is an exception/error with the invocation or thrown from it
    3.11 +     */
    3.12 +    Object construct(final Object... arguments) throws Throwable {
    3.13 +        return data.construct(this, arguments);
    3.14 +    }
    3.15 +
    3.16 +    /**
    3.17       * Allocate function. Called from generated {@link ScriptObject} code
    3.18       * for allocation as a factory method
    3.19       *
     4.1 --- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Sat Jun 22 10:12:19 2013 -0300
     4.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Mon Jun 24 19:06:01 2013 +0530
     4.3 @@ -216,6 +216,12 @@
     4.4          return composeGenericMethod(code.mostGeneric().getInvoker());
     4.5      }
     4.6  
     4.7 +    final MethodHandle getGenericConstructor() {
     4.8 +        ensureCodeGenerated();
     4.9 +        ensureConstructor(code.mostGeneric());
    4.10 +        return composeGenericMethod(code.mostGeneric().getConstructor());
    4.11 +    }
    4.12 +
    4.13      private CompiledFunction getBest(final MethodType callSiteType) {
    4.14          ensureCodeGenerated();
    4.15          return code.best(callSiteType);
    4.16 @@ -535,10 +541,74 @@
    4.17          }
    4.18      }
    4.19  
    4.20 +    Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
    4.21 +        final MethodHandle mh = getGenericConstructor();
    4.22 +
    4.23 +        final Object[]     args       = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
    4.24 +
    4.25 +        if (isVarArg(mh)) {
    4.26 +            if (needsCallee(mh)) {
    4.27 +                return mh.invokeExact(fn, args);
    4.28 +            }
    4.29 +            return mh.invokeExact(args);
    4.30 +        }
    4.31 +
    4.32 +        final int paramCount = mh.type().parameterCount();
    4.33 +        if (needsCallee(mh)) {
    4.34 +            switch (paramCount) {
    4.35 +            case 1:
    4.36 +                return mh.invokeExact(fn);
    4.37 +            case 2:
    4.38 +                return mh.invokeExact(fn, getArg(args, 0));
    4.39 +            case 3:
    4.40 +                return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1));
    4.41 +            case 4:
    4.42 +                return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2));
    4.43 +            default:
    4.44 +                return mh.invokeWithArguments(withArguments(fn, paramCount, args));
    4.45 +            }
    4.46 +        }
    4.47 +
    4.48 +        switch (paramCount) {
    4.49 +        case 0:
    4.50 +            return mh.invokeExact();
    4.51 +        case 1:
    4.52 +            return mh.invokeExact(getArg(args, 0));
    4.53 +        case 2:
    4.54 +            return mh.invokeExact(getArg(args, 0), getArg(args, 1));
    4.55 +        case 3:
    4.56 +            return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2));
    4.57 +        default:
    4.58 +            return mh.invokeWithArguments(withArguments(null, paramCount, args));
    4.59 +        }
    4.60 +    }
    4.61 +
    4.62      private static Object getArg(final Object[] args, final int i) {
    4.63          return i < args.length ? args[i] : UNDEFINED;
    4.64      }
    4.65  
    4.66 +    private static Object[] withArguments(final ScriptFunction fn, final int argCount, final Object[] args) {
    4.67 +        final Object[] finalArgs = new Object[argCount];
    4.68 +
    4.69 +        int nextArg = 0;
    4.70 +        if (fn != null) {
    4.71 +            //needs callee
    4.72 +            finalArgs[nextArg++] = fn;
    4.73 +        }
    4.74 +
    4.75 +        // Don't add more args that there is argCount in the handle (including self and callee).
    4.76 +        for (int i = 0; i < args.length && nextArg < argCount;) {
    4.77 +            finalArgs[nextArg++] = args[i++];
    4.78 +        }
    4.79 +
    4.80 +        // If we have fewer args than argCount, pad with undefined.
    4.81 +        while (nextArg < argCount) {
    4.82 +            finalArgs[nextArg++] = UNDEFINED;
    4.83 +        }
    4.84 +
    4.85 +        return finalArgs;
    4.86 +    }
    4.87 +
    4.88      private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
    4.89          final Object[] finalArgs = new Object[argCount];
    4.90  
     5.1 --- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Sat Jun 22 10:12:19 2013 -0300
     5.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Mon Jun 24 19:06:01 2013 +0530
     5.3 @@ -361,6 +361,47 @@
     5.4      }
     5.5  
     5.6      /**
     5.7 +     * Check that the target function is associated with current Context.
     5.8 +     * And also make sure that 'self', if ScriptObject, is from current context.
     5.9 +     *
    5.10 +     * Call a function as a constructor given args.
    5.11 +     *
    5.12 +     * @param target ScriptFunction object.
    5.13 +     * @param args   Call arguments.
    5.14 +     * @return Constructor call result.
    5.15 +     */
    5.16 +    public static Object checkAndConstruct(final ScriptFunction target, final Object... args) {
    5.17 +        final ScriptObject global = Context.getGlobalTrusted();
    5.18 +        if (! (global instanceof GlobalObject)) {
    5.19 +            throw new IllegalStateException("No current global set");
    5.20 +        }
    5.21 +
    5.22 +        if (target.getContext() != global.getContext()) {
    5.23 +            throw new IllegalArgumentException("'target' function is not from current Context");
    5.24 +        }
    5.25 +
    5.26 +        // all in order - call real 'construct'
    5.27 +        return construct(target, args);
    5.28 +    }
    5.29 +
    5.30 +    /*
    5.31 +     * Call a script function as a constructor with given args.
    5.32 +     *
    5.33 +     * @param target ScriptFunction object.
    5.34 +     * @param args   Call arguments.
    5.35 +     * @return Constructor call result.
    5.36 +     */
    5.37 +    public static Object construct(final ScriptFunction target, final Object... args) {
    5.38 +        try {
    5.39 +            return target.construct(args);
    5.40 +        } catch (final RuntimeException | Error e) {
    5.41 +            throw e;
    5.42 +        } catch (final Throwable t) {
    5.43 +            throw new RuntimeException(t);
    5.44 +        }
    5.45 +    }
    5.46 +
    5.47 +    /**
    5.48       * Generic implementation of ECMA 9.12 - SameValue algorithm
    5.49       *
    5.50       * @param x first value to compare
     6.1 --- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Sat Jun 22 10:12:19 2013 -0300
     6.2 +++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Mon Jun 24 19:06:01 2013 +0530
     6.3 @@ -45,44 +45,14 @@
     6.4   * as ScriptObjects from other Nashorn contexts.
     6.5   */
     6.6  final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
     6.7 -   /**
     6.8 -     * Instances of this class are used to represent a method member of a JSObject
     6.9 -     */
    6.10 -    private static final class JSObjectMethod {
    6.11 -        // The name of the JSObject method property
    6.12 -        private final String name;
    6.13 -
    6.14 -        JSObjectMethod(final String name) {
    6.15 -            this.name = name;
    6.16 -        }
    6.17 -
    6.18 -        String getName() {
    6.19 -            return name;
    6.20 -        }
    6.21 -
    6.22 -        static GuardedInvocation lookup(final CallSiteDescriptor desc) {
    6.23 -            final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
    6.24 -            switch (operator) {
    6.25 -                case "call": {
    6.26 -                    // collect everything except the first two - JSObjectMethod instance and the actual 'self'
    6.27 -                    final int paramCount = desc.getMethodType().parameterCount();
    6.28 -                    final MethodHandle caller = MH.asCollector(JSOBJECTMETHOD_CALL, Object[].class, paramCount - 2);
    6.29 -                    return new GuardedInvocation(caller, null, IS_JSOBJECTMETHOD_GUARD);
    6.30 -                }
    6.31 -                default:
    6.32 -                    return null;
    6.33 -            }
    6.34 -        }
    6.35 -    }
    6.36 -
    6.37      @Override
    6.38      public boolean canLinkType(final Class<?> type) {
    6.39          return canLinkTypeStatic(type);
    6.40      }
    6.41  
    6.42      static boolean canLinkTypeStatic(final Class<?> type) {
    6.43 -        // can link JSObject and JSObjectMethod
    6.44 -        return JSObject.class.isAssignableFrom(type) || JSObjectMethod.class.isAssignableFrom(type);
    6.45 +        // can link JSObject
    6.46 +        return JSObject.class.isAssignableFrom(type);
    6.47      }
    6.48  
    6.49      @Override
    6.50 @@ -99,8 +69,6 @@
    6.51          final GuardedInvocation inv;
    6.52          if (self instanceof JSObject) {
    6.53              inv = lookup(desc);
    6.54 -        } else if (self instanceof JSObjectMethod) {
    6.55 -            inv = JSObjectMethod.lookup(desc);
    6.56          } else {
    6.57              throw new AssertionError(); // Should never reach here.
    6.58          }
    6.59 @@ -115,7 +83,7 @@
    6.60              case "getProp":
    6.61              case "getElem":
    6.62              case "getMethod":
    6.63 -                return c > 2 ? findGetMethod(desc, operator) : findGetIndexMethod();
    6.64 +                return c > 2 ? findGetMethod(desc) : findGetIndexMethod();
    6.65              case "setProp":
    6.66              case "setElem":
    6.67                  return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
    6.68 @@ -123,15 +91,14 @@
    6.69              case "callMethod":
    6.70                  return findCallMethod(desc, operator);
    6.71              case "new":
    6.72 +                return findNewMethod(desc);
    6.73              default:
    6.74                  return null;
    6.75          }
    6.76      }
    6.77  
    6.78 -    private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final String operator) {
    6.79 -        // if "getMethod" then return JSObjectMethod object - which just holds the name of the method
    6.80 -        // subsequently, link on dyn:call for JSObjectMethod will actually call that method
    6.81 -        final MethodHandle getter = MH.insertArguments("getMethod".equals(operator)? JSOBJECT_GETMETHOD : JSOBJECT_GET, 1, desc.getNameToken(2));
    6.82 +    private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) {
    6.83 +        final MethodHandle getter = MH.insertArguments(JSOBJECT_GET, 1, desc.getNameToken(2));
    6.84          return new GuardedInvocation(getter, null, IS_JSOBJECT_GUARD);
    6.85      }
    6.86  
    6.87 @@ -156,9 +123,9 @@
    6.88          return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD);
    6.89      }
    6.90  
    6.91 -    @SuppressWarnings("unused")
    6.92 -    private static boolean isJSObjectMethod(final Object self) {
    6.93 -        return self instanceof JSObjectMethod;
    6.94 +    private static GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
    6.95 +        MethodHandle func = MH.asCollector(JSOBJECT_NEW, Object[].class, desc.getMethodType().parameterCount() - 1);
    6.96 +        return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD);
    6.97      }
    6.98  
    6.99      @SuppressWarnings("unused")
   6.100 @@ -166,12 +133,6 @@
   6.101          return self instanceof JSObject;
   6.102      }
   6.103  
   6.104 -
   6.105 -    @SuppressWarnings("unused")
   6.106 -    private static Object getMethod(final Object jsobj, final Object key) {
   6.107 -        return new JSObjectMethod(Objects.toString(key));
   6.108 -    }
   6.109 -
   6.110      @SuppressWarnings("unused")
   6.111      private static Object get(final Object jsobj, final Object key) {
   6.112          if (key instanceof String) {
   6.113 @@ -200,11 +161,8 @@
   6.114      }
   6.115  
   6.116      @SuppressWarnings("unused")
   6.117 -    private static Object jsObjectMethodCall(final Object jsObjMethod, final Object jsobj, final Object... args) {
   6.118 -        // we have JSObjectMethod, JSObject and args. Get method name from JSObjectMethod instance
   6.119 -        final String methodName = ((JSObjectMethod)jsObjMethod).getName();
   6.120 -        // call the method on JSObject
   6.121 -        return ((JSObject)jsobj).call(methodName, args);
   6.122 +    private static Object newObject(final Object jsobj, final Object... args) {
   6.123 +        return ((JSObject)jsobj).newObject(null, args);
   6.124      }
   6.125  
   6.126      private static int getIndex(final Number n) {
   6.127 @@ -214,13 +172,11 @@
   6.128  
   6.129      private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
   6.130  
   6.131 -    private static final MethodHandle IS_JSOBJECTMETHOD_GUARD = findOwnMH("isJSObjectMethod", boolean.class, Object.class);
   6.132      private static final MethodHandle IS_JSOBJECT_GUARD = findOwnMH("isJSObject", boolean.class, Object.class);
   6.133 -    private static final MethodHandle JSOBJECT_GETMETHOD = findOwnMH("getMethod", Object.class, Object.class, Object.class);
   6.134      private static final MethodHandle JSOBJECT_GET = findOwnMH("get", Object.class, Object.class, Object.class);
   6.135      private static final MethodHandle JSOBJECT_PUT = findOwnMH("put", Void.TYPE, Object.class, Object.class, Object.class);
   6.136      private static final MethodHandle JSOBJECT_CALL = findOwnMH("call", Object.class, Object.class, Object.class, Object[].class);
   6.137 -    private static final MethodHandle JSOBJECTMETHOD_CALL = findOwnMH("jsObjectMethodCall", Object.class, Object.class, Object.class, Object[].class);
   6.138 +    private static final MethodHandle JSOBJECT_NEW = findOwnMH("newObject", Object.class, Object.class, Object[].class);
   6.139  
   6.140      private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
   6.141          final Class<?>   own = JSObjectLinker.class;
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/test/script/basic/JDK-8015959.js	Mon Jun 24 19:06:01 2013 +0530
     7.3 @@ -0,0 +1,54 @@
     7.4 +/*
     7.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     7.7 + * 
     7.8 + * This code is free software; you can redistribute it and/or modify it
     7.9 + * under the terms of the GNU General Public License version 2 only, as
    7.10 + * published by the Free Software Foundation.
    7.11 + * 
    7.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    7.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    7.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    7.15 + * version 2 for more details (a copy is included in the LICENSE file that
    7.16 + * accompanied this code).
    7.17 + * 
    7.18 + * You should have received a copy of the GNU General Public License version
    7.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    7.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    7.21 + * 
    7.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    7.23 + * or visit www.oracle.com if you need additional information or have any
    7.24 + * questions.
    7.25 + */
    7.26 +
    7.27 +/**
    7.28 + * JDK-8015959: Can't call foreign constructor
    7.29 + *
    7.30 + * @test
    7.31 + * @run
    7.32 + */
    7.33 +
    7.34 +function check(global) {
    7.35 +    var obj = new global.Point(344, 12);
    7.36 +    print("obj.x " + obj.x);
    7.37 +    print("obj.y " + obj.y);
    7.38 +    print("obj instanceof global.Point? " + (obj instanceof global.Point))
    7.39 +
    7.40 +    var P = global.Point;
    7.41 +    var p = new P(343, 54);
    7.42 +    print("p.x " + p.x);
    7.43 +    print("p.y " + p.y);
    7.44 +    print("p instanceof P? " + (p instanceof P))
    7.45 +}
    7.46 +
    7.47 +print("check with loadWithNewGlobal");
    7.48 +check(loadWithNewGlobal({
    7.49 +   name: "myscript",
    7.50 +   script: "function Point(x, y) { this.x = x; this.y = y }; this"
    7.51 +}));
    7.52 +
    7.53 +print("check with script engine");
    7.54 +var m = new javax.script.ScriptEngineManager();
    7.55 +var e = m.getEngineByName('nashorn');
    7.56 +check(e.eval("function Point(x, y) { this.x = x; this.y = y }; this"));
    7.57 +
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/test/script/basic/JDK-8015959.js.EXPECTED	Mon Jun 24 19:06:01 2013 +0530
     8.3 @@ -0,0 +1,14 @@
     8.4 +check with loadWithNewGlobal
     8.5 +obj.x 344
     8.6 +obj.y 12
     8.7 +obj instanceof global.Point? true
     8.8 +p.x 343
     8.9 +p.y 54
    8.10 +p instanceof P? true
    8.11 +check with script engine
    8.12 +obj.x 344
    8.13 +obj.y 12
    8.14 +obj instanceof global.Point? true
    8.15 +p.x 343
    8.16 +p.y 54
    8.17 +p instanceof P? true

mercurial