Introduce an API to force the deletion of a property ignoring
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
This commit is contained in:
parent
f04220d25a
commit
24d22b567a
10
include/v8.h
10
include/v8.h
@ -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);
|
||||
|
||||
/**
|
||||
|
13
src/api.cc
13
src/api.cc
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user