Add ES5 Object.isExtensible and Object.preventExtensions.
Review URL: http://codereview.chromium.org/2819034 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5011 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
c56b92d65d
commit
eed4ed99c8
@ -196,6 +196,7 @@ function FormatMessage(message) {
|
||||
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"
|
||||
};
|
||||
}
|
||||
|
@ -2199,6 +2199,20 @@ bool Map::is_access_check_needed() {
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
|
@ -1386,6 +1386,11 @@ Object* JSObject::AddProperty(String* name,
|
||||
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() <
|
||||
@ -2574,6 +2579,25 @@ bool JSObject::ReferencesObject(Object* obj) {
|
||||
}
|
||||
|
||||
|
||||
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:
|
||||
// - This object and all prototypes has an enum cache (which means that it has
|
||||
// no interceptors and needs no access checks).
|
||||
@ -3074,7 +3098,7 @@ Object* Map::CopyDropTransitions() {
|
||||
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 @@ Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) {
|
||||
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)) {
|
||||
|
@ -1517,6 +1517,10 @@ class JSObject: public HeapObject {
|
||||
// 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 @@ class Map: public HeapObject {
|
||||
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) {
|
||||
|
@ -678,6 +678,12 @@ static Object* Runtime_GetOwnProperty(Arguments args) {
|
||||
}
|
||||
|
||||
|
||||
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]);
|
||||
|
@ -72,6 +72,7 @@ namespace internal {
|
||||
F(GetOwnProperty, 2, 1) \
|
||||
\
|
||||
F(IsExtensible, 1, 1) \
|
||||
F(PreventExtensions, 1, 1)\
|
||||
\
|
||||
/* Utilities */ \
|
||||
F(GetFunctionDelegate, 1, 1) \
|
||||
|
@ -745,6 +745,27 @@ function ObjectDefineProperties(obj, properties) {
|
||||
}
|
||||
|
||||
|
||||
// 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) {
|
||||
if (%_IsConstructCall()) {
|
||||
if (x == null) return this;
|
||||
@ -780,7 +801,9 @@ function SetupObject() {
|
||||
"defineProperties", ObjectDefineProperties,
|
||||
"getPrototypeOf", ObjectGetPrototypeOf,
|
||||
"getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
|
||||
"getOwnPropertyNames", ObjectGetOwnPropertyNames
|
||||
"getOwnPropertyNames", ObjectGetOwnPropertyNames,
|
||||
"isExtensible", ObjectIsExtensible,
|
||||
"preventExtensions", ObjectPreventExtension
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -3335,6 +3335,42 @@ THREADED_TEST(UndetectableObject) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
@ -44,7 +44,6 @@ chapter11/11.1/11.1.5: UNIMPLEMENTED
|
||||
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.1: FAIL_OK
|
||||
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 @@ chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-20: UNIMPLEMENTED
|
||||
# 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
|
||||
|
||||
|
157
test/mjsunit/object-prevent-extensions.js
Normal file
157
test/mjsunit/object-prevent-extensions.js
Normal file
@ -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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user