changeset 512:b7c04b3b01a7

8023368: Instance __proto__ property should exist and be writable. Reviewed-by: attila, hannesw
author sundar
date Wed, 21 Aug 2013 17:28:53 +0530
parents dc322503ce36
children 54f60d91024c 6b6a8fc714a9
files src/jdk/nashorn/api/scripting/ScriptObjectMirror.java src/jdk/nashorn/internal/objects/NativeObject.java src/jdk/nashorn/internal/runtime/PropertyListener.java src/jdk/nashorn/internal/runtime/PropertyListenerManager.java src/jdk/nashorn/internal/runtime/PropertyMap.java src/jdk/nashorn/internal/runtime/ScriptEnvironment.java src/jdk/nashorn/internal/runtime/ScriptObject.java src/jdk/nashorn/internal/runtime/resources/Messages.properties src/jdk/nashorn/internal/runtime/resources/Options.properties src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js test/script/basic/JDK-8023368.js test/script/basic/JDK-8023368.js.EXPECTED test/script/basic/JDK-8023368_2.js test/script/basic/JDK-8023368_2.js.EXPECTED test/script/basic/circular_proto.js test/script/basic/circular_proto.js.EXPECTED test/script/basic/mirror_proto_assign.js test/script/basic/mirror_proto_assign.js.EXPECTED test/script/basic/nonextensible_proto_assign.js test/script/basic/nonextensible_proto_assign.js.EXPECTED
diffstat 20 files changed, 433 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/src/jdk/nashorn/internal/objects/NativeObject.java	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/src/jdk/nashorn/internal/runtime/PropertyListener.java	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/src/jdk/nashorn/internal/runtime/PropertyMap.java	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/src/jdk/nashorn/internal/runtime/ScriptObject.java	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/src/jdk/nashorn/internal/runtime/resources/Options.properties	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js	Wed Aug 21 13:39:40 2013 +0200
+++ b/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/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/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/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/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/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/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/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/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/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/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]