开发者

How to embed Node.js interpreter into C/C++?

I want to use Node.js scripts in my C/C++ applications. Some people suggested me to start with v8, libev and libeio; but it means rewri开发者_开发技巧ting Node.js from scratch.

So, is it possible to embed Node.js into C or C++?


You should first consider whether it would be sufficient to implement your application as a C++ module for Node and then glue the main part as a Node script.

Otherwise you may wish to "re-implement Node", by taking the core code as the example and removing the parts which you don't need (e.g. HTTP module) and then putting your components into it. The least painful way would be to do a sub-tree merge and ripping-out the build system, then adding prefixes in the build scripts to point to the directory where it lives. Then you can stop certain parts from being built. However Node's build system contains several parts and it may be quite a difficult job to do.

You can also try re-packaging Node with your stuff loaded by default and changing the name of the executable. However, that is just a more complex way of taking the first approach I have described, you can just install a script in /usr/bin/ which will go as:

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

You can use a JSlint as your parser, then grep for dangerous calls and then eval(conf_script) or just use require(config.js), though you will need to add exports.someMethod = function (...) {...}. But require() is much safer in general, however you may wish to implement a pre-processor for your config which will substitute exports.someMethod = function (...) {...} instead of your functions and will append require('OnlyCallMySafeMethods') and reject any attempts to require('fs') or other lib which you may be afraid of letting the someone to use. This sort of safety is just an optional thing you may wish to have, it's all up to you really. Though I suppose you might want to do the bit with exports.someMethod = .... substitution and have one require('myAppConfigLib) added on the top so the user will just use you API plus anything they may wish to put into their script/config!

UPDATE: There is a quite useful comment on line 66 of src/node.js:

  // 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.

Please also note that the contents of src/ are being compiled to bytecode at build time.


Embedding Node.JS is now officially supported by a Node.JS fork JXcore. Embedding docs are available from this link.


I've build something close to what I think you're looking for:

https://github.com/ZECTBynmo/tacnode

It's a library that allows node.js to be linked statically into a C++ application. It's definitely not polished, but I've used it to launch simple node scripts.


The official documentation has a page explaining how how to embed Node.js into C++. This has been around since Node 12.x.

There is a full example in the Node repo. Reproduced below for convenience:


#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ıı: '
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