force-set

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1751 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
christian.plesner.hansen@gmail.com 2009-04-21 13:28:11 +00:00
parent 123f671a17
commit d26e6dd0b4
8 changed files with 225 additions and 9 deletions

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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.

View File

@ -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);

View File

@ -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.

View File

@ -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);
}