Thu, 14 Feb 2013 14:16:58 +0530
8008197: Cross script engine function calls do not work as expected
Reviewed-by: lagergren, hannesw
1.1 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Thu Feb 14 09:14:31 2013 +0530 1.2 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Thu Feb 14 14:16:58 2013 +0530 1.3 @@ -329,7 +329,7 @@ 1.4 final ScriptObject ctxtGlobal = getNashornGlobalFrom(context); 1.5 final boolean globalChanged = (oldGlobal != ctxtGlobal); 1.6 1.7 - Object self = selfObject; 1.8 + Object self = globalChanged? ScriptObjectMirror.wrap(selfObject, oldGlobal) : selfObject; 1.9 1.10 try { 1.11 if (globalChanged) { 1.12 @@ -354,7 +354,8 @@ 1.13 if (value instanceof ScriptFunction) { 1.14 final Object res; 1.15 try { 1.16 - res = ScriptRuntime.apply((ScriptFunction)value, self, ScriptObjectMirror.unwrapArray(args, ctxtGlobal)); 1.17 + final Object[] modArgs = globalChanged? ScriptObjectMirror.wrapArray(args, oldGlobal) : args; 1.18 + res = ScriptRuntime.checkAndApply((ScriptFunction)value, self, ScriptObjectMirror.unwrapArray(modArgs, ctxtGlobal)); 1.19 } catch (final Exception e) { 1.20 throwAsScriptException(e); 1.21 throw new AssertionError("should not reach here");
2.1 --- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Thu Feb 14 09:14:31 2013 +0530 2.2 +++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Thu Feb 14 14:16:58 2013 +0530 2.3 @@ -104,38 +104,30 @@ 2.4 // JSObject methods 2.5 @Override 2.6 public Object call(final String methodName, final Object args[]) { 2.7 - final Object val = sobj.get(methodName); 2.8 final ScriptObject oldGlobal = NashornScriptEngine.getNashornGlobal(); 2.9 final boolean globalChanged = (oldGlobal != global); 2.10 2.11 - if (val instanceof ScriptFunction) { 2.12 - final Object[] modifiedArgs = unwrapArray(args, global); 2.13 - if (modifiedArgs != null) { 2.14 - for (int i = 0; i < modifiedArgs.length; i++) { 2.15 - final Object arg = modifiedArgs[i]; 2.16 - if (arg instanceof ScriptObject) { 2.17 - modifiedArgs[i] = wrap(arg, oldGlobal); 2.18 - } 2.19 - } 2.20 + try { 2.21 + if (globalChanged) { 2.22 + NashornScriptEngine.setNashornGlobal(global); 2.23 } 2.24 2.25 - try { 2.26 - if (globalChanged) { 2.27 - NashornScriptEngine.setNashornGlobal(global); 2.28 - } 2.29 - return wrap(((ScriptFunction)val).invoke(sobj, modifiedArgs), global); 2.30 - } catch (final RuntimeException | Error e) { 2.31 - throw e; 2.32 - } catch (final Throwable t) { 2.33 - throw new RuntimeException(t); 2.34 - } finally { 2.35 - if (globalChanged) { 2.36 - NashornScriptEngine.setNashornGlobal(oldGlobal); 2.37 - } 2.38 + final Object val = sobj.get(methodName); 2.39 + if (! (val instanceof ScriptFunction)) { 2.40 + throw new RuntimeException("No such method: " + methodName); 2.41 } 2.42 - } 2.43 2.44 - throw new RuntimeException("No such method: " + methodName); 2.45 + final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args; 2.46 + return wrap(ScriptRuntime.checkAndApply((ScriptFunction)val, sobj, unwrapArray(modArgs, global)), global); 2.47 + } catch (final RuntimeException | Error e) { 2.48 + throw e; 2.49 + } catch (final Throwable t) { 2.50 + throw new RuntimeException(t); 2.51 + } finally { 2.52 + if (globalChanged) { 2.53 + NashornScriptEngine.setNashornGlobal(oldGlobal); 2.54 + } 2.55 + } 2.56 } 2.57 2.58 @Override 2.59 @@ -180,7 +172,7 @@ 2.60 2.61 @Override 2.62 public void setMember(final String name, final Object value) { 2.63 - put(name, wrap(value, NashornScriptEngine.getNashornGlobal())); 2.64 + put(name, value); 2.65 } 2.66 2.67 @Override 2.68 @@ -275,20 +267,27 @@ 2.69 2.70 @Override 2.71 public Object put(final String key, final Object value) { 2.72 + final ScriptObject oldGlobal = NashornScriptEngine.getNashornGlobal(); 2.73 + final boolean globalChanged = (oldGlobal != global); 2.74 return inGlobal(new Callable<Object>() { 2.75 @Override public Object call() { 2.76 - return sobj.put(key, unwrap(value, global)); 2.77 + final Object modValue = globalChanged? wrap(value, oldGlobal) : value; 2.78 + return translateUndefined(wrap(sobj.put(key, unwrap(modValue, global)), global)); 2.79 } 2.80 }); 2.81 } 2.82 2.83 @Override 2.84 public void putAll(final Map<? extends String, ? extends Object> map) { 2.85 + final ScriptObject oldGlobal = NashornScriptEngine.getNashornGlobal(); 2.86 + final boolean globalChanged = (oldGlobal != global); 2.87 final boolean strict = sobj.isStrictContext(); 2.88 inGlobal(new Callable<Object>() { 2.89 @Override public Object call() { 2.90 for (final Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) { 2.91 - sobj.set(entry.getKey(), unwrap(entry.getValue(), global), strict); 2.92 + final Object value = entry.getValue(); 2.93 + final Object modValue = globalChanged? wrap(value, oldGlobal) : value; 2.94 + sobj.set(entry.getKey(), unwrap(modValue, global), strict); 2.95 } 2.96 return null; 2.97 } 2.98 @@ -321,7 +320,7 @@ 2.99 final Iterator<Object> iter = sobj.valueIterator(); 2.100 2.101 while (iter.hasNext()) { 2.102 - values.add(wrap(iter.next(), global)); 2.103 + values.add(translateUndefined(wrap(iter.next(), global))); 2.104 } 2.105 2.106 return Collections.unmodifiableList(values);
3.1 --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu Feb 14 09:14:31 2013 +0530 3.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu Feb 14 14:16:58 2013 +0530 3.3 @@ -192,7 +192,7 @@ 3.4 * @return ScriptFunction result. 3.5 * @throws Throwable if there is an exception/error with the invocation or thrown from it 3.6 */ 3.7 - public Object invoke(final Object self, final Object... arguments) throws Throwable { 3.8 + Object invoke(final Object self, final Object... arguments) throws Throwable { 3.9 if (Context.DEBUG) { 3.10 invokes++; 3.11 }
4.1 --- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Thu Feb 14 09:14:31 2013 +0530 4.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Thu Feb 14 14:16:58 2013 +0530 4.3 @@ -302,6 +302,37 @@ 4.4 } 4.5 4.6 /** 4.7 + * Check that the target function is associated with current Context. And also make sure that 'self', if 4.8 + * ScriptObject, is from current context. 4.9 + * 4.10 + * Call a function given self and args. If the number of the arguments is known in advance, you can likely achieve 4.11 + * better performance by {@link Bootstrap#createDynamicInvoker(String, Class, Class...) creating a dynamic invoker} 4.12 + * for operation {@code "dyn:call"}, then using its {@link MethodHandle#invokeExact(Object...)} method instead. 4.13 + * 4.14 + * @param target ScriptFunction object. 4.15 + * @param self Receiver in call. 4.16 + * @param args Call arguments. 4.17 + * @return Call result. 4.18 + */ 4.19 + public static Object checkAndApply(final ScriptFunction target, final Object self, final Object... args) { 4.20 + final ScriptObject global = Context.getGlobalTrusted(); 4.21 + if (! (global instanceof GlobalObject)) { 4.22 + throw new IllegalStateException("No current global set"); 4.23 + } 4.24 + 4.25 + if (target.getContext() != global.getContext()) { 4.26 + throw new IllegalArgumentException("'target' function is not from current Context"); 4.27 + } 4.28 + 4.29 + if (self instanceof ScriptObject && ((ScriptObject)self).getContext() != global.getContext()) { 4.30 + throw new IllegalArgumentException("'self' object is not from current Context"); 4.31 + } 4.32 + 4.33 + // all in order - call real 'apply' 4.34 + return apply(target, self, args); 4.35 + } 4.36 + 4.37 + /** 4.38 * Call a function given self and args. If the number of the arguments is known in advance, you can likely achieve 4.39 * better performance by {@link Bootstrap#createDynamicInvoker(String, Class, Class...) creating a dynamic invoker} 4.40 * for operation {@code "dyn:call"}, then using its {@link MethodHandle#invokeExact(Object...)} method instead.
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/script/basic/JDK-8008197.js Thu Feb 14 14:16:58 2013 +0530 5.3 @@ -0,0 +1,69 @@ 5.4 +/* 5.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.23 + * or visit www.oracle.com if you need additional information or have any 5.24 + * questions. 5.25 + */ 5.26 + 5.27 +/** 5.28 + * JDK-8008197: Cross script engine function calls do not work as expected 5.29 + * 5.30 + * @test 5.31 + * @run 5.32 + */ 5.33 + 5.34 + 5.35 +var m = new javax.script.ScriptEngineManager(); 5.36 +var e = m.getEngineByName("nashorn"); 5.37 + 5.38 +var obj = { 5.39 + func: function(str) { 5.40 + return /hello/.exec(str); 5.41 + } 5.42 +}; 5.43 + 5.44 +// set our object as object to the engine created 5.45 +e.put("obj", obj); 5.46 + 5.47 +// try to invoke method from the other engine 5.48 +if (e.eval("obj.func('hello')") == null) { 5.49 + fail("FAILED: #1 obj.func('hello') returns null"); 5.50 +} 5.51 + 5.52 +// define an object in the engine 5.53 + e.eval("var foo = { callMe: function(callback) { return callback() } }"); 5.54 + 5.55 +// try to invoke a script method from here but callback is from this engine 5.56 +var res = e.invokeMethod(e.get("foo"), "callMe", function() { 5.57 + return /nashorn/; 5.58 +}); 5.59 + 5.60 +if (! res.exec("nashorn")) { 5.61 + fail("FAILED: #2 /nashorn/ does not match 'nashorn'"); 5.62 +} 5.63 + 5.64 +// invoke a script method from here with callback from this engine. 5.65 +// This uses JSObject.call interface 5.66 +res = e.get("foo").callMe(function() { 5.67 + return /ecmascript/; 5.68 +}); 5.69 + 5.70 +if (! res.exec("ecmascript")) { 5.71 + fail("FAILED: #3 /ecmascript/ does not match 'ecmascript'"); 5.72 +}
6.1 --- a/test/script/basic/jquery.js Thu Feb 14 09:14:31 2013 +0530 6.2 +++ b/test/script/basic/jquery.js Thu Feb 14 14:16:58 2013 +0530 6.3 @@ -60,23 +60,13 @@ 6.4 var name; 6.5 6.6 try { 6.7 + var split = url.split('/'); 6.8 + name = split[split.length - 1]; 6.9 + var path = __DIR__ + "../external/jquery/" + name; 6.10 try { 6.11 - var split = url.split('/'); 6.12 - name = split[split.length - 1]; 6.13 - var path = __DIR__ + "../external/jquery/" + name; 6.14 - try { 6.15 - load(path); 6.16 - } catch (e) { 6.17 - checkWindow(e); 6.18 - } 6.19 + load(path); 6.20 } catch (e) { 6.21 - // try to load it from the internet, if we for some licensing reason 6.22 - // aren't allowed to ship third party code under MIT license 6.23 - try { 6.24 - load(url); 6.25 - } catch (e) { 6.26 - checkWindow(e); 6.27 - } 6.28 + checkWindow(e); 6.29 } 6.30 } catch (e) { 6.31 print("Unexpected exception " + e); 6.32 @@ -85,8 +75,6 @@ 6.33 print("done " + name); 6.34 } 6.35 6.36 - 6.37 - 6.38 for each (url in urls) { 6.39 test_jquery(url); 6.40 }