Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Introduce NODE_MODULE_INIT() to pass full module object to addons #4634

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 37 additions & 29 deletions doc/api/addons.markdown
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ Node statically compiles all its dependencies into the executable. When
compiling your module, you don't need to worry about linking to any of these compiling your module, you don't need to worry about linking to any of these
libraries. libraries.


All of the following examples are available for [download](https://github.com/rvagg/node-addon-examples)
and may be used as a starting-point for your own Addon.


## Hello world ## Hello world


To get started let's make a small Addon which is the C++ equivalent of To get started let's make a small Addon which is the C++ equivalent of
the following JavaScript code: the following JavaScript code:


exports.hello = function() { return 'world'; }; module.exports.hello = function() { return 'world'; };


First we create a file `hello.cc`: First we create a file `hello.cc`:


Expand All @@ -43,15 +45,16 @@ First we create a file `hello.cc`:
return scope.Close(String::New("world")); return scope.Close(String::New("world"));
} }


void init(Handle<Object> target) { void init(Handle<Object> exports) {
target->Set(String::NewSymbol("hello"), exports->Set(String::NewSymbol("hello"),
FunctionTemplate::New(Method)->GetFunction()); FunctionTemplate::New(Method)->GetFunction());
} }

NODE_MODULE(hello, init) NODE_MODULE(hello, init)


Note that all Node addons must export an initialization function: Note that all Node addons must export an initialization function:


void Initialize (Handle<Object> target); void Initialize (Handle<Object> exports);
NODE_MODULE(module_name, Initialize) NODE_MODULE(module_name, Initialize)


There is no semi-colon after `NODE_MODULE` as it's not a function (see `node.h`). There is no semi-colon after `NODE_MODULE` as it's not a function (see `node.h`).
Expand Down Expand Up @@ -154,8 +157,8 @@ function calls and return a result. This is the main and only needed source
return scope.Close(num); return scope.Close(num);
} }


void Init(Handle<Object> target) { void Init(Handle<Object> exports) {
target->Set(String::NewSymbol("add"), exports->Set(String::NewSymbol("add"),
FunctionTemplate::New(Add)->GetFunction()); FunctionTemplate::New(Add)->GetFunction());
} }


Expand Down Expand Up @@ -189,18 +192,23 @@ there. Here's `addon.cc`:
return scope.Close(Undefined()); return scope.Close(Undefined());
} }


void Init(Handle<Object> target) { void Init(Handle<Object> exports, Handle<Object> module) {
target->Set(String::NewSymbol("runCallback"), module->Set(String::NewSymbol("exports"),
FunctionTemplate::New(RunCallback)->GetFunction()); FunctionTemplate::New(RunCallback)->GetFunction());
} }


NODE_MODULE(addon, Init) NODE_MODULE(addon, Init)


Note that this example uses a two-argument form of `Init()` that receives
the full `module` object as the second argument. This allows the addon
to completely overwrite `exports` with a single function instead of
adding the function as a property of `exports`.

To test it run the following JavaScript snippet: To test it run the following JavaScript snippet:


var addon = require('./build/Release/addon'); var addon = require('./build/Release/addon');


addon.runCallback(function(msg){ addon(function(msg){
console.log(msg); // 'hello world' console.log(msg); // 'hello world'
}); });


Expand All @@ -225,8 +233,8 @@ the string passed to `createObject()`:
return scope.Close(obj); return scope.Close(obj);
} }


void Init(Handle<Object> target) { void Init(Handle<Object> exports, Handle<Object> module) {
target->Set(String::NewSymbol("createObject"), module->Set(String::NewSymbol("exports"),
FunctionTemplate::New(CreateObject)->GetFunction()); FunctionTemplate::New(CreateObject)->GetFunction());
} }


Expand All @@ -236,8 +244,8 @@ To test it in JavaScript:


var addon = require('./build/Release/addon'); var addon = require('./build/Release/addon');


