Spec-Zone.ru › Node.js 14 LTS

C++ встраиваемый API

Node.js предоставляет ряд C++ API, которые можно использовать для выполнения JavaScript в среде Node.js из другого C++ программного обеспечения.

Документация по этим API находится в src/node.h в дереве исходного кода Node.js. Помимо API, предоставляемых Node.js, некоторые необходимые концепции предоставляются API встраиваемого V8.

Поскольку использование Node.js в качестве встроенной библиотеки отличается от написания кода, выполняемого Node.js, изменения, вносящие разрывы в совместимости, не следуют типичной политике устаревания Node.js политики устаревания и могут произойти на каждом релизе semver-major без предварительного предупреждения.

Пример приложения для встраивания

В следующих разделах будет представлен обзор того, как использовать эти API для создания приложения с нуля, которое выполнит эквивалент node -e <code>, т. е. примет фрагмент JavaScript и запустит его в среде, специфичной для Node.js.

Полный код можно найти в дереве исходного кода Node.js.

Настройка состояния на процесс

Node.js требует некоторого управления состоянием на процесс для выполнения:

  • Парсинг аргументов для Node.js параметров командной строки,
  • Требований V8 для каждого процесса, таких как экземпляр v8::Platform.

Следующий пример показывает, как это можно настроить. Некоторые имена классов взяты из node и v8 пространств имен C++ соответственно.

int main(int argc, char** argv) {
  argv = uv_setup_args(argc, argv);
  std::vector<std::string> args(argv, argv + argc);
  std::vector<std::string> exec_args;
  std::vector<std::string> errors;
  // Parse Node.js CLI options, and print any errors that have occurred while
  // trying to parse them.
  int exit_code = node::InitializeNodeWithArgs(&args, &exec_args, &errors);
  for (const std::string& error : errors)
    fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
  if (exit_code != 0) {
    return exit_code;
  }

  // Create a v8::Platform instance. `MultiIsolatePlatform::Create()` is a way
  // to create a v8::Platform instance that Node.js can use when creating
  // Worker threads. When no `MultiIsolatePlatform` instance is present,
  // Worker threads are disabled.
  std::unique_ptr<MultiIsolatePlatform> platform =
      MultiIsolatePlatform::Create(4);
  V8::InitializePlatform(platform.get());
  V8::Initialize();

  // See below for the contents of this function.
  int ret = RunNodeInstance(platform.get(), args, exec_args);

  V8::Dispose();
  V8::ShutdownPlatform();
  return ret;
}

Состояние на экземпляр

Node.js имеет понятие «экземпляра Node.js», которое обычно называется node::Environment. Каждый node::Environment связан с:

  • Ровно одним v8::Isolate, т. е. одним экземпляром JS движка,
  • Ровно одним uv_loop_t, т. е. одной цикловой очередью, и
  • Некоторым количеством v8::Contexts, но ровно одной главной v8::Context.
  • Одним экземпляром node::IsolateData, содержащим информацию, которая может быть разделена между несколькими node::Environments, использующими один и тот же v8::Isolate. В настоящее время тестирование этого сценария не выполняется.

Для настройки v8::Isolate, необходимо предоставить v8::ArrayBuffer::Allocator. Одним из возможных вариантов является стандартный Node.js аллокатор, который можно создать с помощью node::ArrayBufferAllocator::Create(). Использование Node.js аллокатора позволяет оптимизировать производительность, когда плагины используют Node.js C++ Buffer API, и требуется для отслеживания памяти ArrayBuffer в process.memoryUsage().

Кроме того, каждый v8::Isolate, используемый для экземпляра Node.js, необходимо регистрировать и аннулировать в экземпляре MultiIsolatePlatform , если он используется, чтобы платформа знала, какую цикловую очередь использовать для задач, запланированных v8::Isolate.

