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}
This commit is contained in:
arv 2015-02-23 15:34:26 -08:00 committed by Commit bot
parent f8abac9331
commit 87f77d6aba
6 changed files with 278 additions and 215 deletions

View File

@ -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) \

View File

@ -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_;
}

View File

@ -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

View File

@ -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);

View File

@ -22,181 +22,182 @@ namespace internal {
// WARNING: RUNTIME_FUNCTION_LIST_ALWAYS_* is a very large macro that caused
// MSVC Intellisense to crash. It was broken into two macros to work around
// this problem. Please avoid large recursive macros whenever possible.
#define RUNTIME_FUNCTION_LIST_ALWAYS_1(F) \
/* Property access */ \
F(GetProperty, 2, 1) \
F(KeyedGetProperty, 2, 1) \
F(DeleteProperty, 3, 1) \
F(HasOwnProperty, 2, 1) \
F(HasProperty, 2, 1) \
F(HasElement, 2, 1) \
F(IsPropertyEnumerable, 2, 1) \
F(GetPropertyNames, 1, 1) \
F(GetPropertyNamesFast, 1, 1) \
F(GetOwnPropertyNames, 2, 1) \
F(GetOwnElementNames, 1, 1) \
F(GetInterceptorInfo, 1, 1) \
F(GetNamedInterceptorPropertyNames, 1, 1) \
F(GetIndexedInterceptorElementNames, 1, 1) \
F(GetArgumentsProperty, 1, 1) \
F(ToFastProperties, 1, 1) \
F(FinishArrayPrototypeSetup, 1, 1) \
F(SpecialArrayFunctions, 0, 1) \
F(IsSloppyModeFunction, 1, 1) \
F(GetDefaultReceiver, 1, 1) \
\
F(SetPrototype, 2, 1) \
F(InternalSetPrototype, 2, 1) \
F(IsInPrototypeChain, 2, 1) \
\
F(GetOwnProperty, 2, 1) \
\
F(IsExtensible, 1, 1) \
F(PreventExtensions, 1, 1) \
\
/* Utilities */ \
F(CheckIsBootstrapping, 0, 1) \
F(GetRootNaN, 0, 1) \
F(Call, -1 /* >= 2 */, 1) \
F(Apply, 5, 1) \
F(GetFunctionDelegate, 1, 1) \
F(GetConstructorDelegate, 1, 1) \
F(DeoptimizeFunction, 1, 1) \
F(ClearFunctionTypeFeedback, 1, 1) \
F(RunningInSimulator, 0, 1) \
F(IsConcurrentRecompilationSupported, 0, 1) \
F(OptimizeFunctionOnNextCall, -1, 1) \
F(OptimizeOsr, 0, 1) \
F(NeverOptimizeFunction, 1, 1) \
F(GetOptimizationStatus, -1, 1) \
F(GetOptimizationCount, 1, 1) \
F(UnblockConcurrentRecompilation, 0, 1) \
F(CompileForOnStackReplacement, 1, 1) \
F(SetAllocationTimeout, -1 /* 2 || 3 */, 1) \
F(SetNativeFlag, 1, 1) \
F(IsConstructor, 1, 1) \
F(SetInlineBuiltinFlag, 1, 1) \
F(StoreArrayLiteralElement, 5, 1) \
F(DebugPrepareStepInIfStepping, 1, 1) \
F(DebugPushPromise, 1, 1) \
F(DebugPopPromise, 0, 1) \
F(DebugPromiseEvent, 1, 1) \
F(DebugAsyncTaskEvent, 1, 1) \
F(PromiseRejectEvent, 3, 1) \
F(PromiseRevokeReject, 1, 1) \
F(PromiseHasHandlerSymbol, 0, 1) \
F(FlattenString, 1, 1) \
F(LoadMutableDouble, 2, 1) \
F(TryMigrateInstance, 1, 1) \
F(NotifyContextDisposed, 0, 1) \
\
/* Array join support */ \
F(PushIfAbsent, 2, 1) \
F(ArrayConcat, 1, 1) \
\
/* Conversions */ \
F(ToBool, 1, 1) \
F(Typeof, 1, 1) \
\
F(StringToNumber, 1, 1) \
F(StringParseInt, 2, 1) \
F(StringParseFloat, 1, 1) \
F(StringToLowerCase, 1, 1) \
F(StringToUpperCase, 1, 1) \
F(StringSplit, 3, 1) \
F(CharFromCode, 1, 1) \
F(URIEscape, 1, 1) \
F(URIUnescape, 1, 1) \
\
F(NumberToInteger, 1, 1) \
F(NumberToIntegerMapMinusZero, 1, 1) \
F(NumberToJSUint32, 1, 1) \
F(NumberToJSInt32, 1, 1) \
\
/* Arithmetic operations */ \
F(NumberAdd, 2, 1) \
F(NumberSub, 2, 1) \
F(NumberMul, 2, 1) \
F(NumberDiv, 2, 1) \
F(NumberMod, 2, 1) \
F(NumberUnaryMinus, 1, 1) \
F(NumberImul, 2, 1) \
\
F(StringBuilderConcat, 3, 1) \
F(StringBuilderJoin, 3, 1) \
F(SparseJoinWithSeparator, 3, 1) \
\
/* Bit operations */ \
F(NumberOr, 2, 1) \
F(NumberAnd, 2, 1) \
F(NumberXor, 2, 1) \
\
F(NumberShl, 2, 1) \
F(NumberShr, 2, 1) \
F(NumberSar, 2, 1) \
\
/* Comparisons */ \
F(NumberEquals, 2, 1) \
F(StringEquals, 2, 1) \
\
F(NumberCompare, 3, 1) \
F(SmiLexicographicCompare, 2, 1) \
\
/* Math */ \
F(MathAcos, 1, 1) \
F(MathAsin, 1, 1) \
F(MathAtan, 1, 1) \
F(MathFloorRT, 1, 1) \
F(MathAtan2, 2, 1) \
F(MathExpRT, 1, 1) \
F(RoundNumber, 1, 1) \
F(MathFround, 1, 1) \
F(RemPiO2, 2, 1) \
\
/* Regular expressions */ \
F(RegExpInitializeAndCompile, 3, 1) \
F(RegExpExecMultiple, 4, 1) \
\
/* JSON */ \
F(ParseJson, 1, 1) \
F(BasicJSONStringify, 1, 1) \
F(QuoteJSONString, 1, 1) \
\
/* Strings */ \
F(StringIndexOf, 3, 1) \
F(StringLastIndexOf, 3, 1) \
F(StringLocaleCompare, 2, 1) \
F(StringReplaceGlobalRegExpWithString, 4, 1) \
F(StringReplaceOneCharWithString, 3, 1) \
F(StringMatch, 3, 1) \
F(StringTrim, 3, 1) \
F(StringToArray, 2, 1) \
F(NewStringWrapper, 1, 1) \
F(NewString, 2, 1) \
F(TruncateString, 2, 1) \
\
/* Numbers */ \
F(NumberToRadixString, 2, 1) \
F(NumberToFixed, 2, 1) \
F(NumberToExponential, 2, 1) \
F(NumberToPrecision, 2, 1) \
F(IsValidSmi, 1, 1) \
\
/* Classes support */ \
F(ToMethod, 2, 1) \
F(HomeObjectSymbol, 0, 1) \
F(DefineClass, 6, 1) \
F(DefineClassMethod, 3, 1) \
F(ClassGetSourceCode, 1, 1) \
F(LoadFromSuper, 3, 1) \
F(LoadKeyedFromSuper, 3, 1) \
F(ThrowConstructorNonCallableError, 0, 1) \
F(ThrowNonMethodError, 0, 1) \
F(ThrowUnsupportedSuperError, 0, 1) \
F(HandleStepInForDerivedConstructors, 1, 1) \
F(StoreToSuper_Strict, 4, 1) \
F(StoreToSuper_Sloppy, 4, 1) \
F(StoreKeyedToSuper_Strict, 4, 1) \
#define RUNTIME_FUNCTION_LIST_ALWAYS_1(F) \
/* Property access */ \
F(GetProperty, 2, 1) \
F(KeyedGetProperty, 2, 1) \
F(DeleteProperty, 3, 1) \
F(HasOwnProperty, 2, 1) \
F(HasProperty, 2, 1) \
F(HasElement, 2, 1) \
F(IsPropertyEnumerable, 2, 1) \
F(GetPropertyNames, 1, 1) \
F(GetPropertyNamesFast, 1, 1) \
F(GetOwnPropertyNames, 2, 1) \
F(GetOwnElementNames, 1, 1) \
F(GetInterceptorInfo, 1, 1) \
F(GetNamedInterceptorPropertyNames, 1, 1) \
F(GetIndexedInterceptorElementNames, 1, 1) \
F(GetArgumentsProperty, 1, 1) \
F(ToFastProperties, 1, 1) \
F(FinishArrayPrototypeSetup, 1, 1) \
F(SpecialArrayFunctions, 0, 1) \
F(IsSloppyModeFunction, 1, 1) \
F(GetDefaultReceiver, 1, 1) \
\
F(SetPrototype, 2, 1) \
F(InternalSetPrototype, 2, 1) \
F(IsInPrototypeChain, 2, 1) \
\
F(GetOwnProperty, 2, 1) \
\
F(IsExtensible, 1, 1) \
F(PreventExtensions, 1, 1) \
\
/* Utilities */ \
F(CheckIsBootstrapping, 0, 1) \
F(GetRootNaN, 0, 1) \
F(Call, -1 /* >= 2 */, 1) \
F(Apply, 5, 1) \
F(GetFunctionDelegate, 1, 1) \
F(GetConstructorDelegate, 1, 1) \
F(DeoptimizeFunction, 1, 1) \
F(ClearFunctionTypeFeedback, 1, 1) \
F(RunningInSimulator, 0, 1) \
F(IsConcurrentRecompilationSupported, 0, 1) \
F(OptimizeFunctionOnNextCall, -1, 1) \
F(OptimizeOsr, 0, 1) \
F(NeverOptimizeFunction, 1, 1) \
F(GetOptimizationStatus, -1, 1) \
F(GetOptimizationCount, 1, 1) \
F(UnblockConcurrentRecompilation, 0, 1) \
F(CompileForOnStackReplacement, 1, 1) \
F(SetAllocationTimeout, -1 /* 2 || 3 */, 1) \
F(SetNativeFlag, 1, 1) \
F(IsConstructor, 1, 1) \
F(SetInlineBuiltinFlag, 1, 1) \
F(StoreArrayLiteralElement, 5, 1) \
F(DebugPrepareStepInIfStepping, 1, 1) \
F(DebugPushPromise, 1, 1) \
F(DebugPopPromise, 0, 1) \
F(DebugPromiseEvent, 1, 1) \
F(DebugAsyncTaskEvent, 1, 1) \
F(PromiseRejectEvent, 3, 1) \
F(PromiseRevokeReject, 1, 1) \
F(PromiseHasHandlerSymbol, 0, 1) \
F(FlattenString, 1, 1) \
F(LoadMutableDouble, 2, 1) \
F(TryMigrateInstance, 1, 1) \
F(NotifyContextDisposed, 0, 1) \
F(ThrowIteratorResultNotAnObject, 1, 1) \
\
/* Array join support */ \
F(PushIfAbsent, 2, 1) \
F(ArrayConcat, 1, 1) \
\
/* Conversions */ \
F(ToBool, 1, 1) \
F(Typeof, 1, 1) \
\
F(StringToNumber, 1, 1) \
F(StringParseInt, 2, 1) \
F(StringParseFloat, 1, 1) \
F(StringToLowerCase, 1, 1) \
F(StringToUpperCase, 1, 1) \
F(StringSplit, 3, 1) \
F(CharFromCode, 1, 1) \
F(URIEscape, 1, 1) \
F(URIUnescape, 1, 1) \
\
F(NumberToInteger, 1, 1) \
F(NumberToIntegerMapMinusZero, 1, 1) \
F(NumberToJSUint32, 1, 1) \
F(NumberToJSInt32, 1, 1) \
\
/* Arithmetic operations */ \
F(NumberAdd, 2, 1) \
F(NumberSub, 2, 1) \
F(NumberMul, 2, 1) \
F(NumberDiv, 2, 1) \
F(NumberMod, 2, 1) \
F(NumberUnaryMinus, 1, 1) \
F(NumberImul, 2, 1) \
\
F(StringBuilderConcat, 3, 1) \
F(StringBuilderJoin, 3, 1) \
F(SparseJoinWithSeparator, 3, 1) \
\
/* Bit operations */ \
F(NumberOr, 2, 1) \
F(NumberAnd, 2, 1) \
F(NumberXor, 2, 1) \
\
F(NumberShl, 2, 1) \
F(NumberShr, 2, 1) \
F(NumberSar, 2, 1) \
\
/* Comparisons */ \
F(NumberEquals, 2, 1) \
F(StringEquals, 2, 1) \
\
F(NumberCompare, 3, 1) \
F(SmiLexicographicCompare, 2, 1) \
\
/* Math */ \
F(MathAcos, 1, 1) \
F(MathAsin, 1, 1) \
F(MathAtan, 1, 1) \
F(MathFloorRT, 1, 1) \
F(MathAtan2, 2, 1) \
F(MathExpRT, 1, 1) \
F(RoundNumber, 1, 1) \
F(MathFround, 1, 1) \
F(RemPiO2, 2, 1) \
\
/* Regular expressions */ \
F(RegExpInitializeAndCompile, 3, 1) \
F(RegExpExecMultiple, 4, 1) \
\
/* JSON */ \
F(ParseJson, 1, 1) \
F(BasicJSONStringify, 1, 1) \
F(QuoteJSONString, 1, 1) \
\
/* Strings */ \
F(StringIndexOf, 3, 1) \
F(StringLastIndexOf, 3, 1) \
F(StringLocaleCompare, 2, 1) \
F(StringReplaceGlobalRegExpWithString, 4, 1) \
F(StringReplaceOneCharWithString, 3, 1) \
F(StringMatch, 3, 1) \
F(StringTrim, 3, 1) \
F(StringToArray, 2, 1) \
F(NewStringWrapper, 1, 1) \
F(NewString, 2, 1) \
F(TruncateString, 2, 1) \
\
/* Numbers */ \
F(NumberToRadixString, 2, 1) \
F(NumberToFixed, 2, 1) \
F(NumberToExponential, 2, 1) \
F(NumberToPrecision, 2, 1) \
F(IsValidSmi, 1, 1) \
\
/* Classes support */ \
F(ToMethod, 2, 1) \
F(HomeObjectSymbol, 0, 1) \
F(DefineClass, 6, 1) \
F(DefineClassMethod, 3, 1) \
F(ClassGetSourceCode, 1, 1) \
F(LoadFromSuper, 3, 1) \
F(LoadKeyedFromSuper, 3, 1) \
F(ThrowConstructorNonCallableError, 0, 1) \
F(ThrowNonMethodError, 0, 1) \
F(ThrowUnsupportedSuperError, 0, 1) \
F(HandleStepInForDerivedConstructors, 1, 1) \
F(StoreToSuper_Strict, 4, 1) \
F(StoreToSuper_Sloppy, 4, 1) \
F(StoreKeyedToSuper_Strict, 4, 1) \
F(StoreKeyedToSuper_Sloppy, 4, 1)

View File

@ -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)');