8023368: Instance __proto__ property should exist and be writable.
Reviewed-by: attila, hannesw
--- a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Wed Aug 21 17:28:53 2013 +0530
@@ -374,6 +374,19 @@
}
/**
+ * Set the __proto__ of this object.
+ * @param proto new proto for this object
+ */
+ public void setProto(final Object proto) {
+ inGlobal(new Callable<Void>() {
+ @Override public Void call() {
+ sobj.setProtoCheck(unwrap(proto, global));
+ return null;
+ }
+ });
+ }
+
+ /**
* ECMA 8.12.1 [[GetOwnProperty]] (P)
*
* @param key property key
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java Wed Aug 21 17:28:53 2013 +0530
@@ -125,6 +125,28 @@
}
/**
+ * Nashorn extension: Object.setPrototypeOf ( O, proto )
+ * Also found in ES6 draft specification.
+ *
+ * @param self self reference
+ * @param obj object to set prototype for
+ * @param proto prototype object to be used
+ * @return object whose prototype is set
+ */
+ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+ public static Object setPrototypeOf(final Object self, final Object obj, final Object proto) {
+ if (obj instanceof ScriptObject) {
+ ((ScriptObject)obj).setProtoCheck(proto);
+ return obj;
+ } else if (obj instanceof ScriptObjectMirror) {
+ ((ScriptObjectMirror)obj).setProto(proto);
+ return obj;
+ }
+
+ throw notAnObject(obj);
+ }
+
+ /**
* ECMA 15.2.3.3 Object.getOwnPropertyDescriptor ( O, P )
*
* @param self self reference
@@ -184,7 +206,7 @@
// FIXME: should we create a proper object with correct number of
// properties?
final ScriptObject newObj = Global.newEmptyInstance();
- newObj.setProtoCheck(proto);
+ newObj.setProto((ScriptObject)proto);
if (props != UNDEFINED) {
NativeObject.defineProperties(self, newObj, props);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyListener.java Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyListener.java Wed Aug 21 17:28:53 2013 +0530
@@ -54,4 +54,13 @@
*
*/
public void propertyModified(ScriptObject object, Property oldProp, Property newProp);
+
+ /**
+ * Given object's __proto__ has changed.
+ *
+ * @param object object whose __proto__ has changed.
+ * @param oldProto old __proto__
+ * @param newProto new __proto__
+ */
+ public void protoChanged(ScriptObject object, ScriptObject oldProto, ScriptObject newProto);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java Wed Aug 21 17:28:53 2013 +0530
@@ -140,6 +140,21 @@
}
}
+ /**
+ * This method can be called to notify __proto__ modification to this object's listeners.
+ *
+ * @param object The ScriptObject whose __proto__ was changed.
+ * @param oldProto old __proto__
+ * @param newProto new __proto__
+ */
+ protected synchronized final void notifyProtoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
+ if (listeners != null) {
+ for (PropertyListener listener : listeners.keySet()) {
+ listener.protoChanged(object, oldProto, newProto);
+ }
+ }
+ }
+
// PropertyListener methods
@Override
@@ -156,4 +171,9 @@
public final void propertyModified(final ScriptObject object, final Property oldProp, final Property newProp) {
notifyPropertyModified(object, oldProp, newProp);
}
+
+ @Override
+ public final void protoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
+ notifyProtoChanged(object, oldProto, newProto);
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java Wed Aug 21 17:28:53 2013 +0530
@@ -230,7 +230,7 @@
}
/**
- * Indicate that a prototype property hash changed.
+ * Indicate that a prototype property has changed.
*
* @param property {@link Property} to invalidate.
*/
@@ -251,6 +251,18 @@
}
/**
+ * Indicate that proto itself has changed in hierachy somewhere.
+ */
+ private void invalidateAllProtoGetSwitchPoints() {
+ assert !isShared() : "proto invalidation on a shared PropertyMap";
+
+ if (protoGetSwitches != null) {
+ final Collection<SwitchPoint> sws = protoGetSwitches.values();
+ SwitchPoint.invalidateAll(sws.toArray(new SwitchPoint[sws.size()]));
+ }
+ }
+
+ /**
* Add a property to the map, re-binding its getters and setters,
* if available, to a given receiver. This is typically the global scope. See
* {@link ScriptObject#addBoundProperties(ScriptObject)}
@@ -878,6 +890,15 @@
invalidateProtoGetSwitchPoint(oldProp);
}
+ @Override
+ public void protoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
+ // We may walk and invalidate SwitchPoints for properties inherited
+ // from 'object' or it's old proto chain. But, it may not be worth it.
+ // For example, a new proto may have a user defined getter/setter for
+ // a data property down the chain. So, invalidating all is better.
+ invalidateAllProtoGetSwitchPoints();
+ }
+
/*
* Debugging and statistics.
*/
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Wed Aug 21 17:28:53 2013 +0530
@@ -128,9 +128,6 @@
/** Do not support typed arrays. */
public final boolean _no_typed_arrays;
- /** Package to which generated class files are added */
- public final String _package;
-
/** Only parse the source code, do not compile */
public final boolean _parse_only;
@@ -216,7 +213,6 @@
_no_java = options.getBoolean("no.java");
_no_syntax_extensions = options.getBoolean("no.syntax.extensions");
_no_typed_arrays = options.getBoolean("no.typed.arrays");
- _package = options.getString("package");
_parse_only = options.getBoolean("parse.only");
_print_ast = options.getBoolean("print.ast");
_print_lower_ast = options.getBoolean("print.lower.ast");
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Aug 21 17:28:53 2013 +0530
@@ -1129,6 +1129,9 @@
proto = newProto;
if (isPrototype()) {
+ // tell listeners that my __proto__ has been changed
+ notifyProtoChanged(this, oldProto, newProto);
+
if (oldProto != null) {
oldProto.removePropertyListener(this);
}
@@ -1144,7 +1147,19 @@
* @param newProto Prototype to set.
*/
public final void setProtoCheck(final Object newProto) {
+ if (!isExtensible()) {
+ throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
+ }
+
if (newProto == null || newProto instanceof ScriptObject) {
+ // check for circularity
+ ScriptObject proto = (ScriptObject)newProto;
+ while (proto != null) {
+ if (proto == this) {
+ throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
+ }
+ proto = proto.getProto();
+ }
setProto((ScriptObject)newProto);
} else {
final ScriptObject global = Context.getGlobalTrusted();
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties Wed Aug 21 17:28:53 2013 +0530
@@ -94,6 +94,8 @@
type.error.cant.redefine.property=Cannot redefine property "{0}" of {1}
type.error.property.not.writable="{0}" is not a writable property of {1}
type.error.object.non.extensible=Cannot add new property "{0}" to non-extensible {1}
+type.error.__proto__.set.non.extensible=Cannot set __proto__ of non-extensible {0}
+type.error.circular.__proto__.set=Cannot create__proto__ cycle for {0}
# miscellaneous
type.error.regex.cant.supply.flags=Cannot supply flags when constructing one RegExp from another
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties Wed Aug 21 17:28:53 2013 +0530
@@ -216,15 +216,6 @@
default=false \
}
-nashorn.option.package = { \
- name="--package", \
- is_undocumented=true, \
- desc="Package to which generated .class files are added.", \
- params="<package>", \
- type=String, \
- default="" \
-}
-
nashorn.option.parse.only = { \
name="--parse-only", \
is_undocumented=true, \
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js Wed Aug 21 13:39:40 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js Wed Aug 21 17:28:53 2013 +0530
@@ -144,7 +144,7 @@
return Object.getPrototypeOf(this);
},
set: function(x) {
- throw new TypeError("__proto__ set not supported");
+ Object.setPrototypeOf(this, x);
}
});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8023368.js Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010, 2013, 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-8023368: Instance __proto__ property should exist and be writable.
+ *
+ * @test
+ * @run
+ */
+
+load("nashorn:mozilla_compat.js");
+
+// function to force same callsites
+function check(obj) {
+ print(obj.func());
+ print(obj.x);
+ print(obj.toString());
+}
+
+function Func() {
+}
+
+Func.prototype.func = function() {
+ return "Func.prototype.func";
+}
+
+Func.prototype.x = "hello";
+
+var obj = new Func();
+var obj2 = Object.create(obj);
+
+// check direct and indirect __proto__ change
+check(obj);
+check(obj2);
+obj.__proto__ = {
+ func: function() {
+ return "obj.__proto__.func @ " + __LINE__;
+ },
+ x: 344
+};
+
+check(obj);
+check(obj2);
+
+// check indirect (1 and 2 levels) __proto__ function change
+obj.__proto__.__proto__ = {
+ toString: function() {
+ return "new object.toString";
+ }
+};
+
+check(obj);
+check(obj2);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8023368.js.EXPECTED Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,18 @@
+Func.prototype.func
+hello
+[object Object]
+Func.prototype.func
+hello
+[object Object]
+obj.__proto__.func @ 57
+344
+[object Object]
+obj.__proto__.func @ 57
+344
+[object Object]
+obj.__proto__.func @ 57
+344
+new object.toString
+obj.__proto__.func @ 57
+344
+new object.toString
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8023368_2.js Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010, 2013, 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-8023368: Instance __proto__ property should exist and be writable.
+ *
+ * @test
+ * @run
+ */
+
+// check Object.setPrototypeOf extension rather than using __proto__
+
+// function to force same callsites
+function check(obj) {
+ print(obj.func());
+ print(obj.x);
+ print(obj.toString());
+}
+
+function Func() {
+}
+
+Func.prototype.func = function() {
+ return "Func.prototype.func";
+}
+
+Func.prototype.x = "hello";
+
+var obj = new Func();
+var obj2 = Object.create(obj);
+
+// check direct and indirect __proto__ change
+check(obj);
+check(obj2);
+Object.setPrototypeOf(obj, {
+ func: function() {
+ return "obj.__proto__.func @ " + __LINE__;
+ },
+ x: 344
+});
+
+check(obj);
+check(obj2);
+
+// check indirect (1 and 2 levels) __proto__ function change
+Object.setPrototypeOf(Object.getPrototypeOf(obj), {
+ toString: function() {
+ return "new object.toString";
+ }
+});
+
+check(obj);
+check(obj2);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8023368_2.js.EXPECTED Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,18 @@
+Func.prototype.func
+hello
+[object Object]
+Func.prototype.func
+hello
+[object Object]
+obj.__proto__.func @ 57
+344
+[object Object]
+obj.__proto__.func @ 57
+344
+[object Object]
+obj.__proto__.func @ 57
+344
+new object.toString
+obj.__proto__.func @ 57
+344
+new object.toString
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/circular_proto.js Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2010, 2013, 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-8023368: Instance __proto__ property should exist and be writable.
+ *
+ * @test
+ * @run
+ */
+
+// check that we cannot create __proto__ cycle
+load("nashorn:mozilla_compat.js");
+
+var obj = {};
+var obj2 = Object.create(obj);
+
+// attempt to create __proto__ cycle
+try {
+ obj.__proto__ = obj2;
+ fail("Should have thrown TypeError");
+} catch (e) {
+ if (! (e instanceof TypeError)) {
+ fail("Expected TypeError, got " + e);
+ }
+ print(e);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/circular_proto.js.EXPECTED Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,1 @@
+TypeError: Cannot create__proto__ cycle for [object Object]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/mirror_proto_assign.js Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010, 2013, 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-8023368: Instance __proto__ property should exist and be writable.
+ *
+ * @test
+ * @run
+ */
+
+// check that Object.setPrototypeOf works for mirror objects as well.
+
+var global = loadWithNewGlobal({
+ name: "test",
+ script: "var obj = {}; this"
+});
+
+var proto = global.eval("({ foo: 323 })");
+
+Object.setPrototypeOf(global.obj, proto);
+
+function func(obj) {
+ // check proto inherited value
+ print("obj.foo = " + obj.foo);
+}
+
+func(global.obj);
+
+var newProto = global.eval("({ foo: 'hello' })");
+Object.setPrototypeOf(global.obj, newProto);
+
+func(global.obj);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/mirror_proto_assign.js.EXPECTED Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,2 @@
+obj.foo = 323
+obj.foo = hello
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/nonextensible_proto_assign.js Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2010, 2013, 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-8023368: Instance __proto__ property should exist and be writable.
+ *
+ * @test
+ * @run
+ */
+
+load("nashorn:mozilla_compat.js")
+
+// check that we cannot assign to __proto__ of a non-extensible object
+try {
+ var obj = {}
+ Object.preventExtensions(obj);
+ obj.__proto__ = { };
+ fail("Should have thrown TypeError");
+} catch (e) {
+ if (! (e instanceof TypeError)) {
+ fail("Expected TypeError, got " + e);
+ }
+ print(e);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/nonextensible_proto_assign.js.EXPECTED Wed Aug 21 17:28:53 2013 +0530
@@ -0,0 +1,1 @@
+TypeError: Cannot set __proto__ of non-extensible [object Object]