From: wingo@igalia.com Date: Thu, 12 Jun 2014 17:31:54 +0000 (+0000) Subject: For-of calls [Symbol.iterator]() on RHS to get iterator X-Git-Tag: upstream/4.7.83~8693 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dfb1c7dc9e1a0504f108c06bb15fb7c563215203;p=platform%2Fupstream%2Fv8.git For-of calls [Symbol.iterator]() on RHS to get iterator R=rossberg@chromium.org BUG=http://code.google.com/p/v8/issues/detail?id=2735 LOG=N Review URL: https://codereview.chromium.org/332663004 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21820 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/include/v8.h b/include/v8.h index e037ca7..56492f7 100644 --- a/include/v8.h +++ b/include/v8.h @@ -5543,7 +5543,7 @@ class Internals { static const int kNullValueRootIndex = 7; static const int kTrueValueRootIndex = 8; static const int kFalseValueRootIndex = 9; - static const int kEmptyStringRootIndex = 162; + static const int kEmptyStringRootIndex = 163; // The external allocation limit should be below 256 MB on all architectures // to avoid that resource-constrained embedders run low on memory. diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index bb4618a..241991c 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -1273,8 +1273,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { Iteration loop_statement(this, stmt); increment_loop_depth(); - // var iterator = iterable[@@iterator]() - VisitForAccumulatorValue(stmt->assign_iterator()); + // var iterable = subject + VisitForAccumulatorValue(stmt->assign_iterable()); // As with for-in, skip the loop if the iterator is null or undefined. __ CompareRoot(r0, Heap::kUndefinedValueRootIndex); @@ -1282,16 +1282,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { __ CompareRoot(r0, Heap::kNullValueRootIndex); __ b(eq, loop_statement.break_label()); - // Convert the iterator to a JS object. - Label convert, done_convert; - __ JumpIfSmi(r0, &convert); - __ CompareObjectType(r0, r1, r1, FIRST_SPEC_OBJECT_TYPE); - __ b(ge, &done_convert); - __ bind(&convert); - __ push(r0); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ bind(&done_convert); - __ push(r0); + // var iterator = iterable[Symbol.iterator](); + VisitForEffect(stmt->assign_iterator()); // Loop entry. __ bind(loop_statement.continue_label()); diff --git a/src/arm64/full-codegen-arm64.cc b/src/arm64/full-codegen-arm64.cc index ba3b5d0..b74a058 100644 --- a/src/arm64/full-codegen-arm64.cc +++ b/src/arm64/full-codegen-arm64.cc @@ -1280,8 +1280,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { Iteration loop_statement(this, stmt); increment_loop_depth(); - // var iterator = iterable[@@iterator]() - VisitForAccumulatorValue(stmt->assign_iterator()); + // var iterable = subject + VisitForAccumulatorValue(stmt->assign_iterable()); // As with for-in, skip the loop if the iterator is null or undefined. Register iterator = x0; @@ -1290,16 +1290,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { __ JumpIfRoot(iterator, Heap::kNullValueRootIndex, loop_statement.break_label()); - // Convert the iterator to a JS object. - Label convert, done_convert; - __ JumpIfSmi(iterator, &convert); - __ CompareObjectType(iterator, x1, x1, FIRST_SPEC_OBJECT_TYPE); - __ B(ge, &done_convert); - __ Bind(&convert); - __ Push(iterator); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ Bind(&done_convert); - __ Push(iterator); + // var iterator = iterable[Symbol.iterator](); + VisitForEffect(stmt->assign_iterator()); // Loop entry. __ Bind(loop_statement.continue_label()); diff --git a/src/array-iterator.js b/src/array-iterator.js index a96b9cb..9511b6d 100644 --- a/src/array-iterator.js +++ b/src/array-iterator.js @@ -18,6 +18,11 @@ var arrayIterationKindSymbol = GLOBAL_PRIVATE("ArrayIterator#kind"); function ArrayIterator() {} +// TODO(wingo): Update section numbers when ES6 has stabilized. The +// section numbers below are already out of date as of the May 2014 +// draft. + + // 15.4.5.1 CreateArrayIterator Abstract Operation function CreateArrayIterator(array, kind) { var object = ToObject(array); @@ -35,6 +40,12 @@ function CreateIteratorResultObject(value, done) { } +// 22.1.5.2.2 %ArrayIteratorPrototype%[@@iterator] +function ArrayIteratorIterator() { + return this; +} + + // 15.4.5.2.2 ArrayIterator.prototype.next( ) function ArrayIteratorNext() { var iterator = ToObject(this); @@ -98,6 +109,9 @@ function SetUpArrayIterator() { InstallFunctions(ArrayIterator.prototype, DONT_ENUM, $Array( 'next', ArrayIteratorNext )); + %FunctionSetName(ArrayIteratorIterator, '[Symbol.iterator]'); + %SetProperty(ArrayIterator.prototype, symbolIterator, ArrayIteratorIterator, + DONT_ENUM | DONT_DELETE | READ_ONLY); } SetUpArrayIterator(); diff --git a/src/ast.h b/src/ast.h index 0882b25..f79c2d2 100644 --- a/src/ast.h +++ b/src/ast.h @@ -956,11 +956,13 @@ class ForOfStatement V8_FINAL : public ForEachStatement { void Initialize(Expression* each, Expression* subject, Statement* body, + Expression* assign_iterable, Expression* assign_iterator, Expression* next_result, Expression* result_done, Expression* assign_each) { ForEachStatement::Initialize(each, subject, body); + assign_iterable_ = assign_iterable; assign_iterator_ = assign_iterator; next_result_ = next_result; result_done_ = result_done; @@ -971,7 +973,12 @@ class ForOfStatement V8_FINAL : public ForEachStatement { return subject(); } - // var iterator = iterable; + // var iterable = subject; + Expression* assign_iterable() const { + return assign_iterable_; + } + + // var iterator = iterable[Symbol.iterator](); Expression* assign_iterator() const { return assign_iterator_; } @@ -1006,6 +1013,7 @@ class ForOfStatement V8_FINAL : public ForEachStatement { back_edge_id_(GetNextId(zone)) { } + Expression* assign_iterable_; Expression* assign_iterator_; Expression* next_result_; Expression* result_done_; diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 19a4394..87e1d20 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1622,6 +1622,10 @@ void Genesis::InstallExperimentalNativeFunctions() { INSTALL_NATIVE(JSFunction, "DerivedSetTrap", derived_set_trap); INSTALL_NATIVE(JSFunction, "ProxyEnumerate", proxy_enumerate); } + + if (FLAG_harmony_symbols) { + INSTALL_NATIVE(Symbol, "symbolIterator", iterator_symbol); + } } #undef INSTALL_NATIVE diff --git a/src/contexts.h b/src/contexts.h index 51a7162..2792c2e 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -187,7 +187,8 @@ enum BindingFlags { generator_object_prototype_map) \ V(ITERATOR_RESULT_MAP_INDEX, Map, iterator_result_map) \ V(MAP_ITERATOR_MAP_INDEX, Map, map_iterator_map) \ - V(SET_ITERATOR_MAP_INDEX, Map, set_iterator_map) + V(SET_ITERATOR_MAP_INDEX, Map, set_iterator_map) \ + V(ITERATOR_SYMBOL_INDEX, Symbol, iterator_symbol) // JSFunctions are pairs (context, function code), sometimes also called // closures. A Context object is used to represent function contexts and @@ -360,6 +361,7 @@ class Context: public FixedArray { ITERATOR_RESULT_MAP_INDEX, MAP_ITERATOR_MAP_INDEX, SET_ITERATOR_MAP_INDEX, + ITERATOR_SYMBOL_INDEX, // Properties from here are treated as weak references by the full GC. // Scavenge treats them as strong references. diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 558b91c..f36a577 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -180,6 +180,7 @@ DEFINE_implication(harmony, harmony_arrays) DEFINE_implication(harmony_modules, harmony_scoping) DEFINE_implication(harmony_collections, harmony_symbols) DEFINE_implication(harmony_generators, harmony_symbols) +DEFINE_implication(harmony_iteration, harmony_symbols) DEFINE_implication(harmony, es_staging) DEFINE_implication(es_staging, harmony_maths) diff --git a/src/generator.js b/src/generator.js index abcb867..a0c2aff 100644 --- a/src/generator.js +++ b/src/generator.js @@ -62,6 +62,7 @@ function SetUpGenerators() { DONT_ENUM | DONT_DELETE | READ_ONLY, ["next", GeneratorObjectNext, "throw", GeneratorObjectThrow]); + %FunctionSetName(GeneratorObjectIterator, '[Symbol.iterator]'); %SetProperty(GeneratorObjectPrototype, symbolIterator, GeneratorObjectIterator, DONT_ENUM | DONT_DELETE | READ_ONLY); %SetProperty(GeneratorObjectPrototype, "constructor", diff --git a/src/heap.h b/src/heap.h index b9da734..c50107f 100644 --- a/src/heap.h +++ b/src/heap.h @@ -277,6 +277,7 @@ namespace internal { V(constructor_string, "constructor") \ V(dot_result_string, ".result") \ V(dot_for_string, ".for.") \ + V(dot_iterable_string, ".iterable") \ V(dot_iterator_string, ".iterator") \ V(dot_generator_object_string, ".generator_object") \ V(eval_string, "eval") \ diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 030e6f6..c9e280d 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -1214,8 +1214,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { Iteration loop_statement(this, stmt); increment_loop_depth(); - // var iterator = iterable[@@iterator]() - VisitForAccumulatorValue(stmt->assign_iterator()); + // var iterable = subject + VisitForAccumulatorValue(stmt->assign_iterable()); // As with for-in, skip the loop if the iterator is null or undefined. __ CompareRoot(eax, Heap::kUndefinedValueRootIndex); @@ -1223,15 +1223,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { __ CompareRoot(eax, Heap::kNullValueRootIndex); __ j(equal, loop_statement.break_label()); - // Convert the iterator to a JS object. - Label convert, done_convert; - __ JumpIfSmi(eax, &convert); - __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx); - __ j(above_equal, &done_convert); - __ bind(&convert); - __ push(eax); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ bind(&done_convert); + // var iterator = iterable[Symbol.iterator](); + VisitForEffect(stmt->assign_iterator()); // Loop entry. __ bind(loop_statement.continue_label()); diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc index da4756a..9b613b4 100644 --- a/src/mips/full-codegen-mips.cc +++ b/src/mips/full-codegen-mips.cc @@ -1282,8 +1282,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { Iteration loop_statement(this, stmt); increment_loop_depth(); - // var iterator = iterable[@@iterator]() - VisitForAccumulatorValue(stmt->assign_iterator()); + // var iterable = subject + VisitForAccumulatorValue(stmt->assign_iterable()); __ mov(a0, v0); // As with for-in, skip the loop if the iterator is null or undefined. @@ -1292,17 +1292,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { __ LoadRoot(at, Heap::kNullValueRootIndex); __ Branch(loop_statement.break_label(), eq, a0, Operand(at)); - // Convert the iterator to a JS object. - Label convert, done_convert; - __ JumpIfSmi(a0, &convert); - __ GetObjectType(a0, a1, a1); - __ Branch(&done_convert, ge, a1, Operand(FIRST_SPEC_OBJECT_TYPE)); - __ bind(&convert); - __ push(a0); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ mov(a0, v0); - __ bind(&done_convert); - __ push(a0); + // var iterator = iterable[Symbol.iterator](); + VisitForEffect(stmt->assign_iterator()); // Loop entry. __ bind(loop_statement.continue_label()); diff --git a/src/parser.cc b/src/parser.cc index 5106556..9bca3d5 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -2778,21 +2778,46 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt, if (for_of != NULL) { Factory* heap_factory = isolate()->factory(); + Variable* iterable = scope_->DeclarationScope()->NewTemporary( + heap_factory->dot_iterable_string()); Variable* iterator = scope_->DeclarationScope()->NewTemporary( heap_factory->dot_iterator_string()); Variable* result = scope_->DeclarationScope()->NewTemporary( heap_factory->dot_result_string()); + Expression* assign_iterable; Expression* assign_iterator; Expression* next_result; Expression* result_done; Expression* assign_each; - // var iterator = iterable; + // var iterable = subject; { + Expression* iterable_proxy = factory()->NewVariableProxy(iterable); + assign_iterable = factory()->NewAssignment( + Token::ASSIGN, iterable_proxy, subject, subject->position()); + } + + // var iterator = iterable[Symbol.iterator](); + { + Expression* iterable_proxy = factory()->NewVariableProxy(iterable); + Handle iterator_symbol( + isolate()->native_context()->iterator_symbol(), isolate()); + Expression* iterator_symbol_literal = factory()->NewLiteral( + iterator_symbol, RelocInfo::kNoPosition); + // FIXME(wingo): Unhappily, it will be a common error that the RHS of a + // for-of doesn't have a Symbol.iterator property. We should do better + // than informing the user that "undefined is not a function". + int pos = subject->position(); + Expression* iterator_property = factory()->NewProperty( + iterable_proxy, iterator_symbol_literal, pos); + ZoneList* iterator_arguments = + new(zone()) ZoneList(0, zone()); + Expression* iterator_call = factory()->NewCall( + iterator_property, iterator_arguments, pos); Expression* iterator_proxy = factory()->NewVariableProxy(iterator); assign_iterator = factory()->NewAssignment( - Token::ASSIGN, iterator_proxy, subject, RelocInfo::kNoPosition); + Token::ASSIGN, iterator_proxy, iterator_call, RelocInfo::kNoPosition); } // var result = iterator.next(); @@ -2832,7 +2857,11 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt, } for_of->Initialize(each, subject, body, - assign_iterator, next_result, result_done, assign_each); + assign_iterable, + assign_iterator, + next_result, + result_done, + assign_each); } else { stmt->Initialize(each, subject, body); } diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 8de4226..1816abe 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -1252,24 +1252,17 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { Iteration loop_statement(this, stmt); increment_loop_depth(); - // var iterator = iterable[@@iterator]() - VisitForAccumulatorValue(stmt->assign_iterator()); + // var iterable = subject + VisitForAccumulatorValue(stmt->assign_iterable()); - // As with for-in, skip the loop if the iterator is null or undefined. + // As with for-in, skip the loop if the iterable is null or undefined. __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); __ j(equal, loop_statement.break_label()); __ CompareRoot(rax, Heap::kNullValueRootIndex); __ j(equal, loop_statement.break_label()); - // Convert the iterator to a JS object. - Label convert, done_convert; - __ JumpIfSmi(rax, &convert); - __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx); - __ j(above_equal, &done_convert); - __ bind(&convert); - __ Push(rax); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ bind(&done_convert); + // var iterator = iterable[Symbol.iterator](); + VisitForEffect(stmt->assign_iterator()); // Loop entry. __ bind(loop_statement.continue_label()); diff --git a/src/x87/full-codegen-x87.cc b/src/x87/full-codegen-x87.cc index d5a9886..05daab6 100644 --- a/src/x87/full-codegen-x87.cc +++ b/src/x87/full-codegen-x87.cc @@ -1211,8 +1211,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { Iteration loop_statement(this, stmt); increment_loop_depth(); - // var iterator = iterable[@@iterator]() - VisitForAccumulatorValue(stmt->assign_iterator()); + // var iterable = subject + VisitForAccumulatorValue(stmt->assign_iterable()); // As with for-in, skip the loop if the iterator is null or undefined. __ CompareRoot(eax, Heap::kUndefinedValueRootIndex); @@ -1220,15 +1220,8 @@ void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { __ CompareRoot(eax, Heap::kNullValueRootIndex); __ j(equal, loop_statement.break_label()); - // Convert the iterator to a JS object. - Label convert, done_convert; - __ JumpIfSmi(eax, &convert); - __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx); - __ j(above_equal, &done_convert); - __ bind(&convert); - __ push(eax); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ bind(&done_convert); + // var iterator = iterable[Symbol.iterator](); + VisitForEffect(stmt->assign_iterator()); // Loop entry. __ bind(loop_statement.continue_label()); diff --git a/test/mjsunit/harmony/iteration-semantics.js b/test/mjsunit/harmony/iteration-semantics.js index 2449115..27033e1 100644 --- a/test/mjsunit/harmony/iteration-semantics.js +++ b/test/mjsunit/harmony/iteration-semantics.js @@ -27,6 +27,7 @@ // Flags: --harmony-iteration // Flags: --harmony-generators --harmony-scoping --harmony-proxies +// Flags: --harmony-symbols // Test for-of semantics. @@ -41,13 +42,19 @@ function* values() { } } +function wrap_iterator(iterator) { + var iterable = {}; + iterable[Symbol.iterator] = function() { return iterator; }; + return iterable; +} + function integers_until(max) { function next() { var ret = { value: this.n, done: this.n == max }; this.n++; return ret; } - return { next: next, n: 0 } + return wrap_iterator({ next: next, n: 0 }); } function results(results) { @@ -55,7 +62,7 @@ function results(results) { function next() { return results[i++]; } - return { next: next } + return wrap_iterator({ next: next }); } function* integers_from(n) { @@ -72,44 +79,44 @@ function sum(x, tail) { return x + tail; } -function fold(cons, seed, iter) { - for (var x of iter) { +function fold(cons, seed, iterable) { + for (var x of iterable) { seed = cons(x, seed); } return seed; } -function* take(iter, n) { +function* take(iterable, n) { if (n == 0) return; - for (let x of iter) { + for (let x of iterable) { yield x; if (--n == 0) break; } } -function nth(iter, n) { - for (let x of iter) { +function nth(iterable, n) { + for (let x of iterable) { if (n-- == 0) return x; } throw "unreachable"; } -function* skip_every(iter, n) { +function* skip_every(iterable, n) { var i = 0; - for (let x of iter) { + for (let x of iterable) { if (++i % n == 0) continue; yield x; } } -function* iter_map(iter, f) { - for (var x of iter) { +function* iter_map(iterable, f) { + for (var x of iterable) { yield f(x); } } -function nested_fold(cons, seed, iter) { +function nested_fold(cons, seed, iterable) { var visited = [] - for (let x of iter) { + for (let x of iterable) { for (let y of x) { seed = cons(y, seed); } @@ -117,8 +124,8 @@ function nested_fold(cons, seed, iter) { return seed; } -function* unreachable(iter) { - for (let x of iter) { +function* unreachable(iterable) { + for (let x of iterable) { throw "not reached"; } } @@ -141,17 +148,19 @@ function never_getter(o, prop) { return o; } -function remove_next_after(iter, n) { +function remove_next_after(iterable, n) { + var iterator = iterable[Symbol.iterator](); function next() { if (n-- == 0) delete this.next; - return iter.next(); + return iterator.next(); } - return { next: next } + return wrap_iterator({ next: next }); } -function poison_next_after(iter, n) { +function poison_next_after(iterable, n) { + var iterator = iterable[Symbol.iterator](); function next() { - return iter.next(); + return iterator.next(); } function next_getter() { if (n-- < 0) @@ -160,7 +169,7 @@ function poison_next_after(iter, n) { } var o = {}; Object.defineProperty(o, 'next', { get: next_getter }); - return o; + return wrap_iterator(o); } // Now, the tests. @@ -223,33 +232,33 @@ assertEquals(45, assertEquals(45, fold(sum, 0, poison_next_after(integers_until(10), 10))); -function labelled_continue(iter) { +function labelled_continue(iterable) { var n = 0; outer: while (true) { n++; - for (var x of iter) continue outer; + for (var x of iterable) continue outer; break; } return n; } assertEquals(11, labelled_continue(integers_until(10))); -function labelled_break(iter) { +function labelled_break(iterable) { var n = 0; outer: while (true) { n++; - for (var x of iter) break outer; + for (var x of iterable) break outer; } return n; } assertEquals(1, labelled_break(integers_until(10))); // Test continue/break in catch. -function catch_control(iter, k) { +function catch_control(iterable, k) { var n = 0; - for (var x of iter) { + for (var x of iterable) { try { return k(x); } catch (e) { @@ -274,9 +283,9 @@ assertEquals(5, })); // Test continue/break in try. -function try_control(iter, k) { +function try_control(iterable, k) { var n = 0; - for (var x of iter) { + for (var x of iterable) { try { var e = k(x); if (e == "continue") continue; @@ -313,16 +322,17 @@ assertEquals([1, 2], .map(transparent_proxy)))); // Proxy iterators. -function poison_proxy_after(x, n) { - return Proxy.create({ +function poison_proxy_after(iterable, n) { + var iterator = iterable[Symbol.iterator](); + return wrap_iterator(Proxy.create({ get: function(receiver, name) { if (name == 'next' && n-- < 0) throw "unreachable"; - return x[name]; + return iterator[name]; }, // Needed for integers_until(10)'s this.n++. set: function(receiver, name, val) { - return x[name] = val; + return iterator[name] = val; } - }); + })); } assertEquals(45, fold(sum, 0, poison_proxy_after(integers_until(10), 10))); diff --git a/tools/generate-runtime-tests.py b/tools/generate-runtime-tests.py index 1811f9e..a4414be 100755 --- a/tools/generate-runtime-tests.py +++ b/tools/generate-runtime-tests.py @@ -51,7 +51,7 @@ EXPECTED_FUNCTION_COUNT = 358 EXPECTED_FUZZABLE_COUNT = 325 EXPECTED_CCTEST_COUNT = 6 EXPECTED_UNKNOWN_COUNT = 5 -EXPECTED_BUILTINS_COUNT = 797 +EXPECTED_BUILTINS_COUNT = 798 # Don't call these at all.