Commit 87f77d6a authored by arv's avatar arv Committed by Commit bot

for-of should throw if result object is not an object

This is done using desugaring. Before this we had:

  result = iterator.next()

with this we instead do:

  !%_IsSpecObject(result = iterator.next()) &&
      %ThrowIteratorResultNotAnObject(result)

BUG=v8:3916
LOG=N

Review URL: https://codereview.chromium.org/929733003

Cr-Commit-Position: refs/heads/master@{#26805}
parent f8abac93
......@@ -230,38 +230,40 @@ class AstValue : public ZoneObject {
// For generating constants.
#define STRING_CONSTANTS(F) \
F(anonymous_function, "(anonymous function)") \
F(arguments, "arguments") \
F(constructor, "constructor") \
F(default, "default") \
F(done, "done") \
F(dot, ".") \
F(dot_for, ".for") \
F(dot_generator, ".generator") \
F(dot_generator_object, ".generator_object") \
F(dot_iterator, ".iterator") \
F(dot_module, ".module") \
F(dot_result, ".result") \
F(empty, "") \
F(eval, "eval") \
F(get_template_callsite, "GetTemplateCallSite") \
F(initialize_const_global, "initializeConstGlobal") \
F(initialize_var_global, "initializeVarGlobal") \
F(is_construct_call, "_IsConstructCall") \
F(let, "let") \
F(make_reference_error, "MakeReferenceErrorEmbedded") \
F(make_syntax_error, "MakeSyntaxErrorEmbedded") \
F(make_type_error, "MakeTypeErrorEmbedded") \
F(native, "native") \
F(new_target, "new.target") \
F(next, "next") \
F(proto, "__proto__") \
F(prototype, "prototype") \
F(this, "this") \
F(use_asm, "use asm") \
F(use_strong, "use strong") \
F(use_strict, "use strict") \
#define STRING_CONSTANTS(F) \
F(anonymous_function, "(anonymous function)") \
F(arguments, "arguments") \
F(constructor, "constructor") \
F(default, "default") \
F(done, "done") \
F(dot, ".") \
F(dot_for, ".for") \
F(dot_generator, ".generator") \
F(dot_generator_object, ".generator_object") \
F(dot_iterator, ".iterator") \
F(dot_module, ".module") \
F(dot_result, ".result") \
F(empty, "") \
F(eval, "eval") \
F(get_template_callsite, "GetTemplateCallSite") \
F(initialize_const_global, "initializeConstGlobal") \
F(initialize_var_global, "initializeVarGlobal") \
F(is_construct_call, "_IsConstructCall") \
F(is_spec_object, "_IsSpecObject") \
F(let, "let") \
F(make_reference_error, "MakeReferenceErrorEmbedded") \
F(make_syntax_error, "MakeSyntaxErrorEmbedded") \
F(make_type_error, "MakeTypeErrorEmbedded") \
F(native, "native") \
F(new_target, "new.target") \
F(next, "next") \
F(proto, "__proto__") \
F(prototype, "prototype") \
F(this, "this") \
F(throw_iterator_result_not_an_object, "ThrowIteratorResultNotAnObject") \
F(use_asm, "use asm") \
F(use_strong, "use strong") \
F(use_strict, "use strict") \
F(value, "value")
#define OTHER_CONSTANTS(F) \
......
......@@ -942,12 +942,12 @@ class ForOfStatement FINAL : public ForEachStatement {
return subject();
}
// var iterator = subject[Symbol.iterator]();
// iterator = subject[Symbol.iterator]()
Expression* assign_iterator() const {
return assign_iterator_;
}
// var result = iterator.next();
// result = iterator.next() // with type check
Expression* next_result() const {
return next_result_;
}
......
......@@ -2925,25 +2925,51 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt,
Expression* result_done;
Expression* assign_each;
// var iterator = subject[Symbol.iterator]();
// iterator = subject[Symbol.iterator]()
assign_iterator = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(iterator),
GetIterator(subject, factory()), subject->position());
// var result = iterator.next();
// !%_IsSpecObject(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
{
// result = iterator.next()
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
Expression* next_literal = factory()->NewStringLiteral(
ast_value_factory()->next_string(), RelocInfo::kNoPosition);
Expression* next_property = factory()->NewProperty(
iterator_proxy, next_literal, RelocInfo::kNoPosition);
ZoneList<Expression*>* next_arguments =
new(zone()) ZoneList<Expression*>(0, zone());
new (zone()) ZoneList<Expression*>(0, zone());
Expression* next_call = factory()->NewCall(next_property, next_arguments,
subject->position());
Expression* result_proxy = factory()->NewVariableProxy(result);
next_result = factory()->NewAssignment(Token::ASSIGN, result_proxy,
next_call, subject->position());
// %_IsSpecObject(...)
ZoneList<Expression*>* is_spec_object_args =
new (zone()) ZoneList<Expression*>(1, zone());
is_spec_object_args->Add(next_result, zone());
Expression* is_spec_object_call = factory()->NewCallRuntime(
ast_value_factory()->is_spec_object_string(),
Runtime::FunctionForId(Runtime::kInlineIsSpecObject),
is_spec_object_args, subject->position());
// %ThrowIteratorResultNotAnObject(result)
Expression* result_proxy_again = factory()->NewVariableProxy(result);
ZoneList<Expression*>* throw_arguments =
new (zone()) ZoneList<Expression*>(1, zone());
throw_arguments->Add(result_proxy_again, zone());
Expression* throw_call = factory()->NewCallRuntime(
ast_value_factory()->throw_iterator_result_not_an_object_string(),
Runtime::FunctionForId(Runtime::kThrowIteratorResultNotAnObject),
throw_arguments, subject->position());
next_result = factory()->NewBinaryOperation(
Token::AND, factory()->NewUnaryOperation(
Token::NOT, is_spec_object_call, subject->position()),
throw_call, subject->position());
}
// result.done
......
......@@ -55,6 +55,16 @@ RUNTIME_FUNCTION(Runtime_ThrowReferenceError) {
}
RUNTIME_FUNCTION(Runtime_ThrowIteratorResultNotAnObject) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 0);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError("iterator_result_not_an_object", HandleVector(&value, 1)));
}
RUNTIME_FUNCTION(Runtime_PromiseRejectEvent) {
DCHECK(args.length() == 3);
HandleScope scope(isolate);
......
This diff is collapsed.
......@@ -200,9 +200,11 @@ assertEquals([undefined, 1, 2, 3],
// Done.
{ value: 4, done: 42 }])));
// Results that are not objects.
assertEquals([undefined, undefined, undefined],
fold(append, [],
results([10, "foo", /qux/, { value: 37, done: true }])));
assertThrows(function() {
assertEquals([undefined, undefined, undefined],
fold(append, [],
results([10, "foo", /qux/, { value: 37, done: true }])));
}, TypeError);
// Getters (shudder).
assertEquals([1, 2],
fold(append, [],
......@@ -334,3 +336,25 @@ function poison_proxy_after(iterable, n) {
}));
}
assertEquals(45, fold(sum, 0, poison_proxy_after(integers_until(10), 10)));
function test_iterator_result_object_non_object(value, descr) {
var arr = [];
var ex;
var message = 'Iterator result ' + (descr || value) + ' is not an object';
try {
fold(append, arr,
results([{value: 1}, {}, value, {value: 2}, {done: true}]));
} catch (e) {
ex = e;
}
assertInstanceof(ex, TypeError);
assertEquals(message, ex.message);
assertArrayEquals([1, undefined], arr);
}
test_iterator_result_object_non_object(null);
test_iterator_result_object_non_object(undefined);
test_iterator_result_object_non_object(42);
test_iterator_result_object_non_object('abc');
test_iterator_result_object_non_object(false);
test_iterator_result_object_non_object(Symbol('x'), 'Symbol(x)');
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment