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
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);
/**
}
+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;
}
+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,
IgnoreAttributesAndSetLocalProperty(*key, *value, attributes), Object);
}
+
Handle<Object> SetPropertyWithInterceptor(Handle<JSObject> object,
Handle<String> key,
Handle<Object> value,
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);
}
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,
void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
- Register scratch,
- Label* miss) {
+ Register scratch,
+ Label* miss) {
Label same_contexts;
ASSERT(!holder_reg.is(scratch));
}
-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);
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);
}
}
-Object* JSObject::DeletePropertyPostInterceptor(String* name) {
+Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) {
// Check local property, ignore interceptor.
LookupResult result;
LocalLookupRealNamedProperty(name, &result);
// 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();
}
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()) :
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();
}
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)) {
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);
}
} 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());
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);
// 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();
}
}
}
-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();
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();
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,
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);
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:
}
+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);
CONVERT_CHECKED(JSObject, object, args[0]);
CONVERT_CHECKED(String, key, args[1]);
- return object->DeleteProperty(key);
+ return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
}
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.
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;
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) { "
"}"
"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);"));
" var bar = 2;"
" with (x) { return eval('bar'); }"
"}"
- "f(global)"));
+ "f(this)"));
script->Run();
CHECK(try_catch.HasCaught());
try_catch.Reset();
}
+// 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;
}
+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;
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
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));
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));