var obj1 = addon.createObject('hello'); var obj1 = addon('hello');
var obj2 = addon.createObject('world'); var obj2 = addon('world');
console.log(obj1.msg+' '+obj2.msg); // 'hello world' console.log(obj1.msg+' '+obj2.msg); // 'hello world'




Expand Down Expand Up @@ -266,8 +274,8 @@ wraps a C++ function:
return scope.Close(fn); return scope.Close(fn);
} }


void Init(Handle<Object> target) { void Init(Handle<Object> exports, Handle<Object> module) {
target->Set(String::NewSymbol("createFunction"), module->Set(String::NewSymbol("exports"),
FunctionTemplate::New(CreateFunction)->GetFunction()); FunctionTemplate::New(CreateFunction)->GetFunction());
} }


Expand All @@ -278,7 +286,7 @@ To test:


var addon = require('./build/Release/addon'); var addon = require('./build/Release/addon');


var fn = addon.createFunction(); var fn = addon();
console.log(fn()); // 'hello world' console.log(fn()); // 'hello world'




Expand All @@ -294,8 +302,8 @@ module `addon.cc`:


using namespace v8; using namespace v8;


void InitAll(Handle<Object> target) { void InitAll(Handle<Object> exports) {
MyObject::Init(target); MyObject::Init(exports);
} }


NODE_MODULE(addon, InitAll) NODE_MODULE(addon, InitAll)
Expand All @@ -309,7 +317,7 @@ Then in `myobject.h` make your wrapper inherit from `node::ObjectWrap`:


class MyObject : public node::ObjectWrap { class MyObject : public node::ObjectWrap {
public: public:
static void Init(v8::Handle<v8::Object> target); static void Init(v8::Handle<v8::Object> exports);


private: private:
MyObject(); MyObject();
Expand All @@ -335,7 +343,7 @@ prototype:
MyObject::MyObject() {}; MyObject::MyObject() {};
MyObject::~MyObject() {}; MyObject::~MyObject() {};


void MyObject::Init(Handle<Object> target) { void MyObject::Init(Handle<Object> exports) {
// Prepare constructor template // Prepare constructor template
Local<FunctionTemplate> tpl = FunctionTemplate::New(New); Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol("MyObject")); tpl->SetClassName(String::NewSymbol("MyObject"));
Expand All @@ -345,7 +353,7 @@ prototype:
FunctionTemplate::New(PlusOne)->GetFunction()); FunctionTemplate::New(PlusOne)->GetFunction());


Persistent<Function> constructor = Persistent<Function>::New(tpl->GetFunction()); Persistent<Function> constructor = Persistent<Function>::New(tpl->GetFunction());
target->Set(String::NewSymbol("MyObject"), constructor); exports->Set(String::NewSymbol("MyObject"), constructor);
} }


Handle<Value> MyObject::New(const Arguments& args) { Handle<Value> MyObject::New(const Arguments& args) {
Expand Down Expand Up @@ -399,10 +407,10 @@ Let's register our `createObject` method in `addon.cc`:
return scope.Close(MyObject::NewInstance(args)); return scope.Close(MyObject::NewInstance(args));
} }


void InitAll(Handle<Object> target) { void InitAll(Handle<Object> exports, Handle<Object> module) {
MyObject::Init(); MyObject::Init();


target->Set(String::NewSymbol("createObject"), module->Set(String::NewSymbol("exports"),
FunctionTemplate::New(CreateObject)->GetFunction()); FunctionTemplate::New(CreateObject)->GetFunction());
} }


Expand Down Expand Up @@ -490,14 +498,14 @@ The implementation is similar to the above in `myobject.cc`:


Test it with: Test it with:


var addon = require('./build/Release/addon'); var createObject = require('./build/Release/addon');


var obj = addon.createObject(10); var obj = createObject(10);
console.log( obj.plusOne() ); // 11 console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12 console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13 console.log( obj.plusOne() ); // 13


