Revision: 5011
Author: [email protected]
Date: Fri Jul  2 07:36:34 2010
Log: Add ES5 Object.isExtensible and Object.preventExtensions.

Review URL: http://codereview.chromium.org/2819034
http://code.google.com/p/v8/source/detail?r=5011

Added:
 /branches/bleeding_edge/test/mjsunit/object-prevent-extensions.js
Modified:
 /branches/bleeding_edge/src/messages.js
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/src/v8natives.js
 /branches/bleeding_edge/test/cctest/test-api.cc
 /branches/bleeding_edge/test/es5conform/es5conform.status

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/object-prevent-extensions.js Fri Jul 2 07:36:34 2010
@@ -0,0 +1,157 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests the Object.preventExtensions method - ES 15.2.3.10
+
+
+var obj1 = {};
+// Extensible defaults to true.
+assertTrue(Object.isExtensible(obj1));
+Object.preventExtensions(obj1);
+
+// Make sure the is_extensible flag is set.
+assertFalse(Object.isExtensible(obj1));
+// Try adding a new property.
+try {
+  obj1.x = 42;
+  assertUnreachable();
+} catch (e) {
+  assertTrue(/object is not extensible/.test(e));
+}
+assertEquals(undefined, obj1.x);
+
+// Try adding a new element.
+try {
+  obj1[1] = 42;
+  assertUnreachable();
+} catch (e) {
+  assertTrue(/object is not extensible/.test(e));
+}
+assertEquals(undefined, obj1[1]);
+
+
+// Try when the object has an existing property.
+var obj2 = {};
+assertTrue(Object.isExtensible(obj2));
+obj2.x = 42;
+assertEquals(42, obj2.x);
+assertTrue(Object.isExtensible(obj2));
+
+Object.preventExtensions(obj2);
+assertEquals(42, obj2.x);
+
+try {
+  obj2.y = 42;
+  assertUnreachable();
+} catch (e) {
+  assertTrue(/object is not extensible/.test(e));
+}
+
+// obj2.y should still be undefined.
+assertEquals(undefined, obj2.y);
+// Make sure we can still write values to obj.x.
+obj2.x = 43;
+assertEquals(43, obj2.x)
+
+try {
+  obj2.y = new function() { return 42; };
+  assertUnreachable();
+} catch (e) {
+  assertTrue(/object is not extensible/.test(e));
+}
+// obj2.y should still be undefined.
+assertEquals(undefined, obj2.y);
+assertEquals(43, obj2.x)
+
+try {
+  Object.defineProperty(obj2, "y", {value: 42});
+} catch (e) {
+  assertTrue(/object is not extensible/.test(e));
+}
+
+// obj2.y should still be undefined.
+assertEquals(undefined, obj2.y);
+assertEquals(43, obj2.x);
+
+try {
+  obj2[1] = 42;
+} catch (e) {
+  assertTrue(/object is not extensible/.test(e));
+}
+
+assertEquals(undefined, obj2[1]);
+
+var arr = new Array();
+arr[1] = 10;
+
+Object.preventExtensions(arr);
+
+try {
+  arr[2] = 42;
+  assertUnreachable();
+} catch (e) {
+  assertTrue(/object is not extensible/.test(e));
+}
+assertEquals(10, arr[1]);
+
+// We should still be able to change exiting elements.
+arr[1]= 42;
+assertEquals(42, arr[1]);
+
+
+// Test the the extensible flag is not inherited.
+var parent = {};
+parent.x = 42;
+Object.preventExtensions(parent);
+
+var child = Object.create(parent);
+
+// We should be able to add new properties to the child object.
+child.y = 42;
+
+// This should have no influence on the parent class.
+try {
+  parent.y = 29;
+  assertUnreachable();
+} catch (e) {
+  assertTrue(/object is not extensible/.test(e));
+}
+
+
+// Test that attributes on functions are also handled correctly.
+function foo() {
+  return 42;
+}
+
+Object.preventExtensions(foo);
+
+try {
+  foo.x = 29;
+  assertUnreachable();
+} catch (e) {
+  assertTrue(/object is not extensible/.test(e));
+}
=======================================
--- /branches/bleeding_edge/src/messages.js     Thu May  6 00:32:44 2010
+++ /branches/bleeding_edge/src/messages.js     Fri Jul  2 07:36:34 2010
@@ -196,6 +196,7 @@
circular_structure: "Converting circular structure to JSON",
       obj_ctor_property_non_object: "Object.%0 called on non-object",
       array_indexof_not_defined:    "Array.getIndexOf: Argument undefined",
+ object_not_extensible: "Can't add property %0, object is not extensible",
       illegal_access:               "illegal access"
     };
   }
=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Fri Jul  2 07:15:04 2010
+++ /branches/bleeding_edge/src/objects-inl.h   Fri Jul  2 07:36:34 2010
@@ -2199,6 +2199,20 @@
 }


