Fork me on GitHub

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

JavaScript 数组遍历动态增长问题(V8源码解析)

数组 arr 在遍历同时动态增长会发生什么呢

let arr = [1,2]
arr.forEach(item => arr.push(...arr))
console.log(arr)
// 输出: [ 1, 2, 1, 2, 1, 2, 1, 2 ]

并未输出 [1,2] 或 [1,2,1,2...] 无限循环下去

JavaScript引擎:V8源码git地址:https://github.com/v8/v8


vscode打开源码文件夹
暂无图片

V8-MASTER/src/builtins-collections-gen.cc

   数组遍历的循环由 Goto 与 BIND 搭配完成,Goto(adress) 指跳转的目标adress , BIND(adress) 指对应接受的目的地adress

  • 记录数组初始信息:所以遍历时Label loop(this, {&var_index, &var_table}), done_loop(this);
    • var_index:已遍历的长度 var_table:可遍历的容量(长度)done_loop循环完成时的地址
    • 在遍历时 push 并未改变 var_table 指向的值
  • Goto(&loop) 跳到 BIND(&loop) Goto(&done_loop)跳到BIND(&done_loop)
  • callback_not_callable 函数不可调用的话也会跳出去
TF_BUILTIN(MapPrototypeForEach, CollectionsBuiltinsAssembler) {
  const char* const kMethodName = "Map.prototype.forEach"; // Array 继承自 MapCollections
  ......
  // Ensure that {callback} is actually callable.
  Label callback_not_callable(this, Label::kDeferred);
  GotoIf(TaggedIsSmi(callback), &callback_not_callable);
  GotoIfNot(IsCallable(CAST(callback)), &callback_not_callable);

  TVARIABLE(IntPtrT, var_index, IntPtrConstant(0));
  TVARIABLE(OrderedHashMap, var_table,
            CAST(LoadObjectField(CAST(receiver), JSMap::kTableOffset)));
  Label loop(this, {&var_index, &var_table}), done_loop(this); // 记录了数组的初始信息,后期判断是否遍历结束的标志
  Goto(&loop);
  BIND(&loop);
  {
    // 中括号内的依然会执行,只是产生了一个块作用域
    // 代码快内部执行完后,会被回收资源
    // Transition {table} and {index} if there was any modification to
    // the {receiver} while we're iterating.
    TNode<IntPtrT> index = var_index.value();
    TNode<OrderedHashMap> table = var_table.value();
    std::tie(table, index) = Transition<OrderedHashMap>(
        table, index, [](const TNode<OrderedHashMap>, const TNode<IntPtrT>) {});

    // Read the next entry from the {table}, skipping holes.
    TNode<Object> entry_key;
    TNode<IntPtrT> entry_start_position;
    std::tie(entry_key, entry_start_position, index) =
        NextSkipHoles<OrderedHashMap>(table, index, &done_loop);

    // Load the entry value as well.
    TNode<Object> entry_value = LoadFixedArrayElement(
        table, entry_start_position,
        (OrderedHashMap::HashTableStartIndex() + OrderedHashMap::kValueOffset) *
            kTaggedSize);

    // Invoke the {callback} passing the {entry_key}, {entry_value} and the
    // {receiver}.
    Call(context, callback, this_arg, entry_value, entry_key, receiver);

    // Continue with the next entry.
    var_index = index;
    var_table = table;
    Goto(&loop);
  }
  BIND(&done_loop);
  args.PopAndReturn(UndefinedConstant());
  BIND(&callback_not_callable);
  ......
}
暂无图片
  • Goto(&loop) 进入 {中括号循环代码块,运行完后会清除资源} 循环

  • NextSkipHoles<OrderedHashMap>(table, index, &done_loop); 判断是否结束,传入了 &done_loop

  • NextSkipHoles 函数找一下源码

    • GotoIfNot 根据比较条件判断是否 Goto(if_end)
    • done_loop 的值传给了 if_end
暂无图片
CollectionsBuiltinsAssembler::NextSkipHoles(TNode<TableType> table,
                                            TNode<IntPtrT> index,
                                            Label* if_end) {
  // 比较已使用的容量与总容量
  // Compute the used capacity for the {table}.
  TNode<IntPtrT> number_of_buckets =
      LoadAndUntagObjectField(table, TableType::NumberOfBucketsOffset());
  TNode<IntPtrT> number_of_elements =
      LoadAndUntagObjectField(table, TableType::NumberOfElementsOffset());
  TNode<IntPtrT> number_of_deleted_elements = LoadAndUntagObjectField(
      table, TableType::NumberOfDeletedElementsOffset());
  TNode<IntPtrT> used_capacity =
      IntPtrAdd(number_of_elements, number_of_deleted_elements);

  TNode<Object> entry_key;
  TNode<IntPtrT> entry_start_position;
  TVARIABLE(IntPtrT, var_index, index);
  Label loop(this, &var_index), done_loop(this);
  Goto(&loop);
  BIND(&loop);
  {
    GotoIfNot(IntPtrLessThan(var_index.value(), used_capacity), if_end);  //判断是否跳出
    entry_start_position = IntPtrAdd(
        IntPtrMul(var_index.value(), IntPtrConstant(TableType::kEntrySize)),
        number_of_buckets);
    entry_key = UnsafeLoadFixedArrayElement(
        table, entry_start_position,
        TableType::HashTableStartIndex() * kTaggedSize);
    Increment(&var_index);
    Branch(IsTheHole(entry_key), &loop, &done_loop);
}
posted @ 2021-05-10 21:44  365/24/60  阅读(85)  评论(0编辑  收藏  举报