var obj2 = addon.createObject(20); var obj2 = createObject(20);
console.log( obj2.plusOne() ); // 21 console.log( obj2.plusOne() ); // 21
console.log( obj2.plusOne() ); // 22 console.log( obj2.plusOne() ); // 22
console.log( obj2.plusOne() ); // 23 console.log( obj2.plusOne() ); // 23
Expand Down Expand Up @@ -533,13 +541,13 @@ In the following `addon.cc` we introduce a function `add()` that can take on two
return scope.Close(Number::New(sum)); return scope.Close(Number::New(sum));
} }


void InitAll(Handle<Object> target) { void InitAll(Handle<Object> exports) {
MyObject::Init(); MyObject::Init();


target->Set(String::NewSymbol("createObject"), exports->Set(String::NewSymbol("createObject"),
FunctionTemplate::New(CreateObject)->GetFunction()); FunctionTemplate::New(CreateObject)->GetFunction());


target->Set(String::NewSymbol("add"), exports->Set(String::NewSymbol("add"),
FunctionTemplate::New(Add)->GetFunction()); FunctionTemplate::New(Add)->GetFunction());
} }


Expand Down
4 changes: 1 addition & 3 deletions lib/module.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -488,9 +488,7 @@ Module._extensions['.json'] = function(module, filename) {




//Native extension for .node //Native extension for .node
Module._extensions['.node'] = function(module, filename) { Module._extensions['.node'] = process.dlopen;
process.dlopen(filename, module.exports);
};




// bootstrap main module. // bootstrap main module.
Expand Down
23 changes: 16 additions & 7 deletions src/node.cc
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ Persistent<String> domain_symbol;


static Persistent<Object> process; static Persistent<Object> process;


static Persistent<String> exports_symbol;

static Persistent<String> errno_symbol; static Persistent<String> errno_symbol;
static Persistent<String> syscall_symbol; static Persistent<String> syscall_symbol;
static Persistent<String> errpath_symbol; static Persistent<String> errpath_symbol;
Expand Down Expand Up @@ -1786,8 +1788,8 @@ Handle<Value> Hrtime(const v8::Arguments& args) {


typedef void (UV_DYNAMIC* extInit)(Handle<Object> exports); typedef void (UV_DYNAMIC* extInit)(Handle<Object> exports);


// DLOpen is node.dlopen(). Used to load 'module.node' dynamically shared // DLOpen is process.dlopen(module, filename).
// objects. // Used to load 'module.node' dynamically shared objects.
Handle<Value> DLOpen(const v8::Arguments& args) { Handle<Value> DLOpen(const v8::Arguments& args) {
HandleScope scope; HandleScope scope;
char symbol[1024], *base, *pos; char symbol[1024], *base, *pos;
Expand All @@ -1800,8 +1802,13 @@ Handle<Value> DLOpen(const v8::Arguments& args) {
return ThrowException(exception); return ThrowException(exception);
} }


String::Utf8Value filename(args[0]); // Cast Local<Object> module = args[0]->ToObject(); // Cast
Local<Object> target = args[1]->ToObject(); // Cast String::Utf8Value filename(args[1]); // Cast

if (exports_symbol.IsEmpty()) {
exports_symbol = NODE_PSYMBOL("exports");
}
Local<Object> exports = module->Get(exports_symbol)->ToObject();


if (uv_dlopen(*filename, &lib)) { if (uv_dlopen(*filename, &lib)) {
Local<String> errmsg = String::New(uv_dlerror(&lib)); Local<String> errmsg = String::New(uv_dlerror(&lib));
Expand All @@ -1812,7 +1819,7 @@ Handle<Value> DLOpen(const v8::Arguments& args) {
return ThrowException(Exception::Error(errmsg)); return ThrowException(Exception::Error(errmsg));
} }


String::Utf8Value path(args[0]); String::Utf8Value path(args[1]);
base = *path; base = *path;


/* Find the shared library filename within the full path. */ /* Find the shared library filename within the full path. */
Expand Down Expand Up @@ -1869,7 +1876,7 @@ Handle<Value> DLOpen(const v8::Arguments& args) {
} }


// Execute the C++ module // Execute the C++ module
mod->register_func(target); mod->register_func(exports, module);


// Tell coverity that 'handle' should not be freed when we return. // Tell coverity that 'handle' should not be freed when we return.
// coverity[leaked_storage] // coverity[leaked_storage]
Expand Down Expand Up @@ -1953,7 +1960,9 @@ static Handle<Value> Binding(const Arguments& args) {


if ((modp = get_builtin_module(*module_v)) != NULL) { if ((modp = get_builtin_module(*module_v)) != NULL) {
exports = Object::New(); exports = Object::New();
modp->register_func(exports); // Internal bindings don't have a "module" object,
// only exports.
modp->register_func(exports, Undefined());
binding_cache->Set(module, exports); binding_cache->Set(module, exports);


} else if (!strcmp(*module_v, "constants")) { } else if (!strcmp(*module_v, "constants")) {
Expand Down
11 changes: 8 additions & 3 deletions src/node.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
# endif # endif
#endif #endif



namespace node { namespace node {


NODE_EXTERN extern bool no_deprecation; NODE_EXTERN extern bool no_deprecation;
Expand Down Expand Up @@ -198,11 +199,15 @@ NODE_EXTERN v8::Local<v8::Value> WinapiErrnoException(int errorno,


const char *signo_string(int errorno); const char *signo_string(int errorno);



NODE_EXTERN typedef void (* addon_register_func)(
v8::Handle<v8::Object> exports, v8::Handle<v8::Value> module);

struct node_module_struct { struct node_module_struct {
int version; int version;
void *dso_handle; void *dso_handle;
const char *filename; const char *filename;
void (*register_func) (v8::Handle<v8::Object> target); node::addon_register_func register_func;
const char *modname; const char *modname;
}; };


Expand All @@ -214,7 +219,7 @@ node_module_struct* get_builtin_module(const char *name);
* an API is broken in the C++ side, including in v8 or * an API is broken in the C++ side, including in v8 or
* other dependencies. * other dependencies.
*/ */
#define NODE_MODULE_VERSION 0x000A /* v0.10 */ #define NODE_MODULE_VERSION 0x000B /* v0.11 */


#define NODE_STANDARD_MODULE_STUFF \ #define NODE_STANDARD_MODULE_STUFF \
NODE_MODULE_VERSION, \ NODE_MODULE_VERSION, \
Expand All @@ -232,7 +237,7 @@ node_module_struct* get_builtin_module(const char *name);
NODE_MODULE_EXPORT node::node_module_struct modname ## _module = \ NODE_MODULE_EXPORT node::node_module_struct modname ## _module = \
{ \ { \
NODE_STANDARD_MODULE_STUFF, \ NODE_STANDARD_MODULE_STUFF, \
regfunc, \ (node::addon_register_func)regfunc, \
NODE_STRINGIFY(modname) \ NODE_STRINGIFY(modname) \
}; \ }; \
} }
Expand Down
15 changes: 15 additions & 0 deletions test/addons/hello-world-function-export/binding.cc
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <node.h>
#include <v8.h>

using namespace v8;

Handle<Value> Method(const Arguments& args) {
HandleScope scope;
return scope.Close(String::New("world"));
}

void init(Handle<Object> exports, Handle<Object> module) {
NODE_SET_METHOD(module, "exports", Method);
}

NODE_MODULE(binding, init);
8 changes: 8 additions & 0 deletions test/addons/hello-world-function-export/binding.gyp
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,8 @@
{
'targets': [
{
'target_name': 'binding',
'sources': [ 'binding.cc' ]
}
]
}
4 changes: 4 additions & 0 deletions test/addons/hello-world-function-export/test.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,4 @@
var assert = require('assert');
var binding = require('./build/Release/binding');
assert.equal('world', binding());
console.log('binding.hello() =', binding());