force-set
authorchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 21 Apr 2009 13:28:11 +0000 (13:28 +0000)
committerchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 21 Apr 2009 13:28:11 +0000 (13:28 +0000)
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1751 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8.h
src/api.cc
src/handles.cc
src/handles.h
src/objects.cc
src/runtime.cc
src/runtime.h
test/cctest/test-api.cc

index 892507b4f69f73b9fbcddd4ab8fae2dd527c7439..ec10c0555f4995cf2bb47f2a1406f805e7f3b130 100644 (file)
@@ -1045,6 +1045,9 @@ class V8EXPORT Object : public Value {
   bool Set(Handle<Value> key,
            Handle<Value> value,
            PropertyAttribute attribs = None);
+  bool ForceSet(Handle<Value> key,
+                Handle<Value> value,
+                PropertyAttribute attribs = None);
   Local<Value> Get(Handle<Value> key);
 
   // TODO(1245389): Replace the type-specific versions of these
index 1a55830ed33fa2363157055231f3ac3848c72ead..9fd1ee0f51510d19f1fed7ae8ef110d15eb8d5af 100644 (file)
@@ -1835,6 +1835,26 @@ bool v8::Object::Set(v8::Handle<Value> key, v8::Handle<Value> value,
 }
 
 
+bool v8::Object::ForceSet(v8::Handle<Value> key,
+                          v8::Handle<Value> value,
+                          v8::PropertyAttribute attribs) {
+  ON_BAILOUT("v8::Object::ForceSet()", return false);
+  ENTER_V8;
+  i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+  i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+  i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+  EXCEPTION_PREAMBLE();
+  i::Handle<i::Object> obj = i::ForceSetProperty(
+      self,
+      key_obj,
+      value_obj,
+      static_cast<PropertyAttributes>(attribs));
+  has_pending_exception = obj.is_null();
+  EXCEPTION_BAILOUT_CHECK(false);
+  return true;
+}
+
+
 Local<Value> v8::Object::Get(v8::Handle<Value> key) {
   ON_BAILOUT("v8::Object::Get()", return Local<v8::Value>());
   ENTER_V8;
index b023201f4f781aaec9cc31940acc9103f5467fd0..99161ce7338c90dd5e6673428773f40b7e81d976 100644 (file)
@@ -212,6 +212,15 @@ Handle<Object> SetProperty(Handle<Object> object,
 }
 
 
+Handle<Object> ForceSetProperty(Handle<JSObject> object,
+                                Handle<Object> key,
+                                Handle<Object> value,
+                                PropertyAttributes attributes) {
+  CALL_HEAP_FUNCTION(
+      Runtime::ForceSetObjectProperty(object, key, value, attributes), Object);
+}
+
+
 Handle<Object> IgnoreAttributesAndSetLocalProperty(
     Handle<JSObject> object,
     Handle<String> key,
index c8e534eecdf56dba43a914f51da4d28458911d2c..9cc1db44c54b5929f7ae976ffe5bd162b7e1df9c 100644 (file)
@@ -196,6 +196,11 @@ Handle<Object> SetProperty(Handle<Object> object,
                            Handle<Object> value,
                            PropertyAttributes attributes);
 
+Handle<Object> ForceSetProperty(Handle<JSObject> object,
+                                Handle<Object> key,
+                                Handle<Object> value,
+                                PropertyAttributes attributes);
+
 Handle<Object> IgnoreAttributesAndSetLocalProperty(Handle<JSObject> object,
                                                    Handle<String> key,
                                                    Handle<Object> value,
index 82f93cc63fb0abeb390c11ae19d7476939504767..4b05ef31d0a6acacfd153e6a62904923471118bd 100644 (file)
@@ -1783,6 +1783,17 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
     && !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);
@@ -1803,20 +1814,15 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
         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.
index 309700ec6569c627d72bdc8b070a9903e05263a8..2b350a5748fcefe0d6a44a0db6fe55eed33f2e9a 100644 (file)
@@ -2688,6 +2688,59 @@ Object* Runtime::SetObjectProperty(Handle<Object> object,
 }
 
 
+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);
index 4d52b3163ee0c426cc6c667c1a9d38ee557ddf8a..6bd19f65c75a71048eec90224c1aab059d5d40e2 100644 (file)
@@ -373,6 +373,11 @@ class Runtime : public AllStatic {
                                    Handle<Object> value,
                                    PropertyAttributes attr);
 
+  static Object* ForceSetObjectProperty(Handle<JSObject> object,
+                                        Handle<Object> key,
+                                        Handle<Object> value,
+                                        PropertyAttributes attr);
+
   static Object* GetObjectProperty(Handle<Object> object, Handle<Object> key);
 
   // This function is used in FunctionNameUsing* tests.
index ccbb262a1c1c8037d9d2195b45bf25cab30aab7c..eaba62749cc86434b4f2fca622840eb2d47080cf 100644 (file)
@@ -6238,3 +6238,118 @@ TEST(ReadOnlyPropertyInGlobalProto) {
   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);
+}