From 32d9aea9d81f82492396ddf3f952c46f69435bf7 Mon Sep 17 00:00:00 2001 From: "wingo@igalia.com" Date: Mon, 25 Aug 2014 09:12:22 +0000 Subject: [PATCH] Arguments object has @@iterator R=arv@chromium.org, verwaest@chromium.org, rossberg@chromium.org BUG=v8:3391 LOG=N TEST=mjsunit/harmony/arguments-iterator.js Review URL: https://codereview.chromium.org/342453002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23341 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/accessors.cc | 40 ++++++ src/accessors.h | 43 +++--- src/bootstrapper.cc | 31 +++++ src/contexts.h | 4 +- src/runtime.cc | 4 + test/mjsunit/es6/arguments-iterator.js | 230 +++++++++++++++++++++++++++++++++ test/mjsunit/mjsunit.status | 1 + 7 files changed, 331 insertions(+), 22 deletions(-) create mode 100644 test/mjsunit/es6/arguments-iterator.js diff --git a/src/accessors.cc b/src/accessors.cc index c2361b3..9a2431b 100644 --- a/src/accessors.cc +++ b/src/accessors.cc @@ -156,6 +156,46 @@ bool SetPropertyOnInstanceIfInherited( // +// Accessors::ArgumentsIterator +// + + +void Accessors::ArgumentsIteratorGetter( + v8::Local name, const v8::PropertyCallbackInfo& info) { + i::Isolate* isolate = reinterpret_cast(info.GetIsolate()); + DisallowHeapAllocation no_allocation; + HandleScope scope(isolate); + Object* result = isolate->native_context()->array_values_iterator(); + info.GetReturnValue().Set(Utils::ToLocal(Handle(result, isolate))); +} + + +void Accessors::ArgumentsIteratorSetter( + v8::Local name, v8::Local val, + const v8::PropertyCallbackInfo& info) { + i::Isolate* isolate = reinterpret_cast(info.GetIsolate()); + HandleScope scope(isolate); + Handle object = Utils::OpenHandle(*info.This()); + Handle value = Utils::OpenHandle(*val); + + if (SetPropertyOnInstanceIfInherited(isolate, info, name, value)) return; + + LookupIterator it(object, Utils::OpenHandle(*name)); + CHECK(it.HasProperty()); + DCHECK(it.HolderIsReceiverOrHiddenPrototype()); + Object::SetDataProperty(&it, value); +} + + +Handle Accessors::ArgumentsIteratorInfo( + Isolate* isolate, PropertyAttributes attributes) { + Handle name(isolate->native_context()->iterator_symbol(), isolate); + return MakeAccessor(isolate, name, &ArgumentsIteratorGetter, + &ArgumentsIteratorSetter, attributes); +} + + +// // Accessors::ArrayLength // diff --git a/src/accessors.h b/src/accessors.h index 75099a0..8fc1f84 100644 --- a/src/accessors.h +++ b/src/accessors.h @@ -13,27 +13,28 @@ namespace internal { // The list of accessor descriptors. This is a second-order macro // taking a macro to be applied to all accessor descriptor names. -#define ACCESSOR_INFO_LIST(V) \ - V(ArrayLength) \ - V(FunctionArguments) \ - V(FunctionCaller) \ - V(FunctionName) \ - V(FunctionLength) \ - V(FunctionPrototype) \ - V(ScriptColumnOffset) \ - V(ScriptCompilationType) \ - V(ScriptContextData) \ - V(ScriptEvalFromScript) \ - V(ScriptEvalFromScriptPosition) \ - V(ScriptEvalFromFunctionName) \ - V(ScriptId) \ - V(ScriptLineEnds) \ - V(ScriptLineOffset) \ - V(ScriptName) \ - V(ScriptSource) \ - V(ScriptType) \ - V(ScriptSourceUrl) \ - V(ScriptSourceMappingUrl) \ +#define ACCESSOR_INFO_LIST(V) \ + V(ArgumentsIterator) \ + V(ArrayLength) \ + V(FunctionArguments) \ + V(FunctionCaller) \ + V(FunctionName) \ + V(FunctionLength) \ + V(FunctionPrototype) \ + V(ScriptColumnOffset) \ + V(ScriptCompilationType) \ + V(ScriptContextData) \ + V(ScriptEvalFromScript) \ + V(ScriptEvalFromScriptPosition) \ + V(ScriptEvalFromFunctionName) \ + V(ScriptId) \ + V(ScriptLineEnds) \ + V(ScriptLineOffset) \ + V(ScriptName) \ + V(ScriptSource) \ + V(ScriptType) \ + V(ScriptSourceUrl) \ + V(ScriptSourceMappingUrl) \ V(StringLength) // Accessors contains all predefined proxy accessors. diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index fc0af00..7a27035 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1204,6 +1204,7 @@ void Genesis::InitializeGlobal(Handle global_object, DONT_ENUM, Representation::Tagged()); map->AppendDescriptor(&d); } + // @@iterator method is added later. map->set_function_with_prototype(true); map->set_pre_allocated_property_fields(2); @@ -1262,6 +1263,7 @@ void Genesis::InitializeGlobal(Handle global_object, CallbacksDescriptor d(factory->caller_string(), caller, attributes); map->AppendDescriptor(&d); } + // @@iterator method is added later. map->set_function_with_prototype(true); map->set_prototype(native_context()->object_function()->prototype()); @@ -1596,6 +1598,7 @@ void Genesis::InstallNativeFunctions() { INSTALL_NATIVE(Symbol, "symbolIterator", iterator_symbol); INSTALL_NATIVE(Symbol, "symbolUnscopables", unscopables_symbol); + INSTALL_NATIVE(JSFunction, "ArrayValues", array_values_iterator); INSTALL_NATIVE_MATH(abs) INSTALL_NATIVE_MATH(acos) @@ -2039,6 +2042,34 @@ bool Genesis::InstallNatives() { native_context()->set_regexp_result_map(*initial_map); } + // Add @@iterator method to the arguments object maps. + { + PropertyAttributes attribs = DONT_ENUM; + Handle arguments_iterator = + Accessors::ArgumentsIteratorInfo(isolate(), attribs); + { + CallbacksDescriptor d(Handle(native_context()->iterator_symbol()), + arguments_iterator, attribs); + Handle map(native_context()->sloppy_arguments_map()); + Map::EnsureDescriptorSlack(map, 1); + map->AppendDescriptor(&d); + } + { + CallbacksDescriptor d(Handle(native_context()->iterator_symbol()), + arguments_iterator, attribs); + Handle map(native_context()->aliased_arguments_map()); + Map::EnsureDescriptorSlack(map, 1); + map->AppendDescriptor(&d); + } + { + CallbacksDescriptor d(Handle(native_context()->iterator_symbol()), + arguments_iterator, attribs); + Handle map(native_context()->strict_arguments_map()); + Map::EnsureDescriptorSlack(map, 1); + map->AppendDescriptor(&d); + } + } + #ifdef VERIFY_HEAP builtins->ObjectVerify(); #endif diff --git a/src/contexts.h b/src/contexts.h index 63c9955..e8d2291 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -201,7 +201,8 @@ enum BindingFlags { V(MAP_ITERATOR_MAP_INDEX, Map, map_iterator_map) \ V(SET_ITERATOR_MAP_INDEX, Map, set_iterator_map) \ V(ITERATOR_SYMBOL_INDEX, Symbol, iterator_symbol) \ - V(UNSCOPABLES_SYMBOL_INDEX, Symbol, unscopables_symbol) + V(UNSCOPABLES_SYMBOL_INDEX, Symbol, unscopables_symbol) \ + V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) // JSFunctions are pairs (context, function code), sometimes also called // closures. A Context object is used to represent function contexts and @@ -396,6 +397,7 @@ class Context: public FixedArray { SET_ITERATOR_MAP_INDEX, ITERATOR_SYMBOL_INDEX, UNSCOPABLES_SYMBOL_INDEX, + ARRAY_VALUES_ITERATOR_INDEX, // Properties from here are treated as weak references by the full GC. // Scavenge treats them as strong references. diff --git a/src/runtime.cc b/src/runtime.cc index 32ecf52..ea8b81c 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -6011,6 +6011,10 @@ RUNTIME_FUNCTION(Runtime_GetArgumentsProperty) { HandleScope scope(isolate); if (raw_key->IsSymbol()) { + Handle symbol = Handle::cast(raw_key); + if (symbol->Equals(isolate->native_context()->iterator_symbol())) { + return isolate->native_context()->array_values_iterator(); + } // Lookup in the initial Object.prototype object. Handle result; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( diff --git a/test/mjsunit/es6/arguments-iterator.js b/test/mjsunit/es6/arguments-iterator.js new file mode 100644 index 0000000..a65bf8b --- /dev/null +++ b/test/mjsunit/es6/arguments-iterator.js @@ -0,0 +1,230 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + + +// Note in general that "arguments.foo" and "var o = arguments; o.foo" +// are treated differently by full-codegen, and so both cases need to be +// tested. + +function TestDirectArgumentsIteratorProperty() { + assertTrue(arguments.hasOwnProperty(Symbol.iterator)); + assertFalse(arguments.propertyIsEnumerable(Symbol.iterator)); + var descriptor = Object.getOwnPropertyDescriptor(arguments, Symbol.iterator); + assertTrue(descriptor.writable); + assertFalse(descriptor.enumerable); + assertTrue(descriptor.configurable); + assertEquals(descriptor.value, [].values); + assertEquals(arguments[Symbol.iterator], [].values); +} +TestDirectArgumentsIteratorProperty(); + + +function TestIndirectArgumentsIteratorProperty() { + var o = arguments; + assertTrue(o.hasOwnProperty(Symbol.iterator)); + assertFalse(o.propertyIsEnumerable(Symbol.iterator)); + assertEquals(o[Symbol.iterator], [].values); +} +TestIndirectArgumentsIteratorProperty(); + + +function assertIteratorResult(value, done, result) { + assertEquals({value: value, done: done}, result); +} + + +function TestDirectValues1(a, b, c) { + var iterator = arguments[Symbol.iterator](); + assertIteratorResult(a, false, iterator.next()); + assertIteratorResult(b, false, iterator.next()); + assertIteratorResult(c, false, iterator.next()); + assertIteratorResult(undefined, true, iterator.next()); +} +TestDirectValues1(1, 2, 3); + + +function TestIndirectValues1(a, b, c) { + var args = arguments; + var iterator = args[Symbol.iterator](); + assertIteratorResult(a, false, iterator.next()); + assertIteratorResult(b, false, iterator.next()); + assertIteratorResult(c, false, iterator.next()); + assertIteratorResult(undefined, true, iterator.next()); +} +TestIndirectValues1(1, 2, 3); + + +function TestDirectValues2(a, b, c) { + var iterator = arguments[Symbol.iterator](); + assertIteratorResult(a, false, iterator.next()); + assertIteratorResult(b, false, iterator.next()); + assertIteratorResult(c, false, iterator.next()); + assertIteratorResult(undefined, true, iterator.next()); + + arguments[3] = 4; + arguments.length = 4; + assertIteratorResult(undefined, true, iterator.next()); +} +TestDirectValues2(1, 2, 3); + + +function TestIndirectValues2(a, b, c) { + var args = arguments; + var iterator = args[Symbol.iterator](); + assertIteratorResult(a, false, iterator.next()); + assertIteratorResult(b, false, iterator.next()); + assertIteratorResult(c, false, iterator.next()); + assertIteratorResult(undefined, true, iterator.next()); + + arguments[3] = 4; + arguments.length = 4; + assertIteratorResult(undefined, true, iterator.next()); +} +TestIndirectValues2(1, 2, 3); + + +function TestDirectValues3(a, b, c) { + var iterator = arguments[Symbol.iterator](); + assertIteratorResult(a, false, iterator.next()); + assertIteratorResult(b, false, iterator.next()); + + arguments.length = 2; + assertIteratorResult(undefined, true, iterator.next()); +} +TestDirectValues3(1, 2, 3); + + +function TestIndirectValues3(a, b, c) { + var args = arguments; + var iterator = args[Symbol.iterator](); + assertIteratorResult(a, false, iterator.next()); + assertIteratorResult(b, false, iterator.next()); + + arguments.length = 2; + assertIteratorResult(undefined, true, iterator.next()); +} +TestIndirectValues3(1, 2, 3); + + +function TestDirectValues4(a, b, c) { + var iterator = arguments[Symbol.iterator](); + assertIteratorResult(a, false, iterator.next()); + assertIteratorResult(b, false, iterator.next()); + assertIteratorResult(c, false, iterator.next()); + + arguments.length = 4; + assertIteratorResult(undefined, false, iterator.next()); + assertIteratorResult(undefined, true, iterator.next()); +} +TestDirectValues4(1, 2, 3); + + +function TestIndirectValues4(a, b, c) { + var args = arguments; + var iterator = args[Symbol.iterator](); + assertIteratorResult(a, false, iterator.next()); + assertIteratorResult(b, false, iterator.next()); + assertIteratorResult(c, false, iterator.next()); + + arguments.length = 4; + assertIteratorResult(undefined, false, iterator.next()); + assertIteratorResult(undefined, true, iterator.next()); +} +TestIndirectValues4(1, 2, 3); + + +function TestForOf() { + var i = 0; + for (var value of arguments) { + assertEquals(arguments[i++], value); + } + + assertEquals(arguments.length, i); +} +TestForOf(1, 2, 3, 4, 5); + + +function TestAssignmentToIterator() { + var i = 0; + arguments[Symbol.iterator] = [].entries; + for (var entry of arguments) { + assertEquals([i, arguments[i]], entry); + i++; + } + + assertEquals(arguments.length, i); +} +TestAssignmentToIterator(1, 2, 3, 4, 5); + + +function TestArgumentsMutation() { + var i = 0; + for (var x of arguments) { + assertEquals(arguments[i], x); + arguments[i+1] *= 2; + i++; + } + + assertEquals(arguments.length, i); +} +TestArgumentsMutation(1, 2, 3, 4, 5); + + +function TestSloppyArgumentsAliasing(a0, a1, a2, a3, a4) { + var i = 0; + for (var x of arguments) { + assertEquals(arguments[i], x); + a0 = a1; a1 = a2; a3 = a4; + i++; + } + + assertEquals(arguments.length, i); +} +TestSloppyArgumentsAliasing(1, 2, 3, 4, 5); + + +function TestStrictArgumentsAliasing(a0, a1, a2, a3, a4) { + "use strict"; + var i = 0; + for (var x of arguments) { + a0 = a1; a1 = a2; a3 = a4; + assertEquals(arguments[i], x); + i++; + } + + assertEquals(arguments.length, i); +} +TestStrictArgumentsAliasing(1, 2, 3, 4, 5); + + +function TestArgumentsAsProto() { + "use strict"; + + var o = {__proto__:arguments}; + assertSame([].values, o[Symbol.iterator]); + // Make o dict-mode. + %OptimizeObjectForAddingMultipleProperties(o, 0); + assertFalse(o.hasOwnProperty(Symbol.iterator)); + assertSame([].values, o[Symbol.iterator]); + o[Symbol.iterator] = 10; + assertTrue(o.hasOwnProperty(Symbol.iterator)); + assertEquals(10, o[Symbol.iterator]); + assertSame([].values, arguments[Symbol.iterator]); + + // Frozen o. + o = Object.freeze({__proto__:arguments}); + assertSame([].values, o[Symbol.iterator]); + assertFalse(o.hasOwnProperty(Symbol.iterator)); + assertSame([].values, o[Symbol.iterator]); + // This should throw, but currently it doesn't, because + // ExecutableAccessorInfo callbacks don't see the current strict mode. + // See note in accessors.cc:SetPropertyOnInstanceIfInherited. + o[Symbol.iterator] = 10; + assertFalse(o.hasOwnProperty(Symbol.iterator)); + assertEquals([].values, o[Symbol.iterator]); + assertSame([].values, arguments[Symbol.iterator]); +} +TestArgumentsAsProto(); diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status index 636b708..2183c31 100644 --- a/test/mjsunit/mjsunit.status +++ b/test/mjsunit/mjsunit.status @@ -160,6 +160,7 @@ 'es6/iteration-semantics': [PASS, NO_VARIANTS], 'es6/string-iterator': [PASS, NO_VARIANTS], 'es6/typed-array-iterator': [PASS, NO_VARIANTS], + 'es6/arguments-iterator': [PASS, NO_VARIANTS], ############################################################################## # Too slow in debug mode with --stress-opt mode. -- 2.7.4