30

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

hippietrail
  • 15,848
  • 18
  • 99
  • 158
user3025492
  • 2,853
  • 4
  • 18
  • 19

3 Answers3

20

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
Community
  • 1
  • 1
kraman
  • 221
  • 2
  • 4
  • I get an error with this using Go 1.9.3: `ld: warning: ignoring file ../calculator.a, file was built for archive which is not the architecture being linked (x86_64): ../calculator.a` – user2300902 Oct 31 '17 at 16:38
12

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)

Community
  • 1
  • 1
damphat
  • 18,246
  • 8
  • 45
  • 59
  • 1
    I thought that Go needed to be the entry point, even if you are calling into it from C? As far as I understood, you couldn't write "extensions" for other languages. – jdi Jan 02 '14 at 22:27
  • I am installing gccgo now. Have you read the answer at http://stackoverflow.com/a/15760986/813080, I see they can compile a c program with the `main` entry. – damphat Jan 02 '14 at 22:54
  • I haven't until now. I guess I just haven't seen any acknowledgment of this functionality on the golang-nuts mailing list. Any time the question came up they have said you can't write extensions to embed into other languages. Not sure what the limitations of this approach would be. Interested to know. – jdi Jan 03 '14 at 02:13
  • Please see [this answer from the golang-nuts mailing list](https://groups.google.com/d/msg/golang-nuts/FzPbOwbTlPs/dAJVWQHx6m4J). It is not yet officially supported, and very limited in functionality. As I suspected, the runtime would not be properly initialized and you would have to do things that don't generate garbage or need the runtime. That would seem to make it an option with very little benefit. – jdi Jan 03 '14 at 20:32
  • @jdi Thanks! I don't know about that limitation, I think you post a correct answer to this question. – damphat Jan 03 '14 at 20:54
3

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.

jdi
  • 90,542
  • 19
  • 167
  • 203