Вспомогательная функция node::NewIsolate() создает v8::Isolate, настраивает его с некоторыми специфичными для Node.js хуками (например, обработчиком ошибок Node.js) и регистрирует его на платформе автоматически.

int RunNodeInstance(MultiIsolatePlatform* platform,
                    const std::vector<std::string>& args,
                    const std::vector<std::string>& exec_args) {
  int exit_code = 0;
  // Set up a libuv event loop.
  uv_loop_t loop;
  int ret = uv_loop_init(&loop);
  if (ret != 0) {
    fprintf(stderr, "%s: Failed to initialize loop: %s\n",
            args[0].c_str(),
            uv_err_name(ret));
    return 1;
  }

  std::shared_ptr<ArrayBufferAllocator> allocator =
      ArrayBufferAllocator::Create();

  Isolate* isolate = NewIsolate(allocator, &loop, platform);
  if (isolate == nullptr) {
    fprintf(stderr, "%s: Failed to initialize V8 Isolate\n", args[0].c_str());
    return 1;
  }

  {
    Locker locker(isolate);
    Isolate::Scope isolate_scope(isolate);

    // Create a node::IsolateData instance that will later be released using
    // node::FreeIsolateData().
    std::unique_ptr<IsolateData, decltype(&node::FreeIsolateData)> isolate_data(
        node::CreateIsolateData(isolate, &loop, platform, allocator.get()),
        node::FreeIsolateData);

    // Set up a new v8::Context.
    HandleScope handle_scope(isolate);
    Local<Context> context = node::NewContext(isolate);
    if (context.IsEmpty()) {
      fprintf(stderr, "%s: Failed to initialize V8 Context\n", args[0].c_str());
      return 1;
    }

    // The v8::Context needs to be entered when node::CreateEnvironment() and
    // node::LoadEnvironment() are being called.
    Context::Scope context_scope(context);

    // Create a node::Environment instance that will later be released using
    // node::FreeEnvironment().
    std::unique_ptr<Environment, decltype(&node::FreeEnvironment)> env(
        node::CreateEnvironment(isolate_data.get(), context, args, exec_args),
        node::FreeEnvironment);

    // 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.get(),
        "const publicRequire ="
        "  require('module').createRequire(process.cwd() + '/');"
        "globalThis.require = publicRequire;"
        "require('vm').runInThisContext(process.argv[1]);");

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

    {
      // SealHandleScope protects against handle leaks from callbacks.
      SealHandleScope seal(isolate);
      bool more;
      do {
        uv_run(&loop, UV_RUN_DEFAULT);

        // V8 tasks on background threads may end up scheduling new tasks in the
        // foreground, which in turn can keep the event loop going. For example,
        // WebAssembly.compile() may do so.
        platform->DrainTasks(isolate);

        // If there are new tasks, continue.
        more = uv_loop_alive(&loop);
        if (more) continue;

        // node::EmitBeforeExit() is used to emit the 'beforeExit' event on
        // the `process` object.
        node::EmitBeforeExit(env.get());

        // 'beforeExit' can also schedule new work that keeps the event loop
        // running.
        more = uv_loop_alive(&loop);
      } while (more == true);
    }

    // node::EmitExit() returns the current exit code.
    exit_code = node::EmitExit(env.get());

    // 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.get());
  }

  // Unregister the Isolate with the platform and add a listener that is called
  // when the Platform is done cleaning up any state it had associated with
  // the Isolate.
  bool platform_finished = false;
  platform->AddIsolateFinishedCallback(isolate, [](void* data) {
    *static_cast<bool*>(data) = true;
  }, &platform_finished);
  platform->UnregisterIsolate(isolate);
  isolate->Dispose();

  // Wait until the platform has cleaned up all relevant resources.
  while (!platform_finished)
    uv_run(&loop, UV_RUN_ONCE);
  int err = uv_loop_close(&loop);
  assert(err == 0);

  return exit_code;
}

© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v14.x/docs/api/embedding.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API