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:
parent
5f1a8dc71f
commit
75a2c49c1d
@ -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());
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user