&& !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(result, name, value);
}
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return value;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
+ name,
+ value,
+ attributes);
+ }
+
// Check for accessor in prototype chain removed here in clone.
if (result->IsNotFound()) {
return AddProperty(name, value, attributes);
return AddFastPropertyUsingMap(result->GetTransitionMap(),
name,
value);
- } else {
- return ConvertDescriptorToField(name, value, attributes);
}
+ return ConvertDescriptorToField(name, value, attributes);
case CONSTANT_FUNCTION:
if (value == result->GetConstantFunction()) return value;
// Only replace the function if necessary.
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
- case CALLBACKS:
- return SetPropertyWithCallback(result->GetCallbackObject(),
- name,
- value,
- result->holder());
- case INTERCEPTOR:
- return SetPropertyWithInterceptor(name, value, attributes);
+ case CALLBACKS: case INTERCEPTOR:
+ // Override callback in clone
+ return ConvertDescriptorToField(name, value, attributes);
case CONSTANT_TRANSITION:
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
// if the value is a function.
}
+Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attr) {
+ HandleScope scope;
+
+ // Check if the given key is an array index.
+ uint32_t index;
+ if (Array::IndexFromObject(*key, &index)) {
+ ASSERT(attr == NONE);
+
+ // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
+ // of a string using [] notation. We need to support this too in
+ // JavaScript.
+ // In the case of a String object we just need to redirect the assignment to
+ // the underlying string if the index is in range. Since the underlying
+ // string does nothing with the assignment then we can ignore such
+ // assignments.
+ if (js_object->IsStringObjectWithCharacterAt(index)) {
+ return *value;
+ }
+
+ return js_object->SetElement(index, *value);
+ }
+
+ if (key->IsString()) {
+ if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
+ ASSERT(attr == NONE);
+ return js_object->SetElement(index, *value);
+ } else {
+ Handle<String> key_string = Handle<String>::cast(key);
+ key_string->TryFlattenIfNotFlat();
+ return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
+ *value,
+ attr);
+ }
+ }
+
+ // Call-back into JavaScript to convert the key to a string.
+ bool has_pending_exception = false;
+ Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
+ if (has_pending_exception) return Failure::Exception();
+ Handle<String> name = Handle<String>::cast(converted);
+
+ if (name->AsArrayIndex(&index)) {
+ ASSERT(attr == NONE);
+ return js_object->SetElement(index, *value);
+ } else {
+ return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
+ }
+}
+
+
static Object* Runtime_SetProperty(Arguments args) {
NoHandleAllocation ha;
RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
CHECK_EQ(v8::Integer::New(42), res);
}
+
+static int force_set_set_count = 0;
+static int force_set_get_count = 0;
+bool pass_on_get = false;
+
+static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
+ const v8::AccessorInfo& info) {
+ force_set_get_count++;
+ if (pass_on_get) {
+ return v8::Handle<v8::Value>();
+ } else {
+ return v8::Int32::New(3);
+ }
+}
+
+static void ForceSetSetter(v8::Local<v8::String> name,
+ v8::Local<v8::Value> value,
+ const v8::AccessorInfo& info) {
+ force_set_set_count++;
+}
+
+static v8::Handle<v8::Value> ForceSetInterceptSetter(
+ v8::Local<v8::String> name,
+ v8::Local<v8::Value> value,
+ const v8::AccessorInfo& info) {
+ force_set_set_count++;
+ return v8::Undefined();
+}
+
+TEST(ForceSet) {
+ force_set_get_count = 0;
+ force_set_set_count = 0;
+ pass_on_get = false;
+
+ v8::HandleScope scope;
+ v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
+ v8::Handle<v8::String> access_property = v8::String::New("a");
+ templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
+ LocalContext context(NULL, templ);
+ v8::Handle<v8::Object> global = context->Global();
+
+ // Ordinary properties
+ v8::Handle<v8::String> simple_property = v8::String::New("p");
+ global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
+ CHECK_EQ(4, global->Get(simple_property)->Int32Value());
+ // This should fail because the property is read-only
+ global->Set(simple_property, v8::Int32::New(5));
+ CHECK_EQ(4, global->Get(simple_property)->Int32Value());
+ // This should succeed even though the property is read-only
+ global->ForceSet(simple_property, v8::Int32::New(6));
+ CHECK_EQ(6, global->Get(simple_property)->Int32Value());
+
+ // Accessors
+ CHECK_EQ(0, force_set_set_count);
+ CHECK_EQ(0, force_set_get_count);
+ CHECK_EQ(3, global->Get(access_property)->Int32Value());
+ // CHECK_EQ the property shouldn't override it, just call the setter
+ // which in this case does nothing.
+ global->Set(access_property, v8::Int32::New(7));
+ CHECK_EQ(3, global->Get(access_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(2, force_set_get_count);
+ // Forcing the property to be set should override the accessor without
+ // calling it
+ global->ForceSet(access_property, v8::Int32::New(8));
+ CHECK_EQ(8, global->Get(access_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(2, force_set_get_count);
+}
+
+TEST(ForceSetWithInterceptor) {
+ force_set_get_count = 0;
+ force_set_set_count = 0;
+ pass_on_get = false;
+
+ v8::HandleScope scope;
+ v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
+ templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
+ LocalContext context(NULL, templ);
+ v8::Handle<v8::Object> global = context->Global();
+
+ v8::Handle<v8::String> some_property = v8::String::New("a");
+ CHECK_EQ(0, force_set_set_count);
+ CHECK_EQ(0, force_set_get_count);
+ CHECK_EQ(3, global->Get(some_property)->Int32Value());
+ // Setting the property shouldn't override it, just call the setter
+ // which in this case does nothing.
+ global->Set(some_property, v8::Int32::New(7));
+ CHECK_EQ(3, global->Get(some_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(2, force_set_get_count);
+ // Getting the property when the interceptor returns an empty handle
+ // should yield undefined, since the property isn't present on the
+ // object itself yet.
+ pass_on_get = true;
+ CHECK(global->Get(some_property)->IsUndefined());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(3, force_set_get_count);
+ // Forcing the property to be set should cause the value to be
+ // set locally without calling the interceptor.
+ global->ForceSet(some_property, v8::Int32::New(8));
+ CHECK_EQ(8, global->Get(some_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(4, force_set_get_count);
+ // Reenabling the interceptor should cause it to take precedence over
+ // the property
+ pass_on_get = false;
+ CHECK_EQ(3, global->Get(some_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(5, force_set_get_count);
+ // The interceptor should also work for other properties
+ CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(6, force_set_get_count);
+}