Introduce an API to force the deletion of a property ignoring
authorager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 8 Jun 2009 09:46:09 +0000 (09:46 +0000)
committerager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 8 Jun 2009 09:46:09 +0000 (09:46 +0000)
interceptors and dont-delete attributes.

Minor change to the behavior of eval: throw exception when calling
eval in a context for which the global has been detached.  This
matches the behavior of both Firefox and Safari post navigation in the
browser.
Review URL: http://codereview.chromium.org/118374

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2118 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

12 files changed:
include/v8.h
src/api.cc
src/handles.cc
src/handles.h
src/ia32/macro-assembler-ia32.cc
src/objects.cc
src/objects.h
src/runtime.cc
src/runtime.h
src/v8natives.js
test/cctest/test-api.cc
test/cctest/test-heap.cc

index 3fd6fc6605194cbe9ced65b8ac5523050f234865..87ce2a20cfba585a17cba0daa03d5ccb1628b2ea 100644 (file)
@@ -1056,7 +1056,7 @@ class V8EXPORT Object : public Value {
            Handle<Value> value,
            PropertyAttribute attribs = None);
 
-  // Sets a local property on this object, bypassing interceptors and
+  // Sets a local property on this object bypassing interceptors and
   // overriding accessors or read-only properties.
   //
   // Note that if the object has an interceptor the property will be set
