Arguments object has @@iterator
authorwingo@igalia.com <wingo@igalia.com>
Mon, 25 Aug 2014 09:12:22 +0000 (09:12 +0000)
committerwingo@igalia.com <wingo@igalia.com>
Mon, 25 Aug 2014 09:12:22 +0000 (09:12 +0000)
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
src/accessors.h
src/bootstrapper.cc
src/contexts.h
src/runtime.cc
test/mjsunit/es6/arguments-iterator.js [new file with mode: 0644]
test/mjsunit/mjsunit.status

index c2361b3..9a2431b 100644 (file)
@@ -156,6 +156,46 @@ bool SetPropertyOnInstanceIfInherited(
 
 
 //
+// Accessors::ArgumentsIterator
+//
+
+
+void Accessors::ArgumentsIteratorGetter(
+    v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
+  DisallowHeapAllocation no_allocation;
+  HandleScope scope(isolate);
+  Object* result = isolate->native_context()->array_values_iterator();
+  info.GetReturnValue().Set(Utils::ToLocal(Handle<Object>(result, isolate)));
+}
+
+
+void Accessors::ArgumentsIteratorSetter(
+    v8::Local<v8::Name> name, v8::Local<v8::Value> val,
+    const v8::PropertyCallbackInfo<void>& info) {
+  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
+  HandleScope scope(isolate);
+  Handle<JSObject> object = Utils::OpenHandle(*info.This());
+  Handle<Object> 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<AccessorInfo> Accessors::ArgumentsIteratorInfo(
+    Isolate* isolate, PropertyAttributes attributes) {
+  Handle<Name> name(isolate->native_context()->iterator_symbol(), isolate);
+  return MakeAccessor(isolate, name, &ArgumentsIteratorGetter,
+                      &ArgumentsIteratorSetter, attributes);
+}
+
+
+//
 // Accessors::ArrayLength
 //
 
index 75099a0..8fc1f84 100644 (file)
@@ -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.
index fc0af00..7a27035 100644 (file)
@@ -1204,6 +1204,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> 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<GlobalObject> 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<AccessorInfo> arguments_iterator =
+        Accessors::ArgumentsIteratorInfo(isolate(), attribs);
+    {
+      CallbacksDescriptor d(Handle<Name>(native_context()->iterator_symbol()),
+                            arguments_iterator, attribs);
+      Handle<Map> map(native_context()->sloppy_arguments_map());
+      Map::EnsureDescriptorSlack(map, 1);
+      map->AppendDescriptor(&d);
+    }
+    {
+      CallbacksDescriptor d(Handle<Name>(native_context()->iterator_symbol()),
+                            arguments_iterator, attribs);
+      Handle<Map> map(native_context()->aliased_arguments_map());
+      Map::EnsureDescriptorSlack(map, 1);
+      map->AppendDescriptor(&d);
+    }
+    {
+      CallbacksDescriptor d(Handle<Name>(native_context()->iterator_symbol()),
+                            arguments_iterator, attribs);
+      Handle<Map> map(native_context()->strict_arguments_map());
+      Map::EnsureDescriptorSlack(map, 1);
+      map->AppendDescriptor(&d);
+    }
+  }
+
 #ifdef VERIFY_HEAP
   builtins->ObjectVerify();
 #endif
index 63c9955..e8d2291 100644 (file)
@@ -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.
index 32ecf52..ea8b81c 100644 (file)
@@ -6011,6 +6011,10 @@ RUNTIME_FUNCTION(Runtime_GetArgumentsProperty) {
 
   HandleScope scope(isolate);
   if (raw_key->IsSymbol()) {
+    Handle<Symbol> symbol = Handle<Symbol>::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<Object> 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 (file)
index 0000000..a65bf8b
--- /dev/null
@@ -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();
index 636b708..2183c31 100644 (file)
   '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.