From 13d99fe778bdb8b165b7168812e1b6bfda0c0d19 Mon Sep 17 00:00:00 2001 From: "rossberg@chromium.org" Date: Wed, 19 Feb 2014 11:59:05 +0000 Subject: [PATCH] ES6: Tighten up Object.prototype.__proto__ The spec requires that we throw under certain conditions. BUG=v8:3064 LOG=y R=rossberg@chromium.org Review URL: https://codereview.chromium.org/103853006 Patch from Erik Arvidsson . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19479 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/v8natives.js | 14 +++-- test/mjsunit/proto-accessor.js | 139 +++++++++++++++++++++++++++++------------ 2 files changed, 109 insertions(+), 44 deletions(-) diff --git a/src/v8natives.js b/src/v8natives.js index e4f0a3b..256343a 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -1384,15 +1384,19 @@ function ObjectIs(obj1, obj2) { } -// Harmony __proto__ getter. +// ECMA-262, Edition 6, section B.2.2.1.1 function ObjectGetProto() { - return %GetPrototype(this); + return %GetPrototype(ToObject(this)); } -// Harmony __proto__ setter. -function ObjectSetProto(obj) { - return %SetPrototype(this, obj); +// ECMA-262, Edition 6, section B.2.2.1.2 +function ObjectSetProto(proto) { + CHECK_OBJECT_COERCIBLE(this, "Object.prototype.__proto__"); + + if (IS_SPEC_OBJECT(proto) || IS_NULL(proto) && IS_SPEC_OBJECT(this)) { + %SetPrototype(this, proto); + } } diff --git a/test/mjsunit/proto-accessor.js b/test/mjsunit/proto-accessor.js index aca6ec5..5eb48bb 100644 --- a/test/mjsunit/proto-accessor.js +++ b/test/mjsunit/proto-accessor.js @@ -25,57 +25,118 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Flags: --harmony-symbols + var desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__"); -assertEquals("function", typeof desc.get); -assertEquals("function", typeof desc.set); -assertDoesNotThrow("desc.get.call({})"); -assertDoesNotThrow("desc.set.call({}, {})"); +var getProto = desc.get; +var setProto = desc.set; + +function TestNoPoisonPill() { + assertEquals("function", typeof desc.get); + assertEquals("function", typeof desc.set); + assertDoesNotThrow("desc.get.call({})"); + assertDoesNotThrow("desc.set.call({}, {})"); + + var obj = {}; + var obj2 = {}; + desc.set.call(obj, obj2); + assertEquals(obj.__proto__, obj2); + assertEquals(desc.get.call(obj), obj2); +} +TestNoPoisonPill(); + + +function TestRedefineObjectPrototypeProtoGetter() { + Object.defineProperty(Object.prototype, "__proto__", { + get: function() { + return 42; + } + }); + assertEquals({}.__proto__, 42); + assertEquals(desc.get.call({}), Object.prototype); + + var desc2 = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__"); + assertEquals(desc2.get.call({}), 42); + assertEquals(desc2.set.call({}), undefined); + + Object.defineProperty(Object.prototype, "__proto__", { + set: function(x) {} + }); + var desc3 = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__"); + assertEquals(desc3.get.call({}), 42); + assertEquals(desc3.set.call({}), undefined); +} +TestRedefineObjectPrototypeProtoGetter(); + +function TestRedefineObjectPrototypeProtoSetter() { + Object.defineProperty(Object.prototype, "__proto__", { set: undefined }); + assertThrows(function() { + "use strict"; + var o = {}; + var p = {}; + o.__proto__ = p; + }, TypeError); +} +TestRedefineObjectPrototypeProtoSetter(); -var obj = {}; -var obj2 = {}; -desc.set.call(obj, obj2); -assertEquals(obj.__proto__, obj2); -assertEquals(desc.get.call(obj), obj2); +function TestGetProtoOfValues() { + assertEquals(getProto.call(1), Number.prototype); + assertEquals(getProto.call(true), Boolean.prototype); + assertEquals(getProto.call(false), Boolean.prototype); + assertEquals(getProto.call('s'), String.prototype); + assertEquals(getProto.call(Symbol()), Symbol.prototype); -// Check that any redefinition of the __proto__ accessor works. -Object.defineProperty(Object.prototype, "__proto__", { - get: function() { - return 42; + assertThrows(function() { getProto.call(null); }, TypeError); + assertThrows(function() { getProto.call(undefined); }, TypeError); +} +TestGetProtoOfValues(); + + +var values = [1, true, false, 's', Symbol()]; + + +function TestSetProtoOfValues() { + for (var i = 0; i < values.length; i++) { + assertEquals(setProto.call(values[i], i), undefined); } -}); -assertEquals({}.__proto__, 42); -assertEquals(desc.get.call({}), Object.prototype); + assertThrows(function() { setProto.call(null, 7); }, TypeError); + assertThrows(function() { setProto.call(undefined, 8); }, TypeError); +} +TestSetProtoOfValues(); -var desc2 = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__"); -assertEquals(desc2.get.call({}), 42); -assertDoesNotThrow("desc2.set.call({})"); +function TestSetProtoToValue() { + var object = {}; + var proto = {}; + setProto.call(object, proto); + + var valuesWithUndefined = values.concat(undefined); + + for (var i = 0; i < valuesWithUndefined.length; i++) { + assertEquals(setProto.call(object, valuesWithUndefined[i]), undefined); + assertEquals(getProto.call(object), proto); + } -Object.defineProperty(Object.prototype, "__proto__", { set:function(x){} }); -var desc3 = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__"); -assertDoesNotThrow("desc3.get.call({})"); -assertDoesNotThrow("desc3.set.call({})"); + // null is the only valid value that can be used as a [[Prototype]]. + assertEquals(setProto.call(object, null), undefined); + assertEquals(getProto.call(object), null); +} +TestSetProtoToValue(); -Object.defineProperty(Object.prototype, "__proto__", { set: undefined }); -assertThrows(function() { - "use strict"; +function TestDeleteProto() { + assertTrue(delete Object.prototype.__proto__); var o = {}; var p = {}; o.__proto__ = p; -}, TypeError); - - -assertTrue(delete Object.prototype.__proto__); -var o = {}; -var p = {}; -o.__proto__ = p; -assertEquals(Object.getPrototypeOf(o), Object.prototype); -var desc4 = Object.getOwnPropertyDescriptor(o, "__proto__"); -assertTrue(desc4.configurable); -assertTrue(desc4.enumerable); -assertTrue(desc4.writable); -assertEquals(desc4.value, p); + assertEquals(Object.getPrototypeOf(o), Object.prototype); + var desc4 = Object.getOwnPropertyDescriptor(o, "__proto__"); + assertTrue(desc4.configurable); + assertTrue(desc4.enumerable); + assertTrue(desc4.writable); + assertEquals(desc4.value, p); +} +TestDeleteProto(); -- 2.7.4