+void Map::set_is_extensible(bool value) {
+  if (value) {
+    set_bit_field2(bit_field2() | (1 << kIsExtensible));
+  } else {
+    set_bit_field2(bit_field2() & ~(1 << kIsExtensible));
+  }
+}
+
+bool Map::is_extensible() {
+  return ((1 << kIsExtensible) & bit_field2()) != 0;
+}
+
+
+
 Code::Flags Code::flags() {
   return static_cast<Flags>(READ_INT_FIELD(this, kFlagsOffset));
 }
=======================================
--- /branches/bleeding_edge/src/objects.cc      Fri Jul  2 07:15:04 2010
+++ /branches/bleeding_edge/src/objects.cc      Fri Jul  2 07:36:34 2010
@@ -1386,6 +1386,11 @@
                               Object* value,
                               PropertyAttributes attributes) {
   ASSERT(!IsJSGlobalProxy());
+  if (!map()->is_extensible()) {
+    Handle<Object> args[1] = {Handle<String>(name)};
+    return Top::Throw(*Factory::NewTypeError("object_not_extensible",
+                                               HandleVector(args, 1)));
+  }
   if (HasFastProperties()) {
     // Ensure the descriptor array does not get too big.
     if (map()->instance_descriptors()->number_of_descriptors() <
@@ -2572,6 +2577,25 @@
   // No references to object.
   return false;
 }
+
+
+Object* JSObject::PreventExtensions() {
+  // If there are fast elements we normalize.
+  if (HasFastElements()) {
+    NormalizeElements();
+  }
+  // Make sure that we never go back to fast case.
+  element_dictionary()->set_requires_slow_elements();
+
+  // Do a map transition, other objects with this map may still
+  // be extensible.
+  Object* new_map = map()->CopyDropTransitions();
+  if (new_map->IsFailure()) return new_map;
+  Map::cast(new_map)->set_is_extensible(false);
+  set_map(Map::cast(new_map));
+  ASSERT(!map()->is_extensible());
+  return new_map;
+}


 // Tests for the fast common case for property enumeration:
@@ -3074,7 +3098,7 @@
   Object* descriptors = instance_descriptors()->RemoveTransitions();
   if (descriptors->IsFailure()) return descriptors;
cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
-  return cast(new_map);
+  return new_map;
 }


@@ -6207,6 +6231,15 @@
             return value;
           }
         }
+        // When we set the is_extensible flag to false we always force
+        // the element into dictionary mode (and force them to stay there).
+        if (!map()->is_extensible()) {
+          Handle<Object> number(Heap::NumberFromUint32(index));
+          Handle<String> index_string(Factory::NumberToString(number));
+          Handle<Object> args[1] = { index_string };
+          return Top::Throw(*Factory::NewTypeError("object_not_extensible",
+                                                   HandleVector(args, 1)));
+        }
         Object* result = dictionary->AtNumberPut(index, value);
         if (result->IsFailure()) return result;
         if (elms != FixedArray::cast(result)) {
=======================================
--- /branches/bleeding_edge/src/objects.h       Fri Jul  2 07:15:04 2010
+++ /branches/bleeding_edge/src/objects.h       Fri Jul  2 07:36:34 2010
@@ -1517,6 +1517,10 @@
   // Casting.
   static inline JSObject* cast(Object* obj);

+  // Disalow further properties to be added to the object.
+  Object* PreventExtensions();
+
+
   // Dispatched behavior.
   void JSObjectIterateBody(int object_size, ObjectVisitor* v);
   void JSObjectShortPrint(StringStream* accumulator);
@@ -2989,13 +2993,8 @@
     return ((1 << kHasInstanceCallHandler) & bit_field()) != 0;
   }

-  inline void set_is_extensible() {
-    set_bit_field2(bit_field2() | (1 << kIsExtensible));
-  }
-
-  inline bool is_extensible() {
-    return ((1 << kIsExtensible) & bit_field2()) != 0;
-  }
+  inline void set_is_extensible(bool value);
+  inline bool is_extensible();

   // Tells whether the instance has fast elements.
   void set_has_fast_elements(bool value) {
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Wed Jun 30 00:40:40 2010
+++ /branches/bleeding_edge/src/runtime.cc      Fri Jul  2 07:36:34 2010
@@ -678,6 +678,12 @@
 }


+static Object* Runtime_PreventExtensions(Arguments args) {
+  ASSERT(args.length() == 1);
+  CONVERT_CHECKED(JSObject, obj, args[0]);
+  return obj->PreventExtensions();
+}
+
 static Object* Runtime_IsExtensible(Arguments args) {
   ASSERT(args.length() == 1);
   CONVERT_CHECKED(JSObject, obj, args[0]);
=======================================
--- /branches/bleeding_edge/src/runtime.h       Fri Jun  4 03:19:19 2010
+++ /branches/bleeding_edge/src/runtime.h       Fri Jul  2 07:36:34 2010
@@ -72,6 +72,7 @@
   F(GetOwnProperty, 2, 1) \
   \
   F(IsExtensible, 1, 1) \
+  F(PreventExtensions, 1, 1)\
   \
   /* Utilities */ \
   F(GetFunctionDelegate, 1, 1) \
=======================================
--- /branches/bleeding_edge/src/v8natives.js    Mon Jun 28 02:25:09 2010
+++ /branches/bleeding_edge/src/v8natives.js    Fri Jul  2 07:36:34 2010
@@ -743,6 +743,27 @@
   }
   return obj;
 }
