From: ricow@chromium.org Date: Thu, 15 Jul 2010 07:51:14 +0000 (+0000) Subject: Implement ES5 Object.seal and Object.isSealed. X-Git-Tag: upstream/4.7.83~21489 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f5f0b8036381c6a4d19115b1796e3bdcf81cfc6b;p=platform%2Fupstream%2Fv8.git Implement ES5 Object.seal and Object.isSealed. This change adds the ES5 Object.seal 15.2.3.8 and Object.isSealed 15.2.3.11 methods. Review URL: http://codereview.chromium.org/2993006 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5072 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/v8natives.js b/src/v8natives.js index 0db38c6..f77b559 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -745,6 +745,23 @@ function ObjectDefineProperties(obj, properties) { } +// ES5 section 15.2.3.8. +function ObjectSeal(obj) { + if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) && + !IS_UNDETECTABLE(obj)) { + throw MakeTypeError("obj_ctor_property_non_object", ["seal"]); + } + var names = ObjectGetOwnPropertyNames(obj); + for (var key in names) { + var name = names[key]; + var desc = GetOwnProperty(obj, name); + if (desc.isConfigurable()) desc.setConfigurable(false); + DefineOwnProperty(obj, name, desc, true); + } + ObjectPreventExtension(obj); +} + + // ES5 section 15.2.3.9. function ObjectFreeze(obj) { if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) && @@ -774,6 +791,25 @@ function ObjectPreventExtension(obj) { } +// ES5 section 15.2.3.11 +function ObjectIsSealed(obj) { + if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) && + !IS_UNDETECTABLE(obj)) { + throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]); + } + var names = ObjectGetOwnPropertyNames(obj); + for (var key in names) { + var name = names[key]; + var desc = GetOwnProperty(obj, name); + if (desc.isConfigurable()) return false; + } + if (!ObjectIsExtensible(obj)) { + return true; + } + return false; +} + + // ES5 section 15.2.3.12 function ObjectIsFrozen(obj) { if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) && @@ -843,7 +879,9 @@ function SetupObject() { "getOwnPropertyNames", ObjectGetOwnPropertyNames, "isExtensible", ObjectIsExtensible, "isFrozen", ObjectIsFrozen, - "preventExtensions", ObjectPreventExtension + "isSealed", ObjectIsSealed, + "preventExtensions", ObjectPreventExtension, + "seal", ObjectSeal )); } diff --git a/test/es5conform/es5conform.status b/test/es5conform/es5conform.status index 4fb8f7b..5add082 100644 --- a/test/es5conform/es5conform.status +++ b/test/es5conform/es5conform.status @@ -47,17 +47,6 @@ 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 -# NOT IMPLEMENTED: seal -chapter15/15.2/15.2.3/15.2.3.8: UNIMPLEMENTED -# NOT IMPLEMENTED: isSealed -chapter15/15.2/15.2.3/15.2.3.11: UNIMPLEMENTED - -# NOT IMPLEMENTED: seal -chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-20: UNIMPLEMENTED - -# NOT IMPLEMENTED: isSealed -chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-23: UNIMPLEMENTED - # NOT IMPLEMENTED: bind chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-38: UNIMPLEMENTED diff --git a/test/mjsunit/object-seal.js b/test/mjsunit/object-seal.js new file mode 100644 index 0000000..896411c --- /dev/null +++ b/test/mjsunit/object-seal.js @@ -0,0 +1,195 @@ +// 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.seal and Object.isSealed methods - ES 15.2.3.9 and +// ES 15.2.3.12 + + +// Test that we throw an error if an object is not passed as argument. +var non_objects = new Array(undefined, null, 1, -1, 0, 42.43); +for (var key in non_objects) { + try { + Object.seal(non_objects[key]); + assertUnreachable(); + } catch(e) { + assertTrue(/Object.seal called on non-object/.test(e)); + } +} + +for (var key in non_objects) { + try { + Object.isSealed(non_objects[key]); + assertUnreachable(); + } catch(e) { + assertTrue(/Object.isSealed called on non-object/.test(e)); + } +} + +// Test normal data properties. +var obj = { x: 42, z: 'foobar' }; +var desc = Object.getOwnPropertyDescriptor(obj, 'x'); +assertTrue(desc.writable); +assertTrue(desc.configurable); +assertEquals(42, desc.value); + +desc = Object.getOwnPropertyDescriptor(obj, 'z'); +assertTrue(desc.writable); +assertTrue(desc.configurable); +assertEquals('foobar', desc.value); + +assertTrue(Object.isExtensible(obj)); +assertFalse(Object.isSealed(obj)); + +Object.seal(obj); + +// Make sure we are no longer extensible. +assertFalse(Object.isExtensible(obj)); +assertTrue(Object.isSealed(obj)); + +// We should not be frozen, since we are still able to +// update values. +assertFalse(Object.isFrozen(obj)); + +// We should not allow new properties to be added. +try { + obj.foo = 42; + assertUnreachable(); +} catch(e) { + assertTrue(/object is not extensible/.test(e)); +} + +desc = Object.getOwnPropertyDescriptor(obj, 'x'); +assertTrue(desc.writable); +assertFalse(desc.configurable); +assertEquals(42, desc.value); + +desc = Object.getOwnPropertyDescriptor(obj, 'z'); +assertTrue(desc.writable); +assertFalse(desc.configurable); +assertEquals("foobar", desc.value); + +// Since writable is not affected by seal we should still be able to +// update the values. +obj.x = "43"; +assertEquals(43, obj.x); + +// Test on accessors. +var obj2 = {}; +function get() { return 43; }; +function set() {}; +Object.defineProperty(obj2, 'x', { get: get, set: set, configurable: true }); + +desc = Object.getOwnPropertyDescriptor(obj2, 'x'); +assertTrue(desc.configurable); +assertEquals(undefined, desc.value); +assertEquals(set, desc.set); +assertEquals(get, desc.get); + +assertTrue(Object.isExtensible(obj2)); +assertFalse(Object.isSealed(obj2)); +Object.seal(obj2); + +// Since this is an accessor property the object is now effectively both +// sealed and frozen (accessors has no writable attribute). +assertTrue(Object.isFrozen(obj2)); +assertFalse(Object.isExtensible(obj2)); +assertTrue(Object.isSealed(obj2)); + +desc = Object.getOwnPropertyDescriptor(obj2, 'x'); +assertFalse(desc.configurable); +assertEquals(undefined, desc.value); +assertEquals(set, desc.set); +assertEquals(get, desc.get); + +try { + obj2.foo = 42; + assertUnreachable(); +} catch(e) { + assertTrue(/object is not extensible/.test(e)); +} + + +// Test seal on arrays. +var arr = new Array(42,43); + +desc = Object.getOwnPropertyDescriptor(arr, '0'); +assertTrue(desc.configurable); +assertTrue(desc.writable); +assertEquals(42, desc.value); + +desc = Object.getOwnPropertyDescriptor(arr, '1'); +assertTrue(desc.configurable); +assertTrue(desc.writable); +assertEquals(43, desc.value); + +assertTrue(Object.isExtensible(arr)); +assertFalse(Object.isSealed(arr)); +Object.seal(arr); +assertTrue(Object.isSealed(arr)); +assertFalse(Object.isExtensible(arr)); +// Since the values in the array is still writable this object +// is not frozen. +assertFalse(Object.isFrozen(arr)); + +desc = Object.getOwnPropertyDescriptor(arr, '0'); +assertFalse(desc.configurable); +assertTrue(desc.writable); +assertEquals(42, desc.value); + +desc = Object.getOwnPropertyDescriptor(arr, '1'); +assertFalse(desc.configurable); +assertTrue(desc.writable); +assertEquals(43, desc.value); + +arr[0] = 'foo'; + +// We should be able to overwrite the existing value. +assertEquals('foo', arr[0]); + + +// Test that isSealed returns the correct value even if configurable +// has been set to false on all properties manually and the extensible +// flag has also been set to false manually. +var obj3 = { x: 42, y: 'foo' }; + +assertFalse(Object.isFrozen(obj3)); + +Object.defineProperty(obj3, 'x', {configurable: false, writable: true}); +Object.defineProperty(obj3, 'y', {configurable: false, writable: false}); +Object.preventExtensions(obj3); + +assertTrue(Object.isSealed(obj3)); + + +// Make sure that an object that has a configurable property +// is not classified as sealed. +var obj4 = {}; +Object.defineProperty(obj4, 'x', {configurable: true, writable: false}); +Object.defineProperty(obj4, 'y', {configurable: false, writable: false}); +Object.preventExtensions(obj4); + +assertFalse(Object.isSealed(obj4));