Implement delete trap for proxies.

R=ager@chromium.org
BUG=1543
TEST=

Review URL: http://codereview.chromium.org/7369001

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8660 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rossberg@chromium.org 2011-07-15 09:10:20 +00:00
parent 5f1a8dc71f
commit 75a2c49c1d
5 changed files with 132 additions and 34 deletions

View File

@ -189,7 +189,7 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
AccessorInfo* data = AccessorInfo::cast(structure);
Object* fun_obj = data->getter();
v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
HandleScope scope;
HandleScope scope(isolate);
JSObject* self = JSObject::cast(receiver);
JSObject* holder_handle = JSObject::cast(holder);
Handle<String> key(name);
@ -229,7 +229,7 @@ MaybeObject* Object::GetPropertyWithHandler(Object* receiver_raw,
String* name_raw,
Object* handler_raw) {
Isolate* isolate = name_raw->GetIsolate();
HandleScope scope;
HandleScope scope(isolate);
Handle<Object> receiver(receiver_raw);
Handle<Object> name(name_raw);
Handle<Object> handler(handler_raw);
@ -2173,9 +2173,9 @@ MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(
}
}
HandleScope scope;
Handle<Object> value_handle(value);
Heap* heap = GetHeap();
HandleScope scope(heap->isolate());
Handle<Object> value_handle(value);
heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
return *value_handle;
}
@ -2202,7 +2202,7 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler(
PropertyAttributes attributes,
StrictModeFlag strict_mode) {
Isolate* isolate = GetIsolate();
HandleScope scope;
HandleScope scope(isolate);
Handle<Object> receiver(this);
Handle<Object> name(name_raw);
Handle<Object> value(value_raw);
@ -2228,12 +2228,50 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler(
}
MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
String* name_raw, DeleteMode mode) {
Isolate* isolate = GetIsolate();
HandleScope scope(isolate);
Handle<Object> receiver(this);
Handle<Object> name(name_raw);
Handle<Object> handler(this->handler());
// Extract trap function.
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete");
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
if (trap->IsUndefined()) {
Handle<Object> args[] = { handler, trap_name };
Handle<Object> error = isolate->factory()->NewTypeError(
"handler_trap_missing", HandleVector(args, ARRAY_SIZE(args)));
isolate->Throw(*error);
return Failure::Exception();
}
// Call trap function.
Object** args[] = { name.location() };
bool has_exception;
Handle<Object> result =
Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
if (has_exception) return Failure::Exception();
Object* bool_result = result->ToBoolean();
if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) {
Handle<Object> args[] = { handler, trap_name };
Handle<Object> error = isolate->factory()->NewTypeError(
"handler_failed", HandleVector(args, ARRAY_SIZE(args)));
isolate->Throw(*error);
return Failure::Exception();
}
return bool_result;
}
MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
JSReceiver* receiver_raw,
String* name_raw,
bool* has_exception) {
Isolate* isolate = GetIsolate();
HandleScope scope;
HandleScope scope(isolate);
Handle<JSReceiver> receiver(receiver_raw);
Handle<Object> name(name_raw);
Handle<Object> handler(this->handler());
@ -2322,7 +2360,7 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
}
if (result->IsReadOnly() && result->IsProperty()) {
if (strict_mode == kStrictMode) {
HandleScope scope;
HandleScope scope(heap->isolate());
Handle<String> key(name);
Handle<Object> holder(this);
Handle<Object> args[2] = { key, holder };
@ -3176,6 +3214,15 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
}
MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) {
if (IsJSProxy()) {
return JSProxy::cast(this)->DeletePropertyWithHandler(name, mode);
} else {
return JSObject::cast(this)->DeleteProperty(name, mode);
}
}
MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
Isolate* isolate = GetIsolate();
// ECMA-262, 3rd, 8.6.2.5
@ -7527,7 +7574,7 @@ void JSArray::Expand(int required_size) {
static Failure* ArrayLengthRangeError(Heap* heap) {
HandleScope scope;
HandleScope scope(heap->isolate());
return heap->isolate()->Throw(
*FACTORY->NewRangeError("invalid_array_length",
HandleVector<Object>(NULL, 0)));
@ -7725,7 +7772,7 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
// or [[Extensible]] must not violate the invariants defined in the preceding
// paragraph.
if (!this->map()->is_extensible()) {
HandleScope scope;
HandleScope scope(heap->isolate());
Handle<Object> handle(this, heap->isolate());
return heap->isolate()->Throw(
*FACTORY->NewTypeError("non_extensible_proto",
@ -7739,7 +7786,7 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
for (Object* pt = value; pt != heap->null_value(); pt = pt->GetPrototype()) {
if (JSObject::cast(pt) == this) {
// Cycle detected.
HandleScope scope;
HandleScope scope(heap->isolate());
return heap->isolate()->Throw(
*FACTORY->NewError("cyclic_proto", HandleVector<Object>(NULL, 0)));
}
@ -8523,7 +8570,7 @@ MaybeObject* JSObject::SetElement(uint32_t index,
if (IsAccessCheckNeeded()) {
Heap* heap = GetHeap();
if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) {
HandleScope scope;
HandleScope scope(heap->isolate());
Handle<Object> value_handle(value);
heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
return *value_handle;
@ -11069,11 +11116,11 @@ void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
template<typename Shape, typename Key>
Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
JSObject::DeleteMode mode) {
JSReceiver::DeleteMode mode) {
Heap* heap = Dictionary<Shape, Key>::GetHeap();
PropertyDetails details = DetailsAt(entry);
// Ignore attributes if forcing a deletion.
if (details.IsDontDelete() && mode != JSObject::FORCE_DELETION) {
if (details.IsDontDelete() && mode != JSReceiver::FORCE_DELETION) {
return heap->false_value();
}
SetEntry(entry, heap->null_value(), heap->null_value());

View File

@ -1359,6 +1359,12 @@ class HeapNumber: public HeapObject {
// JSObject and JSProxy.
class JSReceiver: public HeapObject {
public:
enum DeleteMode {
NORMAL_DELETION,
STRICT_DELETION,
FORCE_DELETION
};
// Casting.
static inline JSReceiver* cast(Object* obj);
@ -1373,6 +1379,8 @@ class JSReceiver: public HeapObject {
PropertyAttributes attributes,
StrictModeFlag strict_mode);
MUST_USE_RESULT MaybeObject* DeleteProperty(String* name, DeleteMode mode);
// Returns the class name ([[Class]] property in the specification).
String* class_name();
@ -1422,12 +1430,6 @@ class JSReceiver: public HeapObject {
// caching.
class JSObject: public JSReceiver {
public:
enum DeleteMode {
NORMAL_DELETION,
STRICT_DELETION,
FORCE_DELETION
};
enum ElementsKind {
// The "fast" kind for tagged values. Must be first to make it possible
// to efficiently check maps if they have fast elements.
@ -6477,14 +6479,18 @@ class JSProxy: public JSReceiver {
static inline JSProxy* cast(Object* obj);
MUST_USE_RESULT MaybeObject* SetPropertyWithHandler(
String* name_raw,
Object* value_raw,
String* name,
Object* value,
PropertyAttributes attributes,
StrictModeFlag strict_mode);
MUST_USE_RESULT MaybeObject* DeletePropertyWithHandler(
String* name,
DeleteMode mode);
MUST_USE_RESULT PropertyAttributes GetPropertyAttributeWithHandler(
JSReceiver* receiver,
String* name_raw,
String* name,
bool* has_exception);
// Dispatched behavior.

View File

@ -3872,7 +3872,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineAccessorProperty) {
|| result.type() == CONSTANT_FUNCTION)) {
Object* ok;
{ MaybeObject* maybe_ok =
obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
obj->DeleteProperty(name, JSReceiver::NORMAL_DELETION);
if (!maybe_ok->ToObject(&ok)) return maybe_ok;
}
}
@ -4126,24 +4126,25 @@ MaybeObject* Runtime::ForceSetObjectProperty(Isolate* isolate,
MaybeObject* Runtime::ForceDeleteObjectProperty(Isolate* isolate,
Handle<JSObject> js_object,
Handle<JSReceiver> receiver,
Handle<Object> key) {
HandleScope scope(isolate);
// Check if the given key is an array index.
uint32_t index;
if (key->ToArrayIndex(&index)) {
if (receiver->IsJSObject() && key->ToArrayIndex(&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)) {
if (receiver->IsStringObjectWithCharacterAt(index)) {
return isolate->heap()->true_value();
}
return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
return JSObject::cast(*receiver)->DeleteElement(
index, JSReceiver::FORCE_DELETION);
}
Handle<String> key_string;
@ -4158,7 +4159,7 @@ MaybeObject* Runtime::ForceDeleteObjectProperty(Isolate* isolate,
}
key_string->TryFlatten();
return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
return receiver->DeleteProperty(*key_string, JSReceiver::FORCE_DELETION);
}
@ -4237,12 +4238,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteProperty) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
CONVERT_CHECKED(JSObject, object, args[0]);
CONVERT_CHECKED(JSReceiver, object, args[0]);
CONVERT_CHECKED(String, key, args[1]);
CONVERT_SMI_ARG_CHECKED(strict, 2);
return object->DeleteProperty(key, (strict == kStrictMode)
? JSObject::STRICT_DELETION
: JSObject::NORMAL_DELETION);
? JSReceiver::STRICT_DELETION
: JSReceiver::NORMAL_DELETION);
}
@ -8198,9 +8199,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteContextSlot) {
// index is non-negative.
Handle<JSObject> object = Handle<JSObject>::cast(holder);
if (index >= 0) {
return object->DeleteElement(index, JSObject::NORMAL_DELETION);
return object->DeleteElement(index, JSReceiver::NORMAL_DELETION);
} else {
return object->DeleteProperty(*name, JSObject::NORMAL_DELETION);
return object->DeleteProperty(*name, JSReceiver::NORMAL_DELETION);
}
}

View File

@ -636,7 +636,7 @@ class Runtime : public AllStatic {
MUST_USE_RESULT static MaybeObject* ForceDeleteObjectProperty(
Isolate* isolate,
Handle<JSObject> object,
Handle<JSReceiver> object,
Handle<Object> key);
MUST_USE_RESULT static MaybeObject* GetObjectProperty(

View File

@ -289,6 +289,50 @@ TestDefine(Proxy.create({
// Property deletion (delete).
var key
function TestDelete(handler) {
var o = Proxy.create(handler)
assertEquals(true, delete o.a)
assertEquals("a", key)
assertEquals(true, delete o["b"])
assertEquals("b", key)
assertEquals(false, delete o.z1)
assertEquals("z1", key)
assertEquals(false, delete o["z2"])
assertEquals("z2", key);
(function() {
"use strict"
assertEquals(true, delete o.c)
assertEquals("c", key)
assertEquals(true, delete o["d"])
assertEquals("d", key)
assertThrows(function() { delete o.z3 }, TypeError)
assertEquals("z3", key)
assertThrows(function() { delete o["z4"] }, TypeError)
assertEquals("z4", key)
})()
}
TestDelete({
'delete': function(k) { key = k; return k < "z" }
})
TestDelete({
'delete': function(k) { return this.delete2(k) },
delete2: function(k) { key = k; return k < "z" }
})
TestDelete(Proxy.create({
get: function(pr, pk) {
return function(k) { key = k; return k < "z" }
}
}))
// Property descriptors (Object.getOwnPropertyDescriptor).
function TestDescriptor(handler) {