Home | History | Annotate | Download | only in builtins
      1 // Copyright 2017 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/builtins/builtins-iterator-gen.h"
      6 #include "src/builtins/growable-fixed-array-gen.h"
      7 
      8 #include "src/heap/factory-inl.h"
      9 
     10 namespace v8 {
     11 namespace internal {
     12 
     13 using compiler::Node;
     14 
     15 TNode<Object> IteratorBuiltinsAssembler::GetIteratorMethod(Node* context,
     16                                                            Node* object) {
     17   return GetProperty(context, object, factory()->iterator_symbol());
     18 }
     19 
     20 IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
     21                                                       Node* object,
     22                                                       Label* if_exception,
     23                                                       Variable* exception) {
     24   Node* method = GetIteratorMethod(context, object);
     25   return GetIterator(context, object, method, if_exception, exception);
     26 }
     27 
     28 IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
     29                                                       Node* object,
     30                                                       Node* method,
     31                                                       Label* if_exception,
     32                                                       Variable* exception) {
     33   GotoIfException(method, if_exception, exception);
     34 
     35   Label if_not_callable(this, Label::kDeferred), if_callable(this);
     36   GotoIf(TaggedIsSmi(method), &if_not_callable);
     37   Branch(IsCallable(method), &if_callable, &if_not_callable);
     38 
     39   BIND(&if_not_callable);
     40   {
     41     Node* ret = CallRuntime(Runtime::kThrowTypeError, context,
     42                             SmiConstant(MessageTemplate::kNotIterable), object);
     43     GotoIfException(ret, if_exception, exception);
     44     Unreachable();
     45   }
     46 
     47   BIND(&if_callable);
     48   {
     49     Callable callable = CodeFactory::Call(isolate());
     50     Node* iterator = CallJS(callable, context, method, object);
     51     GotoIfException(iterator, if_exception, exception);
     52 
     53     Label get_next(this), if_notobject(this, Label::kDeferred);
     54     GotoIf(TaggedIsSmi(iterator), &if_notobject);
     55     Branch(IsJSReceiver(iterator), &get_next, &if_notobject);
     56 
     57     BIND(&if_notobject);
     58     {
     59       Node* ret = CallRuntime(Runtime::kThrowSymbolIteratorInvalid, context);
     60       GotoIfException(ret, if_exception, exception);
     61       Unreachable();
     62     }
     63 
     64     BIND(&get_next);
     65     Node* const next = GetProperty(context, iterator, factory()->next_string());
     66     GotoIfException(next, if_exception, exception);
     67 
     68     return IteratorRecord{TNode<JSReceiver>::UncheckedCast(iterator),
     69                           TNode<Object>::UncheckedCast(next)};
     70   }
     71 }
     72 
     73 Node* IteratorBuiltinsAssembler::IteratorStep(
     74     Node* context, const IteratorRecord& iterator, Label* if_done,
     75     Node* fast_iterator_result_map, Label* if_exception, Variable* exception) {
     76   DCHECK_NOT_NULL(if_done);
     77   // 1. a. Let result be ? Invoke(iterator, "next",  ).
     78   Callable callable = CodeFactory::Call(isolate());
     79   Node* result = CallJS(callable, context, iterator.next, iterator.object);
     80   GotoIfException(result, if_exception, exception);
     81 
     82   // 3. If Type(result) is not Object, throw a TypeError exception.
     83   Label if_notobject(this, Label::kDeferred), return_result(this);
     84   GotoIf(TaggedIsSmi(result), &if_notobject);
     85   Node* result_map = LoadMap(result);
     86 
     87   if (fast_iterator_result_map != nullptr) {
     88     // Fast iterator result case:
     89     Label if_generic(this);
     90 
     91     // 4. Return result.
     92     GotoIfNot(WordEqual(result_map, fast_iterator_result_map), &if_generic);
     93 
     94     // IteratorComplete
     95     // 2. Return ToBoolean(? Get(iterResult, "done")).
     96     Node* done = LoadObjectField(result, JSIteratorResult::kDoneOffset);
     97     BranchIfToBooleanIsTrue(done, if_done, &return_result);
     98 
     99     BIND(&if_generic);
    100   }
    101 
    102   // Generic iterator result case:
    103   {
    104     // 3. If Type(result) is not Object, throw a TypeError exception.
    105     GotoIfNot(IsJSReceiverMap(result_map), &if_notobject);
    106 
    107     // IteratorComplete
    108     // 2. Return ToBoolean(? Get(iterResult, "done")).
    109     Node* done = GetProperty(context, result, factory()->done_string());
    110     GotoIfException(done, if_exception, exception);
    111     BranchIfToBooleanIsTrue(done, if_done, &return_result);
    112   }
    113 
    114   BIND(&if_notobject);
    115   {
    116     Node* ret =
    117         CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context, result);
    118     GotoIfException(ret, if_exception, exception);
    119     Unreachable();
    120   }
    121 
    122   BIND(&return_result);
    123   return result;
    124 }
    125 
    126 Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
    127                                                Node* fast_iterator_result_map,
    128                                                Label* if_exception,
    129                                                Variable* exception) {
    130   CSA_ASSERT(this, IsJSReceiver(result));
    131 
    132   Label exit(this);
    133   VARIABLE(var_value, MachineRepresentation::kTagged);
    134   if (fast_iterator_result_map != nullptr) {
    135     // Fast iterator result case:
    136     Label if_generic(this);
    137     Node* map = LoadMap(result);
    138     GotoIfNot(WordEqual(map, fast_iterator_result_map), &if_generic);
    139     var_value.Bind(LoadObjectField(result, JSIteratorResult::kValueOffset));
    140     Goto(&exit);
    141 
    142     BIND(&if_generic);
    143   }
    144 
    145   // Generic iterator result case:
    146   {
    147     Node* value = GetProperty(context, result, factory()->value_string());
    148     GotoIfException(value, if_exception, exception);
    149     var_value.Bind(value);
    150     Goto(&exit);
    151   }
    152 
    153   BIND(&exit);
    154   return var_value.value();
    155 }
    156 
    157 void IteratorBuiltinsAssembler::IteratorCloseOnException(
    158     Node* context, const IteratorRecord& iterator, Label* if_exception,
    159     Variable* exception) {
    160   // Perform ES #sec-iteratorclose when an exception occurs. This simpler
    161   // algorithm does not include redundant steps which are never reachable from
    162   // the spec IteratorClose algorithm.
    163   DCHECK_NOT_NULL(if_exception);
    164   DCHECK_NOT_NULL(exception);
    165   CSA_ASSERT(this, IsNotTheHole(exception->value()));
    166   CSA_ASSERT(this, IsJSReceiver(iterator.object));
    167 
    168   // Let return be ? GetMethod(iterator, "return").
    169   Node* method =
    170       GetProperty(context, iterator.object, factory()->return_string());
    171   GotoIfException(method, if_exception, exception);
    172 
    173   // If return is undefined, return Completion(completion).
    174   GotoIf(Word32Or(IsUndefined(method), IsNull(method)), if_exception);
    175 
    176   {
    177     // Let innerResult be Call(return, iterator,  ).
    178     // If an exception occurs, the original exception remains bound
    179     Node* inner_result =
    180         CallJS(CodeFactory::Call(isolate()), context, method, iterator.object);
    181     GotoIfException(inner_result, if_exception, nullptr);
    182 
    183     // (If completion.[[Type]] is throw) return Completion(completion).
    184     Goto(if_exception);
    185   }
    186 }
    187 
    188 void IteratorBuiltinsAssembler::IteratorCloseOnException(
    189     Node* context, const IteratorRecord& iterator, Variable* exception) {
    190   Label rethrow(this, Label::kDeferred);
    191   IteratorCloseOnException(context, iterator, &rethrow, exception);
    192 
    193   BIND(&rethrow);
    194   CallRuntime(Runtime::kReThrow, context, exception->value());
    195   Unreachable();
    196 }
    197 
    198 TNode<JSArray> IteratorBuiltinsAssembler::IterableToList(
    199     TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn) {
    200   Label fast_path(this), slow_path(this), done(this);
    201 
    202   TVARIABLE(JSArray, created_list);
    203 
    204   Branch(IsFastJSArrayWithNoCustomIteration(iterable, context), &fast_path,
    205          &slow_path);
    206 
    207   // This is a fast-path for ignoring the iterator.
    208   BIND(&fast_path);
    209   {
    210     TNode<JSArray> input_array = CAST(iterable);
    211     created_list = CAST(CloneFastJSArray(context, input_array));
    212     Goto(&done);
    213   }
    214 
    215   BIND(&slow_path);
    216   {
    217     // 1. Let iteratorRecord be ? GetIterator(items, method).
    218     IteratorRecord iterator_record =
    219         GetIterator(context, iterable, iterator_fn);
    220 
    221     // 2. Let values be a new empty List.
    222     GrowableFixedArray values(state());
    223 
    224     Variable* vars[] = {values.var_array(), values.var_length(),
    225                         values.var_capacity()};
    226     Label loop_start(this, 3, vars), loop_end(this);
    227     Goto(&loop_start);
    228     // 3. Let next be true.
    229     // 4. Repeat, while next is not false
    230     BIND(&loop_start);
    231     {
    232       //  a. Set next to ? IteratorStep(iteratorRecord).
    233       TNode<Object> next =
    234           CAST(IteratorStep(context, iterator_record, &loop_end));
    235       //  b. If next is not false, then
    236       //   i. Let nextValue be ? IteratorValue(next).
    237       TNode<Object> next_value = CAST(IteratorValue(context, next));
    238       //   ii. Append nextValue to the end of the List values.
    239       values.Push(next_value);
    240       Goto(&loop_start);
    241     }
    242     BIND(&loop_end);
    243 
    244     created_list = values.ToJSArray(context);
    245     Goto(&done);
    246   }
    247 
    248   BIND(&done);
    249   return created_list.value();
    250 }
    251 
    252 TNode<JSArray> IteratorBuiltinsAssembler::IterableToList(
    253     TNode<Context> context, TNode<Object> iterable) {
    254   TNode<Object> method = GetIteratorMethod(context, iterable);
    255   return IterableToList(context, iterable, method);
    256 }
    257 
    258 }  // namespace internal
    259 }  // namespace v8
    260