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

Reply via email to