diff options
Diffstat (limited to 'src/v8natives.js')
-rw-r--r-- | src/v8natives.js | 206 |
1 files changed, 134 insertions, 72 deletions
diff --git a/src/v8natives.js b/src/v8natives.js index 233f8b4d..83b00b0f 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -491,28 +491,29 @@ PropertyDescriptor.prototype.hasSetter = function() { } +// Converts an array returned from Runtime_GetOwnProperty to an actual +// property descriptor. For a description of the array layout please +// see the runtime.cc file. +function ConvertDescriptorArrayToDescriptor(desc_array) { + if (desc_array == false) { + throw 'Internal error: invalid desc_array'; + } -// ES5 section 8.12.1. -function GetOwnProperty(obj, p) { - var desc = new PropertyDescriptor(); - - // GetOwnProperty returns an array indexed by the constants - // defined in macros.py. - // If p is not a property on obj undefined is returned. - var props = %GetOwnProperty(ToObject(obj), ToString(p)); - - if (IS_UNDEFINED(props)) return void 0; + if (IS_UNDEFINED(desc_array)) { + return void 0; + } - // This is an accessor - if (props[IS_ACCESSOR_INDEX]) { - desc.setGet(props[GETTER_INDEX]); - desc.setSet(props[SETTER_INDEX]); + var desc = new PropertyDescriptor(); + // This is an accessor. + if (desc_array[IS_ACCESSOR_INDEX]) { + desc.setGet(desc_array[GETTER_INDEX]); + desc.setSet(desc_array[SETTER_INDEX]); } else { - desc.setValue(props[VALUE_INDEX]); - desc.setWritable(props[WRITABLE_INDEX]); + desc.setValue(desc_array[VALUE_INDEX]); + desc.setWritable(desc_array[WRITABLE_INDEX]); } - desc.setEnumerable(props[ENUMERABLE_INDEX]); - desc.setConfigurable(props[CONFIGURABLE_INDEX]); + desc.setEnumerable(desc_array[ENUMERABLE_INDEX]); + desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]); return desc; } @@ -535,9 +536,27 @@ function HasProperty(obj, p) { } +// ES5 section 8.12.1. +function GetOwnProperty(obj, p) { + // GetOwnProperty returns an array indexed by the constants + // defined in macros.py. + // If p is not a property on obj undefined is returned. + var props = %GetOwnProperty(ToObject(obj), ToString(p)); + + // A false value here means that access checks failed. + if (props == false) return void 0; + + return ConvertDescriptorArrayToDescriptor(props); +} + + // ES5 8.12.9. function DefineOwnProperty(obj, p, desc, should_throw) { - var current = GetOwnProperty(obj, p); + var current_or_access = %GetOwnProperty(ToObject(obj), ToString(p)); + // A false value here means that access checks failed. + if (current_or_access == false) return void 0; + + var current = ConvertDescriptorArrayToDescriptor(current_or_access); var extensible = %IsExtensible(ToObject(obj)); // Error handling according to spec. @@ -545,10 +564,12 @@ function DefineOwnProperty(obj, p, desc, should_throw) { if (IS_UNDEFINED(current) && !extensible) throw MakeTypeError("define_disallowed", ["defineProperty"]); - if (!IS_UNDEFINED(current) && !current.isConfigurable()) { + if (!IS_UNDEFINED(current)) { // Step 5 and 6 - if ((!desc.hasEnumerable() || - SameValue(desc.isEnumerable() && current.isEnumerable())) && + if ((IsGenericDescriptor(desc) || + IsDataDescriptor(desc) == IsDataDescriptor(current)) && + (!desc.hasEnumerable() || + SameValue(desc.isEnumerable(), current.isEnumerable())) && (!desc.hasConfigurable() || SameValue(desc.isConfigurable(), current.isConfigurable())) && (!desc.hasWritable() || @@ -561,29 +582,35 @@ function DefineOwnProperty(obj, p, desc, should_throw) { SameValue(desc.getSet(), current.getSet()))) { return true; } - - // Step 7 - if (desc.isConfigurable() || desc.isEnumerable() != current.isEnumerable()) - throw MakeTypeError("redefine_disallowed", ["defineProperty"]); - // Step 9 - if (IsDataDescriptor(current) != IsDataDescriptor(desc)) - throw MakeTypeError("redefine_disallowed", ["defineProperty"]); - // Step 10 - if (IsDataDescriptor(current) && IsDataDescriptor(desc)) { - if (!current.isWritable() && desc.isWritable()) - throw MakeTypeError("redefine_disallowed", ["defineProperty"]); - if (!current.isWritable() && desc.hasValue() && - !SameValue(desc.getValue(), current.getValue())) { - throw MakeTypeError("redefine_disallowed", ["defineProperty"]); - } - } - // Step 11 - if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) { - if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())){ + if (!current.isConfigurable()) { + // Step 7 + if (desc.isConfigurable() || + (desc.hasEnumerable() && + desc.isEnumerable() != current.isEnumerable())) throw MakeTypeError("redefine_disallowed", ["defineProperty"]); + // Step 8 + if (!IsGenericDescriptor(desc)) { + // Step 9a + if (IsDataDescriptor(current) != IsDataDescriptor(desc)) + throw MakeTypeError("redefine_disallowed", ["defineProperty"]); + // Step 10a + if (IsDataDescriptor(current) && IsDataDescriptor(desc)) { + if (!current.isWritable() && desc.isWritable()) + throw MakeTypeError("redefine_disallowed", ["defineProperty"]); + if (!current.isWritable() && desc.hasValue() && + !SameValue(desc.getValue(), current.getValue())) { + throw MakeTypeError("redefine_disallowed", ["defineProperty"]); + } + } + // Step 11 + if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) { + if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())){ + throw MakeTypeError("redefine_disallowed", ["defineProperty"]); + } + if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) + throw MakeTypeError("redefine_disallowed", ["defineProperty"]); + } } - if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) - throw MakeTypeError("redefine_disallowed", ["defineProperty"]); } } @@ -607,7 +634,16 @@ function DefineOwnProperty(obj, p, desc, should_throw) { } else flag |= DONT_DELETE; - if (IsDataDescriptor(desc) || IsGenericDescriptor(desc)) { + if (IsDataDescriptor(desc) || + (IsGenericDescriptor(desc) && + (IS_UNDEFINED(current) || IsDataDescriptor(current)))) { + // There are 3 cases that lead here: + // Step 4a - defining a new data property. + // Steps 9b & 12 - replacing an existing accessor property with a data + // property. + // Step 12 - updating an existing data property with a data or generic + // descriptor. + if (desc.hasWritable()) { flag |= desc.isWritable() ? 0 : READ_ONLY; } else if (!IS_UNDEFINED(current)) { @@ -615,20 +651,30 @@ function DefineOwnProperty(obj, p, desc, should_throw) { } else { flag |= READ_ONLY; } + var value = void 0; // Default value is undefined. if (desc.hasValue()) { value = desc.getValue(); - } else if (!IS_UNDEFINED(current)) { + } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) { value = current.getValue(); } + %DefineOrRedefineDataProperty(obj, p, value, flag); + } else if (IsGenericDescriptor(desc)) { + // Step 12 - updating an existing accessor property with generic + // descriptor. Changing flags only. + %DefineOrRedefineAccessorProperty(obj, p, GETTER, current.getGet(), flag); } else { - if (desc.hasGetter() && - (IS_FUNCTION(desc.getGet()) || IS_UNDEFINED(desc.getGet()))) { - %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag); + // There are 3 cases that lead here: + // Step 4b - defining a new accessor property. + // Steps 9c & 12 - replacing an existing data property with an accessor + // property. + // Step 12 - updating an existing accessor property with an accessor + // descriptor. + if (desc.hasGetter()) { + %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag); } - if (desc.hasSetter() && - (IS_FUNCTION(desc.getSet()) || IS_UNDEFINED(desc.getSet()))) { + if (desc.hasSetter()) { %DefineOrRedefineAccessorProperty(obj, p, SETTER, desc.getSet(), flag); } } @@ -1107,33 +1153,49 @@ function FunctionBind(this_arg) { // Length is 1. } // this_arg is not an argument that should be bound. var argc_bound = (%_ArgumentsLength() || 1) - 1; - if (argc_bound > 0) { + var fn = this; + if (argc_bound == 0) { + var result = function() { + if (%_IsConstructCall()) { + // %NewObjectFromBound implicitly uses arguments passed to this + // function. We do not pass the arguments object explicitly to avoid + // materializing it and guarantee that this function will be optimized. + return %NewObjectFromBound(fn, null); + } + + return fn.apply(this_arg, arguments); + }; + } else { var bound_args = new $Array(argc_bound); for(var i = 0; i < argc_bound; i++) { bound_args[i] = %_Arguments(i+1); } + + var result = function() { + // If this is a construct call we use a special runtime method + // to generate the actual object using the bound function. + if (%_IsConstructCall()) { + // %NewObjectFromBound implicitly uses arguments passed to this + // function. We do not pass the arguments object explicitly to avoid + // materializing it and guarantee that this function will be optimized. + return %NewObjectFromBound(fn, bound_args); + } + + // Combine the args we got from the bind call with the args + // given as argument to the invocation. + var argc = %_ArgumentsLength(); + var args = new $Array(argc + argc_bound); + // Add bound arguments. + for (var i = 0; i < argc_bound; i++) { + args[i] = bound_args[i]; + } + // Add arguments from call. + for (var i = 0; i < argc; i++) { + args[argc_bound + i] = %_Arguments(i); + } + return fn.apply(this_arg, args); + }; } - var fn = this; - var result = function() { - // Combine the args we got from the bind call with the args - // given as argument to the invocation. - var argc = %_ArgumentsLength(); - var args = new $Array(argc + argc_bound); - // Add bound arguments. - for (var i = 0; i < argc_bound; i++) { - args[i] = bound_args[i]; - } - // Add arguments from call. - for (var i = 0; i < argc; i++) { - args[argc_bound + i] = %_Arguments(i); - } - // If this is a construct call we use a special runtime method - // to generate the actual object using the bound function. - if (%_IsConstructCall()) { - return %NewObjectFromBound(fn, args); - } - return fn.apply(this_arg, args); - }; // We already have caller and arguments properties on functions, // which are non-configurable. It therefore makes no sence to |