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

Commit 75b9c9e

Browse files
committed
close
1 parent ecfcab0 commit 75b9c9e

33 files changed

+1278
-837
lines changed

binding.gyp

Lines changed: 8 additions & 2 deletions
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': [],
35-
'GCC_ENABLE_CPP_EXCEPTIONS': 'NO',
38+
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
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"', {

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"install": "node scripts/install.js",
3232
"postinstall": "node scripts/build.js",
3333
"lint": "node_modules/.bin/eslint bin/node-sass lib scripts test",
34-
"test": "node_modules/.bin/mocha test/{*,**/**}.js",
34+
"test": "node_modules/.bin/mocha test/api.js",
3535
"build": "node scripts/build.js --force",
3636
"prepublish": "not-in-install && node scripts/prepublish.js || in-install"
3737
},
@@ -66,12 +66,14 @@
6666
"meow": "^3.7.0",
6767
"mkdirp": "^0.5.1",
6868
"nan": "^2.10.0",
69+
"node-addon-api": "^1.3.0",
6970
"node-gyp": "^3.3.1",
7071
"npmlog": "^4.0.0",
7172
"request": "2.87.0",
7273
"sass-graph": "^2.2.4",
7374
"stdout-stream": "^1.4.0",
74-
"true-case-path": "^1.0.2"
75+
"true-case-path": "^1.0.2",
76+
"unicode-9.0.0": "^0.7.5"
7577
},
7678
"devDependencies": {
7779
"coveralls": "^3.0.2",

src/binding.cpp

Lines changed: 306 additions & 150 deletions
Large diffs are not rendered by default.

src/callback_bridge.h

Lines changed: 133 additions & 68 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 <napi.h>
8+
#include "common.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;
3840

3941

40-
virtual std::vector<v8::Local<v8::Value>> pre_process_args(std::vector<L>) const =0;
42+
virtual std::vector<napi_value> pre_process_args(napi_env, std::vector<L>) const = 0;
4143

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

4651
uv_mutex_t cv_mutex;
@@ -52,39 +57,68 @@ 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+
void CallbackBridge_Destructor(napi_env env, void* obj, void* hint) {
64+
CallbackBridge<T, L>* bridge = static_cast<CallbackBridge<T, L>*>(obj);
65+
delete bridge;
66+
}
67+
68+
template <typename T, typename L>
69+
napi_value CallbackBridge<T, L>::NewInstance(napi_env env) {
70+
Napi::EscapableHandleScope scope(env);
71+
72+
napi_value instance;
73+
napi_value constructorHandle;
74+
CHECK_NAPI_RESULT(napi_get_reference_value(env, get_wrapper_constructor(env), &constructorHandle));
75+
CHECK_NAPI_RESULT(napi_new_instance(env, constructorHandle, 0, nullptr, &instance));
76+
77+
return scope.Escape(instance);
78+
}
79+
80+
template <typename T, typename L>
81+
napi_value CallbackBridge<T, L>::New(napi_env env, napi_callback_info info) {
82+
napi_value _this;
83+
CHECK_NAPI_RESULT(napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
84+
return _this;
85+
}
5686

5787
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) {
88+
CallbackBridge<T, L>::CallbackBridge(napi_env env, napi_value callback, bool is_sync) : e(env), is_sync(is_sync) {
5989
/*
6090
* This is invoked from the main JavaScript thread.
6191
* V8 context is available.
6292
*/
63-
Nan::HandleScope scope;
6493
uv_mutex_init(&this->cv_mutex);
6594
uv_cond_init(&this->condition_variable);
6695
if (!is_sync) {
6796
this->async = new uv_async_t;
6897
this->async->data = (void*) this;
6998
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");
99+
// ASYNC
100+
// this->async_resource = new Nan::AsyncResource("node-sass:CallbackBridge");
71101
}
72102

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);
103+
CHECK_NAPI_RESULT(napi_create_reference(env, callback, 1, &this->callback));
104+
105+
napi_value instance = CallbackBridge::NewInstance(env);
106+
CHECK_NAPI_RESULT(napi_wrap(env, instance, this, CallbackBridge_Destructor<T,L>, nullptr, nullptr));
107+
CHECK_NAPI_RESULT(napi_create_reference(env, instance, 1, &this->wrapper));
76108
}
77109

78110
template <typename T, typename L>
79111
CallbackBridge<T, L>::~CallbackBridge() {
80-
delete this->callback;
81-
this->wrapper.Reset();
112+
CHECK_NAPI_RESULT(napi_delete_reference(e, this->callback));
113+
CHECK_NAPI_RESULT(napi_delete_reference(e, this->wrapper));
114+
82115
uv_cond_destroy(&this->condition_variable);
83116
uv_mutex_destroy(&this->cv_mutex);
84117

85118
if (!is_sync) {
86119
uv_close((uv_handle_t*)this->async, &async_gone);
87-
delete this->async_resource;
120+
// ASYNC
121+
// delete this->async_resource;
88122
}
89123
}
90124

@@ -100,18 +134,32 @@ T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
100134
* from types invoked by pre_process_args() and
101135
* post_process_args().
102136
*/
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);
137+
Napi::HandleScope scope(this->e);
138+
139+
std::vector<napi_value> argv_v8 = pre_process_args(this->e, argv);
140+
141+
bool isPending;
142+
CHECK_NAPI_RESULT(napi_is_exception_pending(this->e, &isPending));
143+
144+
if (isPending) {
145+
CHECK_NAPI_RESULT(napi_throw_error(this->e, nullptr, "Error processing arguments"));
146+
// This should be a FatalException but we still have to return something, this value might be uninitialized
147+
return this->return_value;
108148
}
109149

