tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ForOfIterator.cpp (4648B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "js/ForOfIterator.h"
      8 
      9 #include "js/Exception.h"
     10 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     11 #include "vm/Interpreter.h"
     12 #include "vm/Iteration.h"
     13 #include "vm/JSContext.h"
     14 #include "vm/JSObject.h"
     15 
     16 #include "vm/JSContext-inl.h"
     17 #include "vm/JSObject-inl.h"
     18 
     19 using namespace js;
     20 using JS::ForOfIterator;
     21 
     22 bool ForOfIterator::init(HandleValue iterable,
     23                         NonIterableBehavior nonIterableBehavior) {
     24  JSContext* cx = cx_;
     25  RootedObject iterableObj(cx, ToObject(cx, iterable));
     26  if (!iterableObj) {
     27    return false;
     28  }
     29 
     30  MOZ_ASSERT(index == NOT_ARRAY);
     31 
     32  if (IsArrayWithDefaultIterator<MustBePacked::No>(iterableObj, cx)) {
     33    // Array is optimizable.
     34    index = 0;
     35    iterator = iterableObj;
     36    nextMethod.setUndefined();
     37    return true;
     38  }
     39 
     40  MOZ_ASSERT(index == NOT_ARRAY);
     41 
     42  RootedValue callee(cx);
     43  RootedId iteratorId(cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator));
     44  if (!GetProperty(cx, iterableObj, iterable, iteratorId, &callee)) {
     45    return false;
     46  }
     47 
     48  // If obj[@@iterator] is undefined and we were asked to allow non-iterables,
     49  // bail out now without setting iterator.  This will make valueIsIterable(),
     50  // which our caller should check, return false.
     51  if (nonIterableBehavior == AllowNonIterable && callee.isUndefined()) {
     52    return true;
     53  }
     54 
     55  // Throw if obj[@@iterator] isn't callable.
     56  // js::Invoke is about to check for this kind of error anyway, but it would
     57  // throw an inscrutable error message about |method| rather than this nice
     58  // one about |obj|.
     59  if (!callee.isObject() || !callee.toObject().isCallable()) {
     60    UniqueChars bytes =
     61        DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, nullptr);
     62    if (!bytes) {
     63      return false;
     64    }
     65    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
     66                             bytes.get());
     67    return false;
     68  }
     69 
     70  RootedValue res(cx);
     71  if (!js::Call(cx, callee, iterable, &res)) {
     72    return false;
     73  }
     74 
     75  if (!res.isObject()) {
     76    return ThrowCheckIsObject(cx, CheckIsObjectKind::GetIterator);
     77  }
     78 
     79  RootedObject iteratorObj(cx, &res.toObject());
     80  if (!GetProperty(cx, iteratorObj, iteratorObj, cx->names().next, &res)) {
     81    return false;
     82  }
     83 
     84  iterator = iteratorObj;
     85  nextMethod = res;
     86  return true;
     87 }
     88 
     89 inline bool ForOfIterator::nextFromOptimizedArray(MutableHandleValue vp,
     90                                                  bool* done) {
     91  MOZ_ASSERT(index != NOT_ARRAY);
     92 
     93  if (!CheckForInterrupt(cx_)) {
     94    return false;
     95  }
     96 
     97  ArrayObject* arr = &iterator->as<ArrayObject>();
     98 
     99  if (index >= arr->length()) {
    100    vp.setUndefined();
    101    *done = true;
    102    return true;
    103  }
    104  *done = false;
    105 
    106  // Try to get array element via direct access.
    107  if (index < arr->getDenseInitializedLength()) {
    108    vp.set(arr->getDenseElement(index));
    109    if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
    110      ++index;
    111      return true;
    112    }
    113  }
    114 
    115  return GetElement(cx_, iterator, iterator, index++, vp);
    116 }
    117 
    118 bool ForOfIterator::next(MutableHandleValue vp, bool* done) {
    119  MOZ_ASSERT(iterator);
    120  if (index != NOT_ARRAY) {
    121    return nextFromOptimizedArray(vp, done);
    122  }
    123 
    124  RootedValue v(cx_);
    125  if (!js::Call(cx_, nextMethod, iterator, &v)) {
    126    return false;
    127  }
    128 
    129  if (!v.isObject()) {
    130    return ThrowCheckIsObject(cx_, CheckIsObjectKind::IteratorNext);
    131  }
    132 
    133  RootedObject resultObj(cx_, &v.toObject());
    134  if (!GetProperty(cx_, resultObj, resultObj, cx_->names().done, &v)) {
    135    return false;
    136  }
    137 
    138  *done = ToBoolean(v);
    139  if (*done) {
    140    vp.setUndefined();
    141    return true;
    142  }
    143 
    144  return GetProperty(cx_, resultObj, resultObj, cx_->names().value, vp);
    145 }
    146 
    147 void ForOfIterator::closeThrow() {
    148  MOZ_ASSERT(iterator);
    149 
    150  // Don't handle uncatchable exceptions to match `for-of` bytecode behavior,
    151  // which also doesn't run IteratorClose when an interrupt was requested.
    152  if (!cx_->isExceptionPending()) {
    153    return;
    154  }
    155 
    156  // Save the current exception state. The destructor restores the saved
    157  // exception state, unless there's a new pending exception.
    158  JS::AutoSaveExceptionState savedExc(cx_);
    159 
    160  // Perform IteratorClose on the iterator.
    161  MOZ_ALWAYS_TRUE(CloseIterOperation(cx_, iterator, CompletionKind::Throw));
    162 
    163  // CloseIterOperation clears any pending exception.
    164  MOZ_ASSERT(!cx_->isExceptionPending());
    165 }