From dba2bf3f0253df6c4dafb2e21f927dd1e85a0837 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Tue, 3 Jun 2014 14:13:03 +0530 Subject: [PATCH] 8044520: Nashorn cannot execute node.js's express module Reviewed-by: hannesw, lagergren --- .../api/scripting/ScriptObjectMirror.java | 2 +- .../internal/codegen/CodeGenerator.java | 4 +- .../jdk/nashorn/internal/objects/Global.java | 5 +- .../internal/objects/NativeObject.java | 37 ++++- .../internal/runtime/GlobalFunctions.java | 1 - .../internal/runtime/ScriptObject.java | 49 ++++-- nashorn/test/script/basic/JDK-8044520.js | 145 ++++++++++++++++++ 7 files changed, 221 insertions(+), 22 deletions(-) create mode 100644 nashorn/test/script/basic/JDK-8044520.js diff --git a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java index 600dfc21c35..7edfc26a7c0 100644 --- a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java +++ b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java @@ -502,7 +502,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin public void setProto(final Object proto) { inGlobal(new Callable() { @Override public Void call() { - sobj.setProtoCheck(unwrap(proto, global)); + sobj.setPrototypeOf(unwrap(proto, global)); return null; } }); diff --git a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java index fc1ac50b838..de72cb260fd 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -2346,7 +2346,9 @@ final class CodeGenerator extends NodeOperatorVisitor rtype, final Class... types) { + return MH.findStatic(MethodHandles.lookup(), NativeObject.class, name, MH.type(rtype, types)); + } } diff --git a/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java b/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java index fa306bec250..28ba36f5f78 100644 --- a/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java +++ b/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java @@ -503,5 +503,4 @@ loop: private static MethodHandle findOwnMH(final String name, final Class rtype, final Class... types) { return MH.findStatic(MethodHandles.lookup(), GlobalFunctions.class, name, MH.type(rtype, types)); } - } diff --git a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java index 87fc7df8d06..4b82abfff8f 100644 --- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java +++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java @@ -155,8 +155,6 @@ public abstract class ScriptObject implements PropertyAccess { /** Method handle to retrieve prototype of this object */ public static final MethodHandle GETPROTO = findOwnMH_V("getProto", ScriptObject.class); - /** Method handle to set prototype of this object */ - public static final MethodHandle SETPROTOCHECK = findOwnMH_V("setProtoCheck", void.class, Object.class); static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class, boolean.class); static final MethodHandle GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); @@ -191,7 +189,7 @@ public abstract class ScriptObject implements PropertyAccess { public static final Call SET_GLOBAL_OBJECT_PROTO = staticCallNoLookup(ScriptObject.class, "setGlobalObjectProto", void.class, ScriptObject.class); /** Method handle for setting the proto of a ScriptObject after checking argument */ - public static final Call SET_PROTO_CHECK = virtualCallNoLookup(ScriptObject.class, "setProtoCheck", void.class, Object.class); + public static final Call SET_PROTO_FROM_LITERAL = virtualCallNoLookup(ScriptObject.class, "setProtoFromLiteral", void.class, Object.class); /** Method handle for setting the user accessors of a ScriptObject */ //TODO fastpath this @@ -1268,14 +1266,22 @@ public abstract class ScriptObject implements PropertyAccess { /** * Set the __proto__ of an object with checks. + * This is the built-in operation [[SetPrototypeOf]] + * See ES6 draft spec: 9.1.2 [[SetPrototypeOf]] (V) + * * @param newProto Prototype to set. */ - public final void setProtoCheck(final Object newProto) { - if (!isExtensible()) { - throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this)); - } - + public final void setPrototypeOf(final Object newProto) { if (newProto == null || newProto instanceof ScriptObject) { + if (! isExtensible()) { + // okay to set same proto again - even if non-extensible + + if (newProto == getProto()) { + return; + } + throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this)); + } + // check for circularity ScriptObject p = (ScriptObject)newProto; while (p != null) { @@ -1286,14 +1292,27 @@ public abstract class ScriptObject implements PropertyAccess { } setProto((ScriptObject)newProto); } else { - final Global global = Context.getGlobal(); - final Object newProtoObject = JSType.toScriptObject(global, newProto); + throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto)); + } + } - if (newProtoObject instanceof ScriptObject) { - setProto((ScriptObject)newProtoObject); - } else { - throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto)); - } + /** + * Set the __proto__ of an object from an object literal. + * See ES6 draft spec: B.3.1 __proto__ Property Names in + * Object Initializers. Step 6 handling of "__proto__". + * + * @param newProto Prototype to set. + */ + public final void setProtoFromLiteral(final Object newProto) { + if (newProto == null || newProto instanceof ScriptObject) { + setPrototypeOf(newProto); + } else { + // Some non-object, non-null. Then, we need to set + // Object.prototype as the new __proto__ + // + // var obj = { __proto__ : 34 }; + // print(obj.__proto__ === Object.prototype); // => true + setPrototypeOf(Global.objectPrototype()); } } diff --git a/nashorn/test/script/basic/JDK-8044520.js b/nashorn/test/script/basic/JDK-8044520.js new file mode 100644 index 00000000000..edcaa11674e --- /dev/null +++ b/nashorn/test/script/basic/JDK-8044520.js @@ -0,0 +1,145 @@ +/* + * 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-8044520: Nashorn cannot execute node.js's express module + * + * @test + * @run + */ + +function checkNullProto() { + var obj = {}; + obj.__proto__ = null; + var proto = Object.getPrototypeOf(obj); + if (typeof proto != 'object' || proto !== null) { + fail("__proto__ can't be set to null!"); + } +} + +checkNullProto(); + +function checkSetProto(proto) { + var obj = {}; + obj.__proto__ = proto; + if (Object.getPrototypeOf(obj) !== Object.prototype) { + fail("obj.__proto__ set not ignored for " + proto); + } +} + +checkSetProto(undefined); +checkSetProto(42); +checkSetProto(false); +checkSetProto("hello"); + +function checkLiteralSetProto(proto) { + var obj = { __proto__: proto }; + if (obj.__proto__ !== Object.prototype) { + fail("object literal _proto__ set not ignored for " + proto); + } +} + +checkLiteralSetProto(undefined); +checkLiteralSetProto(34); +checkLiteralSetProto(true); +checkLiteralSetProto("world"); + +function checkNullProtoFromLiteral() { + var obj = { __proto__: null }; + var proto = Object.getPrototypeOf(obj); + if (typeof proto != 'object' || proto !== null) { + fail("__proto__ can't be set to null!"); + } +} + +checkNullProtoFromLiteral(); + +function checkSetPrototypeOf(proto) { + try { + Object.setPrototypeOf({}, proto); + fail("should have thrown error for " + proto); + } catch (e) { + if (! (e instanceof TypeError)) { + fail("should have thrown TypeError, got " + e); + } + } +} + +checkSetPrototypeOf(undefined); +checkSetPrototypeOf(43); +checkSetPrototypeOf(false); +checkSetPrototypeOf("nashorn"); + +function checkNullSetPrototypeOf() { + var obj = { }; + Object.setPrototypeOf(obj, null); + var proto = Object.getPrototypeOf(obj); + if (typeof proto != 'object' || proto !== null) { + fail("__proto__ can't be set to null!"); + } +} + +checkNullSetPrototypeOf(); + +var desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__"); + +function checkProtoGetterOnPrimitive(value) { + // call __proto__ getter on primitive - check ToObject + // is called on 'this' value as per draft spec + if (desc.get.call(value) !== Object(value).__proto__) { + fail("can't call __proto__ getter on " + value); + } +} + +checkProtoGetterOnPrimitive(32); +checkProtoGetterOnPrimitive(false); +checkProtoGetterOnPrimitive("great!"); + +function checkProtoSetterOnNonObjectThis(self) { + try { + desc.set.call(self); + fail("should have thrown TypeError"); + } catch (e) { + if (! (e instanceof TypeError)) { + fail("should throw TypeError on non-object self, got " +e); + } + } +} + +checkProtoSetterOnNonObjectThis(undefined); +checkProtoSetterOnNonObjectThis(null); + +function checkProtoSetterReturnValue(obj, p) { + if (typeof desc.set.call(obj, p) != "undefined") { + fail("__proto__ setter does not return undefined: " + obj + " " + p); + } +} + +// try non-object 'this'. setter is expected to return undefined. +checkProtoSetterReturnValue(23); +checkProtoSetterReturnValue(false); +checkProtoSetterReturnValue("foo"); + +// set proper __proto__. Still return value is undefined. +checkProtoSetterReturnValue({}, {}); +checkProtoSetterReturnValue({}, null);