110-
argv_v8.push_back(Nan::New(wrapper));
150+
napi_value _this;
151+
CHECK_NAPI_RESULT(napi_get_reference_value(this->e, this->wrapper, &_this));
152+
argv_v8.push_back(_this);
153+
154+
napi_value cb;
155+
CHECK_NAPI_RESULT(napi_get_reference_value(this->e, this->callback, &cb));
156+
assert(cb != nullptr);
111157

112-
return this->post_process_return_value(
113-
Nan::Call(*this->callback, argv_v8.size(), &argv_v8[0]).ToLocalChecked()
114-
);
158+
napi_value result;
159+
// TODO: Is receiver set correctly ?
160+
CHECK_NAPI_RESULT(napi_make_callback(this->e, nullptr, _this, cb, argv_v8.size(), &argv_v8[0], &result));
161+
162+
return this->post_process_return_value(this->e, result);
115163
} else {
116164
/*
117165
* This is invoked from the worker thread.
@@ -153,24 +201,46 @@ void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) {
153201
* from types invoked by pre_process_args() and
154202
* post_process_args().
155203
*/
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);
204+
Napi::HandleScope scope(bridge->e);
205+
std::vector<napi_value> argv_v8 = bridge->pre_process_args(bridge->e, bridge->argv);
206+
bool isPending;
207+
208+
CHECK_NAPI_RESULT(napi_is_exception_pending(bridge->e, &isPending));
209+
if (isPending) {
210+
CHECK_NAPI_RESULT(napi_throw_error(bridge->e, nullptr, "Error processing arguments"));
211+
// This should be a FatalException
212+
return;
162213
}
163-
argv_v8.push_back(Nan::New(bridge->wrapper));
164214

165-
bridge->callback->Call(argv_v8.size(), &argv_v8[0], bridge->async_resource);
166-
167-
if (try_catch.HasCaught()) {
168-
Nan::FatalException(try_catch);
215+
napi_value _this;
216+
CHECK_NAPI_RESULT(napi_get_reference_value(bridge->e, bridge->wrapper, &_this));
217+
argv_v8.push_back(_this);
218+
219+
napi_value cb;
220+
CHECK_NAPI_RESULT(napi_get_reference_value(bridge->e, bridge->callback, &cb));
221+
assert(cb != nullptr);
222+
223+
napi_value result;
224+
// TODO: Is receiver set correctly ?
225+
CHECK_NAPI_RESULT(napi_make_callback(bridge->e, nullptr, _this, cb, argv_v8.size(), &argv_v8[0], &result));
226+
CHECK_NAPI_RESULT(napi_is_exception_pending(bridge->e, &isPending));
227+
if (isPending) {
228+
CHECK_NAPI_RESULT(napi_throw_error(bridge->e, nullptr, "Error thrown in callback"));
229+
// This should be a FatalException
230+
return;
169231
}
170232
}
171233

172234
template <typename T, typename L>
173-
NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
235+
napi_value CallbackBridge<T, L>::ReturnCallback(napi_env env, napi_callback_info info) {
236+
size_t argc = 1;
237+
napi_value arg;
238+
napi_value _this;
239+
CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &arg, &_this, nullptr));
240+
241+
void* unwrapped;
242+
CHECK_NAPI_RESULT(napi_unwrap(env, _this, &unwrapped));
243+
CallbackBridge* bridge = static_cast<CallbackBridge*>(unwrapped);
174244

175245
/*
176246
* Callback function invoked by the user code.
@@ -179,10 +249,7 @@ NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
179249
*
180250
* Implicit Local<> handle scope created by NAN_METHOD(.)
181251
*/
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]);
252+
bridge->return_value = bridge->post_process_return_value(env, arg);
186253

187254
{
188255
uv_mutex_lock(&bridge->cv_mutex);
@@ -192,32 +259,30 @@ NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
192259

193260
uv_cond_broadcast(&bridge->condition_variable);
194261

195-
if (try_catch.HasCaught()) {
196-
Nan::FatalException(try_catch);
197-
}
198-
}
262+
bool isPending;
263+
CHECK_NAPI_RESULT(napi_is_exception_pending(env, &isPending));
199264

200-
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());
265+
if (isPending) {
266+
// TODO: Call node::FatalException or add napi_fatal_exception ?
267+
assert(false);
213268
}
214269

215-
return Nan::New(wrapper_constructor);
270+
return nullptr;
216271
}
217272

218273
template <typename T, typename L>
219-
NAN_METHOD(CallbackBridge<T COMMA L>::New) {
220-
info.GetReturnValue().Set(info.This());
274+
napi_ref CallbackBridge<T, L>::get_wrapper_constructor(napi_env env) {
275+
// TODO: cache wrapper_constructor
276+
277+
napi_property_descriptor methods[] = {
278+
{ "success", nullptr, CallbackBridge::ReturnCallback },
279+
};
280+
281+
napi_value ctor;
282+
CHECK_NAPI_RESULT(napi_define_class(env, "CallbackBridge", NAPI_AUTO_LENGTH, CallbackBridge::New, nullptr, 1, methods, &ctor));
283+
CHECK_NAPI_RESULT(napi_create_reference(env, ctor, 1, &wrapper_constructor));
284+
285+
return wrapper_constructor;
221286
}
222287

223288
template <typename T, typename L>

src/common.h

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

0 commit comments

Comments
 (0)