@@ -1067,13 +1067,21 @@ class V8EXPORT Object : public Value {
   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
   // functions with generic ones that accept a Handle<Value> key.
   bool Has(Handle<String> key);
+
   bool Delete(Handle<String> key);
+
+  // Delete a property on this object bypassing interceptors and
+  // ignoring dont-delete attributes.
+  bool ForceDelete(Handle<Value> key);
+
   bool Has(uint32_t index);
+
   bool Delete(uint32_t index);
 
   /**
index 4bdc0e863ab3010d2499cda0b4c0c978ff5ef133..7b7f290816232cb77fccd0c86f18f5aab24c1962 100644 (file)
@@ -1892,6 +1892,19 @@ bool v8::Object::ForceSet(v8::Handle<Value> key,
 }
 
 
+bool v8::Object::ForceDelete(v8::Handle<Value> key) {
+  ON_BAILOUT("v8::Object::ForceDelete()", return false);
+  ENTER_V8;
+  i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+  i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+  EXCEPTION_PREAMBLE();
+  i::Handle<i::Object> obj = i::ForceDeleteProperty(self, key_obj);
+  has_pending_exception = obj.is_null();
+  EXCEPTION_BAILOUT_CHECK(false);
+  return obj->IsTrue();
+}
+
+
 Local<Value> v8::Object::Get(v8::Handle<Value> key) {
   ON_BAILOUT("v8::Object::Get()", return Local<v8::Value>());
   ENTER_V8;
index 0b9fc0f3f23884edd9db2e79a4a6bd606c8b8b6f..44ca60221cbcd1868407250cb7fdd07aa3bea63a 100644 (file)
@@ -222,6 +222,12 @@ Handle<Object> ForceSetProperty(Handle<JSObject> object,
 }
 
 
+Handle<Object> ForceDeleteProperty(Handle<JSObject> object,
+                                   Handle<Object> key) {
+  CALL_HEAP_FUNCTION(Runtime::ForceDeleteObjectProperty(object, key), Object);
+}
+
+
 Handle<Object> IgnoreAttributesAndSetLocalProperty(
     Handle<JSObject> object,
     Handle<String> key,
@@ -231,6 +237,7 @@ Handle<Object> IgnoreAttributesAndSetLocalProperty(
       IgnoreAttributesAndSetLocalProperty(*key, *value, attributes), Object);
 }
 
+
 Handle<Object> SetPropertyWithInterceptor(Handle<JSObject> object,
                                           Handle<String> key,
                                           Handle<Object> value,
@@ -308,13 +315,15 @@ Handle<Object> GetHiddenProperties(Handle<JSObject> obj,
 
 Handle<Object> DeleteElement(Handle<JSObject> obj,
                              uint32_t index) {
-  CALL_HEAP_FUNCTION(obj->DeleteElement(index), Object);
+  CALL_HEAP_FUNCTION(obj->DeleteElement(index, JSObject::NORMAL_DELETION),
+                     Object);
 }
 
 
 Handle<Object> DeleteProperty(Handle<JSObject> obj,
                               Handle<String> prop) {
-  CALL_HEAP_FUNCTION(obj->DeleteProperty(*prop), Object);
+  CALL_HEAP_FUNCTION(obj->DeleteProperty(*prop, JSObject::NORMAL_DELETION),
+                     Object);
 }
 
 
index be795d58dc82c253207ffaf2113e56af35185418..af638b804b2d9f11723ef6e6a0b5b231cab4324d 100644 (file)
@@ -202,6 +202,9 @@ Handle<Object> ForceSetProperty(Handle<JSObject> object,
                                 Handle<Object> value,
                                 PropertyAttributes attributes);
 
+Handle<Object> ForceDeleteProperty(Handle<JSObject> object,
+                                   Handle<Object> key);
+
 Handle<Object> IgnoreAttributesAndSetLocalProperty(Handle<JSObject> object,
                                                    Handle<String> key,
                                                    Handle<Object> value,
index 41613702042143783e03d1d4dce46ba34772227c..7636c4ed80f5992745744cb8f324cbc53642ae5d 100644 (file)
@@ -561,8 +561,8 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
 
 
 void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
-                                          Register scratch,
-                                          Label* miss) {
+                                            Register scratch,
+                                            Label* miss) {
   Label same_contexts;
 
   ASSERT(!holder_reg.is(scratch));
index 013cc702319534578f7f88bb3b5f639e465134a0..0546578ab148184447a13c09dd3f2b10219212b1 100644 (file)
@@ -385,7 +385,9 @@ Object* JSObject::SetLazyProperty(LookupResult* result,
 }
 
 
-Object* JSObject::DeleteLazyProperty(LookupResult* result, String* name) {
+Object* JSObject::DeleteLazyProperty(LookupResult* result,
+                                     String* name,
+                                     DeleteMode mode) {
   HandleScope scope;
   Handle<JSObject> this_handle(this);
   Handle<String> name_handle(name);
@@ -393,7 +395,7 @@ Object* JSObject::DeleteLazyProperty(LookupResult* result, String* name) {
   LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
            &pending_exception);
   if (pending_exception) return Failure::Exception();
-  return this_handle->DeleteProperty(*name_handle);
+  return this_handle->DeleteProperty(*name_handle, mode);
 }
 
 
@@ -2120,7 +2122,7 @@ Object* JSObject::NormalizeElements() {
 }
 
 
-Object* JSObject::DeletePropertyPostInterceptor(String* name) {
+Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) {
   // Check local property, ignore interceptor.
   LookupResult result;
   LocalLookupRealNamedProperty(name, &result);
@@ -2134,7 +2136,7 @@ Object* JSObject::DeletePropertyPostInterceptor(String* name) {
   // Attempt to remove the property from the property dictionary.
   Dictionary* dictionary = property_dictionary();
   int entry = dictionary->FindStringEntry(name);
-  if (entry != -1) return dictionary->DeleteProperty(entry);
+  if (entry != -1) return dictionary->DeleteProperty(entry, mode);
   return Heap::true_value();
 }
 
@@ -2164,13 +2166,15 @@ Object* JSObject::DeletePropertyWithInterceptor(String* name) {
       return *v8::Utils::OpenHandle(*result);
     }
   }
-  Object* raw_result = this_handle->DeletePropertyPostInterceptor(*name_handle);
+  Object* raw_result =
+      this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
   RETURN_IF_SCHEDULED_EXCEPTION();
   return raw_result;
 }
 
 
-Object* JSObject::DeleteElementPostInterceptor(uint32_t index) {
+Object* JSObject::DeleteElementPostInterceptor(uint32_t index,
+                                               DeleteMode mode) {
   if (HasFastElements()) {
     uint32_t length = IsJSArray() ?
       static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
@@ -2183,7 +2187,7 @@ Object* JSObject::DeleteElementPostInterceptor(uint32_t index) {
   ASSERT(!HasFastElements());
   Dictionary* dictionary = element_dictionary();
   int entry = dictionary->FindNumberEntry(index);
-  if (entry != -1) return dictionary->DeleteProperty(entry);
+  if (entry != -1) return dictionary->DeleteProperty(entry, mode);
   return Heap::true_value();
 }
 
@@ -2214,13 +2218,14 @@ Object* JSObject::DeleteElementWithInterceptor(uint32_t index) {
     ASSERT(result->IsBoolean());
     return *v8::Utils::OpenHandle(*result);
   }
-  Object* raw_result = this_handle->DeleteElementPostInterceptor(index);
+  Object* raw_result =
+      this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
   RETURN_IF_SCHEDULED_EXCEPTION();
   return raw_result;
 }
 
 
-Object* JSObject::DeleteElement(uint32_t index) {
+Object* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
   // Check access rights if needed.
   if (IsAccessCheckNeeded() &&
       !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
@@ -2232,10 +2237,14 @@ Object* JSObject::DeleteElement(uint32_t index) {
     Object* proto = GetPrototype();
     if (proto->IsNull()) return Heap::false_value();
     ASSERT(proto->IsJSGlobalObject());
-    return JSGlobalObject::cast(proto)->DeleteElement(index);
+    return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
   }
 
   if (HasIndexedInterceptor()) {
+    // Skip interceptor if forcing deletion.
+    if (mode == FORCE_DELETION) {
+      return DeleteElementPostInterceptor(index, mode);
+    }
     return DeleteElementWithInterceptor(index);
   }
 
@@ -2250,13 +2259,13 @@ Object* JSObject::DeleteElement(uint32_t index) {
   } else {
     Dictionary* dictionary = element_dictionary();
     int entry = dictionary->FindNumberEntry(index);
-    if (entry != -1) return dictionary->DeleteProperty(entry);
+    if (entry != -1) return dictionary->DeleteProperty(entry, mode);
   }
   return Heap::true_value();
 }
 
 
-Object* JSObject::DeleteProperty(String* name) {
+Object* JSObject::DeleteProperty(String* name, DeleteMode mode) {
   // ECMA-262, 3rd, 8.6.2.5
   ASSERT(name->IsString());
 
@@ -2271,23 +2280,32 @@ Object* JSObject::DeleteProperty(String* name) {
     Object* proto = GetPrototype();
     if (proto->IsNull()) return Heap::false_value();
     ASSERT(proto->IsJSGlobalObject());
-    return JSGlobalObject::cast(proto)->DeleteProperty(name);
+    return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
   }
 
   uint32_t index = 0;
   if (name->AsArrayIndex(&index)) {
-    return DeleteElement(index);
+    return DeleteElement(index, mode);
   } else {
     LookupResult result;
     LocalLookup(name, &result);
     if (!result.IsValid()) return Heap::true_value();
-    if (result.IsDontDelete()) return Heap::false_value();
+    // Ignore attributes if forcing a deletion.
+    if (result.IsDontDelete() && mode != FORCE_DELETION) {
+      return Heap::false_value();
+    }
     // Check for interceptor.
     if (result.type() == INTERCEPTOR) {
+      // Skip interceptor if forcing a deletion.
+      if (mode == FORCE_DELETION) {
+        return DeletePropertyPostInterceptor(name, mode);
+      }
       return DeletePropertyWithInterceptor(name);
     }
     if (!result.IsLoaded()) {
-      return JSObject::cast(this)->DeleteLazyProperty(&result, name);
+      return JSObject::cast(this)->DeleteLazyProperty(&result,
+                                                      name,
+                                                      mode);
     }
     // Normalize object if needed.
     Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
@@ -2295,7 +2313,7 @@ Object* JSObject::DeleteProperty(String* name) {
     // Make sure the properties are normalized before removing the entry.
     Dictionary* dictionary = property_dictionary();
     int entry = dictionary->FindStringEntry(name);
-    if (entry != -1) return dictionary->DeleteProperty(entry);
+    if (entry != -1) return dictionary->DeleteProperty(entry, mode);
     return Heap::true_value();
   }
 }
@@ -6943,9 +6961,12 @@ void Dictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
 }
 
 
-Object* Dictionary::DeleteProperty(int entry) {
+Object* Dictionary::DeleteProperty(int entry, JSObject::DeleteMode mode) {
   PropertyDetails details = DetailsAt(entry);
-  if (details.IsDontDelete()) return Heap::false_value();
+  // Ignore attributes if forcing a deletion.
+  if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
+    return Heap::false_value();
+  }
   SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
   ElementRemoved();
   return Heap::true_value();
index 99bab3975a22a1dcb7aa94b54d03b792c3dbe2f8..8fa2d0ca4735a454646e1a023368f4fe2ecad88f 100644 (file)
@@ -1275,9 +1275,12 @@ class JSObject: public HeapObject {
     return GetLocalPropertyAttribute(name) != ABSENT;
   }
 
-  Object* DeleteProperty(String* name);
-  Object* DeleteElement(uint32_t index);
-  Object* DeleteLazyProperty(LookupResult* result, String* name);
+  enum DeleteMode { NORMAL_DELETION, FORCE_DELETION };
+  Object* DeleteProperty(String* name, DeleteMode mode);
+  Object* DeleteElement(uint32_t index, DeleteMode mode);
+  Object* DeleteLazyProperty(LookupResult* result,
+                             String* name,
+                             DeleteMode mode);
 
   // Tests for the fast common case for property enumeration.
   bool IsSimpleEnum();
@@ -1519,10 +1522,10 @@ class JSObject: public HeapObject {
 
   Object* GetElementPostInterceptor(JSObject* receiver, uint32_t index);
 
-  Object* DeletePropertyPostInterceptor(String* name);
+  Object* DeletePropertyPostInterceptor(String* name, DeleteMode mode);
   Object* DeletePropertyWithInterceptor(String* name);
 
-  Object* DeleteElementPostInterceptor(uint32_t index);
+  Object* DeleteElementPostInterceptor(uint32_t index, DeleteMode mode);
   Object* DeleteElementWithInterceptor(uint32_t index);
 
   PropertyAttributes GetPropertyAttributePostInterceptor(JSObject* receiver,
@@ -2057,7 +2060,7 @@ class Dictionary: public DictionaryBase {
   int FindNumberEntry(uint32_t index);
 
   // Delete a property from the dictionary.
-  Object* DeleteProperty(int entry);
+  Object* DeleteProperty(int entry, JSObject::DeleteMode mode);
 
   // Type specific at put (default NONE attributes is used when adding).
   Object* AtStringPut(String* key, Object* value);
@@ -2628,7 +2631,7 @@ class Map: public HeapObject {
   static const int kHasInstanceCallHandler = 6;
   static const int kIsAccessCheckNeeded = 7;
 
-  // Bit positions for but field 2
+  // Bit positions for bit field 2
   static const int kNeedsLoading = 0;
 
  private:
index 2fcdff1560b098b5ba51782ba8133e45dfd00af2..78be5129250a409143fabe98309624e29620f0c2 100644 (file)
@@ -2780,6 +2780,42 @@ Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
 }
 
 
+Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
+                                           Handle<Object> key) {
+  HandleScope scope;
+
+  // Check if the given key is an array index.
+  uint32_t index;
+  if (Array::IndexFromObject(*key, &index)) {
+    // In Firefox/SpiderMonkey, Safari and Opera you can access the
+    // characters of a string using [] notation.  In the case of a
+    // String object we just need to redirect the deletion to the
+    // underlying string if the index is in range.  Since the
+    // underlying string does nothing with the deletion, we can ignore
+    // such deletions.
+    if (js_object->IsStringObjectWithCharacterAt(index)) {
+      return Heap::true_value();
+    }
+
+    return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
+  }
+
+  Handle<String> key_string;
+  if (key->IsString()) {
+    key_string = Handle<String>::cast(key);
+  } else {
+    // 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();
+    key_string = Handle<String>::cast(converted);
+  }
+
+  key_string->TryFlattenIfNotFlat();
+  return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
+}
+
+
 static Object* Runtime_SetProperty(Arguments args) {
   NoHandleAllocation ha;
   RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
@@ -2831,7 +2867,7 @@ static Object* Runtime_DeleteProperty(Arguments args) {
 
   CONVERT_CHECKED(JSObject, object, args[0]);
   CONVERT_CHECKED(String, key, args[1]);
-  return object->DeleteProperty(key);
+  return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
 }
 
 
index 474f1b344df31bc61d251b0d85281e8693525a29..30bb7c5aec4103bb1ea516f00e19e0fcb2993458 100644 (file)
@@ -387,6 +387,9 @@ class Runtime : public AllStatic {
                                         Handle<Object> value,
                                         PropertyAttributes attr);
 
+  static Object* ForceDeleteObjectProperty(Handle<JSObject> object,
+                                           Handle<Object> key);
+
   static Object* GetObjectProperty(Handle<Object> object, Handle<Object> key);
 
   // This function is used in FunctionNameUsing* tests.
index 2edb191d215ea9fc710469a2f564b91730760e8f..fe463513d971bda62e228183ff8b7bb785548b25 100644 (file)
@@ -115,11 +115,15 @@ function GlobalParseFloat(string) {
 function GlobalEval(x) {
   if (!IS_STRING(x)) return x;
 
-  if (this !== global && this !== %GlobalReceiver(global)) {
-    throw new $EvalError('The "this" object passed to eval must ' + 
+  var global_receiver = %GlobalReceiver(global);
+  var this_is_global_receiver = (this === global_receiver);
+  var global_is_detached = (global === global_receiver);
+
+  if (!this_is_global_receiver || global_is_detached) {
+    throw new $EvalError('The "this" object passed to eval must ' +
                          'be the global object from which eval originated');
   }
-  
+
   var f = %CompileString(x, false);
   if (!IS_FUNCTION(f)) return f;
 
index b34095777705cf5f5441dff00946a1d5bd2975f4..48157d80b9b7acaeb451bee2ae053aca1e54478b 100644 (file)
@@ -4515,10 +4515,6 @@ THREADED_TEST(EvalAliasedDynamic) {
   v8::HandleScope scope;
   LocalContext current;
 
-  // This sets 'global' to the real global object (as opposed to the
-  // proxy). It is highly implementation dependent, so take care.
-  current->Global()->Set(v8_str("global"), current->Global()->GetPrototype());
-
   // Tests where aliased eval can only be resolved dynamically.
   Local<Script> script =
       Script::Compile(v8_str("function f(x) { "
@@ -4527,7 +4523,7 @@ THREADED_TEST(EvalAliasedDynamic) {
                              "}"
                              "foo = 0;"
                              "result1 = f(new Object());"
-                             "result2 = f(global);"
+                             "result2 = f(this);"
                              "var x = new Object();"
                              "x.eval = function(x) { return 1; };"
                              "result3 = f(x);"));
@@ -4542,7 +4538,7 @@ THREADED_TEST(EvalAliasedDynamic) {
                            "  var bar = 2;"
                            "  with (x) { return eval('bar'); }"
                            "}"
-                           "f(global)"));
+                           "f(this)"));
   script->Run();
   CHECK(try_catch.HasCaught());
   try_catch.Reset();
@@ -4629,6 +4625,44 @@ THREADED_TEST(CrossEval) {
 }
 
 
+// Test that calling eval in a context which has been detached from
+// its global throws an exception.  This behavior is consistent with
+// other JavaScript implementations.
+THREADED_TEST(EvalInDetachedGlobal) {
+  v8::HandleScope scope;
+
+  v8::Persistent<Context> context0 = Context::New();
+  v8::Persistent<Context> context1 = Context::New();
+
+  // Setup function in context0 that uses eval from context0.
+  context0->Enter();
+  v8::Handle<v8::Value> fun =
+      CompileRun("var x = 42;"
+                 "(function() {"
+                 "  var e = eval;"
+                 "  return function(s) { return e(s); }"
+                 "})()");
+  context0->Exit();
+
+  // Put the function into context1 and call it before and after
+  // detaching the global.  Before detaching, the call succeeds and
+  // after detaching and exception is thrown.
+  context1->Enter();
+  context1->Global()->Set(v8_str("fun"), fun);
+  v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
+  CHECK_EQ(42, x_value->Int32Value());
+  context0->DetachGlobal();
+  v8::TryCatch catcher;
+  x_value = CompileRun("fun('x')");
+  CHECK(x_value.IsEmpty());
+  CHECK(catcher.HasCaught());
+  context1->Exit();
+
+  context1.Dispose();
+  context0.Dispose();
+}
+
+
 THREADED_TEST(CrossLazyLoad) {
   v8::HandleScope scope;
   LocalContext other;
@@ -6723,6 +6757,74 @@ TEST(ForceSetWithInterceptor) {
 }
 
 
+THREADED_TEST(ForceDelete) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
+  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::DontDelete);
+  CHECK_EQ(4, global->Get(simple_property)->Int32Value());
+  // This should fail because the property is dont-delete.
+  CHECK(!global->Delete(simple_property));
+  CHECK_EQ(4, global->Get(simple_property)->Int32Value());
+  // This should succeed even though the property is dont-delete.
+  CHECK(global->ForceDelete(simple_property));
+  CHECK(global->Get(simple_property)->IsUndefined());
+}
+
+
+static int force_delete_interceptor_count = 0;
+static bool pass_on_delete = false;
+
+
+static v8::Handle<v8::Boolean> ForceDeleteDeleter(
+    v8::Local<v8::String> name,
+    const v8::AccessorInfo& info) {
+  force_delete_interceptor_count++;
+  if (pass_on_delete) {
+    return v8::Handle<v8::Boolean>();
+  } else {
+    return v8::True();
+  }
+}
+
+
+THREADED_TEST(ForceDeleteWithInterceptor) {
+  force_delete_interceptor_count = 0;
+  pass_on_delete = false;
+
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
+  templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
+  LocalContext context(NULL, templ);
+  v8::Handle<v8::Object> global = context->Global();
+
+  v8::Handle<v8::String> some_property = v8::String::New("a");
+  global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
+
+  // Deleting a property should get intercepted and nothing should
+  // happen.
+  CHECK_EQ(0, force_delete_interceptor_count);
+  CHECK(global->Delete(some_property));
+  CHECK_EQ(1, force_delete_interceptor_count);
+  CHECK_EQ(42, global->Get(some_property)->Int32Value());
+  // Deleting the property when the interceptor returns an empty
+  // handle should not delete the property since it is DontDelete.
+  pass_on_delete = true;
+  CHECK(!global->Delete(some_property));
+  CHECK_EQ(2, force_delete_interceptor_count);
+  CHECK_EQ(42, global->Get(some_property)->Int32Value());
+  // Forcing the property to be deleted should delete the value
+  // without calling the interceptor.
+  CHECK(global->ForceDelete(some_property));
+  CHECK(global->Get(some_property)->IsUndefined());
+  CHECK_EQ(2, force_delete_interceptor_count);
+}
+
+
 v8::Persistent<Context> calling_context0;
 v8::Persistent<Context> calling_context1;
 v8::Persistent<Context> calling_context2;
index 452b31f0033224da0174667334f34a8e3316f323..515657f7127d4513c8b11ea931eccdf4114d5356 100644 (file)
@@ -553,7 +553,7 @@ TEST(ObjectProperties) {
   CHECK(obj->HasLocalProperty(first));
 
   // delete first
-  CHECK(obj->DeleteProperty(first));
+  CHECK(obj->DeleteProperty(first, JSObject::NORMAL_DELETION));
   CHECK(!obj->HasLocalProperty(first));
 
   // add first and then second
@@ -563,9 +563,9 @@ TEST(ObjectProperties) {
   CHECK(obj->HasLocalProperty(second));
 
   // delete first and then second
-  CHECK(obj->DeleteProperty(first));
+  CHECK(obj->DeleteProperty(first, JSObject::NORMAL_DELETION));
   CHECK(obj->HasLocalProperty(second));
-  CHECK(obj->DeleteProperty(second));
+  CHECK(obj->DeleteProperty(second, JSObject::NORMAL_DELETION));
   CHECK(!obj->HasLocalProperty(first));
   CHECK(!obj->HasLocalProperty(second));
 
@@ -576,9 +576,9 @@ TEST(ObjectProperties) {
   CHECK(obj->HasLocalProperty(second));
 
   // delete second and then first
-  CHECK(obj->DeleteProperty(second));
+  CHECK(obj->DeleteProperty(second, JSObject::NORMAL_DELETION));
   CHECK(obj->HasLocalProperty(first));
-  CHECK(obj->DeleteProperty(first));
+  CHECK(obj->DeleteProperty(first, JSObject::NORMAL_DELETION));
   CHECK(!obj->HasLocalProperty(first));
   CHECK(!obj->HasLocalProperty(second));