<-- home

Node addons written in Go

Since the release of Go 1.5, it has been possible to build shared libraries in Go and use them in C/C++ code. I was curious to see what it would take to use a Go shared library in node.js code.

The Go side of this is pretty simple, and most complexity comes up when working with isolates in the Node.js extension code.

file: calculator.go

// package name: calculator
package main

import "C"

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

func main() {
}

file: 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
    double total = Sum(args[0]->NumberValue(), args[1]->NumberValue());
    Local<Number> num = Number::New(isolate, total);

    // 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)
}

file: test.js

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

file: binding.gyp

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

Build:

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

Output:

#> node test.js
4+5= 9

This is a very simplistic addon. Next I plan to experiment on the how Go and Node.js concurrency patterns (Channels vs Events vs Callbacks) can map to each other.