如何在C/C++中嵌入Node.js解释器?

flseospp  于 2023-02-20  发布在  C/C++
关注(0)|答案(7)|浏览(410)

我想在我的C/C应用程序中使用Node.js脚本,有人建议我从v8、libev和libeio开始;但这意味着要从头重写Node.js。
那么,是否可以将Node.js嵌入到C或C
中呢?

kognpnkq

kognpnkq1#

您应该首先考虑是否 * 足以将应用程序实现为Node的C++模块 *,然后 * 将主要部分粘合为Node脚本 *。
否则,您可能希望“重新实现Node”,方法是以核心代码为例,删除不需要的部分(例如HTTP模块),然后把你的组件放进去。最不痛苦的方法是做一个子树合并,然后剥离构建系统,然后在生成脚本中添加前缀以指向它所在的目录。然后,您可以停止生成某些部分。但是,节点“的构建系统包含几个部分,这可能是一项相当困难的工作。
你也可以尝试用默认加载的东西重新打包Node,并更改可执行文件的名称。然而,这只是采用我所描述的第一种方法的一种更复杂的方法,你可以只在/usr/bin/中安装一个脚本,它将如下所示:

#!/usr/bin/node
  var myAppMain = require('libmyApp');
  myAppMain.withConfig(filename,
  function(err, cnf) {
     if (err) throw err; // parser or file access error
     cnf.evalMe();
  });

