Could one write a native Node.js extension in Go, as opposed to C++?

8.5k Views Asked by At

That's all there is to my question, really, but I think it's an interesting thing to have answered.

3

There are 3 best solutions below

5
On

Native module for node.js must deeply interact with V8 process which contains a lot of v8 concepts such as gc, javascript context, ...

And I don't think V8 has exposed compatible and stable APIs for other language to interact with it. That is why node.js native addon should be built with C++ and always imports V8 C++ headers.


But you can use GO to write node.js native addons by wrapping GO code with C++:

file: module.go

package main

func Add(a, b int) int {
    return a + b
}

file: module.c

#include <node.h>
#include <v8.h>

using namespace v8;

extern int go_add(int, int) __asm__ ("example.main.Add");

void init(Handle<Object> exports) {
    // call go_add
}

NODE_MODULE(module, init)

More about "how to call GO functionn from C/C++":

Call Go functions from C


Edit:

Please see @jdi comments and the link: https://groups.google.com/forum/#!msg/golang-nuts/FzPbOwbTlPs/dAJVWQHx6m4J

Quote: It might be doable for simple things like add (that don't generate garbage or require the runtime), but it's not supported (yet) by either compiler as far as I know. Part of the work is done for linux (see golang.org/issue/256), but there are a number of open questions (what happens when you load two shared objects? etc)

0
On

Just to repost this as an answer instead of a comment...

I followed up with golang-nuts mailing list regarding the support for writing extensions in Go for other languages. The source of the response can be found here.

It might be doable for simple things like add (that don't generate garbage or require the runtime), but it's not supported (yet) by either compiler as far as I know. Part of the work is done for linux (see golang.org/issue/256), but there are a number of open questions (what happens when you load two shared objects? etc)

So really, there doesn't seem to be much point in writing an extension in Go, yet, as most of the language features would not be available, and you are already in C/C++ land anyways to add the wrapper for the entry point.

1
On

With the addition of support for shared libraries in go, this is possible now.

calculator.go:

// package name: calculator
package main

import "C"

//export Sum
func Sum(x, y float64) float64 {
    return x + y
}

func main() {
}

node-calculator.cc:

#include "calculator.h"
#include <node.h>

namespace calc {

  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Local;
  using v8::Object;
  using v8::String;
  using v8::Value;
  using v8::Number;
  using v8::Exception;

  void add(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();

    // Check the number of arguments passed.
    if (args.Length() < 2) {
      // Throw an Error that is passed back to JavaScript
      isolate->ThrowException(Exception::TypeError(
          String::NewFromUtf8(isolate, "Wrong number of arguments")));
      return;
    }

    // Check the argument types
    if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
      isolate->ThrowException(Exception::TypeError(
          String::NewFromUtf8(isolate, "Wrong arguments")));
      return;
    }

    // Perform the operation
    Local<Number> num = Number::New(isolate, Sum(args[0]->NumberValue(), args[1]->NumberValue()));

    // Set the return value (using the passed in
    // FunctionCallbackInfo<Value>&)
    args.GetReturnValue().Set(num);
  }

  void init(Local<Object> exports) {
    NODE_SET_METHOD(exports, "add", add);
  }

  NODE_MODULE(calculator, init)
}

binding.gyp:

{
  "targets": [
    {
      "target_name": "node-calculator",
      "sources": [
        "node-calculator.cc"
      ],
      "libraries": [
        "../calculator.a"
      ],
    },
  ],
}

test.js:

const calculator = require('./build/Release/node-calculator');
console.log('4+5=', calculator.add(4, 5));

Build:

go build -buildmode c-archive -o calculator.a calculator.go
node-gyp configure
node-gyp build

Output:

#> node test.js 
4+5= 9