From e149f81ebaf3d9e9c37808bc96732ad295ee50fd Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Fri, 17 Oct 2014 13:19:45 +0000 Subject: [PATCH] Keyed stores to super with numeric keys. R=verwaest@chromium.org, arv@chromium.org, ishell@chromium.org BUG=v8:3330 LOG=N Review URL: https://codereview.chromium.org/649603003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24696 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/objects.cc | 124 ++++++++- src/objects.h | 34 ++- src/runtime/runtime-classes.cc | 35 ++- test/mjsunit/harmony/super.js | 584 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 732 insertions(+), 45 deletions(-) diff --git a/src/objects.cc b/src/objects.cc index 5e9e154..930342d 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -802,6 +802,82 @@ MaybeHandle Object::GetElementWithReceiver(Isolate* isolate, } +MaybeHandle Object::SetElementWithReceiver( + Isolate* isolate, Handle object, Handle receiver, + uint32_t index, Handle value, StrictMode strict_mode) { + // Iterate up the prototype chain until an element is found or the null + // prototype is encountered. + bool done = false; + for (PrototypeIterator iter(isolate, object, + object->IsJSProxy() || object->IsJSObject() + ? PrototypeIterator::START_AT_RECEIVER + : PrototypeIterator::START_AT_PROTOTYPE); + !iter.IsAtEnd() && !done; iter.Advance()) { + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { + // TODO(dslomov): implement. + isolate->ThrowIllegalOperation(); + return MaybeHandle(); + } + + Handle js_object = + Handle::cast(PrototypeIterator::GetCurrent(iter)); + + // Check access rights if needed. + if (js_object->IsAccessCheckNeeded()) { + if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_SET)) { + isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_SET); + RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); + return isolate->factory()->undefined_value(); + } + } + + if (js_object->HasIndexedInterceptor()) { + Maybe from_interceptor = + JSObject::GetElementAttributeFromInterceptor(js_object, receiver, + index); + if (!from_interceptor.has_value) return MaybeHandle(); + if ((from_interceptor.value & READ_ONLY) != 0) { + return WriteToReadOnlyElement(isolate, receiver, index, value, + strict_mode); + } + done = from_interceptor.value != ABSENT; + } + + if (!done && + js_object->elements() != isolate->heap()->empty_fixed_array()) { + ElementsAccessor* accessor = js_object->GetElementsAccessor(); + PropertyAttributes attrs = + accessor->GetAttributes(receiver, js_object, index); + if ((attrs & READ_ONLY) != 0) { + return WriteToReadOnlyElement(isolate, receiver, index, value, + strict_mode); + } + Handle accessor_pair; + if (accessor->GetAccessorPair(receiver, js_object, index) + .ToHandle(&accessor_pair)) { + return JSObject::SetElementWithCallback(receiver, accessor_pair, index, + value, js_object, strict_mode); + } else { + done = attrs != ABSENT; + } + } + } + + if (!receiver->IsJSObject()) { + return WriteToReadOnlyElement(isolate, receiver, index, value, strict_mode); + } + Handle target = Handle::cast(receiver); + ElementsAccessor* accessor = target->GetElementsAccessor(); + PropertyAttributes attrs = accessor->GetAttributes(receiver, target, index); + if ((attrs & READ_ONLY) != 0) { + return WriteToReadOnlyElement(isolate, receiver, index, value, strict_mode); + } + PropertyAttributes new_attrs = attrs != ABSENT ? attrs : NONE; + return JSObject::SetElement(target, index, value, new_attrs, strict_mode, + false); +} + + Map* Object::GetRootMap(Isolate* isolate) { DisallowHeapAllocation no_alloc; if (IsSmi()) { @@ -2932,6 +3008,21 @@ MaybeHandle Object::WriteToReadOnlyProperty(LookupIterator* it, } +MaybeHandle Object::WriteToReadOnlyElement(Isolate* isolate, + Handle receiver, + uint32_t index, + Handle value, + StrictMode strict_mode) { + if (strict_mode != STRICT) return value; + + Handle args[] = {isolate->factory()->NewNumberFromUint(index), + receiver}; + THROW_NEW_ERROR(isolate, NewTypeError("strict_read_only_property", + HandleVector(args, arraysize(args))), + Object); +} + + MaybeHandle Object::SetDataProperty(LookupIterator* it, Handle value) { // Proxies are handled on the WithHandler path. Other non-JSObjects cannot @@ -4075,6 +4166,21 @@ Maybe JSObject::GetElementAttributeWithInterceptor( // callbacks or interceptor calls. AssertNoContextChange ncc(isolate); + Maybe from_interceptor = + GetElementAttributeFromInterceptor(object, receiver, index); + if (!from_interceptor.has_value) return Maybe(); + if (from_interceptor.value != ABSENT) return maybe(from_interceptor.value); + + return GetElementAttributeWithoutInterceptor(object, receiver, index, + check_prototype); +} + + +Maybe JSObject::GetElementAttributeFromInterceptor( + Handle object, Handle receiver, uint32_t index) { + Isolate* isolate = object->GetIsolate(); + AssertNoContextChange ncc(isolate); + Handle interceptor(object->GetIndexedInterceptor()); PropertyCallbackArguments args( isolate, interceptor->data(), *receiver, *object); @@ -4095,9 +4201,8 @@ Maybe JSObject::GetElementAttributeWithInterceptor( v8::Handle result = args.Call(getter, index); if (!result.IsEmpty()) return maybe(NONE); } - - return GetElementAttributeWithoutInterceptor( - object, receiver, index, check_prototype); + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Maybe()); + return maybe(ABSENT); } @@ -11867,13 +11972,10 @@ MaybeHandle JSObject::GetElementWithCallback( } -MaybeHandle JSObject::SetElementWithCallback(Handle object, - Handle structure, - uint32_t index, - Handle value, - Handle holder, - StrictMode strict_mode) { - Isolate* isolate = object->GetIsolate(); +MaybeHandle JSObject::SetElementWithCallback( + Handle object, Handle structure, uint32_t index, + Handle value, Handle holder, StrictMode strict_mode) { + Isolate* isolate = holder->GetIsolate(); // We should never get here to initialize a const with the hole // value since a const declaration would conflict with the setter. @@ -11889,7 +11991,7 @@ MaybeHandle JSObject::SetElementWithCallback(Handle object, if (call_fun == NULL) return value; Handle number = isolate->factory()->NewNumberFromUint(index); Handle key(isolate->factory()->NumberToString(number)); - LOG(isolate, ApiNamedPropertyAccess("store", *object, *key)); + LOG(isolate, ApiNamedPropertyAccess("store", *holder, *key)); PropertyCallbackArguments args(isolate, data->data(), *object, *holder); args.Call(call_fun, diff --git a/src/objects.h b/src/objects.h index 37ab88d..af6bb66 100644 --- a/src/objects.h +++ b/src/objects.h @@ -1131,6 +1131,9 @@ class Object { StorePropertyMode data_store_mode = NORMAL_PROPERTY); MUST_USE_RESULT static MaybeHandle WriteToReadOnlyProperty( LookupIterator* it, Handle value, StrictMode strict_mode); + MUST_USE_RESULT static MaybeHandle WriteToReadOnlyElement( + Isolate* isolate, Handle receiver, uint32_t index, + Handle value, StrictMode strict_mode); MUST_USE_RESULT static MaybeHandle SetDataProperty( LookupIterator* it, Handle value); MUST_USE_RESULT static MaybeHandle AddDataProperty( @@ -1176,6 +1179,10 @@ class Object { Handle receiver, uint32_t index); + MUST_USE_RESULT static MaybeHandle SetElementWithReceiver( + Isolate* isolate, Handle object, Handle receiver, + uint32_t index, Handle value, StrictMode strict_mode); + static inline Handle GetPrototypeSkipHiddenPrototypes( Isolate* isolate, Handle receiver); @@ -1927,8 +1934,7 @@ class JSObject: public JSReceiver { // These methods do not perform access checks! MUST_USE_RESULT static MaybeHandle GetOwnElementAccessorPair( - Handle object, - uint32_t index); + Handle object, uint32_t index); MUST_USE_RESULT static MaybeHandle SetFastElement( Handle object, @@ -2253,18 +2259,30 @@ class JSObject: public JSReceiver { GetElementAttributeWithInterceptor(Handle object, Handle receiver, uint32_t index, bool continue_search); + + // Queries indexed interceptor on an object for property attributes. + // + // We determine property attributes as follows: + // - if interceptor has a query callback, then the property attributes are + // the result of query callback for index. + // - otherwise if interceptor has a getter callback and it returns + // non-empty value on index, then the property attributes is NONE + // (property is present, and it is enumerable, configurable, writable) + // - otherwise there are no property attributes that can be inferred for + // interceptor, and this function returns ABSENT. + MUST_USE_RESULT static Maybe + GetElementAttributeFromInterceptor(Handle object, + Handle receiver, + uint32_t index); + MUST_USE_RESULT static Maybe GetElementAttributeWithoutInterceptor(Handle object, Handle receiver, uint32_t index, bool continue_search); MUST_USE_RESULT static MaybeHandle SetElementWithCallback( - Handle object, - Handle structure, - uint32_t index, - Handle value, - Handle holder, - StrictMode strict_mode); + Handle object, Handle structure, uint32_t index, + Handle value, Handle holder, StrictMode strict_mode); MUST_USE_RESULT static MaybeHandle SetElementWithInterceptor( Handle object, uint32_t index, diff --git a/src/runtime/runtime-classes.cc b/src/runtime/runtime-classes.cc index 5b1cfe3..15a08b0 100644 --- a/src/runtime/runtime-classes.cc +++ b/src/runtime/runtime-classes.cc @@ -287,6 +287,30 @@ static Object* StoreToSuper(Isolate* isolate, Handle home_object, } +static Object* StoreElementToSuper(Isolate* isolate, + Handle home_object, + Handle receiver, uint32_t index, + Handle value, + StrictMode strict_mode) { + if (home_object->IsAccessCheckNeeded() && + !isolate->MayIndexedAccess(home_object, index, v8::ACCESS_SET)) { + isolate->ReportFailedAccessCheck(home_object, v8::ACCESS_SET); + RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); + } + + PrototypeIterator iter(isolate, home_object); + Handle proto = PrototypeIterator::GetCurrent(iter); + if (!proto->IsJSReceiver()) return isolate->heap()->undefined_value(); + + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, + Object::SetElementWithReceiver(isolate, proto, receiver, index, value, + strict_mode)); + return *result; +} + + RUNTIME_FUNCTION(Runtime_StoreToSuper_Strict) { HandleScope scope(isolate); DCHECK(args.length() == 4); @@ -314,13 +338,18 @@ RUNTIME_FUNCTION(Runtime_StoreToSuper_Sloppy) { static Object* StoreKeyedToSuper(Isolate* isolate, Handle home_object, Handle receiver, Handle key, Handle value, StrictMode strict_mode) { + uint32_t index; + + if (key->ToArrayIndex(&index)) { + return StoreElementToSuper(isolate, home_object, receiver, index, value, + strict_mode); + } Handle name; ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name, Runtime::ToName(isolate, key)); - uint32_t index; if (name->AsArrayIndex(&index)) { - // TODO(dslomov): Implement. - return ThrowUnsupportedSuper(isolate); + return StoreElementToSuper(isolate, home_object, receiver, index, value, + strict_mode); } return StoreToSuper(isolate, home_object, receiver, name, value, strict_mode); } diff --git a/test/mjsunit/harmony/super.js b/test/mjsunit/harmony/super.js index c39944d..a2e07ed 100644 --- a/test/mjsunit/harmony/super.js +++ b/test/mjsunit/harmony/super.js @@ -321,6 +321,74 @@ }()); +(function TestSetterNumericKeyed() { + var x = 42; + function Base() {} + Base.prototype = { + constructor: Base, + _x: 'base' + }; + + Object.defineProperty(Base.prototype, x, + { get: function() { return this._x; }, + set: function(v) { this._x = v; } + }); + + function Derived() {} + Derived.__proto__ = Base; + Derived.prototype = { + __proto__: Base.prototype, + constructor: Derived, + _x: 'derived' + }; + Derived.prototype.testSetter = function() { + assertEquals('foobar', super[x] = 'foobar'); + assertEquals('foobarabc', super[x] += 'abc'); + }.toMethod(Derived.prototype); + var d = new Derived(); + d.testSetter(); + assertEquals('base', Base.prototype._x); + assertEquals('foobarabc', d._x); + d._x = ''; + Derived.prototype.testSetterStrict = function() { + 'use strict'; + assertEquals('foobar', super[x] = 'foobar'); + assertEquals('foobarabc', super[x] += 'abc'); + }.toMethod(Derived.prototype); + d.testSetterStrict(); + assertEquals('base', Base.prototype._x); + assertEquals('foobarabc', d._x); + + + Derived.prototype.testSetterWithToString = function() { + var toStringCalled; + var o = { toString: function() { + toStringCalled++; + return x; + } }; + + toStringCalled = 0; + super[o] = 'set'; + assertEquals(1, toStringCalled); + assertEquals('set', this._x); + + var eToThrow = new Error(); + var oThrowsInToString = { toString: function() { + throw eToThrow; + } }; + + var ex = null; + try { + super[oThrowsInToString] = 'xyz'; + } catch(e) { ex = e } + assertEquals(eToThrow, ex); + assertEquals('set', this._x); + }.toMethod(Derived.prototype); + d = new Derived(); + d.testSetterWithToString(); +}()); + + (function TestSetterKeyed() { var x = 'x'; function Base() {} @@ -389,19 +457,11 @@ return "1"; } }; - ex = null; - try { - super[oReturnsNumericString] = 'abc'; - } catch(e) { ex = e } - assertTrue(ex instanceof ReferenceError); + assertEquals('abc', super[oReturnsNumericString] = 'abc'); assertEquals('set', this._x); - ex = null; - try { - super[1] = 10; // Indexed properties unsupported yet. - } catch (e) { ex = e; } - assertTrue(ex instanceof ReferenceError); + assertEquals(10, super[1] = 10); }.toMethod(Derived.prototype); d = new Derived(); d.testSetterWithToString(); @@ -457,6 +517,31 @@ }()); +(function TestKeyedNumericSetterDataProperties() { + var x = 42; + function Base() {} + Base.prototype = { + constructor: Base, + 42: 'x from Base' + }; + + function Derived() {} + Derived.prototype = { + __proto__: Base.prototype, + constructor: Derived, + }; + + Derived.prototype.testSetter = function() { + assertEquals('x from Base', super[x]); + super[x] = 'data property'; + assertEquals('x from Base', super[x]); + assertEquals('data property', this[x]); + }.toMethod(Derived.prototype); + + new Derived().testSetter(); +}()); + + (function TestAccessorsOnPrimitives() { var getCalled = 0; var setCalled = 0; @@ -640,6 +725,122 @@ }()); +(function TestNumericKeyedAccessorsOnPrimitives() { + var x = 42; + var newProperty = 43; + var getCalled = 0; + var setCalled = 0; + function Base() {} + Base.prototype = { + constructor: Base, + }; + + Object.defineProperty(Base.prototype, x, { + get: function() { + getCalled++; + return 1; + }, + set: function(v) { + setCalled++; + return v; + } + }); + + function Derived() {} + Derived.prototype = { + __proto__: Base.prototype, + constructor: Derived, + }; + Derived.prototype.testSetter = function() { + setCalled = 0; + getCalled = 0; + assertEquals('object', typeof this); + assertTrue(this instanceof Number) + assertEquals(42, this.valueOf()); + assertEquals(1, super[x]); + assertEquals(1, getCalled); + assertEquals(0, setCalled); + + assertEquals(5, super[x] = 5); + assertEquals(1, getCalled); + assertEquals(1, setCalled); + + assertEquals(6, super[x] += 5); + assertEquals(2, getCalled); + assertEquals(2, setCalled); + + super[newProperty] = 15; + assertEquals(15, this[newProperty]); + assertEquals(undefined, super[newProperty]); + }.toMethod(Derived.prototype); + + Derived.prototype.testSetterStrict = function() { + 'use strict'; + getCalled = 0; + setCalled = 0; + assertTrue(42 === this); + + assertEquals(1, super[x]); + assertEquals(1, getCalled); + assertEquals(0, setCalled); + + assertEquals(5, super[x] = 5); + assertEquals(1, getCalled); + assertEquals(1, setCalled); + + assertEquals(6, super[x] += 5); + assertEquals(2, getCalled); + assertEquals(2, setCalled); + + var ex; + try { + super[newProperty] = 15; + } catch (e) { ex = e; } + assertTrue(ex instanceof TypeError); + }.toMethod(Derived.prototype); + + Derived.prototype.testSetter.call(42); + Derived.prototype.testSetterStrict.call(42); +}()); + + +(function TestKeyedNumericSetterOnExotics() { + function Base() {} + function Derived() {} + Derived.prototype = { __proto__: Base.prototype }; + + Derived.prototype.callSetterOnArray = function() { + super[42] = 1; + }.toMethod(Derived.prototype); + + Derived.prototype.callStrictSetterOnString = function() { + 'use strict'; + assertEquals('string', typeof this); + assertTrue('abcdef' === this); + var ex = null; + try { + super[5] = 'q'; + } catch(e) { ex = e; } + assertTrue(ex instanceof TypeError); + + ex = null; + try { + super[1024] = 'q'; + } catch(e) { ex = e; } + assertTrue(ex instanceof TypeError); + }.toMethod(Derived.prototype); + + var x = []; + assertEquals(0, x.length); + Derived.prototype.callSetterOnArray.call(x); + assertEquals(43, x.length); + assertEquals(1, x[42]); + + var s = 'abcdef'; + Derived.prototype.callStrictSetterOnString.call(s) +}()); + + (function TestSetterUndefinedProperties() { function Base() {} function Derived() {} @@ -699,6 +900,36 @@ }()); +(function TestKeyedNumericSetterUndefinedProperties() { + var x = 42; + function Base() {} + function Derived() {} + Derived.prototype = { __proto__ : Base.prototype }; + Derived.prototype.mSloppy = function () { + assertEquals(undefined, super[x]); + assertEquals(undefined, this[x]); + super[x] = 10; + assertEquals(10, this[x]); + assertEquals(undefined, super[x]); + }.toMethod(Derived.prototype); + + Derived.prototype.mStrict = function () { + 'use strict'; + assertEquals(undefined, super[x]); + assertEquals(undefined, this[x]); + super[x] = 10; + assertEquals(10, this[x]); + assertEquals(undefined, super[x]); + }.toMethod(Derived.prototype); + var d = new Derived(); + d.mSloppy(); + assertEquals(10, d[x]); + var d1 = new Derived(); + d1.mStrict(); + assertEquals(10, d[x]); +}()); + + (function TestSetterCreatingOwnProperties() { function Base() {} function Derived() {} @@ -870,6 +1101,63 @@ }()); +(function TestKeyedNumericSetterCreatingOwnProperties() { + var ownReadOnly = 42; + var ownReadonlyAccessor = 43; + var ownSetter = 44; + function Base() {} + function Derived() {} + Derived.prototype = { __proto__ : Base.prototype }; + var setterCalled; + + Derived.prototype.mSloppy = function() { + assertEquals(42, this[ownReadOnly]); + super[ownReadOnly] = 55; + assertEquals(42, this[ownReadOnly]); + + assertEquals(15, this[ownReadonlyAccessor]); + super[ownReadonlyAccessor] = 55; + assertEquals(15, this[ownReadonlyAccessor]); + + setterCalled = 0; + super[ownSetter] = 42; + assertEquals(1, setterCalled); + }.toMethod(Derived.prototype); + + Derived.prototype.mStrict = function() { + 'use strict'; + assertEquals(42, this[ownReadOnly]); + var ex; + try { + super[ownReadOnly] = 55; + } catch(e) { ex = e; } + assertTrue(ex instanceof TypeError); + assertEquals(42, this[ownReadOnly]); + + assertEquals(15, this[ownReadonlyAccessor]); + ex = null; + try { + super[ownReadonlyAccessor] = 55; + } catch(e) { ex = e; } + assertTrue(ex instanceof TypeError); + assertEquals(15, this[ownReadonlyAccessor]); + + setterCalled = 0; + super[ownSetter] = 42; + assertEquals(1, setterCalled); + }.toMethod(Derived.prototype); + + var d = new Derived(); + Object.defineProperty(d, ownReadOnly, { value : 42, writable : false }); + Object.defineProperty(d, ownSetter, + { set : function() { setterCalled++; } }); + Object.defineProperty(d, ownReadonlyAccessor, + { get : function() { return 15; }}); + d.mSloppy(); + d.mStrict(); +}()); + + (function TestSetterNoProtoWalk() { function Base() {} function Derived() {} @@ -1005,6 +1293,77 @@ }()); +(function TestKeyedNumericSetterNoProtoWalk() { + var x = 42; + function Base() {} + function Derived() {} + var getCalled; + var setCalled; + Derived.prototype = { + __proto__ : Base.prototype, + }; + + Object.defineProperty(Derived.prototype, x, { + get: function() { getCalled++; return 42; }, + set: function(v) { setCalled++; } + }); + + Derived.prototype.mSloppy = function() { + setCalled = 0; + getCalled = 0; + assertEquals(42, this[x]); + assertEquals(1, getCalled); + assertEquals(0, setCalled); + + getCalled = 0; + setCalled = 0; + this[x] = 43; + assertEquals(0, getCalled); + assertEquals(1, setCalled); + + getCalled = 0; + setCalled = 0; + super[x] = 15; + assertEquals(0, setCalled); + assertEquals(0, getCalled); + + assertEquals(15, this[x]); + assertEquals(0, getCalled); + assertEquals(0, setCalled); + + }.toMethod(Derived.prototype); + + Derived.prototype.mStrict = function() { + 'use strict'; + setCalled = 0; + getCalled = 0; + assertEquals(42, this[x]); + assertEquals(1, getCalled); + assertEquals(0, setCalled); + + getCalled = 0; + setCalled = 0; + this[x] = 43; + assertEquals(0, getCalled); + assertEquals(1, setCalled); + + getCalled = 0; + setCalled = 0; + super[x] = 15; + assertEquals(0, setCalled); + assertEquals(0, getCalled); + + assertEquals(15, this[x]); + assertEquals(0, getCalled); + assertEquals(0, setCalled); + + }.toMethod(Derived.prototype); + + new Derived().mSloppy(); + new Derived().mStrict(); +}()); + + (function TestSetterDoesNotReconfigure() { function Base() {} function Derived() {} @@ -1054,16 +1413,18 @@ function Base() {} function Derived() {} + Derived.prototype = { __proto__: Base.prototype }; + Derived.prototype.mStrict = function (){ 'use strict'; super[nonEnumConfig] = 5; - var d1 = Object.getOwnPropertyDescriptor(this, 'nonEnumConfig'); + var d1 = Object.getOwnPropertyDescriptor(this, nonEnumConfig); assertEquals(5, d1.value); assertTrue(d1.configurable); assertFalse(d1.enumerable); super[nonEnumNonConfig] = 5; - var d1 = Object.getOwnPropertyDescriptor(this, 'nonEnumNonConfig'); + var d1 = Object.getOwnPropertyDescriptor(this, nonEnumNonConfig); assertEquals(5, d1.value); assertFalse(d1.configurable); assertFalse(d1.enumerable); @@ -1071,22 +1432,69 @@ Derived.prototype.mSloppy = function (){ super[nonEnumConfig] = 42; - var d1 = Object.getOwnPropertyDescriptor(this, 'nonEnumConfig'); + var d1 = Object.getOwnPropertyDescriptor(this, nonEnumConfig); assertEquals(42, d1.value); assertTrue(d1.configurable); assertFalse(d1.enumerable); super[nonEnumNonConfig] = 42; - var d1 = Object.getOwnPropertyDescriptor(this, 'nonEnumNonConfig'); + var d1 = Object.getOwnPropertyDescriptor(this, nonEnumNonConfig); assertEquals(42, d1.value); assertFalse(d1.configurable); assertFalse(d1.enumerable); }.toMethod(Derived.prototype); var d = new Derived(); - Object.defineProperty(d, 'nonEnumConfig', + Object.defineProperty(d, nonEnumConfig, { value : 0, enumerable : false, configurable : true, writable : true }); - Object.defineProperty(d, 'nonEnumNonConfig', + Object.defineProperty(d, nonEnumNonConfig, + { value : 0, enumerable : false, configurable : false, writable : true }); + d.mStrict(); + d.mSloppy(); +}()); + + +(function TestKeyedNumericSetterDoesNotReconfigure() { + var nonEnumConfig = 42; + var nonEnumNonConfig = 43; + function Base() {} + function Derived() {} + + Derived.prototype = { __proto__: Base.prototype }; + + Derived.prototype.mStrict = function (){ + 'use strict'; + super[nonEnumConfig] = 5; + var d1 = Object.getOwnPropertyDescriptor(this, nonEnumConfig); + assertEquals(5, d1.value); + assertTrue(d1.configurable); + assertFalse(d1.enumerable); + + super[nonEnumNonConfig] = 5; + var d1 = Object.getOwnPropertyDescriptor(this, nonEnumNonConfig); + assertEquals(5, d1.value); + assertFalse(d1.configurable); + assertFalse(d1.enumerable); + }.toMethod(Derived.prototype); + + Derived.prototype.mSloppy = function (){ + super[nonEnumConfig] = 42; + var d1 = Object.getOwnPropertyDescriptor(this, nonEnumConfig); + assertEquals(42, d1.value); + assertTrue(d1.configurable); + assertFalse(d1.enumerable); + + super[nonEnumNonConfig] = 42; + var d1 = Object.getOwnPropertyDescriptor(this, nonEnumNonConfig); + assertEquals(42, d1.value); + assertFalse(d1.configurable); + assertFalse(d1.enumerable); + }.toMethod(Derived.prototype); + + var d = new Derived(); + Object.defineProperty(d, nonEnumConfig, + { value : 0, enumerable : false, configurable : true, writable : true }); + Object.defineProperty(d, nonEnumNonConfig, { value : 0, enumerable : false, configurable : false, writable : true }); d.mStrict(); d.mSloppy(); @@ -1190,6 +1598,143 @@ }()); +(function TestKeyedNumericCountOperations() { + var x = 42; + function Base() {} + Base.prototype = { + constructor: Base, + _x: 1 + }; + + Object.defineProperty(Base.prototype, x, { + get: function() { return this._x; }, + set: function(v) { this._x = v;; } + }); + + function Derived() {} + Derived.__proto__ = Base; + Derived.prototype = { + __proto__: Base.prototype, + constructor: Derived, + _x: 2 + }; + + Derived.prototype.testCounts = function() { + assertEquals(2, this._x); + assertEquals(2, super[x]); + super[x]++; + assertEquals(3, super[x]); + ++super[x]; + assertEquals(4, super[x]); + assertEquals(4, super[x]++); + assertEquals(5, super[x]); + assertEquals(6, ++super[x]); + assertEquals(6, super[x]); + assertEquals(6, this._x); + + super[x]--; + assertEquals(5, super[x]); + --super[x]; + assertEquals(4, super[x]); + assertEquals(4, super[x]--); + assertEquals(3, super[x]); + assertEquals(2, --super[x]); + assertEquals(2, super[x]); + assertEquals(2, this._x); + }.toMethod(Derived.prototype); + new Derived().testCounts(); +}()); + + +(function TestSetterSuperNonWritable() { + function Base() {} + Object.defineProperty(Base.prototype, 'x', { value : 27, writable: false }); + function Derived() {} + + Derived.prototype = { __proto__: Base.prototype, constructor: Derived }; + + Derived.prototype.mSloppy = function() { + assertEquals(27, super.x); + assertEquals(27, this.x); + super.x = 10; + assertEquals(27, super.x); + assertEquals(27, this.x); + }.toMethod(Derived.prototype); + Derived.prototype.mStrict = function() { + 'use strict'; + assertEquals(27, super.x); + assertEquals(27, this.x); + var ex = null; + try { super.x = 10; } catch(e) { ex = e; } + assertTrue(ex instanceof TypeError); + assertEquals(27, super.x); + assertEquals(27, this.x); + }.toMethod(Derived.prototype); + new Derived().mSloppy(); + new Derived().mStrict(); +}()); + + +(function TestSetterKeyedSuperNonWritable() { + var x = 'xyz'; + function Base() {} + Object.defineProperty(Base.prototype, x, { value : 27, writable: false }); + function Derived() {} + + Derived.prototype = { __proto__: Base.prototype, constructor: Derived }; + + Derived.prototype.mSloppy = function() { + assertEquals(27, super[x]); + assertEquals(27, this[x]); + super[x] = 10; + assertEquals(27, super[x]); + assertEquals(27, this[x]); + }.toMethod(Derived.prototype); + Derived.prototype.mStrict = function() { + 'use strict'; + assertEquals(27, super[x]); + assertEquals(27, this[x]); + var ex = null; + try { super[x] = 10; } catch(e) { ex = e; } + assertTrue(ex instanceof TypeError); + assertEquals(27, super[x]); + assertEquals(27, this[x]); + }.toMethod(Derived.prototype); + new Derived().mSloppy(); + new Derived().mStrict(); +}()); + + +(function TestSetterKeyedNumericSuperNonWritable() { + var x = 42; + function Base() {} + Object.defineProperty(Base.prototype, x, { value : 27, writable: false }); + function Derived() {} + + Derived.prototype = { __proto__: Base.prototype, constructor: Derived }; + + Derived.prototype.mSloppy = function() { + assertEquals(27, super[x]); + assertEquals(27, this[x]); + super[x] = 10; + assertEquals(27, super[x]); + assertEquals(27, this[x]); + }.toMethod(Derived.prototype); + Derived.prototype.mStrict = function() { + 'use strict'; + assertEquals(27, super[x]); + assertEquals(27, this[x]); + var ex = null; + try { super[x] = 10; } catch(e) { ex = e; } + assertTrue(ex instanceof TypeError); + assertEquals(27, super[x]); + assertEquals(27, this[x]); + }.toMethod(Derived.prototype); + new Derived().mSloppy(); + new Derived().mStrict(); +}()); + + (function TestSuperCall() { function Subclass(base, constructor) { var homeObject = { @@ -1284,10 +1829,3 @@ // Filed https://bugs.ecmascript.org/show_bug.cgi?id=3282 assertThrows(function() { new T(); }, TypeError); }()); - - -(function TestUnsupportedCases() { - function f(x) { super[x] = 5; } - var o = {}; - assertThrows(function(){f.toMethod(o)(15);}, ReferenceError); -}()); -- 2.7.4