blob: 8f3cf7e14a1e107a2b3b5ecadc8f7197ac7b5913 [file] [log] [blame]
// Copyright 2017 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.
#include "src/builtins/builtins-iterator-gen.h"
#include "src/builtins/growable-fixed-array-gen.h"
#include "src/heap/factory-inl.h"
namespace v8 {
namespace internal {
using compiler::Node;
TNode<Object> IteratorBuiltinsAssembler::GetIteratorMethod(Node* context,
Node* object) {
return GetProperty(context, object, factory()->iterator_symbol());
}
IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
Node* object,
Label* if_exception,
Variable* exception) {
Node* method = GetIteratorMethod(context, object);
return GetIterator(context, object, method, if_exception, exception);
}
IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
Node* object,
Node* method,
Label* if_exception,
Variable* exception) {
GotoIfException(method, if_exception, exception);
Label if_not_callable(this, Label::kDeferred), if_callable(this);
GotoIf(TaggedIsSmi(method), &if_not_callable);
Branch(IsCallable(method), &if_callable, &if_not_callable);
BIND(&if_not_callable);
{
Node* ret = CallRuntime(Runtime::kThrowTypeError, context,
SmiConstant(MessageTemplate::kNotIterable), object);
GotoIfException(ret, if_exception, exception);
Unreachable();
}
BIND(&if_callable);
{
Callable callable = CodeFactory::Call(isolate());
Node* iterator = CallJS(callable, context, method, object);
GotoIfException(iterator, if_exception, exception);
Label get_next(this), if_notobject(this, Label::kDeferred);
GotoIf(TaggedIsSmi(iterator), &if_notobject);
Branch(IsJSReceiver(iterator), &get_next, &if_notobject);
BIND(&if_notobject);
{
Node* ret = CallRuntime(Runtime::kThrowSymbolIteratorInvalid, context);
GotoIfException(ret, if_exception, exception);
Unreachable();
}
BIND(&get_next);
Node* const next = GetProperty(context, iterator, factory()->next_string());
GotoIfException(next, if_exception, exception);
return IteratorRecord{TNode<JSReceiver>::UncheckedCast(iterator),
TNode<Object>::UncheckedCast(next)};
}
}
Node* IteratorBuiltinsAssembler::IteratorStep(
Node* context, const IteratorRecord& iterator, Label* if_done,
Node* fast_iterator_result_map, Label* if_exception, Variable* exception) {
DCHECK_NOT_NULL(if_done);
// 1. a. Let result be ? Invoke(iterator, "next", « »).
Callable callable = CodeFactory::Call(isolate());
Node* result = CallJS(callable, context, iterator.next, iterator.object);
GotoIfException(result, if_exception, exception);
// 3. If Type(result) is not Object, throw a TypeError exception.
Label if_notobject(this, Label::kDeferred), return_result(this);
GotoIf(TaggedIsSmi(result), &if_notobject);
Node* result_map = LoadMap(result);
if (fast_iterator_result_map != nullptr) {
// Fast iterator result case:
Label if_generic(this);
// 4. Return result.
GotoIfNot(WordEqual(result_map, fast_iterator_result_map), &if_generic);
// IteratorComplete
// 2. Return ToBoolean(? Get(iterResult, "done")).
Node* done = LoadObjectField(result, JSIteratorResult::kDoneOffset);
BranchIfToBooleanIsTrue(done, if_done, &return_result);
BIND(&if_generic);
}
// Generic iterator result case:
{
// 3. If Type(result) is not Object, throw a TypeError exception.
GotoIfNot(IsJSReceiverMap(result_map), &if_notobject);
// IteratorComplete
// 2. Return ToBoolean(? Get(iterResult, "done")).
Node* done = GetProperty(context, result, factory()->done_string());
GotoIfException(done, if_exception, exception);
BranchIfToBooleanIsTrue(done, if_done, &return_result);
}
BIND(&if_notobject);
{
Node* ret =
CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context, result);
GotoIfException(ret, if_exception, exception);
Unreachable();
}
BIND(&return_result);
return result;
}
Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
Node* fast_iterator_result_map,
Label* if_exception,
Variable* exception) {
CSA_ASSERT(this, IsJSReceiver(result));
Label exit(this);
VARIABLE(var_value, MachineRepresentation::kTagged);
if (fast_iterator_result_map != nullptr) {
// Fast iterator result case:
Label if_generic(this);
Node* map = LoadMap(result);
GotoIfNot(WordEqual(map, fast_iterator_result_map), &if_generic);
var_value.Bind(LoadObjectField(result, JSIteratorResult::kValueOffset));
Goto(&exit);
BIND(&if_generic);
}
// Generic iterator result case:
{
Node* value = GetProperty(context, result, factory()->value_string());
GotoIfException(value, if_exception, exception);
var_value.Bind(value);
Goto(&exit);
}
BIND(&exit);
return var_value.value();
}
void IteratorBuiltinsAssembler::IteratorCloseOnException(
Node* context, const IteratorRecord& iterator, Label* if_exception,
Variable* exception) {
// Perform ES #sec-iteratorclose when an exception occurs. This simpler
// algorithm does not include redundant steps which are never reachable from
// the spec IteratorClose algorithm.
DCHECK_NOT_NULL(if_exception);
DCHECK_NOT_NULL(exception);
CSA_ASSERT(this, IsNotTheHole(exception->value()));
CSA_ASSERT(this, IsJSReceiver(iterator.object));
// Let return be ? GetMethod(iterator, "return").
Node* method =
GetProperty(context, iterator.object, factory()->return_string());
GotoIfException(method, if_exception, exception);
// If return is undefined, return Completion(completion).
GotoIf(Word32Or(IsUndefined(method), IsNull(method)), if_exception);
{
// Let innerResult be Call(return, iterator, « »).
// If an exception occurs, the original exception remains bound
Node* inner_result =
CallJS(CodeFactory::Call(isolate()), context, method, iterator.object);
GotoIfException(inner_result, if_exception, nullptr);
// (If completion.[[Type]] is throw) return Completion(completion).
Goto(if_exception);
}
}
void IteratorBuiltinsAssembler::IteratorCloseOnException(
Node* context, const IteratorRecord& iterator, Variable* exception) {
Label rethrow(this, Label::kDeferred);
IteratorCloseOnException(context, iterator, &rethrow, exception);
BIND(&rethrow);
CallRuntime(Runtime::kReThrow, context, exception->value());
Unreachable();
}
TNode<JSArray> IteratorBuiltinsAssembler::IterableToList(
TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn) {
Label fast_path(this), slow_path(this), done(this);
TVARIABLE(JSArray, created_list);
// This is a fast-path for ignoring the iterator.
// TODO(petermarshall): Port IterableToListCanBeElided to CSA.
Node* elided =
CallRuntime(Runtime::kIterableToListCanBeElided, context, iterable);
CSA_ASSERT(this, IsBoolean(elided));
Branch(IsTrue(elided), &fast_path, &slow_path);
BIND(&fast_path);
{
TNode<JSArray> input_array = CAST(iterable);
created_list = CAST(CloneFastJSArray(context, input_array));
Goto(&done);
}
BIND(&slow_path);
{
// 1. Let iteratorRecord be ? GetIterator(items, method).
IteratorRecord iterator_record =
GetIterator(context, iterable, iterator_fn);
// 2. Let values be a new empty List.
GrowableFixedArray values(state());
Variable* vars[] = {values.var_array(), values.var_length(),
values.var_capacity()};
Label loop_start(this, 3, vars), loop_end(this);
Goto(&loop_start);
// 3. Let next be true.
// 4. Repeat, while next is not false
BIND(&loop_start);
{
// a. Set next to ? IteratorStep(iteratorRecord).
TNode<Object> next =
CAST(IteratorStep(context, iterator_record, &loop_end));
// b. If next is not false, then
// i. Let nextValue be ? IteratorValue(next).
TNode<Object> next_value = CAST(IteratorValue(context, next));
// ii. Append nextValue to the end of the List values.
values.Push(next_value);
Goto(&loop_start);
}
BIND(&loop_end);
created_list = values.ToJSArray(context);
Goto(&done);
}
BIND(&done);
return created_list.value();
}
TNode<JSArray> IteratorBuiltinsAssembler::IterableToList(
TNode<Context> context, TNode<Object> iterable) {
TNode<Object> method = GetIteratorMethod(context, iterable);
return IterableToList(context, iterable, method);
}
} // namespace internal
} // namespace v8