8046013: TypeError: Cannot apply "with" to non script object

Reviewed-by: lagergren, hannesw
This commit is contained in:
Athijegannathan Sundararajan 2014-06-27 21:54:16 +05:30
parent 8f69a3e4a8
commit 4b428d5833
6 changed files with 135 additions and 4 deletions
nashorn
src/jdk/nashorn/internal
test
script/basic
src/jdk/nashorn/api/scripting

@ -716,6 +716,32 @@ public final class NativeObject {
return target;
}
/*
* Binds the source mirror object's properties to the target object. Binding
* properties allows two-way read/write for the properties of the source object.
* All inherited, enumerable properties are also bound. This method is used to
* to make 'with' statement work with ScriptObjectMirror as scope object.
*
* @param target the target object to which the source object's properties are bound
* @param source the source object whose properties are bound to the target
* @return the target object after property binding
*/
public static Object bindAllProperties(final ScriptObject target, final ScriptObjectMirror source) {
final Set<String> keys = source.keySet();
// make accessor properties using dynamic invoker getters and setters
final AccessorProperty[] props = new AccessorProperty[keys.size()];
int idx = 0;
for (String name : keys) {
final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE);
final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE);
props[idx] = AccessorProperty.create(name, 0, getter, setter);
idx++;
}
target.addBoundProperties(source, props);
return target;
}
private static void bindBeanProperties(final ScriptObject targetObj, final Object source,
final Collection<String> readablePropertyNames, final Collection<String> writablePropertyNames,
final Collection<String> methodNames) {

@ -49,6 +49,7 @@ import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.ir.debug.JSONWriter;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.objects.NativeObject;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
@ -478,9 +479,21 @@ public final class ScriptRuntime {
throw typeError(global, "cant.apply.with.to.null");
}
final Object wrappedExpr = JSType.toScriptObject(global, expression);
if (wrappedExpr instanceof ScriptObject) {
return new WithObject(scope, (ScriptObject)wrappedExpr);
if (expression instanceof ScriptObjectMirror) {
final Object unwrapped = ScriptObjectMirror.unwrap(expression, global);
if (unwrapped instanceof ScriptObject) {
return new WithObject(scope, (ScriptObject)unwrapped);
} else {
// foreign ScriptObjectMirror
ScriptObject exprObj = global.newObject();
NativeObject.bindAllProperties(exprObj, (ScriptObjectMirror)expression);
return new WithObject(scope, exprObj);
}
} else {
final Object wrappedExpr = JSType.toScriptObject(global, expression);
if (wrappedExpr instanceof ScriptObject) {
return new WithObject(scope, (ScriptObject)wrappedExpr);
}
}
throw typeError(global, "cant.apply.with.to.non.scriptobject");

@ -31,6 +31,8 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
import jdk.nashorn.api.scripting.AbstractJSObject;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@ -312,7 +314,22 @@ public final class WithObject extends ScriptObject implements Scope {
@SuppressWarnings("unused")
private static Object bindToExpression(final Object fn, final Object receiver) {
return fn instanceof ScriptFunction ? bindToExpression((ScriptFunction) fn, receiver) : fn;
if (fn instanceof ScriptFunction) {
return bindToExpression((ScriptFunction) fn, receiver);
} else if (fn instanceof ScriptObjectMirror) {
final ScriptObjectMirror mirror = (ScriptObjectMirror)fn;
if (mirror.isFunction()) {
// We need to make sure correct 'this' is used for calls with Ident call
// expressions. We do so here using an AbstractJSObject instance.
return new AbstractJSObject() {
public Object call(final Object thiz, final Object... args) {
return mirror.call(withFilterExpression(receiver), args);
}
};
}
}
return fn;
}
private static Object bindToExpression(final ScriptFunction fn, final Object receiver) {

@ -0,0 +1,57 @@
/*
* Copyright (c) 2014, 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-8046013: TypeError: Cannot apply "with" to non script object
*
* @test
* @run
*/
var obj = loadWithNewGlobal({
script: "({ f: 33 })",
name: "test"
});
with (obj) {
print("f = " + f);
}
var obj2 = loadWithNewGlobal({
script: "var obj = Object.create({ foo: 42 }); obj.bar = 'hello'; obj",
name: "test2"
});
with (obj2) {
print("foo = " + foo);
print("bar = " + bar);
}
var obj3 = loadWithNewGlobal({
script: "({ f: 33, func: function() { print('this.f =', this.f); } })",
name: "test"
});
with(obj3) {
func();
}

@ -0,0 +1,4 @@
f = 33
foo = 42
bar = hello
this.f = 33

@ -593,6 +593,20 @@ public class ScriptEngineTest {
}
}
// @bug 8046013: TypeError: Cannot apply "with" to non script object
@Test
public void withOnMirrorTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final Object obj = e.eval("({ foo: 'hello'})");
final Object[] arr = new Object[1];
arr[0] = obj;
e.put("arr", arr);
final Object res = e.eval("var res; with(arr[0]) { res = foo; }; res");
assertEquals(res, "hello");
}
private static void checkProperty(final ScriptEngine e, final String name)
throws ScriptException {
final String value = System.getProperty(name);