Skip to content

Commit

Permalink
Use v8/nan APIs in a way that works on node 12
Browse files Browse the repository at this point in the history
  • Loading branch information
maxbrunsfeld committed Oct 22, 2019
1 parent 3f24e0c commit b719ee7
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 139 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ language: node_js

node_js:
- 10
- 12

matrix:
include:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
"prebuild-install": "^5.0.0"
},
"devDependencies": {
"prebuild": "^7.6.0",
"chai": "3.5.x",
"mocha": "^5.2.0",
"superstring": "^2.3.5",
"prebuild": "^7.6.0",
"superstring": "^2.4.1",
"tree-sitter-javascript": "git://github.com/tree-sitter/tree-sitter-javascript.git#master"
},
"scripts": {
Expand Down
65 changes: 39 additions & 26 deletions src/conversions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void InitConversions(Local<Object> exports) {

point_transfer_buffer = static_cast<uint32_t *>(malloc(2 * sizeof(uint32_t)));
auto js_point_transfer_buffer = ArrayBuffer::New(Isolate::GetCurrent(), point_transfer_buffer, 2 * sizeof(uint32_t));
exports->Set(Nan::New("pointTransferArray").ToLocalChecked(), Uint32Array::New(js_point_transfer_buffer, 0, 2));
Nan::Set(exports, Nan::New("pointTransferArray").ToLocalChecked(), Uint32Array::New(js_point_transfer_buffer, 0, 2));
}

void TransferPoint(const TSPoint &point) {
Expand All @@ -39,10 +39,10 @@ void TransferPoint(const TSPoint &point) {

Local<Object> RangeToJS(const TSRange &range) {
Local<Object> result = Nan::New<Object>();
result->Set(Nan::New(start_position_key), PointToJS(range.start_point));
result->Set(Nan::New(start_index_key), ByteCountToJS(range.start_byte));
result->Set(Nan::New(end_position_key), PointToJS(range.end_point));
result->Set(Nan::New(end_index_key), ByteCountToJS(range.end_byte));
Nan::Set(result, Nan::New(start_position_key), PointToJS(range.start_point));
Nan::Set(result, Nan::New(start_index_key), ByteCountToJS(range.start_byte));
Nan::Set(result, Nan::New(end_position_key), PointToJS(range.end_point));
Nan::Set(result, Nan::New(end_index_key), ByteCountToJS(range.end_byte));
return result;
}

Expand All @@ -56,8 +56,13 @@ Nan::Maybe<TSRange> RangeFromJS(const Local<Value> &arg) {

Local<Object> js_range = Local<Object>::Cast(arg);

#define INIT(field, key, Type) { \
auto field = Type(js_range->Get(Nan::New(key))); \
#define INIT(field, key, Convert) { \
auto value = Nan::Get(js_range, Nan::New(key)); \
if (value.IsEmpty()) { \
Nan::ThrowTypeError("Range must be a {startPosition, endPosition, startIndex, endIndex} object"); \
return Nan::Nothing<TSRange>(); \
} \
auto field = Convert(value.ToLocalChecked()); \
if (field.IsJust()) { \
result.field = field.FromJust(); \
} else { \
Expand All @@ -77,41 +82,48 @@ Nan::Maybe<TSRange> RangeFromJS(const Local<Value> &arg) {

Local<Object> PointToJS(const TSPoint &point) {
Local<Object> result = Nan::New<Object>();
result->Set(Nan::New(row_key), Nan::New<Number>(point.row));
result->Set(Nan::New(column_key), ByteCountToJS(point.column));
Nan::Set(result, Nan::New(row_key), Nan::New<Number>(point.row));
Nan::Set(result, Nan::New(column_key), ByteCountToJS(point.column));
return result;
}

Nan::Maybe<TSPoint> PointFromJS(const Local<Value> &arg) {
if (!arg->IsObject()) {
Local<Object> js_point;
if (!arg->IsObject() || !Nan::To<Object>(arg).ToLocal(&js_point)) {
Nan::ThrowTypeError("Point must be a {row, column} object");
return Nan::Nothing<TSPoint>();
}

Local<Object> js_point = Local<Object>::Cast(arg);
Local<Value> js_row = js_point->Get(Nan::New(row_key));
if (!js_row->IsNumber()) {
Nan::ThrowTypeError("Point.row must be a number");
Local<Value> js_row;
if (!Nan::Get(js_point, Nan::New(row_key)).ToLocal(&js_row)) {
Nan::ThrowTypeError("Point must be a {row, column} object");
return Nan::Nothing<TSPoint>();
}

Local<Value> js_column = js_point->Get(Nan::New(column_key));
if (!js_column->IsNumber()) {
Nan::ThrowTypeError("Point.column must be a number");
Local<Value> js_column;
if (!Nan::Get(js_point, Nan::New(column_key)).ToLocal(&js_column)) {
Nan::ThrowTypeError("Point must be a {row, column} object");
return Nan::Nothing<TSPoint>();
}

uint32_t row, column;
if (std::isfinite(js_row->NumberValue())) {
row = static_cast<uint32_t>(js_row->Int32Value());
} else {
uint32_t row;
if (!std::isfinite(Nan::To<double>(js_row).FromMaybe(0))) {
row = UINT32_MAX;
} else if (js_row->IsNumber()) {
row = Nan::To<uint32_t>(js_row).FromJust();
} else {
Nan::ThrowTypeError("Point.row must be a number");
return Nan::Nothing<TSPoint>();
}

if (std::isfinite(js_column->NumberValue())) {
column = static_cast<uint32_t>(js_column->Int32Value()) * BYTES_PER_CHARACTER;
} else {
uint32_t column;
if (!std::isfinite(Nan::To<double>(js_column).FromMaybe(0))) {
column = UINT32_MAX;
} else if (js_column->IsNumber()) {
column = Nan::To<uint32_t>(js_column).FromMaybe(0) * BYTES_PER_CHARACTER;
} else {
Nan::ThrowTypeError("Point.column must be a number");
return Nan::Nothing<TSPoint>();
}

return Nan::Just<TSPoint>({row, column});
Expand All @@ -122,12 +134,13 @@ Local<Number> ByteCountToJS(uint32_t byte_count) {
}

Nan::Maybe<uint32_t> ByteCountFromJS(const v8::Local<v8::Value> &arg) {
if (!arg->IsUint32()) {
auto result = Nan::To<uint32_t>(arg);
if (!arg->IsNumber()) {
Nan::ThrowTypeError("Character index must be a number");
return Nan::Nothing<uint32_t>();
}

return Nan::Just<uint32_t>(arg->Uint32Value() * BYTES_PER_CHARACTER);
return Nan::Just<uint32_t>(result.FromJust() * BYTES_PER_CHARACTER);
}

} // namespace node_tree_sitter
18 changes: 10 additions & 8 deletions src/language.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ static void GetNodeTypeNamesById(const Nan::FunctionCallbackInfo<Value> &info) {
const char *name = ts_language_symbol_name(language, i);
TSSymbolType type = ts_language_symbol_type(language, i);
if (type == TSSymbolTypeRegular) {
result->Set(i, Nan::New(name).ToLocalChecked());
Nan::Set(result, i, Nan::New(name).ToLocalChecked());
} else {
result->Set(i, Nan::Null());
Nan::Set(result, i, Nan::Null());
}
}

Expand All @@ -66,23 +66,25 @@ static void GetNodeFieldNamesById(const Nan::FunctionCallbackInfo<Value> &info)
for (uint32_t i = 0; i < length + 1; i++) {
const char *name = ts_language_field_name_for_id(language, i);
if (name) {
result->Set(i, Nan::New(name).ToLocalChecked());
Nan::Set(result, i, Nan::New(name).ToLocalChecked());
} else {
result->Set(i, Nan::Null());
Nan::Set(result, i, Nan::Null());
}
}
info.GetReturnValue().Set(result);
}

void Init(Local<Object> exports) {
exports->Set(
Nan::Set(
exports,
Nan::New("getNodeTypeNamesById").ToLocalChecked(),
Nan::New<FunctionTemplate>(GetNodeTypeNamesById)->GetFunction()
Nan::GetFunction(Nan::New<FunctionTemplate>(GetNodeTypeNamesById)).ToLocalChecked()
);

exports->Set(
Nan::Set(
exports,
Nan::New("getNodeFieldNamesById").ToLocalChecked(),
Nan::New<FunctionTemplate>(GetNodeFieldNamesById)->GetFunction()
Nan::GetFunction(Nan::New<FunctionTemplate>(GetNodeFieldNamesById)).ToLocalChecked()
);
}

Expand Down
10 changes: 5 additions & 5 deletions src/logger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,21 @@ void Logger::Log(void *payload, TSLogType type, const char *message_str) {

string key = message.substr(key_pos, (value_sep_pos - key_pos));
string value = message.substr(val_pos, (param_sep_pos - val_pos));
params->Set(Nan::New(key).ToLocalChecked(), Nan::New(value).ToLocalChecked());
Nan::Set(params, Nan::New(key).ToLocalChecked(), Nan::New(value).ToLocalChecked());
}

Local<Value> argv[3] = { name, params, type_name };
TryCatch try_catch(Isolate::GetCurrent());
fn->Call(fn->CreationContext()->Global(), 3, argv);
Nan::Call(fn, fn->CreationContext()->Global(), 3, argv);
if (try_catch.HasCaught()) {
Local<Value> log_argv[2] = {
Nan::New("Error in debug callback:").ToLocalChecked(),
try_catch.Exception()
};

Local<Object> console = Local<Object>::Cast(fn->CreationContext()->Global()->Get(Nan::New("console").ToLocalChecked()));
Local<Function> error_fn = Local<Function>::Cast(console->Get(Nan::New("error").ToLocalChecked()));
error_fn->Call(console, 2, log_argv);
Local<Object> console = Local<Object>::Cast(Nan::Get(fn->CreationContext()->Global(), Nan::New("console").ToLocalChecked()).ToLocalChecked());
Local<Function> error_fn = Local<Function>::Cast(Nan::Get(console, Nan::New("error").ToLocalChecked()).ToLocalChecked());
Nan::Call(error_fn, console, 2, log_argv);
}
}

Expand Down
81 changes: 47 additions & 34 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ static inline void setup_transfer_buffer(uint32_t node_count) {
transfer_buffer_length = new_length;
transfer_buffer = static_cast<uint32_t *>(malloc(transfer_buffer_length * sizeof(uint32_t)));
auto js_transfer_buffer = ArrayBuffer::New(Isolate::GetCurrent(), transfer_buffer, transfer_buffer_length * sizeof(uint32_t));
Nan::New(module_exports)->Set(
Nan::Set(
Nan::New(module_exports),
Nan::New("nodeTransferArray").ToLocalChecked(),
Uint32Array::New(js_transfer_buffer, 0, transfer_buffer_length)
);
Expand Down Expand Up @@ -56,13 +57,12 @@ static void MarshalNodes(const Nan::FunctionCallbackInfo<Value> &info,
*(p++) = node.context[2];
*(p++) = node.context[3];
if (node.id) {
result->Set(i, Nan::New(ts_node_symbol(node)));
Nan::Set(result, i, Nan::New(ts_node_symbol(node)));
} else {
result->Set(i, Nan::Null());
Nan::Set(result, i, Nan::Null());
}
} else {
assert(!cache_entry->second->node.IsNearDeath());
result->Set(i, Nan::New(cache_entry->second->node));
Nan::Set(result, i, Nan::New(cache_entry->second->node));
}
}
info.GetReturnValue().Set(result);
Expand All @@ -83,7 +83,6 @@ void MarshalNode(const Nan::FunctionCallbackInfo<Value> &info, const Tree *tree,
info.GetReturnValue().Set(Nan::New(ts_node_symbol(node)));
}
} else {
assert(!cache_entry->second->node.IsNearDeath());
info.GetReturnValue().Set(Nan::New(cache_entry->second->node));
}
}
Expand Down Expand Up @@ -317,7 +316,7 @@ static void Child(const Nan::FunctionCallbackInfo<Value> &info) {
Nan::ThrowTypeError("Second argument must be an integer");
return;
}
uint32_t index = info[1]->Uint32Value();
uint32_t index = Nan::To<uint32_t>(info[1]).FromJust();
MarshalNode(info, tree, ts_node_child(node, index));
return;
}
Expand All @@ -333,7 +332,7 @@ static void NamedChild(const Nan::FunctionCallbackInfo<Value> &info) {
Nan::ThrowTypeError("Second argument must be an integer");
return;
}
uint32_t index = info[1]->Uint32Value();
uint32_t index = Nan::To<uint32_t>(info[1]).FromJust();
MarshalNode(info, tree, ts_node_named_child(node, index));
return;
}
Expand Down Expand Up @@ -470,26 +469,37 @@ bool symbol_set_from_js(SymbolSet *symbols, const Local<Value> &value, const TSL

Local<Array> js_types = Local<Array>::Cast(value);
for (unsigned i = 0, n = js_types->Length(); i < n; i++) {
Local<Value> js_types_i = js_types->Get(i);
if (!js_types_i->IsString()) {
Nan::ThrowTypeError("Argument must be a string or array of strings");
return false;
}

Local<String> js_node_type = Local<String>::Cast(js_types_i);
std::string node_type(js_node_type->Utf8Length(), '\0');
js_node_type->WriteUtf8(&node_type[0]);

if (node_type == "ERROR") {
symbols->add(static_cast<TSSymbol>(-1));
continue;
}
Local<Value> js_node_type_value;
if (Nan::Get(js_types, i).ToLocal(&js_node_type_value)) {
Local<String> js_node_type;
if (Nan::To<String>(js_node_type_value).ToLocal(&js_node_type)) {
std::string node_type(js_node_type->Utf8Length(Isolate::GetCurrent()), '\0');
js_node_type->WriteUtf8(

// Nan doesn't wrap this functionality
#if NODE_MAJOR_VERSION >= 12
Isolate::GetCurrent(),
#endif

&node_type[0]
);

if (node_type == "ERROR") {
symbols->add(static_cast<TSSymbol>(-1));
} else {
for (TSSymbol j = 0; j < symbol_count; j++) {
if (node_type == ts_language_symbol_name(language, j)) {
symbols->add(j);
}
}
}

for (TSSymbol j = 0; j < symbol_count; j++) {
if (node_type == ts_language_symbol_name(language, j)) {
symbols->add(j);
continue;
}
}

Nan::ThrowTypeError("Argument must be a string or array of strings");
return false;
}

return true;
Expand Down Expand Up @@ -542,13 +552,13 @@ static void DescendantsOfType(const Nan::FunctionCallbackInfo<Value> &info) {
TSPoint start_point = {0, 0};
TSPoint end_point = {UINT32_MAX, UINT32_MAX};

if (info[2]->BooleanValue()) {
if (info.Length() > 2 && info[2]->IsObject()) {
auto maybe_start_point = PointFromJS(info[2]);
if (maybe_start_point.IsNothing()) return;
start_point = maybe_start_point.FromJust();
}

if (info[3]->BooleanValue()) {
if (info.Length() > 3 && info[3]->IsObject()) {
auto maybe_end_point = PointFromJS(info[3]);
if (maybe_end_point.IsNothing()) return;
end_point = maybe_end_point.FromJust();
Expand Down Expand Up @@ -602,11 +612,12 @@ static void ChildNodesForFieldId(const Nan::FunctionCallbackInfo<Value> &info) {
TSNode node = UnmarshalNode(tree);
if (!node.id) return;

if (!info[1]->IsUint32()) {
auto maybe_field_id = Nan::To<uint32_t>(info[1]);
if (!maybe_field_id.IsJust()) {
Nan::ThrowTypeError("Second argument must be an integer");
return;
}
uint32_t field_id = info[1]->Uint32Value();
uint32_t field_id = maybe_field_id.FromJust();

vector<TSNode> result;
ts_tree_cursor_reset(&scratch_cursor, node);
Expand All @@ -627,11 +638,12 @@ static void ChildNodeForFieldId(const Nan::FunctionCallbackInfo<Value> &info) {
TSNode node = UnmarshalNode(tree);

if (node.id) {
if (!info[1]->IsUint32()) {
auto maybe_field_id = Nan::To<uint32_t>(info[1]);
if (!maybe_field_id.IsJust()) {
Nan::ThrowTypeError("Second argument must be an integer");
return;
}
uint32_t field_id = info[1]->Uint32Value();
uint32_t field_id = maybe_field_id.FromJust();
MarshalNode(info, tree, ts_node_child_by_field_id(node, field_id));
return;
}
Expand Down Expand Up @@ -710,16 +722,17 @@ void Init(Local<Object> exports) {
};

for (size_t i = 0; i < length_of_array(methods); i++) {
result->Set(
Nan::Set(
result,
Nan::New(methods[i].name).ToLocalChecked(),
Nan::New<FunctionTemplate>(methods[i].callback)->GetFunction()
Nan::GetFunction(Nan::New<FunctionTemplate>(methods[i].callback)).ToLocalChecked()
);
}

module_exports.Reset(exports);
setup_transfer_buffer(1);

exports->Set(Nan::New("NodeMethods").ToLocalChecked(), result);
Nan::Set(exports, Nan::New("NodeMethods").ToLocalChecked(), result);
}

} // namespace node_methods
Expand Down
Loading

0 comments on commit b719ee7

Please sign in to comment.