每个实例状态


【Per-instance state】

Node.js 有一个 “Node.js 实例”的概念,通常被称为 node::Environment。每个 node::Environment 关联着:

【Node.js has a concept of a “Node.js instance”, that is commonly being referred to as node::Environment. Each node::Environment is associated with:】

  • 恰好一个 v8::Isolate,即一个 JS 引擎实例,
  • 恰好一个 uv_loop_t,即一个事件循环,并且
  • 多个 v8::Context,但只有一个主要的 v8::Context
  • 一个 node::IsolateData 实例,它包含可以被使用同一个 v8::Isolate 的多个 node::Environment 共享的信息。目前,还没有针对这种情况进行测试。

为了设置 v8::Isolate,需要提供一个 v8::ArrayBuffer::Allocator。一个可能的选择是默认的 Node.js 分配器,可以通过 node::ArrayBufferAllocator::Create() 创建。使用 Node.js 分配器可以在插件使用 Node.js C++ Buffer API 时实现一些性能优化,并且在 process.memoryUsage() 中跟踪 ArrayBuffer 内存是必须的。

【In order to set up a v8::Isolate, an v8::ArrayBuffer::Allocator needs to be provided. One possible choice is the default Node.js allocator, which can be created through node::ArrayBufferAllocator::Create(). Using the Node.js allocator allows minor performance optimizations when addons use the Node.js C++ Buffer API, and is required in order to track ArrayBuffer memory in process.memoryUsage().】

此外,每个用于 Node.js 实例的 v8::Isolate 都需要在使用的情况下向 MultiIsolatePlatform 实例注册和注销,以便平台知道 v8::Isolate 调度任务所使用的事件循环。

【Additionally, each v8::Isolate that is used for a Node.js instance needs to be registered and unregistered with the MultiIsolatePlatform instance, if one is being used, in order for the platform to know which event loop to use for tasks scheduled by the v8::Isolate.】

node::NewIsolate() 辅助函数创建一个 v8::Isolate,为其设置一些 Node.js 特定的钩子(例如 Node.js 错误处理程序),并自动在平台上注册它。

【The node::NewIsolate() helper function creates a v8::Isolate, sets it up with some Node.js-specific hooks (e.g. the Node.js error handler), and registers it with the platform automatically.】

int RunNodeInstance(MultiIsolatePlatform* platform,
                    const std::vector<std::string>& args,
                    const std::vector<std::string>& exec_args) {
  int exit_code = 0;

  // Setup up a libuv event loop, v8::Isolate, and Node.js Environment.
  std::vector<std::string> errors;
  std::unique_ptr<CommonEnvironmentSetup> setup =
      CommonEnvironmentSetup::Create(platform, &errors, args, exec_args);
  if (!setup) {
    for (const std::string& err : errors)
      fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str());
    return 1;
  }

  Isolate* isolate = setup->isolate();
  Environment* env = setup->env();

  {
    Locker locker(isolate);
    Isolate::Scope isolate_scope(isolate);
    HandleScope handle_scope(isolate);
    // The v8::Context needs to be entered when node::CreateEnvironment() and
    // node::LoadEnvironment() are being called.
    Context::Scope context_scope(setup->context());

    // Set up the Node.js instance for execution, and run code inside of it.
    // There is also a variant that takes a callback and provides it with
    // the `require` and `process` objects, so that it can manually compile
    // and run scripts as needed.
    // The `require` function inside this script does *not* access the file
    // system, and can only load built-in Node.js modules.
    // `module.createRequire()` is being used to create one that is able to
    // load files from the disk, and uses the standard CommonJS file loader
    // instead of the internal-only `require` function.
    MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
        env,
        "const publicRequire ="
        "  require('node:module').createRequire(process.cwd() + '/');"
        "globalThis.require = publicRequire;"
        "require('node:vm').runInThisContext(process.argv[1]);");

    if (loadenv_ret.IsEmpty())  // There has been a JS exception.
      return 1;

    exit_code = node::SpinEventLoop(env).FromMaybe(1);

    // node::Stop() can be used to explicitly stop the event loop and keep
    // further JavaScript from running. It can be called from any thread,
    // and will act like worker.terminate() if called from another thread.
    node::Stop(env);
  }

  return exit_code;
}