Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit b2e7bcb

Browse files
committed
Convert binding to N-API
1 parent 6d6e407 commit b2e7bcb

37 files changed

+1316
-879
lines changed

binding.gyp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,22 @@
2727
'SetChecksum': 'true'
2828
}
2929
},
30+
'cflags!': [ '-fno-exceptions' ],
31+
'cflags_cc!': [ '-fno-exceptions' ],
3032
'xcode_settings': {
3133
'OTHER_CPLUSPLUSFLAGS': [
3234
'-std=c++11'
3335
],
36+
'CLANG_CXX_LIBRARY': 'libc++',
3437
'OTHER_LDFLAGS': [],
3538
'GCC_ENABLE_CPP_EXCEPTIONS': 'NO',
3639
'MACOSX_DEPLOYMENT_TARGET': '10.7'
3740
},
41+
"dependencies": [
42+
'<!(node -p "require(\'node-addon-api\').gyp")'
43+
],
3844
'include_dirs': [
39-
'<!(node -e "require(\'nan\')")',
45+
'<!@(node -p "require(\'node-addon-api\').include")',
4046
],
4147
'conditions': [
4248
['libsass_ext == "" or libsass_ext == "no"', {

lib/extensions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ function getBinaryName() {
200200
binaryName = [
201201
platform, '-',
202202
process.arch, '-',
203-
process.versions.modules
203+
Math.max(process.versions.napi || 0, 3),
204204
].join('');
205205
}
206206

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,14 @@
6464
"meow": "^4.0.0",
6565
"mkdirp": "^0.5.1",
6666
"nan": "^2.10.0",
67+
"node-addon-api": "^1.3.0",
6768
"node-gyp": "^3.3.1",
6869
"npmlog": "^4.0.0",
6970
"request": "2.87.0",
7071
"sass-graph": "^3.0.0",
7172
"stdout-stream": "^1.4.0",
72-
"true-case-path": "^1.0.2"
73+
"true-case-path": "^1.0.2",
74+
"unicode-9.0.0": "^0.7.5"
7375
},
7476
"devDependencies": {
7577
"coveralls": "^3.0.2",

src/binding.cpp

Lines changed: 313 additions & 149 deletions
Large diffs are not rendered by default.

src/callback_bridge.h

Lines changed: 123 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,32 @@
22
#define CALLBACK_BRIDGE_H
33

44
#include <vector>
5-
#include <nan.h>
65
#include <algorithm>
76
#include <uv.h>
8-
9-
#define COMMA ,
7+
#include "common.h"
8+
#include <napi.h>
109

1110
template <typename T, typename L = void*>
1211
class CallbackBridge {
1312
public:
14-
CallbackBridge(v8::Local<v8::Function>, bool);
13+
CallbackBridge(napi_env, napi_value, bool);
1514
virtual ~CallbackBridge();
1615

1716
// Executes the callback
1817
T operator()(std::vector<void*>);
1918

19+
// Needed for napi_wrap
20+
napi_value NewInstance(napi_env env);
21+
2022
protected:
2123
// We will expose a bridge object to the JS callback that wraps this instance so we don't loose context.
2224
// This is the V8 constructor for such objects.
23-
static Nan::MaybeLocal<v8::Function> get_wrapper_constructor();
25+
static napi_ref get_wrapper_constructor(napi_env env);
2426
static void async_gone(uv_handle_t *handle);
25-
static NAN_METHOD(New);
26-
static NAN_METHOD(ReturnCallback);
27-
static Nan::Persistent<v8::Function> wrapper_constructor;
28-
Nan::Persistent<v8::Object> wrapper;
27+
static napi_value New(napi_env env, napi_callback_info info);
28+
static napi_value ReturnCallback(napi_env env, napi_callback_info info);
29+
static napi_ref wrapper_constructor;
30+
napi_ref wrapper;
2931

3032
// The callback that will get called in the main thread after the worker thread used for the sass
3133
// compilation step makes a call to uv_async_send()
@@ -34,13 +36,16 @@ class CallbackBridge {
3436
// The V8 values sent to our ReturnCallback must be read on the main thread not the sass worker thread.
3537
// This gives a chance to specialized subclasses to transform those values into whatever makes sense to
3638
// sass before we resume the worker thread.
37-
virtual T post_process_return_value(v8::Local<v8::Value>) const =0;
39+
virtual T post_process_return_value(napi_env, napi_value) const = 0;
40+
3841

42+
virtual std::vector<napi_value> pre_process_args(napi_env, std::vector<L>) const = 0;
3943

40-
virtual std::vector<v8::Local<v8::Value>> pre_process_args(std::vector<L>) const =0;
44+
// ASYNC
45+
// Nan::AsyncResource* async_resource;
4146

42-
Nan::Callback* callback;
43-
Nan::AsyncResource* async_resource;
47+
napi_env e;
48+
napi_ref callback;
4449
bool is_sync;
4550

4651
uv_mutex_t cv_mutex;
@@ -52,45 +57,57 @@ class CallbackBridge {
5257
};
5358

5459
template <typename T, typename L>
55-
Nan::Persistent<v8::Function> CallbackBridge<T, L>::wrapper_constructor;
60+
napi_ref CallbackBridge<T, L>::wrapper_constructor = nullptr;
61+
62+
template <typename T, typename L>
63+
napi_value CallbackBridge<T, L>::NewInstance(napi_env env) {
64+
Napi::EscapableHandleScope scope(env);
65+
66+
napi_value instance;
67+
napi_value constructorHandle;
68+
CHECK_NAPI_RESULT(napi_get_reference_value(env, get_wrapper_constructor(env), &constructorHandle));
69+
CHECK_NAPI_RESULT(napi_new_instance(env, constructorHandle, 0, nullptr, &instance));
70+
71+
return scope.Escape(instance);
72+
}
5673

5774
template <typename T, typename L>
58-
CallbackBridge<T, L>::CallbackBridge(v8::Local<v8::Function> callback, bool is_sync) : callback(new Nan::Callback(callback)), is_sync(is_sync) {
75+
CallbackBridge<T, L>::CallbackBridge(napi_env env, napi_value callback, bool is_sync) : e(env), is_sync(is_sync) {
5976
/*
6077
* This is invoked from the main JavaScript thread.
6178
* V8 context is available.
6279
*/
63-
Nan::HandleScope scope;
80+
Napi::HandleScope scope(this->e);
6481
uv_mutex_init(&this->cv_mutex);
6582
uv_cond_init(&this->condition_variable);
6683
if (!is_sync) {
6784
this->async = new uv_async_t;
6885
this->async->data = (void*) this;
6986
uv_async_init(uv_default_loop(), this->async, (uv_async_cb) dispatched_async_uv_callback);
70-
this->async_resource = new Nan::AsyncResource("node-sass:CallbackBridge");
7187
}
7288

73-
v8::Local<v8::Function> func = CallbackBridge<T, L>::get_wrapper_constructor().ToLocalChecked();
74-
wrapper.Reset(Nan::NewInstance(func).ToLocalChecked());
75-
Nan::SetInternalFieldPointer(Nan::New(wrapper), 0, this);
89+
CHECK_NAPI_RESULT(napi_create_reference(env, callback, 1, &this->callback));
90+
91+
napi_value instance = CallbackBridge::NewInstance(env);
92+
CHECK_NAPI_RESULT(napi_wrap(env, instance, this, nullptr, nullptr, nullptr));
93+
CHECK_NAPI_RESULT(napi_create_reference(env, instance, 1, &this->wrapper));
7694
}
7795

7896
template <typename T, typename L>
7997
CallbackBridge<T, L>::~CallbackBridge() {
80-
delete this->callback;
81-
this->wrapper.Reset();
98+
CHECK_NAPI_RESULT(napi_delete_reference(e, this->callback));
99+
CHECK_NAPI_RESULT(napi_delete_reference(e, this->wrapper));
100+
82101
uv_cond_destroy(&this->condition_variable);
83102
uv_mutex_destroy(&this->cv_mutex);
84103

85104
if (!is_sync) {
86105
uv_close((uv_handle_t*)this->async, &async_gone);
87-
delete this->async_resource;
88106
}
89107
}
90108

91109
template <typename T, typename L>
92110
T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
93-
// argv.push_back(wrapper);
94111
if (this->is_sync) {
95112
/*
96113
* This is invoked from the main JavaScript thread.
@@ -100,18 +117,32 @@ T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
100117
* from types invoked by pre_process_args() and
101118
* post_process_args().
102119
*/
103-
Nan::HandleScope scope;
104-
Nan::TryCatch try_catch;
105-
std::vector<v8::Local<v8::Value>> argv_v8 = pre_process_args(argv);
106-
if (try_catch.HasCaught()) {
107-
Nan::FatalException(try_catch);
120+
Napi::HandleScope scope(this->e);
121+
122+
std::vector<napi_value> argv_v8 = pre_process_args(this->e, argv);
123+
124+
bool isPending;
125+
CHECK_NAPI_RESULT(napi_is_exception_pending(this->e, &isPending));
126+
127+
if (isPending) {
128+
CHECK_NAPI_RESULT(napi_throw_error(this->e, nullptr, "Error processing arguments"));
129+
// This should be a FatalException but we still have to return something, this value might be uninitialized
130+
return this->return_value;
108131
}
109132

110-
argv_v8.push_back(Nan::New(wrapper));
133+
napi_value _this;
134+
CHECK_NAPI_RESULT(napi_get_reference_value(this->e, this->wrapper, &_this));
135+
argv_v8.push_back(_this);
136+
137+
napi_value cb;
138+
CHECK_NAPI_RESULT(napi_get_reference_value(this->e, this->callback, &cb));
139+
assert(cb != nullptr);
140+
141+
napi_value result;
142+
// TODO: Is receiver set correctly ?
143+
CHECK_NAPI_RESULT(napi_make_callback(this->e, nullptr, _this, cb, argv_v8.size(), &argv_v8[0], &result));
111144

112-
return this->post_process_return_value(
113-
Nan::Call(*this->callback, argv_v8.size(), &argv_v8[0]).ToLocalChecked()
114-
);
145+
return this->post_process_return_value(this->e, result);
115146
} else {
116147
/*
117148
* This is invoked from the worker thread.
@@ -153,24 +184,46 @@ void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) {
153184
* from types invoked by pre_process_args() and
154185
* post_process_args().
155186
*/
156-
Nan::HandleScope scope;
157-
Nan::TryCatch try_catch;
158-
159-
std::vector<v8::Local<v8::Value>> argv_v8 = bridge->pre_process_args(bridge->argv);
160-
if (try_catch.HasCaught()) {
161-
Nan::FatalException(try_catch);
187+
Napi::HandleScope scope(bridge->e);
188+
std::vector<napi_value> argv_v8 = bridge->pre_process_args(bridge->e, bridge->argv);
189+
bool isPending;
190+
191+
CHECK_NAPI_RESULT(napi_is_exception_pending(bridge->e, &isPending));
192+
if (isPending) {
193+
CHECK_NAPI_RESULT(napi_throw_error(bridge->e, nullptr, "Error processing arguments"));
194+
// This should be a FatalException
195+
return;
162196
}
163-
argv_v8.push_back(Nan::New(bridge->wrapper));
164-
165-
bridge->callback->Call(argv_v8.size(), &argv_v8[0], bridge->async_resource);
166197

167-
if (try_catch.HasCaught()) {
168-
Nan::FatalException(try_catch);
198+
napi_value _this;
199+
CHECK_NAPI_RESULT(napi_get_reference_value(bridge->e, bridge->wrapper, &_this));
200+
argv_v8.push_back(_this);
201+
202+
napi_value cb;
203+
CHECK_NAPI_RESULT(napi_get_reference_value(bridge->e, bridge->callback, &cb));
204+
assert(cb != nullptr);
205+
206+
napi_value result;
207+
// TODO: Is receiver set correctly ?
208+
CHECK_NAPI_RESULT(napi_make_callback(bridge->e, nullptr, _this, cb, argv_v8.size(), &argv_v8[0], &result));
209+
CHECK_NAPI_RESULT(napi_is_exception_pending(bridge->e, &isPending));
210+
if (isPending) {
211+
CHECK_NAPI_RESULT(napi_throw_error(bridge->e, nullptr, "Error thrown in callback"));
212+
// This should be a FatalException
213+
return;
169214
}
170215
}
171216

172217
template <typename T, typename L>
173-
NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
218+
napi_value CallbackBridge<T, L>::ReturnCallback(napi_env env, napi_callback_info info) {
219+
size_t argc = 1;
220+
napi_value arg;
221+
napi_value _this;
222+
CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &arg, &_this, nullptr));
223+
224+
void* unwrapped;
225+
CHECK_NAPI_RESULT(napi_unwrap(env, _this, &unwrapped));
226+
CallbackBridge* bridge = static_cast<CallbackBridge*>(unwrapped);
174227

175228
/*
176229
* Callback function invoked by the user code.
@@ -179,10 +232,7 @@ NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
179232
*
180233
* Implicit Local<> handle scope created by NAN_METHOD(.)
181234
*/
182-
CallbackBridge<T, L>* bridge = static_cast<CallbackBridge<T, L>*>(Nan::GetInternalFieldPointer(info.This(), 0));
183-
Nan::TryCatch try_catch;
184-
185-
bridge->return_value = bridge->post_process_return_value(info[0]);
235+
bridge->return_value = bridge->post_process_return_value(env, arg);
186236

187237
{
188238
uv_mutex_lock(&bridge->cv_mutex);
@@ -192,32 +242,38 @@ NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
192242

193243
uv_cond_broadcast(&bridge->condition_variable);
194244

195-
if (try_catch.HasCaught()) {
196-
Nan::FatalException(try_catch);
245+
bool isPending;
246+
CHECK_NAPI_RESULT(napi_is_exception_pending(env, &isPending));
247+
248+
if (isPending) {
249+
napi_value result;
250+
CHECK_NAPI_RESULT(napi_get_and_clear_last_exception(env, &result));
251+
CHECK_NAPI_RESULT(napi_fatal_exception(env, result));
197252
}
253+
254+
return nullptr;
198255
}
199256

200257
template <typename T, typename L>
201-
Nan::MaybeLocal<v8::Function> CallbackBridge<T, L>::get_wrapper_constructor() {
202-
/* Uses handle scope created in the CallbackBridge<T, L> constructor */
203-
if (wrapper_constructor.IsEmpty()) {
204-
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
205-
tpl->SetClassName(Nan::New("CallbackBridge").ToLocalChecked());
206-
tpl->InstanceTemplate()->SetInternalFieldCount(1);
207-
208-
Nan::SetPrototypeTemplate(tpl, "success",
209-
Nan::New<v8::FunctionTemplate>(ReturnCallback)
210-
);
211-
212-
wrapper_constructor.Reset(Nan::GetFunction(tpl).ToLocalChecked());
213-
}
258+
napi_ref CallbackBridge<T, L>::get_wrapper_constructor(napi_env env) {
259+
// TODO: cache wrapper_constructor
260+
261+
napi_property_descriptor methods[] = {
262+
{ "success", nullptr, CallbackBridge::ReturnCallback },
263+
};
264+
265+
napi_value ctor;
266+
CHECK_NAPI_RESULT(napi_define_class(env, "CallbackBridge", NAPI_AUTO_LENGTH, CallbackBridge::New, nullptr, 1, methods, &ctor));
267+
CHECK_NAPI_RESULT(napi_create_reference(env, ctor, 1, &wrapper_constructor));
214268

215-
return Nan::New(wrapper_constructor);
269+
return wrapper_constructor;
216270
}
217271

218272
template <typename T, typename L>
219-
NAN_METHOD(CallbackBridge<T COMMA L>::New) {
220-
info.GetReturnValue().Set(info.This());
273+
napi_value CallbackBridge<T, L>::New(napi_env env, napi_callback_info info) {
274+
napi_value _this;
275+
CHECK_NAPI_RESULT(napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
276+
return _this;
221277
}
222278

223279
template <typename T, typename L>

src/common.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef COMMON_H
2+
#define COMMON_H
3+
4+
#define NAPI_DISABLE_CPP_EXCEPTIONS 1
5+
6+
#include <cassert>
7+
8+
#define CHECK_NAPI_RESULT(condition) \
9+
do { napi_status status = (condition); assert(status == napi_ok || status == napi_pending_exception); } while(0)
10+
11+
#endif // COMMON_H

0 commit comments

Comments
 (0)