您可以使用JSlint作为解析器,然后使用grep作为危险调用,然后使用eval(conf_script),或者只使用require(config.js),尽管您需要添加exports.someMethod = function (...) {...}。但是,您可能希望实现一个预先处理器,它将替换exports.someMethod = function (...) {...}而不是函数,并将附加require('OnlyCallMySafeMethods')并拒绝对require('fs')或其他库的任何尝试你可能害怕让别人使用。这种安全性只是你 * 可能希望拥有 * 的一个可选的东西,这完全取决于你。虽然我想你可能想用exports.someMethod = ....替换,并在顶部添加一个require('myAppConfigLib),这样用户就可以使用你的API加上任何他们可能希望放在他们的脚本/配置!

***UPDATE:***在src/node.js的第66行有一个非常有用的注解:

// To allow people to extend Node in different ways, this hook allows
  // one to drop a file lib/_third_party_main.js into the build
  // directory which will be executed instead of Node's normal loading.

还请注意,src/的内容在构建时被编译为字节码。

wpcxdonn

wpcxdonn2#

嵌入Node.JS现在被Node.JS分支JXcore正式支持。嵌入文档可以从this link获得。

vmjh9lq9

vmjh9lq93#

我已经建立了一些东西接近我认为你在寻找:
https://github.com/ZECTBynmo/tacnode
这是一个允许node.js静态链接到C++应用程序的库,它肯定不是很完善,但我已经用它来启动简单的节点脚本。

vuktfyat

vuktfyat4#

官方文档中有一页解释了how to embed Node.js into C++是如何工作的。这是从Node 12.x开始就有的。
有一个full example in the Node repo。为方便起见,复制如下:

#ifdef NDEBUG
#undef NDEBUG
#endif
#include "node.h"
#include "uv.h"
#include <assert.h>

// Note: This file is being referred to from doc/api/embedding.md, and excerpts
// from it are included in the documentation. Try to keep these in sync.
// Snapshot support is not part of the embedder API docs yet due to its
// experimental nature, although it is of course documented in node.h.

using node::CommonEnvironmentSetup;
using node::Environment;
using node::MultiIsolatePlatform;
using v8::Context;
using v8::HandleScope;
using v8::Isolate;
using v8::Locker;
using v8::MaybeLocal;
using v8::V8;
using v8::Value;

static int RunNodeInstance(MultiIsolatePlatform* platform,
                           const std::vector<std::string>& args,
                           const std::vector<std::string>& exec_args);

int main(int argc, char** argv) {
  argv = uv_setup_args(argc, argv);
  std::vector<std::string> args(argv, argv + argc);
  std::unique_ptr<node::InitializationResult> result =
      node::InitializeOncePerProcess(
          args,
          {node::ProcessInitializationFlags::kNoInitializeV8,
           node::ProcessInitializationFlags::kNoInitializeNodeV8Platform});

  for (const std::string& error : result->errors())
    fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
  if (result->early_return() != 0) {
    return result->exit_code();
  }

  std::unique_ptr<MultiIsolatePlatform> platform =
      MultiIsolatePlatform::Create(4);
  V8::InitializePlatform(platform.get());
  V8::Initialize();

  int ret =
      RunNodeInstance(platform.get(), result->args(), result->exec_args());

  V8::Dispose();
  V8::DisposePlatform();

  node::TearDownOncePerProcess();
  return ret;
}

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

  node::EmbedderSnapshotData::Pointer snapshot;
  auto snapshot_build_mode_it =
      std::find(args.begin(), args.end(), "--embedder-snapshot-create");
  auto snapshot_arg_it =
      std::find(args.begin(), args.end(), "--embedder-snapshot-blob");
  auto snapshot_as_file_it =
      std::find(args.begin(), args.end(), "--embedder-snapshot-as-file");
  if (snapshot_arg_it < args.end() - 1 &&
      snapshot_build_mode_it == args.end()) {
    const char* filename = (snapshot_arg_it + 1)->c_str();
    FILE* fp = fopen(filename, "r");
    assert(fp != nullptr);
    if (snapshot_as_file_it != args.end()) {
      snapshot = node::EmbedderSnapshotData::FromFile(fp);
    } else {
      uv_fs_t req;
      int statret = uv_fs_stat(nullptr, &req, filename, nullptr);
      assert(statret == 0);
      size_t filesize = req.statbuf.st_size;
      uv_fs_req_cleanup(&req);

      std::vector<char> vec(filesize);
      size_t read = fread(vec.data(), filesize, 1, fp);
      assert(read == 1);
      snapshot = node::EmbedderSnapshotData::FromBlob(vec);
    }
    assert(snapshot);
    int ret = fclose(fp);
    assert(ret == 0);
  }

  std::vector<std::string> errors;
  std::unique_ptr<CommonEnvironmentSetup> setup =
      snapshot ? CommonEnvironmentSetup::CreateFromSnapshot(
                     platform, &errors, snapshot.get(), args, exec_args)
      : snapshot_build_mode_it != args.end()
          ? CommonEnvironmentSetup::CreateForSnapshotting(
                platform, &errors, args, exec_args)
          : 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);
    Context::Scope context_scope(setup->context());

    MaybeLocal<Value> loadenv_ret;
    if (snapshot) {
      loadenv_ret = node::LoadEnvironment(env, node::StartExecutionCallback{});
    } else {
      loadenv_ret = node::LoadEnvironment(
          env,
          // Snapshots do not support userland require()s (yet)
          "if (!require('v8').startupSnapshot.isBuildingSnapshot()) {"
          "  const publicRequire ="
          "    require('module').createRequire(process.cwd() + '/');"
          "  globalThis.require = publicRequire;"
          "} else globalThis.require = require;"
          "globalThis.embedVars = { nön_ascıı: '🏳️‍🌈' };"
          "require('vm').runInThisContext(process.argv[1]);");
    }

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

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

  if (snapshot_arg_it < args.end() - 1 &&
      snapshot_build_mode_it != args.end()) {
    snapshot = setup->CreateSnapshot();
    assert(snapshot);

    FILE* fp = fopen((snapshot_arg_it + 1)->c_str(), "w");
    assert(fp != nullptr);
    if (snapshot_as_file_it != args.end()) {
      snapshot->ToFile(fp);
    } else {
      const std::vector<char> vec = snapshot->ToBlob();
      size_t written = fwrite(vec.data(), vec.size(), 1, fp);
      assert(written == 1);
    }
    int ret = fclose(fp);
    assert(ret == 0);
  }

  node::Stop(env);

  return exit_code;
}
vawmfj5a

vawmfj5a5#

有可能,V8是用C编写的,node.js可以在V8上运行,但是除非你有一个非常好的理由为什么你要通过C运行javascript,否则你可能会更好地找到一个合适的C库,并直接在C中实现所需的功能。集成脚本语言和本地代码的任务通常不是微不足道的。例如V8 documentation。Qt在c++和javascript之间提供了一个相当不错的集成,而且在脚本和代码之间来回移动对象也不是件小事。

xyhw6mcr

xyhw6mcr6#

我刚刚检查了js-git,它是为Node.js制作的,也依赖于其他一些Node.js模块。
但是,同一个开发人员编写了一个工具tim-task,用来 Package 一些常见的Node.js函数,最重要的是require,并将一些Node.js模块打包在一起,使其不再依赖Node.js。例如,js-git被打包成一个JS文件,可以在浏览器中使用。打包后的文件看起来像this。这可能也可以在纯V8中使用。
这可能对您有用。但是请注意,这种方法将受到限制。

8yoxcaq7

8yoxcaq77#

嵌入节点有很多很好的理由,包括利用NPM的能力。
不幸的是JXCore正在消亡。这篇文章给出了一些替代方案。http://www.goland.org/nodeapps/

相关问题