force-set
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1751 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
123f671a17
commit
d26e6dd0b4
@ -1045,6 +1045,9 @@ class V8EXPORT Object : public Value {
|
||||
bool Set(Handle<Value> key,
|
||||
Handle<Value> value,
|
||||
PropertyAttribute attribs = None);
|
||||
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
|
||||
|
20
src/api.cc
20
src/api.cc
@ -1835,6 +1835,26 @@ bool v8::Object::Set(v8::Handle<Value> key, v8::Handle<Value> value,
|
||||
}
|
||||
|
||||
|
||||
bool v8::Object::ForceSet(v8::Handle<Value> key,
|
||||
v8::Handle<Value> value,
|
||||
v8::PropertyAttribute attribs) {
|
||||
ON_BAILOUT("v8::Object::ForceSet()", return false);
|
||||
ENTER_V8;
|
||||
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
|
||||
i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
|
||||
i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
|
||||
EXCEPTION_PREAMBLE();
|
||||
i::Handle<i::Object> obj = i::ForceSetProperty(
|
||||
self,
|
||||
key_obj,
|
||||
value_obj,
|
||||
static_cast<PropertyAttributes>(attribs));
|
||||
has_pending_exception = obj.is_null();
|
||||
EXCEPTION_BAILOUT_CHECK(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Local<Value> v8::Object::Get(v8::Handle<Value> key) {
|
||||
ON_BAILOUT("v8::Object::Get()", return Local<v8::Value>());
|
||||
ENTER_V8;
|
||||
|
@ -212,6 +212,15 @@ Handle<Object> SetProperty(Handle<Object> object,
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> ForceSetProperty(Handle<JSObject> object,
|
||||
Handle<Object> key,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes) {
|
||||
CALL_HEAP_FUNCTION(
|
||||
Runtime::ForceSetObjectProperty(object, key, value, attributes), Object);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> IgnoreAttributesAndSetLocalProperty(
|
||||
Handle<JSObject> object,
|
||||
Handle<String> key,
|
||||
|
@ -196,6 +196,11 @@ Handle<Object> SetProperty(Handle<Object> object,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes);
|
||||
|
||||
Handle<Object> ForceSetProperty(Handle<JSObject> object,
|
||||
Handle<Object> key,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes);
|
||||
|
||||
Handle<Object> IgnoreAttributesAndSetLocalProperty(Handle<JSObject> object,
|
||||
Handle<String> key,
|
||||
Handle<Object> value,
|
||||
|
@ -1783,6 +1783,17 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
|
||||
&& !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
||||
return SetPropertyWithFailedAccessCheck(result, name, value);
|
||||
}
|
||||
|
||||
if (IsJSGlobalProxy()) {
|
||||
Object* proto = GetPrototype();
|
||||
if (proto->IsNull()) return value;
|
||||
ASSERT(proto->IsJSGlobalObject());
|
||||
return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
|
||||
name,
|
||||
value,
|
||||
attributes);
|
||||
}
|
||||
|
||||
// Check for accessor in prototype chain removed here in clone.
|
||||
if (result->IsNotFound()) {
|
||||
return AddProperty(name, value, attributes);
|
||||
@ -1803,20 +1814,15 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
|
||||
return AddFastPropertyUsingMap(result->GetTransitionMap(),
|
||||
name,
|
||||
value);
|
||||
} else {
|
||||
return ConvertDescriptorToField(name, value, attributes);
|
||||
}
|
||||
return ConvertDescriptorToField(name, value, attributes);
|
||||
case CONSTANT_FUNCTION:
|
||||
if (value == result->GetConstantFunction()) return value;
|
||||
// Only replace the function if necessary.
|
||||
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
|
||||
case CALLBACKS:
|
||||
return SetPropertyWithCallback(result->GetCallbackObject(),
|
||||
name,
|
||||
value,
|
||||
result->holder());
|
||||
case INTERCEPTOR:
|
||||
return SetPropertyWithInterceptor(name, value, attributes);
|
||||
case CALLBACKS: case INTERCEPTOR:
|
||||
// Override callback in clone
|
||||
return ConvertDescriptorToField(name, value, attributes);
|
||||
case CONSTANT_TRANSITION:
|
||||
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
|
||||
// if the value is a function.
|
||||
|
@ -2688,6 +2688,59 @@ Object* Runtime::SetObjectProperty(Handle<Object> object,
|
||||
}
|
||||
|
||||
|
||||
Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
|
||||
Handle<Object> key,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attr) {
|
||||
HandleScope scope;
|
||||
|
||||
// Check if the given key is an array index.
|
||||
uint32_t index;
|
||||
if (Array::IndexFromObject(*key, &index)) {
|
||||
ASSERT(attr == NONE);
|
||||
|
||||
// In Firefox/SpiderMonkey, Safari and Opera you can access the characters
|
||||
// of a string using [] notation. We need to support this too in
|
||||
// JavaScript.
|
||||
// In the case of a String object we just need to redirect the assignment to
|
||||
// the underlying string if the index is in range. Since the underlying
|
||||
// string does nothing with the assignment then we can ignore such
|
||||
// assignments.
|
||||
if (js_object->IsStringObjectWithCharacterAt(index)) {
|
||||
return *value;
|
||||
}
|
||||
|
||||
return js_object->SetElement(index, *value);
|
||||
}
|
||||
|
||||
if (key->IsString()) {
|
||||
if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
|
||||
ASSERT(attr == NONE);
|
||||
return js_object->SetElement(index, *value);
|
||||
} else {
|
||||
Handle<String> key_string = Handle<String>::cast(key);
|
||||
key_string->TryFlattenIfNotFlat();
|
||||
return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
|
||||
*value,
|
||||
attr);
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
Handle<String> name = Handle<String>::cast(converted);
|
||||
|
||||
if (name->AsArrayIndex(&index)) {
|
||||
ASSERT(attr == NONE);
|
||||
return js_object->SetElement(index, *value);
|
||||
} else {
|
||||
return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Object* Runtime_SetProperty(Arguments args) {
|
||||
NoHandleAllocation ha;
|
||||
RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
|
||||
|
@ -373,6 +373,11 @@ class Runtime : public AllStatic {
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attr);
|
||||
|
||||
static Object* ForceSetObjectProperty(Handle<JSObject> object,
|
||||
Handle<Object> key,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attr);
|
||||
|
||||
static Object* GetObjectProperty(Handle<Object> object, Handle<Object> key);
|
||||
|
||||
// This function is used in FunctionNameUsing* tests.
|
||||
|
@ -6238,3 +6238,118 @@ TEST(ReadOnlyPropertyInGlobalProto) {
|
||||
res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
|
||||
CHECK_EQ(v8::Integer::New(42), res);
|
||||
}
|
||||
|
||||
static int force_set_set_count = 0;
|
||||
static int force_set_get_count = 0;
|
||||
bool pass_on_get = false;
|
||||
|
||||
static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
|
||||
const v8::AccessorInfo& info) {
|
||||
force_set_get_count++;
|
||||
if (pass_on_get) {
|
||||
return v8::Handle<v8::Value>();
|
||||
} else {
|
||||
return v8::Int32::New(3);
|
||||
}
|
||||
}
|
||||
|
||||
static void ForceSetSetter(v8::Local<v8::String> name,
|
||||
v8::Local<v8::Value> value,
|
||||
const v8::AccessorInfo& info) {
|
||||
force_set_set_count++;
|
||||
}
|
||||
|
||||
static v8::Handle<v8::Value> ForceSetInterceptSetter(
|
||||
v8::Local<v8::String> name,
|
||||
v8::Local<v8::Value> value,
|
||||
const v8::AccessorInfo& info) {
|
||||
force_set_set_count++;
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
TEST(ForceSet) {
|
||||
force_set_get_count = 0;
|
||||
force_set_set_count = 0;
|
||||
pass_on_get = false;
|
||||
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
|
||||
v8::Handle<v8::String> access_property = v8::String::New("a");
|
||||
templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
|
||||
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::ReadOnly);
|
||||
CHECK_EQ(4, global->Get(simple_property)->Int32Value());
|
||||
// This should fail because the property is read-only
|
||||
global->Set(simple_property, v8::Int32::New(5));
|
||||
CHECK_EQ(4, global->Get(simple_property)->Int32Value());
|
||||
// This should succeed even though the property is read-only
|
||||
global->ForceSet(simple_property, v8::Int32::New(6));
|
||||
CHECK_EQ(6, global->Get(simple_property)->Int32Value());
|
||||
|
||||
// Accessors
|
||||
CHECK_EQ(0, force_set_set_count);
|
||||
CHECK_EQ(0, force_set_get_count);
|
||||
CHECK_EQ(3, global->Get(access_property)->Int32Value());
|
||||
// CHECK_EQ the property shouldn't override it, just call the setter
|
||||
// which in this case does nothing.
|
||||
global->Set(access_property, v8::Int32::New(7));
|
||||
CHECK_EQ(3, global->Get(access_property)->Int32Value());
|
||||
CHECK_EQ(1, force_set_set_count);
|
||||
CHECK_EQ(2, force_set_get_count);
|
||||
// Forcing the property to be set should override the accessor without
|
||||
// calling it
|
||||
global->ForceSet(access_property, v8::Int32::New(8));
|
||||
CHECK_EQ(8, global->Get(access_property)->Int32Value());
|
||||
CHECK_EQ(1, force_set_set_count);
|
||||
CHECK_EQ(2, force_set_get_count);
|
||||
}
|
||||
|
||||
TEST(ForceSetWithInterceptor) {
|
||||
force_set_get_count = 0;
|
||||
force_set_set_count = 0;
|
||||
pass_on_get = false;
|
||||
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
|
||||
templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
|
||||
LocalContext context(NULL, templ);
|
||||
v8::Handle<v8::Object> global = context->Global();
|
||||
|
||||
v8::Handle<v8::String> some_property = v8::String::New("a");
|
||||
CHECK_EQ(0, force_set_set_count);
|
||||
CHECK_EQ(0, force_set_get_count);
|
||||
CHECK_EQ(3, global->Get(some_property)->Int32Value());
|
||||
// Setting the property shouldn't override it, just call the setter
|
||||
// which in this case does nothing.
|
||||
global->Set(some_property, v8::Int32::New(7));
|
||||
CHECK_EQ(3, global->Get(some_property)->Int32Value());
|
||||
CHECK_EQ(1, force_set_set_count);
|
||||
CHECK_EQ(2, force_set_get_count);
|
||||
// Getting the property when the interceptor returns an empty handle
|
||||
// should yield undefined, since the property isn't present on the
|
||||
// object itself yet.
|
||||
pass_on_get = true;
|
||||
CHECK(global->Get(some_property)->IsUndefined());
|
||||
CHECK_EQ(1, force_set_set_count);
|
||||
CHECK_EQ(3, force_set_get_count);
|
||||
// Forcing the property to be set should cause the value to be
|
||||
// set locally without calling the interceptor.
|
||||
global->ForceSet(some_property, v8::Int32::New(8));
|
||||
CHECK_EQ(8, global->Get(some_property)->Int32Value());
|
||||
CHECK_EQ(1, force_set_set_count);
|
||||
CHECK_EQ(4, force_set_get_count);
|
||||
// Reenabling the interceptor should cause it to take precedence over
|
||||
// the property
|
||||
pass_on_get = false;
|
||||
CHECK_EQ(3, global->Get(some_property)->Int32Value());
|
||||
CHECK_EQ(1, force_set_set_count);
|
||||
CHECK_EQ(5, force_set_get_count);
|
||||
// The interceptor should also work for other properties
|
||||
CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
|
||||
CHECK_EQ(1, force_set_set_count);
|
||||
CHECK_EQ(6, force_set_get_count);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user