diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-object.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-object.cpp index ad752a0442..e000d875e4 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-object.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-object.cpp @@ -178,10 +178,75 @@ ecma_builtin_object_object_get_own_property_names (ecma_value_t this_arg __attr_ * Returned value must be freed with ecma_free_completion_value. */ static ecma_completion_value_t -ecma_builtin_object_object_seal (ecma_value_t this_arg, /**< 'this' argument */ +ecma_builtin_object_object_seal (ecma_value_t this_arg __attr_unused___, /**< 'this' argument */ ecma_value_t arg) /**< routine's argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, arg); + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + // 1. + if (!ecma_is_value_object (arg)) + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); + } + else + { + // 2. + ecma_object_t *obj_p = ecma_get_object_from_value (arg); + + ecma_property_t *property_p; + for (property_p = ecma_get_property_list (obj_p); + property_p != NULL && ecma_is_completion_value_empty (ret_value); + property_p = ECMA_GET_POINTER (ecma_property_t, property_p->next_property_p)) + { + ecma_string_t *property_name_p; + + if (property_p->type == ECMA_PROPERTY_NAMEDDATA) + { + property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, + property_p->u.named_data_property.name_p); + } + else if (property_p->type == ECMA_PROPERTY_NAMEDACCESSOR) + { + property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, + property_p->u.named_accessor_property.name_p); + } + else + { + continue; + } + + // 2.a + ecma_property_descriptor_t prop_desc = ecma_get_property_descriptor_from_property (property_p); + + // 2.b + if (ecma_is_property_configurable (property_p)) + { + prop_desc.is_configurable = false; + } + + // 2.c + ECMA_TRY_CATCH (define_own_prop_ret, + ecma_op_object_define_own_property (obj_p, + property_name_p, + &prop_desc, + true), + ret_value); + ECMA_FINALIZE (define_own_prop_ret); + + ecma_free_property_descriptor (&prop_desc); + } + + if (ecma_is_completion_value_empty (ret_value)) + { + // 3. + ecma_set_object_extensible (obj_p, false); + + // 4. + ret_value = ecma_make_normal_completion_value (ecma_copy_value (arg, true)); + } + } + + return ret_value; } /* ecma_builtin_object_object_seal */ /** @@ -240,10 +305,59 @@ ecma_builtin_object_object_prevent_extensions (ecma_value_t this_arg __attr_unus * Returned value must be freed with ecma_free_completion_value. */ static ecma_completion_value_t -ecma_builtin_object_object_is_sealed (ecma_value_t this_arg, /**< 'this' argument */ +ecma_builtin_object_object_is_sealed (ecma_value_t this_arg __attr_unused___, /**< 'this' argument */ ecma_value_t arg) /**< routine's argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, arg); + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + // 1. + if (!ecma_is_value_object (arg)) + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); + } + else + { + ecma_object_t *obj_p = ecma_get_object_from_value (arg); + ecma_property_t *property_p; + + // This will be the result if the other steps doesn't change it. + bool sealed = false; + + // 3. + // The pseudo code contains multiple early return but this way we get the same + // result. + if (!ecma_get_object_extensible (obj_p)) + { + sealed = true; + } + + // 2. + for (property_p = ecma_get_property_list (obj_p); + property_p != NULL && sealed; + property_p = ECMA_GET_POINTER (ecma_property_t, property_p->next_property_p)) + { + if (property_p->type == ECMA_PROPERTY_INTERNAL) + { + continue; + } + + JERRY_ASSERT (property_p->type == ECMA_PROPERTY_NAMEDDATA || property_p->type == ECMA_PROPERTY_NAMEDACCESSOR); + + // 2.b + if (ecma_is_property_configurable (property_p)) + { + sealed = false; + break; + } + } + + // 4. + ret_value = ecma_make_simple_completion_value (sealed + ? ECMA_SIMPLE_VALUE_TRUE + : ECMA_SIMPLE_VALUE_FALSE); + } + + return ret_value; } /* ecma_builtin_object_object_is_sealed */ /** diff --git a/tests/jerry/object-is-extensible.js b/tests/jerry/object-is-extensible.js index 4d0e397aa0..64bb977478 100644 --- a/tests/jerry/object-is-extensible.js +++ b/tests/jerry/object-is-extensible.js @@ -38,11 +38,11 @@ try assert (e instanceof TypeError); } -// The functions below are unimplemented. // Sealed objects are by definition non-extensible. -// var sealed = Object.seal({}); -// assert(Object.isExtensible(sealed) === false); +var sealed = Object.seal({}); +assert (Object.isExtensible(sealed) === false); +// The functions below are unimplemented. // Frozen objects are also by definition non-extensible. // var frozen = Object.freeze({}); // assert(Object.isExtensible(frozen) === false); diff --git a/tests/jerry/object_seal.js b/tests/jerry/object_seal.js new file mode 100644 index 0000000000..5ea2eb1e07 --- /dev/null +++ b/tests/jerry/object_seal.js @@ -0,0 +1,86 @@ +// Copyright 2015 Samsung Electronics Co., Ltd. +// Copyright 2015 University of Szeged. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +var obj = { + prop: function() {}, + foo: 'bar' +}; +// New properties may be added, existing properties may be changed or removed. +obj.foo = 'baz'; +obj.lumpy = 'woof'; +delete obj.prop; + +var o = Object.seal(obj); + +assert (o === obj); +assert (Object.isSealed (obj) === true); + +// Changing property values on a sealed object still works. +obj.foo = 'quux'; +assert (obj.foo === 'quux'); +// But you can't convert data properties to accessors, or vice versa. +try { + Object.defineProperty(obj, 'foo', { get: function() { return 42; } }); // throws a TypeError + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} + +// Now any changes, other than to property values, will fail. +obj.quaxxor = 'the friendly duck'; // silently doesn't add the property +delete obj.foo; // silently doesn't delete the property + +assert (obj.quaxxor === undefined); +assert (obj.foo === 'quux') + +try { + // Attempted additions through Object.defineProperty will also throw. + Object.defineProperty (obj, 'ohai', { value: 17 }); // throws a TypeError + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} + +try { + Object.defineProperties (obj, { 'ohai' : { value: 17 } }); + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} + +Object.defineProperty (obj, 'foo', { value: 'eit' }); +assert (obj.foo === 'eit') + +// Objects aren't sealed by default. +var empty = {}; +assert (Object.isSealed (empty) === false); + +// If you make an empty object non-extensible, it is vacuously sealed. +Object.preventExtensions (empty); +assert (Object.isSealed (empty) === true); + +// The same is not true of a non-empty object, unless its properties are all non-configurable. +var hasProp = { fee: 'fie foe fum' }; +Object.preventExtensions (hasProp); +assert (Object.isSealed (hasProp) === false); + +// But make them all non-configurable and the object becomes sealed. +Object.defineProperty (hasProp, 'fee', { configurable: false }); +assert (Object.isSealed (hasProp) === true); + +// The easiest way to seal an object, of course, is Object.seal. +var sealed = {}; +Object.seal (sealed); +assert (Object.isSealed (sealed) === true);