Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
+ Handle<String> name,
Handle<JSObject> receiver,
- Handle<JSFunction> setter,
- Handle<String> name) {
+ Handle<JSObject> holder,
+ Handle<JSFunction> setter) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : receiver
// -----------------------------------
Label miss;
- // Check that the map of the object hasn't changed.
- __ CheckMap(r1, r3, Handle<Map>(receiver->map()), &miss, DO_SMI_CHECK,
- ALLOW_ELEMENT_TRANSITION_MAPS);
+ // Check that the maps haven't changed.
+ __ JumpIfSmi(r1, &miss);
+ CheckPrototypes(receiver, r1, holder, r3, r4, r5, name, &miss);
{
FrameScope scope(masm(), StackFrame::INTERNAL);
// Save value register, so we can restore it later.
__ push(r0);
- // Call the JavaScript getter with the receiver and the value on the stack.
+ // Call the JavaScript setter with the receiver and the value on the stack.
__ Push(r1, r0);
ParameterCount actual(1);
__ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
+ Handle<String> name,
Handle<JSObject> receiver,
- Handle<JSFunction> setter,
- Handle<String> name) {
+ Handle<JSObject> holder,
+ Handle<JSFunction> setter) {
// ----------- S t a t e -------------
// -- eax : value
// -- ecx : name
// -----------------------------------
Label miss;
- // Check that the map of the object hasn't changed.
- __ CheckMap(edx, Handle<Map>(receiver->map()), &miss, DO_SMI_CHECK,
- ALLOW_ELEMENT_TRANSITION_MAPS);
+ // Check that the maps haven't changed, preserving the name register.
+ __ push(ecx);
+ __ JumpIfSmi(edx, &miss);
+ CheckPrototypes(receiver, edx, holder, ebx, ecx, edi, name, &miss);
+ __ pop(ecx);
{
FrameScope scope(masm(), StackFrame::INTERNAL);
// Save value register, so we can restore it later.
__ push(eax);
- // Call the JavaScript getter with the receiver and the value on the stack.
+ // Call the JavaScript setter with the receiver and the value on the stack.
__ push(edx);
__ push(eax);
ParameterCount actual(1);
__ ret(0);
__ bind(&miss);
+ __ pop(ecx);
Handle<Code> ic = isolate()->builtins()->StoreIC_Miss();
__ jmp(ic, RelocInfo::CODE_TARGET);
LookupResult* lookup) {
receiver->LocalLookup(*name, lookup);
if (!StoreICableLookup(lookup)) {
- return false;
+ // 2nd chance: There can be accessors somewhere in the prototype chain, but
+ // for compatibility reasons we have to hide this behind a flag. Note that
+ // we explicitly exclude native accessors for now, because the stubs are not
+ // yet prepared for this scenario.
+ if (!FLAG_es5_readonly) return false;
+ receiver->Lookup(*name, lookup);
+ if (!lookup->IsCallbacks()) return false;
+ Handle<Object> callback(lookup->GetCallbackObject());
+ return callback->IsAccessorPair() && StoreICableLookup(lookup);
}
if (lookup->IsInterceptor() &&
if (holder->IsGlobalObject()) return;
if (!receiver->HasFastProperties()) return;
code = isolate()->stub_cache()->ComputeStoreViaSetter(
- name, receiver, Handle<JSFunction>::cast(setter), strict_mode);
+ name, receiver, holder, Handle<JSFunction>::cast(setter),
+ strict_mode);
} else {
ASSERT(callback->IsForeign());
// No IC support for old-style native accessors.
Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
+ Handle<String> name,
Handle<JSObject> receiver,
- Handle<JSFunction> setter,
- Handle<String> name) {
+ Handle<JSObject> holder,
+ Handle<JSFunction> setter) {
// ----------- S t a t e -------------
// -- a0 : value
// -- a1 : receiver
// -----------------------------------
Label miss;
- // Check that the map of the object hasn't changed.
- __ CheckMap(a1, a3, Handle<Map>(receiver->map()), &miss, DO_SMI_CHECK,
- ALLOW_ELEMENT_TRANSITION_MAPS);
+ // Check that the maps haven't changed.
+ __ JumpIfSmi(a1, &miss);
+ CheckPrototypes(receiver, a1, holder, a3, t0, t1, name, &miss);
{
FrameScope scope(masm(), StackFrame::INTERNAL);
// Save value register, so we can restore it later.
__ push(a0);
- // Call the JavaScript getter with the receiver and the value on the stack.
+ // Call the JavaScript setter with the receiver and the value on the stack.
__ push(a1);
__ push(a0);
ParameterCount actual(1);
Handle<Code> StubCache::ComputeStoreViaSetter(Handle<String> name,
Handle<JSObject> receiver,
+ Handle<JSObject> holder,
Handle<JSFunction> setter,
StrictModeFlag strict_mode) {
Code::Flags flags = Code::ComputeMonomorphicFlags(
if (probe->IsCode()) return Handle<Code>::cast(probe);
StoreStubCompiler compiler(isolate_, strict_mode);
- Handle<Code> code = compiler.CompileStoreViaSetter(receiver, setter, name);
+ Handle<Code> code =
+ compiler.CompileStoreViaSetter(name, receiver, holder, setter);
PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name));
GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code));
JSObject::UpdateMapCodeCache(receiver, name, code);
Handle<Code> ComputeStoreViaSetter(Handle<String> name,
Handle<JSObject> receiver,
+ Handle<JSObject> holder,
Handle<JSFunction> setter,
StrictModeFlag strict_mode);
Handle<AccessorInfo> callback,
Handle<String> name);
- Handle<Code> CompileStoreViaSetter(Handle<JSObject> receiver,
- Handle<JSFunction> setter,
- Handle<String> name);
+ Handle<Code> CompileStoreViaSetter(Handle<String> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ Handle<JSFunction> setter);
Handle<Code> CompileStoreInterceptor(Handle<JSObject> object,
Handle<String> name);
Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
+ Handle<String> name,
Handle<JSObject> receiver,
- Handle<JSFunction> setter,
- Handle<String> name) {
+ Handle<JSObject> holder,
+ Handle<JSFunction> setter) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : name
// -----------------------------------
Label miss;
- // Check that the map of the object hasn't changed.
- __ CheckMap(rdx, Handle<Map>(receiver->map()), &miss, DO_SMI_CHECK,
- ALLOW_ELEMENT_TRANSITION_MAPS);
+ // Check that the maps haven't changed.
+ __ JumpIfSmi(rdx, &miss);
+ CheckPrototypes(receiver, rdx, holder, rbx, r8, rdi, name, &miss);
{
FrameScope scope(masm(), StackFrame::INTERNAL);
// Save value register, so we can restore it later.
__ push(rax);
- // Call the JavaScript getter with the receiver and the value on the stack.
+ // Call the JavaScript setter with the receiver and the value on the stack.
__ push(rdx);
__ push(rax);
ParameterCount actual(1);
function setter(x) { print(222); }
function anotherGetter() { return 333; }
function anotherSetter(x) { print(444); }
-var obj1, obj2;
+var obj1, obj2, obj3, obj4;
// Two objects with the same getter.
obj1 = {};
assertEquals(setter, gop(obj1, "papa").set);
assertTrue(gop(obj1, "papa").configurable);
assertFalse(gop(obj1, "papa").enumerable);
+
+// Two objects with the same getter on the prototype chain.
+obj1 = {};
+dp(obj1, "quebec", { get: getter });
+obj2 = Object.create(obj1);
+obj3 = Object.create(obj2);
+obj4 = Object.create(obj2);
+assertTrue(%HaveSameMap(obj3, obj4));
+
+// Two objects with the same setter on the prototype chain.
+obj1 = {};
+dp(obj1, "romeo", { set: setter });
+obj2 = Object.create(obj1);
+obj3 = Object.create(obj2);
+obj4 = Object.create(obj2);
+assertTrue(%HaveSameMap(obj3, obj4));
// Tests the object.defineProperty method - ES 15.2.3.6
-// Flags: --allow-natives-syntax
+// Flags: --allow-natives-syntax --es5-readonly
// Check that an exception is thrown when null is passed as object.
var exception = false;
var objectWithSetter = {};
objectWithSetter.__defineSetter__('foo', function(x) {});
assertEquals(undefined, objectWithSetter.__lookupGetter__('foo'));
+
+// An object with a getter on the prototype chain.
+function getter() { return 111; }
+function anotherGetter() { return 222; }
+
+function testGetterOnProto(expected, o) {
+ assertEquals(expected, o.quebec);
+}
+
+obj1 = {};
+Object.defineProperty(obj1, "quebec", { get: getter, configurable: true });
+obj2 = Object.create(obj1);
+obj3 = Object.create(obj2);
+
+testGetterOnProto(111, obj3);
+testGetterOnProto(111, obj3);
+%OptimizeFunctionOnNextCall(testGetterOnProto);
+testGetterOnProto(111, obj3);
+testGetterOnProto(111, obj3);
+
+Object.defineProperty(obj1, "quebec", { get: anotherGetter });
+
+testGetterOnProto(222, obj3);
+testGetterOnProto(222, obj3);
+%OptimizeFunctionOnNextCall(testGetterOnProto);
+testGetterOnProto(222, obj3);
+testGetterOnProto(222, obj3);
+
+// An object with a setter on the prototype chain.
+var modifyMe;
+function setter(x) { modifyMe = x+1; }
+function anotherSetter(x) { modifyMe = x+2; }
+
+function testSetterOnProto(expected, o) {
+ modifyMe = 333;
+ o.romeo = 444;
+ assertEquals(expected, modifyMe);
+}
+
+obj1 = {};
+Object.defineProperty(obj1, "romeo", { set: setter, configurable: true });
+obj2 = Object.create(obj1);
+obj3 = Object.create(obj2);
+
+testSetterOnProto(445, obj3);
+testSetterOnProto(445, obj3);
+%OptimizeFunctionOnNextCall(testSetterOnProto);
+testSetterOnProto(445, obj3);
+testSetterOnProto(445, obj3);
+
+Object.defineProperty(obj1, "romeo", { set: anotherSetter });
+
+testSetterOnProto(446, obj3);
+testSetterOnProto(446, obj3);
+%OptimizeFunctionOnNextCall(testSetterOnProto);
+testSetterOnProto(446, obj3);
+testSetterOnProto(446, obj3);
+
+// Removing a setter on the prototype chain.
+function testSetterOnProtoStrict(o) {
+ "use strict";
+ o.sierra = 12345;
+}
+
+obj1 = {};
+Object.defineProperty(obj1, "sierra",
+ { get: getter, set: setter, configurable: true });
+obj2 = Object.create(obj1);
+obj3 = Object.create(obj2);
+
+testSetterOnProtoStrict(obj3);
+testSetterOnProtoStrict(obj3);
+%OptimizeFunctionOnNextCall(testSetterOnProtoStrict);
+testSetterOnProtoStrict(obj3);
+testSetterOnProtoStrict(obj3);
+
+Object.defineProperty(obj1, "sierra",
+ { get: getter, set: undefined, configurable: true });
+
+exception = false;
+try {
+ testSetterOnProtoStrict(obj3);
+} catch (e) {
+ exception = true;
+ assertTrue(/which has only a getter/.test(e));
+}
+assertTrue(exception);