Author: [email protected]
Date: Tue Apr 21 06:28:11 2009
New Revision: 1751
Modified:
branches/bleeding_edge/include/v8.h
branches/bleeding_edge/src/api.cc
branches/bleeding_edge/src/handles.cc
branches/bleeding_edge/src/handles.h
branches/bleeding_edge/src/objects.cc
branches/bleeding_edge/src/runtime.cc
branches/bleeding_edge/src/runtime.h
branches/bleeding_edge/test/cctest/test-api.cc
Log:
force-set
Modified: branches/bleeding_edge/include/v8.h
==============================================================================
--- branches/bleeding_edge/include/v8.h (original)
+++ branches/bleeding_edge/include/v8.h Tue Apr 21 06:28:11 2009
@@ -1045,6 +1045,9 @@
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
Modified: branches/bleeding_edge/src/api.cc
==============================================================================
--- branches/bleeding_edge/src/api.cc (original)
+++ branches/bleeding_edge/src/api.cc Tue Apr 21 06:28:11 2009
@@ -1835,6 +1835,26 @@
}
+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;
Modified: branches/bleeding_edge/src/handles.cc
==============================================================================
--- branches/bleeding_edge/src/handles.cc (original)
+++ branches/bleeding_edge/src/handles.cc Tue Apr 21 06:28:11 2009
@@ -212,6 +212,15 @@
}
+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,
Modified: branches/bleeding_edge/src/handles.h
==============================================================================
--- branches/bleeding_edge/src/handles.h (original)
+++ branches/bleeding_edge/src/handles.h Tue Apr 21 06:28:11 2009
@@ -196,6 +196,11 @@
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,
Modified: branches/bleeding_edge/src/objects.cc
==============================================================================
--- branches/bleeding_edge/src/objects.cc (original)
+++ branches/bleeding_edge/src/objects.cc Tue Apr 21 06:28:11 2009
@@ -1783,6 +1783,17 @@
&& !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 @@
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.
Modified: branches/bleeding_edge/src/runtime.cc
==============================================================================
--- branches/bleeding_edge/src/runtime.cc (original)
+++ branches/bleeding_edge/src/runtime.cc Tue Apr 21 06:28:11 2009
@@ -2688,6 +2688,59 @@
}
+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);
Modified: branches/bleeding_edge/src/runtime.h
==============================================================================
--- branches/bleeding_edge/src/runtime.h (original)
+++ branches/bleeding_edge/src/runtime.h Tue Apr 21 06:28:11 2009
@@ -373,6 +373,11 @@
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.
Modified: branches/bleeding_edge/test/cctest/test-api.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/test-api.cc (original)
+++ branches/bleeding_edge/test/cctest/test-api.cc Tue Apr 21 06:28:11 2009
@@ -6238,3 +6238,118 @@
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);
+}
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---