+
+
+// ES5 section 15.2.3.10
+function ObjectPreventExtension(obj) {
+  if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
+      !IS_UNDETECTABLE(obj)) {
+ throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
+  }
+  %PreventExtensions(obj);
+  return obj;
+}
+
+
+// ES5 section 15.2.3.13
+function ObjectIsExtensible(obj) {
+  if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
+      !IS_UNDETECTABLE(obj)) {
+ throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
+  }
+  return %IsExtensible(obj);
+}


 %SetCode($Object, function(x) {
@@ -780,7 +801,9 @@
     "defineProperties", ObjectDefineProperties,
     "getPrototypeOf", ObjectGetPrototypeOf,
     "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
-    "getOwnPropertyNames", ObjectGetOwnPropertyNames
+    "getOwnPropertyNames", ObjectGetOwnPropertyNames,
+    "isExtensible", ObjectIsExtensible,
+    "preventExtensions", ObjectPreventExtension
   ));
 }

=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc     Wed Jun 30 00:40:40 2010
+++ /branches/bleeding_edge/test/cctest/test-api.cc     Fri Jul  2 07:36:34 2010
@@ -3333,6 +3333,42 @@
   ExpectBoolean("undefined===undetectable", false);
   ExpectBoolean("undetectable===undetectable", true);
 }
+
+
+
+THREADED_TEST(ExtensibleOnUndetectable) {
+  v8::HandleScope scope;
+  LocalContext env;
+
+  Local<v8::FunctionTemplate> desc =
+      v8::FunctionTemplate::New(0, v8::Handle<Value>());
+  desc->InstanceTemplate()->MarkAsUndetectable();  // undetectable
+
+  Local<v8::Object> obj = desc->GetFunction()->NewInstance();
+  env->Global()->Set(v8_str("undetectable"), obj);
+
+  Local<String> source = v8_str("undetectable.x = 42;"
+                                "undetectable.x");
+
+  Local<Script> script = Script::Compile(source);
+
+  CHECK_EQ(v8::Integer::New(42), script->Run());
+
+  ExpectBoolean("Object.isExtensible(undetectable)", true);
+
+  source = v8_str("Object.preventExtensions(undetectable);");
+  script = Script::Compile(source);
+  script->Run();
+  ExpectBoolean("Object.isExtensible(undetectable)", false);
+
+  source = v8_str("undetectable.y = 2000;");
+  script = Script::Compile(source);
+  v8::TryCatch try_catch;
+  Local<Value> result = script->Run();
+  CHECK(result.IsEmpty());
+  CHECK(try_catch.HasCaught());
+}
+


 THREADED_TEST(UndetectableString) {
=======================================
--- /branches/bleeding_edge/test/es5conform/es5conform.status Tue Jun 29 02:00:20 2010 +++ /branches/bleeding_edge/test/es5conform/es5conform.status Fri Jul 2 07:36:34 2010
@@ -44,7 +44,6 @@
 chapter11/11.4/11.4.1//11.4.1-4.a-5: FAIL
 chapter11/11.4/11.4.1//11.4.1-4.a-7: FAIL

-
 # We do not have a global object called 'global' as required by tests.
 chapter15/15.1: FAIL_OK

@@ -52,14 +51,10 @@
 chapter15/15.2/15.2.3/15.2.3.8: UNIMPLEMENTED
 # NOT IMPLEMENTED: freeze
 chapter15/15.2/15.2.3/15.2.3.9: UNIMPLEMENTED
-# NOT IMPLEMENTED: preventExtensions
-chapter15/15.2/15.2.3/15.2.3.10: UNIMPLEMENTED
 # NOT IMPLEMENTED: isSealed
 chapter15/15.2/15.2.3/15.2.3.11: UNIMPLEMENTED
 # NOT IMPLEMENTED: isFrozen
 chapter15/15.2/15.2.3/15.2.3.12: UNIMPLEMENTED
-# NOT IMPLEMENTED: isExtensible
-chapter15/15.2/15.2.3/15.2.3.13: UNIMPLEMENTED

 # NOT IMPLEMENTED: seal
 chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-20: UNIMPLEMENTED
@@ -67,18 +62,12 @@
 # NOT IMPLEMENTED: freeze
 chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-21: UNIMPLEMENTED

-# NOT IMPLEMENTED: preventExtensions
-chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-22: UNIMPLEMENTED
-
 # NOT IMPLEMENTED: isSealed
 chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-23: UNIMPLEMENTED

 # NOT IMPLEMENTED: isFrozen
 chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-24: UNIMPLEMENTED

-# NOT IMPLEMENTED: isExtensible
-chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-25: UNIMPLEMENTED
-
 # NOT IMPLEMENTED: bind
 chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-38: UNIMPLEMENTED

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to