Mon, 24 Jun 2013 19:06:01 +0530
8015959: Can't call foreign constructor
Reviewed-by: jlaskey, hannesw
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