├── .gitignore ├── 01_bare_minimum ├── README.md ├── binding.gyp ├── modulename.cpp ├── package.json └── run.js ├── 02_functions ├── README.md ├── binding.gyp ├── modulename.cpp ├── package.json └── run.js ├── 03_function_arguments ├── README.md ├── binding.gyp ├── modulename.cpp ├── package.json └── run.js ├── 04_callback_functions ├── README.md ├── binding.gyp ├── modulename.cpp ├── package.json └── run.js ├── 05_threadpool ├── README.md ├── binding.gyp ├── modulename.cpp ├── package.json └── run.js ├── 06_objects ├── README.md ├── binding.gyp ├── modulename.cpp ├── modulename.hpp ├── package.json └── run.js ├── README.md └── xx_benchmark ├── binding.gyp ├── fn1.js ├── fn2.js ├── fn3.js ├── modulename.cpp ├── native.js ├── package.json └── results.txt /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /01_bare_minimum/README.md: -------------------------------------------------------------------------------- 1 | To compile, run 2 | 3 | ``` 4 | npm build . 5 | ``` 6 | -------------------------------------------------------------------------------- /01_bare_minimum/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "modulename", 5 | "sources": [ "modulename.cpp" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /01_bare_minimum/modulename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void RegisterModule(v8::Handle target) { 4 | // You can add properties to the module in this function. It is called 5 | // when the module is required by node. 6 | } 7 | 8 | // Register the module with node. Note that "modulename" must be the same as 9 | // the basename of the resulting .node file. You can specify that name in 10 | // binding.gyp ("target_name"). When you change it there, change it here too. 11 | NODE_MODULE(modulename, RegisterModule); 12 | -------------------------------------------------------------------------------- /01_bare_minimum/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modulename", 3 | "version": "1.0.0", 4 | "main": "./build/Release/modulename" 5 | } -------------------------------------------------------------------------------- /01_bare_minimum/run.js: -------------------------------------------------------------------------------- 1 | var modulename = require('./build/Release/modulename'); 2 | 3 | console.warn(modulename); 4 | -------------------------------------------------------------------------------- /02_functions/README.md: -------------------------------------------------------------------------------- 1 | To compile, run 2 | 3 | ``` 4 | npm build . 5 | ``` 6 | -------------------------------------------------------------------------------- /02_functions/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "modulename", 5 | "sources": [ "modulename.cpp" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /02_functions/modulename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // C standard library 4 | #include 5 | #include 6 | 7 | using namespace v8; 8 | 9 | // This function returns a JavaScript number that is either 0 or 1. 10 | Handle Random(const Arguments& args) { 11 | // At the top of every function that uses anything about v8, include a 12 | // definition like this. It ensures that any v8 handles you create in that 13 | // function are properly cleaned up. If you see memory rising in your 14 | // application, chances are that a scope isn't properly cleaned up. 15 | HandleScope scope; 16 | 17 | // When returning a value from a function, make sure to wrap it in 18 | // scope.Close(). This ensures that the handle stays valid after the current 19 | // scope (declared with the previous statement) is cleaned up. 20 | return scope.Close( 21 | // Creating a new JavaScript integer is as simple as passing a C int 22 | // (technically a int32_t) to this function. 23 | Integer::New(rand() % 2) 24 | ); 25 | } 26 | 27 | void RegisterModule(Handle target) { 28 | srand(time(NULL)); 29 | 30 | // target is the module object you see when require()ing the .node file. 31 | target->Set(String::NewSymbol("random"), 32 | FunctionTemplate::New(Random)->GetFunction()); 33 | } 34 | 35 | NODE_MODULE(modulename, RegisterModule); 36 | -------------------------------------------------------------------------------- /02_functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modulename", 3 | "version": "1.0.0", 4 | "main": "./build/Release/modulename" 5 | } -------------------------------------------------------------------------------- /02_functions/run.js: -------------------------------------------------------------------------------- 1 | var modulename = require('./build/Release/modulename'); 2 | 3 | console.warn(modulename.random()); 4 | -------------------------------------------------------------------------------- /03_function_arguments/README.md: -------------------------------------------------------------------------------- 1 | To compile, run 2 | 3 | ``` 4 | npm build . 5 | ``` 6 | -------------------------------------------------------------------------------- /03_function_arguments/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "modulename", 5 | "sources": [ "modulename.cpp" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /03_function_arguments/modulename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace v8; 4 | 5 | // Returns the Nth number in the fibonacci sequence where N is the first 6 | // argument passed. 7 | Handle Fibonacci(const Arguments& args) { 8 | HandleScope scope; 9 | 10 | // Check that there are enough arguments. If we access an index that doesn't 11 | // exist, it'll be Undefined(). 12 | if (args.Length() < 1) { 13 | // No argument was passed. Throw an exception to alert the user to 14 | // incorrect usage. Alternatively, we could just use 0. 15 | return ThrowException( 16 | Exception::TypeError(String::New("First argument must be a number")) 17 | ); 18 | } 19 | 20 | // Cast a value to a specific type. See 21 | // http://izs.me/v8-docs/classv8_1_1Value.html for available To*() functions 22 | // and type checking functions. When converting to integer, make sure the 23 | // POD type you use is big enough! 24 | Local integer = args[0]->ToInteger(); 25 | int32_t seq = integer->Value(); 26 | 27 | // Also possible in one call. (Don't forget HandleScope, otherwise the 28 | // intermediate Local handle won't be cleaned up!) 29 | // int32_t seq = args[0]->ToInteger()->Value(); 30 | 31 | // Check for invalid parameter. 32 | if (seq < 0) { 33 | return ThrowException(Exception::TypeError(String::New( 34 | "Fibonacci sequence number must be positive"))); 35 | } 36 | 37 | // The actual algorithm. 38 | int32_t current = 1; 39 | for (int32_t previous = -1, next = 0, i = 0; i <= seq; i++) { 40 | next = previous + current; 41 | previous = current; 42 | current = next; 43 | } 44 | 45 | return scope.Close(Integer::New(current)); 46 | } 47 | 48 | void RegisterModule(Handle target) { 49 | target->Set(String::NewSymbol("fibonacci"), 50 | FunctionTemplate::New(Fibonacci)->GetFunction()); 51 | } 52 | 53 | NODE_MODULE(modulename, RegisterModule); 54 | -------------------------------------------------------------------------------- /03_function_arguments/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modulename", 3 | "version": "1.0.0", 4 | "main": "./build/Release/modulename" 5 | } -------------------------------------------------------------------------------- /03_function_arguments/run.js: -------------------------------------------------------------------------------- 1 | var modulename = require('./build/Release/modulename'); 2 | 3 | console.warn(modulename.fibonacci(9)); 4 | -------------------------------------------------------------------------------- /04_callback_functions/README.md: -------------------------------------------------------------------------------- 1 | To compile, run 2 | 3 | ``` 4 | npm build . 5 | ``` 6 | -------------------------------------------------------------------------------- /04_callback_functions/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "modulename", 5 | "sources": [ "modulename.cpp" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /04_callback_functions/modulename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace v8; 4 | 5 | 6 | Handle Callback(const Arguments& args) { 7 | HandleScope scope; 8 | 9 | // Ensure that we got a callback. Generally, your functions should have 10 | // optional callbacks. In this case, you can declare an empty 11 | // Local handle and check for content before calling. 12 | if (!args[1]->IsFunction()) { 13 | return ThrowException(Exception::TypeError( 14 | String::New("Second argument must be a callback function"))); 15 | } 16 | // There's no ToFunction(), use a Cast instead. 17 | Local callback = Local::Cast(args[1]); 18 | 19 | // Our fake API allows the user to tell us whether we should cause an error. 20 | bool error = args[0]->BooleanValue(); 21 | 22 | if (error) { 23 | Local err = Exception::Error(String::New("Something went wrong!")); 24 | 25 | // Optionally add more properties to the exception object. 26 | err->ToObject()->Set(NODE_PSYMBOL("errno"), Integer::New(23)); 27 | 28 | // Prepare the parameters for the callback function. 29 | const unsigned argc = 1; 30 | Local argv[argc] = { err }; 31 | 32 | // Note: When calling this in an asynchronous function that just returned 33 | // from the threadpool, you have to wrap this in a v8::TryCatch. 34 | // You can also pass another handle as the "this" parameter. 35 | callback->Call(Context::GetCurrent()->Global(), argc, argv); 36 | } else { 37 | // In case the operation succeeded, convention is to pass null as the 38 | // first argument before the result arguments. 39 | const unsigned argc = 2; 40 | Local argv[argc] = { 41 | Local::New(Null()), 42 | Local::New(Integer::New(42)) 43 | }; 44 | 45 | // Note: When calling this in an asynchronous function that just returned 46 | // from the threadpool, you have to wrap this in a v8::TryCatch. 47 | // You can also pass another handle as the "this" parameter. 48 | callback->Call(Context::GetCurrent()->Global(), argc, argv); 49 | } 50 | 51 | return Undefined(); 52 | } 53 | 54 | void RegisterModule(Handle target) { 55 | target->Set(String::NewSymbol("callback"), 56 | FunctionTemplate::New(Callback)->GetFunction()); 57 | } 58 | 59 | NODE_MODULE(modulename, RegisterModule); 60 | -------------------------------------------------------------------------------- /04_callback_functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modulename", 3 | "version": "1.0.0", 4 | "main": "./build/Release/modulename" 5 | } -------------------------------------------------------------------------------- /04_callback_functions/run.js: -------------------------------------------------------------------------------- 1 | var modulename = require('./build/Release/modulename'); 2 | 3 | modulename.callback(false, function(err, result) { 4 | console.warn(result); 5 | }); 6 | -------------------------------------------------------------------------------- /05_threadpool/README.md: -------------------------------------------------------------------------------- 1 | To compile, run 2 | 3 | ``` 4 | npm build . 5 | ``` 6 | -------------------------------------------------------------------------------- /05_threadpool/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "modulename", 5 | "sources": [ "modulename.cpp" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /05_threadpool/modulename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace v8; 5 | 6 | 7 | // Forward declaration. Usually, you do this in a header file. 8 | Handle Async(const Arguments& args); 9 | void AsyncWork(uv_work_t* req); 10 | void AsyncAfter(uv_work_t* req); 11 | 12 | 13 | // We use a struct to store information about the asynchronous "work request". 14 | struct Baton { 15 | // This handle holds the callback function we'll call after the work request 16 | // has been completed in a threadpool thread. It's persistent so that V8 17 | // doesn't garbage collect it away while our request waits to be processed. 18 | // This means that we'll have to dispose of it later ourselves. 19 | Persistent callback; 20 | 21 | // Tracking errors that happened in the worker function. You can use any 22 | // variables you want. E.g. in some cases, it might be useful to report 23 | // an error number. 24 | bool error; 25 | std::string error_message; 26 | 27 | // Custom data you can pass through. 28 | int32_t result; 29 | }; 30 | 31 | // This is the function called directly from JavaScript land. It creates a 32 | // work request object and schedules it for execution. 33 | Handle Async(const Arguments& args) { 34 | HandleScope scope; 35 | 36 | if (!args[0]->IsFunction()) { 37 | return ThrowException(Exception::TypeError( 38 | String::New("First argument must be a callback function"))); 39 | } 40 | // There's no ToFunction(), use a Cast instead. 41 | Local callback = Local::Cast(args[0]); 42 | 43 | // The baton holds our custom status information for this asynchronous call, 44 | // like the callback function we want to call when returning to the main 45 | // thread and the status information. 46 | Baton* baton = new Baton(); 47 | baton->error = false; 48 | baton->callback = Persistent::New(callback); 49 | 50 | // This creates the work request struct. 51 | uv_work_t *req = new uv_work_t(); 52 | req->data = baton; 53 | 54 | // Schedule our work request with libuv. Here you can specify the functions 55 | // that should be executed in the threadpool and back in the main thread 56 | // after the threadpool function completed. 57 | int status = uv_queue_work(uv_default_loop(), req, AsyncWork, 58 | (uv_after_work_cb)AsyncAfter); 59 | assert(status == 0); 60 | 61 | return Undefined(); 62 | } 63 | 64 | // This function is executed in another thread at some point after it has been 65 | // scheduled. IT MUST NOT USE ANY V8 FUNCTIONALITY. Otherwise your extension 66 | // will crash randomly and you'll have a lot of fun debugging. 67 | // If you want to use parameters passed into the original call, you have to 68 | // convert them to PODs or some other fancy method. 69 | void AsyncWork(uv_work_t* req) { 70 | Baton* baton = static_cast(req->data); 71 | 72 | // Do work in threadpool here. 73 | baton->result = 42; 74 | 75 | // If the work we do fails, set baton->error_message to the error string 76 | // and baton->error to true. 77 | } 78 | 79 | // This function is executed in the main V8/JavaScript thread. That means it's 80 | // safe to use V8 functions again. Don't forget the HandleScope! 81 | void AsyncAfter(uv_work_t* req) { 82 | HandleScope scope; 83 | Baton* baton = static_cast(req->data); 84 | 85 | if (baton->error) { 86 | Local err = Exception::Error(String::New(baton->error_message.c_str())); 87 | 88 | // Prepare the parameters for the callback function. 89 | const unsigned argc = 1; 90 | Local argv[argc] = { err }; 91 | 92 | // Wrap the callback function call in a TryCatch so that we can call 93 | // node's FatalException afterwards. This makes it possible to catch 94 | // the exception from JavaScript land using the 95 | // process.on('uncaughtException') event. 96 | TryCatch try_catch; 97 | baton->callback->Call(Context::GetCurrent()->Global(), argc, argv); 98 | if (try_catch.HasCaught()) { 99 | node::FatalException(try_catch); 100 | } 101 | } else { 102 | // In case the operation succeeded, convention is to pass null as the 103 | // first argument before the result arguments. 104 | // In case you produced more complex data, this is the place to convert 105 | // your plain C++ data structures into JavaScript/V8 data structures. 106 | const unsigned argc = 2; 107 | Local argv[argc] = { 108 | Local::New(Null()), 109 | Local::New(Integer::New(baton->result)) 110 | }; 111 | 112 | // Wrap the callback function call in a TryCatch so that we can call 113 | // node's FatalException afterwards. This makes it possible to catch 114 | // the exception from JavaScript land using the 115 | // process.on('uncaughtException') event. 116 | TryCatch try_catch; 117 | baton->callback->Call(Context::GetCurrent()->Global(), argc, argv); 118 | if (try_catch.HasCaught()) { 119 | node::FatalException(try_catch); 120 | } 121 | } 122 | 123 | // The callback is a permanent handle, so we have to dispose of it manually. 124 | baton->callback.Dispose(); 125 | 126 | // We also created the baton and the work_req struct with new, so we have to 127 | // manually delete both. 128 | delete baton; 129 | delete req; 130 | } 131 | 132 | void RegisterModule(Handle target) { 133 | target->Set(String::NewSymbol("async"), 134 | FunctionTemplate::New(Async)->GetFunction()); 135 | } 136 | 137 | NODE_MODULE(modulename, RegisterModule); 138 | -------------------------------------------------------------------------------- /05_threadpool/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modulename", 3 | "version": "1.0.0", 4 | "main": "./build/Release/modulename" 5 | } -------------------------------------------------------------------------------- /05_threadpool/run.js: -------------------------------------------------------------------------------- 1 | var modulename = require('./build/Release/modulename'); 2 | 3 | modulename.async(function(err, result) { 4 | console.warn(result); 5 | }); 6 | -------------------------------------------------------------------------------- /06_objects/README.md: -------------------------------------------------------------------------------- 1 | To compile, run 2 | 3 | ``` 4 | npm build . 5 | ``` 6 | -------------------------------------------------------------------------------- /06_objects/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "modulename", 5 | "sources": [ "modulename.cpp" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /06_objects/modulename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "modulename.hpp" 4 | 5 | using namespace v8; 6 | 7 | 8 | Persistent MyObject::constructor; 9 | 10 | void MyObject::Init(Handle target) { 11 | HandleScope scope; 12 | 13 | Local tpl = FunctionTemplate::New(New); 14 | Local name = String::NewSymbol("MyObject"); 15 | 16 | constructor = Persistent::New(tpl); 17 | // ObjectWrap uses the first internal field to store the wrapped pointer. 18 | constructor->InstanceTemplate()->SetInternalFieldCount(1); 19 | constructor->SetClassName(name); 20 | 21 | // Add all prototype methods, getters and setters here. 22 | NODE_SET_PROTOTYPE_METHOD(constructor, "value", Value); 23 | 24 | // This has to be last, otherwise the properties won't show up on the 25 | // object in JavaScript. 26 | target->Set(name, constructor->GetFunction()); 27 | } 28 | 29 | MyObject::MyObject(int val) 30 | : ObjectWrap(), 31 | value_(val) {} 32 | 33 | 34 | Handle MyObject::New(const Arguments& args) { 35 | HandleScope scope; 36 | 37 | if (!args.IsConstructCall()) { 38 | return ThrowException(Exception::TypeError( 39 | String::New("Use the new operator to create instances of this object.")) 40 | ); 41 | } 42 | 43 | if (args.Length() < 1) { 44 | return ThrowException(Exception::TypeError( 45 | String::New("First argument must be a number"))); 46 | } 47 | 48 | // Creates a new instance object of this type and wraps it. 49 | MyObject* obj = new MyObject(args[0]->ToInteger()->Value()); 50 | obj->Wrap(args.This()); 51 | 52 | return args.This(); 53 | } 54 | 55 | 56 | Handle MyObject::Value(const Arguments& args) { 57 | HandleScope scope; 58 | 59 | // Retrieves the pointer to the wrapped object instance. 60 | MyObject* obj = ObjectWrap::Unwrap(args.This()); 61 | 62 | return scope.Close(Integer::New(obj->value_)); 63 | } 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | void RegisterModule(Handle target) { 73 | MyObject::Init(target); 74 | } 75 | 76 | NODE_MODULE(modulename, RegisterModule); 77 | -------------------------------------------------------------------------------- /06_objects/modulename.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MODULENAME_HPP 2 | #define MODULENAME_HPP 3 | 4 | #include 5 | 6 | 7 | // Do not include this line. It's generally frowned upon to use namespaces 8 | // in header files as it may cause issues with other code that includes your 9 | // header file. 10 | // using namespace v8; 11 | 12 | 13 | class MyObject : public node::ObjectWrap { 14 | public: 15 | static v8::Persistent constructor; 16 | static void Init(v8::Handle target); 17 | 18 | protected: 19 | MyObject(int val); 20 | 21 | static v8::Handle New(const v8::Arguments& args); 22 | static v8::Handle Value(const v8::Arguments& args); 23 | 24 | // Your own object variables here 25 | int value_; 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /06_objects/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modulename", 3 | "version": "1.0.0", 4 | "main": "./build/Release/modulename" 5 | } -------------------------------------------------------------------------------- /06_objects/run.js: -------------------------------------------------------------------------------- 1 | var modulename = require('./build/Release/modulename'); 2 | 3 | var obj = new modulename.MyObject(42); 4 | console.warn(obj); 5 | console.warn(obj.value()); 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to write node.js modules using C++ 2 | 3 | This is a tutorial for writing [node.js](http://nodejs.org/) modules in C++. It is the base and reference for my talk on writing node.js modules with V8 at [JSConf 2011 in Berlin](http://jsconf.eu/2011). 4 | 5 | Further references: 6 | 7 | * [V8 Doxygen](http://izs.me/v8-docs/main.html) 8 | * [uv.h](https://github.com/joyent/libuv/blob/master/include/uv.h) 9 | * [An introduction to libuv](http://nikhilm.github.com/uvbook/) 10 | * [v8 Cookbook](http://create.tpsitulsa.com/wiki/V8_Cookbook) 11 | * [Google's C++ style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml) 12 | -------------------------------------------------------------------------------- /xx_benchmark/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "modulename", 5 | "sources": [ "modulename.cpp" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /xx_benchmark/fn1.js: -------------------------------------------------------------------------------- 1 | var modulename = require('./build/Release/modulename'); 2 | 3 | var start = Date.now(); 4 | for (var i = 0; i < 1000000; i++) { 5 | var val = modulename.fn1(); 6 | if (val !== 42) throw new Error('invalid result!'); 7 | } 8 | 9 | console.warn(Date.now() - start); -------------------------------------------------------------------------------- /xx_benchmark/fn2.js: -------------------------------------------------------------------------------- 1 | var modulename = require('./build/Release/modulename'); 2 | 3 | var start = Date.now(); 4 | for (var i = 0; i < 1000000; i++) { 5 | modulename.fn2(function(err, val) { 6 | if (val !== 42) throw new Error('invalid result!'); 7 | }); 8 | } 9 | 10 | console.warn(Date.now() - start); -------------------------------------------------------------------------------- /xx_benchmark/fn3.js: -------------------------------------------------------------------------------- 1 | var modulename = require('./build/Release/modulename'); 2 | 3 | var start = Date.now(); 4 | for (var i = 0; i < 1000000; i++) { 5 | modulename.fn3(function(err, val) { 6 | if (val !== 42) throw new Error('invalid result!'); 7 | }); 8 | } 9 | 10 | console.warn(Date.now() - start); -------------------------------------------------------------------------------- /xx_benchmark/modulename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace v8; 6 | 7 | struct Baton { 8 | uv_work_t request; 9 | Persistent callback; 10 | int32_t result; 11 | }; 12 | 13 | 14 | Handle Fn1(const Arguments& args) { 15 | HandleScope scope; 16 | int answer = 133.7f / M_PI; 17 | return scope.Close(Integer::New(answer)); 18 | } 19 | 20 | Handle Fn2(const Arguments& args) { 21 | HandleScope scope; 22 | int answer = 133.7f / M_PI; 23 | Local argv[] = { 24 | Local::New(Null()), 25 | Local::New(Integer::New(answer)) 26 | }; 27 | Local callback = Local::Cast(args[0]); 28 | callback->Call(Context::GetCurrent()->Global(), 2, argv); 29 | return Undefined(); 30 | } 31 | 32 | void Fn3Work(uv_work_t* req) { 33 | Baton* baton = static_cast(req->data); 34 | baton->result = 133.7f / M_PI; 35 | } 36 | 37 | void Fn3After(uv_work_t* req) { 38 | HandleScope scope; 39 | Baton* baton = static_cast(req->data); 40 | Local argv[] = { 41 | Local::New(Null()), 42 | Local::New(Integer::New(baton->result)) 43 | }; 44 | TryCatch try_catch; 45 | baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); 46 | if (try_catch.HasCaught()) { 47 | node::FatalException(try_catch); 48 | } 49 | baton->callback.Dispose(); 50 | delete baton; 51 | } 52 | 53 | Handle Fn3(const Arguments& args) { 54 | HandleScope scope; 55 | Baton* baton = new Baton(); 56 | baton->request.data = baton; 57 | Local callback = Local::Cast(args[0]); 58 | baton->callback = Persistent::New(callback); 59 | uv_queue_work(uv_default_loop(), &baton->request, Fn3Work, 60 | (uv_after_work_cb)Fn3After); 61 | return Undefined(); 62 | } 63 | 64 | 65 | void RegisterModule(Handle target) { 66 | NODE_SET_METHOD(target, "fn1", Fn1); 67 | NODE_SET_METHOD(target, "fn2", Fn2); 68 | NODE_SET_METHOD(target, "fn3", Fn3); 69 | } 70 | 71 | NODE_MODULE(modulename, RegisterModule); 72 | -------------------------------------------------------------------------------- /xx_benchmark/native.js: -------------------------------------------------------------------------------- 1 | var modulename = { 2 | fn: function() { return Math.floor(133.7 / Math.PI); } 3 | }; 4 | 5 | var start = Date.now(); 6 | for (var i = 0; i < 1000000; i++) { 7 | var val = modulename.fn(); 8 | if (val !== 42) throw new Error('invalid result!'); 9 | } 10 | 11 | console.warn(Date.now() - start); -------------------------------------------------------------------------------- /xx_benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmark", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /xx_benchmark/results.txt: -------------------------------------------------------------------------------- 1 | native: 90 2 | fn1: 3823 3 | fn2: 30406 4 | 5 | 6 | native: 6 1000000 / (6 / 1000) = 166,666,667 7 | fn1: 75 1000000 / (75 / 1000) = 13,333,333 8 | fn2: 573 1000000 / (573 / 1000) = 1,745,200 9 | fn3: 11950 1000000 / (11950 / 1000) = 83,682 10 | 11 | 12 | 13 | 14 | 15 | native: 10 16 | fn1: 76 17 | fn2: 568 18 | fn3: --------------------------------------------------------------------------------