Research
Security News
Threat Actor Exposes Playbook for Exploiting npm to Build Blockchain-Powered Botnets
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
nbind
is a bindings generator for Node.js plugins inspired by embind from emscripten. The bindings are built along with your C++ library without requiring any external generated code, using only C++11 templates and simple declarations.
To create bindings from a class X with a constructor taking 2 ints and a method Y with pretty much any kind of arguments and optional return value, you just need:
NBIND_CLASS(X) {
construct<int, int>();
method(Y);
}
To compile and run some C++ code through JavaScript, just do:
git clone https://github.com/charto/nbind.git
cd nbind
npm install
npm test
You'll need to install Git, Node.js (0.10 or 0.12 or io.js) and a C++ compiler first.
Working C++ compilers are at least:
nbind
supports:
cbFunction
allows taking a JavaScript callback as a parameter and passing any number of supported types as arguments, casting the result to another supported type.More is coming! Work is ongoing to:
.d.ts
definition files from C++ code for IDE autocompletion and compile-time checks of JavaScript side code.Let's look at a concrete example. Suppose we have a C++ class:
// test.cc part 1
// Get definition of nbind::cbFunction
#include "nbind/api.h"
class Test {
public:
// Can be constructed with or without an initial state.
Test(int newState = 42) : state(newState) {}
// Getter and setter for private member.
int getState() { return(state); }
void setState(int newState) { state = newState; }
// Call a callback function passing it the state variable and return the result.
int callWithState(nbind::cbFunction &callback) {
// The template argument defines what type to cast the return value to.
return(callback.call<int>(state));
}
// Test returning a string from a static function.
static const char *toString() { return("Test class"); }
private:
int state;
};
Creating bindings for it is ridiculously easy, by adding the following in a source (not header) file:
// test.cc part 2
#include "nbind/BindingShort.h"
// Avoid errors if we're not compiling a Node.js module.
#ifdef NBIND_CLASS
NBIND_CLASS(Test) {
// Both possible constructors, nbind will choose one based on how it's called.
construct<>();
construct<int>();
// Getter and setter pair, nbind figures out it's a variable called "state".
getset(getState, setState);
// The methods, one taking a callback and the other static.
method(callWithState);
method(toString);
}
#endif
Then we can call it from JavaScript:
// test.js
var nbind = require('nbind');
var pkg = nbind.module;
nbind.init(__dirname);
// Prints "Test class"
console.log('' + pkg.Test);
var obj = new pkg.Test();
// Prints 42
console.log(obj.state);
// Prints 43
console.log(++obj.state);
// Prints 86
console.log(obj.callWithState(function(state) {
return(state * 2);
}));
Only a bit more configuration is needed to get everything installed, compiled and running.
package.json
to list what needs to be installed and run:
{
"name": "nbind-demo",
"scripts": {
"prepublish": "autogypi && node-gyp configure && node-gyp build",
"test": "node test.js"
},
"dependencies": {
"autogypi": "^0.1.0",
"nbind": "^0.1.0"
}
}
autogypi.json
to list dependencies for autogypi and configure them automatically for node-gyp:
{
"dependencies": [
"nbind"
],
"output": "auto.gypi"
}
binding.gyp
to tell node-gyp
what to do:
{
"targets": [
{
"includes": ["auto.gypi"],
"sources": [
"test.cc"
]
}
]
}
That's it! Install, compile, test:
npm install
npm test
nbind
is intended to be used together with autogypi for easier dependency management. Declare it as a dependency in autogypi.json
, run autogypi
and include the resulting auto.gypi
from your binding.gyp
file. See the example section above for sample contents of these configuration files.
Your code might depend on other npm packages that also contain nbind
exports. autogypi
will make sure that when you run node-gyp
, their sources will also get compiled and their includes will be in the include path when your code is compiled. When you run node-gyp
it produces a module with all the exported classes from all packages you've included. Then you just do in the root directory of your package:
var nbind = require('nbind');
nbind.init(__dirname);
This allows nbind to find the module you compiled. Afterwards, nbind.module
will contain all the exported classes.
Use #include "nbind/BindingShort.h"
at the end of your source file with only the bindings after it. The header defines macros with names like construct
that are otherwise likely to break the code implementing your C++ class.
It's OK to include the file also when not targeting any JavaScript environment. node-gyp
defines BUILDING_NODE_EXTENSION
and Emscripten defines EMSCRIPTEN
so when those are undefined, the include file does nothing.
Use #include "nbind/api.h"
in your header files to use types in the nbind
namespace if you need to report errors without throwing exceptions, or want to pass around callbacks or value objects.
You can use the NBIND_ERR("message here");
macro to report an error before returning. It will be thrown as an error on the JavaScript side (C++ environments like Emscripten may not support throwing exceptions, but the JavaScript side code will).
You can use an #ifdef NBIND_CLASS
guard to skip your nbind
export definitions when the includes weren't loaded.
The NBIND_CLASS(className)
macro takes the name of your C++ class as an argument (without any quotation marks), and exports it to JavaScript using the same name. It's followed by a curly brace enclosed block of method exports, as if it was a function definition.
Constructors are exported with a macro call construct<types...>();
where types
is a comma-separated list of arguments to the constructor, such as int, int
. Calling construct
multiple times allows overloading it, but each overload must have a different number of arguments.
Methods are exported with a macro call method(methodName);
which takes the name of the method as an argument (without any quotation marks). It gets exported to JavaScript with the same name. If the method is static
, it becomes a property of the JavaScript constructor function and can be accessed like className.methodName()
. Otherwise it becomes a property of the prototype and can be accessed like obj = new className(); obj.methodName();
Property getters are exported with a macro call getter(getterName)
which takes the name of the getter method. nbind
automatically strips a get
/Get
/get_
/Get_
prefix and converts the next letter to lowercase, so for example getX
and get_x
both would become getters of x
to be accessed like obj.x
Property setters are exported together with getters using a macro call getset(getterName, setterName)
which works much like getter(getterName)
above. Both getterName
and setterName
are mangled individually so you can pair getX
with set_x
if you like. From JavaScript, ++obj.x
would then call both of them.
Callbacks can be passed to C++ methods by simply adding an argument of type nbind::cbFunction &
to their declaration. They can be called with any number of any supported types without having to declare in any way what they accept. The JavaScript code will receive the parameters as JavaScript variables to do with them as it pleases. A callback argument arg
can be called like arg("foobar", 42);
in which case the return value is ignored. If the return value is needed, it must be called like arg.call<type>("foobar", 42);
where type
is the desired C++ type that the return value should be converted to.
Value objects are the most advanced concept supported so far. They're based on a toJS
function on the C++ side and a fromJS
function on the JavaScript side. Both receive a callback as an argument, and calling it with any parameters calls the constructor of the equivalent type in the other language (the bindings use SFINAE to detect the toJS function and turn the class into a value type). The callback on the C++ side is of type nbind::cbOutput
. Value objects are passed by value on the C++ side to and from the exported function. nbind
uses C++11 move semantics to avoid creating some additional copies on the way.
The equivalent JavaScript constructor must be registered on the JavaScript side by calling nbind.bind('CppClassName', JSClassName);
, so nbind
knows which types to translate between each other.
So if we have a file test.cc
(can be tested by replacing the file with the same name in the example above):
#include "nbind/api.h"
int copies = 0;
int moves = 0;
// Coordinate pair, a value type we'll bind to an equivalent JavaScript type.
class Coord {
public:
Coord(int x = 0, int y = 0) : x(x), y(y) {}
// Let's count copy and move constructor usage.
Coord(Coord &&other) : x(other.x), y(other.y) { ++copies; }
Coord(const Coord &other) : x(other.x), y(other.y) { ++moves; }
void addFrom(const Coord &other) {
x += other.x;
y += other.y;
}
// This is the magic function for nbind, which calls a constructor in JavaScript.
void toJS(nbind::cbOutput output) {
output(x, y);
}
private:
int x, y;
};
// This just sums coordinate pairs and returns them for testing.
class Accumulator {
public:
Coord add(Coord other) {
xy.addFrom(other);
// a copy is made here.
return(xy);
}
// This is a convenient place to read debug output, since we can't call methods of Coord
// from JavaScript (it sees methods defined on the JavaScript object instead).
int getCopies() { return(copies); }
int getMoves() { return(moves); }
private:
Coord xy;
};
#include "nbind/BindingShort.h"
#ifdef NBIND_CLASS
NBIND_CLASS(Coord) {
construct<>();
construct<int, int>();
}
NBIND_CLASS(Accumulator) {
construct<>();
method(add);
getter(getCopies);
getter(getMoves);
}
#endif
We can use it from a JavaScript file test.js
like so:
var nbind = require('nbind');
nbind.init(__dirname);
function Coord(x, y) {
this.x = x;
this.y = y;
}
Coord.prototype.fromJS = function(output) {
output(this.x, this.y);
}
nbind.bind('Coord', Coord);
var accu = new nbind.module.Accumulator();
// Prints: { x: 2, y: 4 }
console.log(accu.add(new Coord(2, 4)));
// Prints: { x: 12, y: 34 }
console.log(accu.add(new Coord(10, 30)));
// Prints: 4 copies, 2 moves.
console.log(accu.copies + ' copies, ' + accu.moves + ' moves.');
Copyright (c) 2014-2015 BusFaster Ltd
FAQs
Magical headers that make your C++ library accessible from JavaScript
The npm package nbind receives a total of 131 weekly downloads. As such, nbind popularity was classified as not popular.
We found that nbind demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
Security News
NVD’s backlog surpasses 20,000 CVEs as analysis slows and NIST announces new system updates to address ongoing delays.
Security News
Research
A malicious npm package disguised as a WhatsApp client is exploiting authentication flows with a remote kill switch to exfiltrate data and destroy files.