Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert module to use NAPI instead of Nan #52

Closed
wants to merge 1 commit 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
12 changes: 8 additions & 4 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
"targets": [
{
"target_name": "tree_sitter_runtime_binding",
"dependencies": ["tree_sitter"],
"dependencies": [
"tree_sitter",
"<!(node -p \"require('node-addon-api').gyp\")"
],
"defines": [
"NAPI_DISABLE_CPP_EXCEPTIONS=",
],
"sources": [
"src/binding.cc",
"src/conversions.cc",
Expand All @@ -17,6 +23,7 @@
"vendor/tree-sitter/lib/include",
"vendor/superstring",
"<!(node -e \"require('nan')\")",
"<!@(node -p \"require('node-addon-api').include\")",
],
'conditions': [
['OS == "mac"', {
Expand All @@ -25,9 +32,6 @@
},
}]
],
"cflags": [
"-std=c++0x",
],
'xcode_settings': {
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
},
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ Parser.prototype.parse = function(input, oldTree, {bufferSize, includedRanges}={
} else {
getText = getTextFromFunction
}

const tree = parse.call(
this,
input,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"types": "tree-sitter.d.ts",
"dependencies": {
"nan": "^2.14.0",
"node-addon-api": "git+https://github.com/nodejs/node-addon-api.git",
"prebuild-install": "^5.0.0"
},
"devDependencies": {
Expand Down
18 changes: 9 additions & 9 deletions src/binding.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include <node.h>
#include <v8.h>
#include <napi.h>
#include "./language.h"
#include "./node.h"
#include "./parser.h"
Expand All @@ -9,17 +8,18 @@

namespace node_tree_sitter {

using namespace v8;
using namespace Napi;

void InitAll(Local<Object> exports) {
Object Init(Env env, Object exports) {
InitConversions(exports);
node_methods::Init(exports);
language_methods::Init(exports);
Parser::Init(exports);
InitNode(exports);
InitLanguage(exports);
InitParser(exports);
InitTreeCursor(exports);
Tree::Init(exports);
TreeCursor::Init(exports);
return exports;
}

NODE_MODULE(tree_sitter_runtime_binding, InitAll)
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)

} // namespace node_tree_sitter
165 changes: 83 additions & 82 deletions src/conversions.cc
Original file line number Diff line number Diff line change
@@ -1,146 +1,147 @@
#include "./node.h"
#include <nan.h>
#include <napi.h>
#include <tree_sitter/api.h>
#include <v8.h>
#include "./node.h"
#include "./conversions.h"
#include <cmath>

namespace node_tree_sitter {

using namespace v8;

Nan::Persistent<String> row_key;
Nan::Persistent<String> column_key;
Nan::Persistent<String> start_index_key;
Nan::Persistent<String> start_position_key;
Nan::Persistent<String> end_index_key;
Nan::Persistent<String> end_position_key;
using namespace Napi;

static unsigned BYTES_PER_CHARACTER = 2;
static uint32_t *point_transfer_buffer;

void InitConversions(Local<Object> exports) {
row_key.Reset(Nan::Persistent<String>(Nan::New("row").ToLocalChecked()));
column_key.Reset(Nan::Persistent<String>(Nan::New("column").ToLocalChecked()));
start_index_key.Reset(Nan::Persistent<String>(Nan::New("startIndex").ToLocalChecked()));
start_position_key.Reset(Nan::Persistent<String>(Nan::New("startPosition").ToLocalChecked()));
end_index_key.Reset(Nan::Persistent<String>(Nan::New("endIndex").ToLocalChecked()));
end_position_key.Reset(Nan::Persistent<String>(Nan::New("endPosition").ToLocalChecked()));

void InitConversions(Object &exports) {
auto env = exports.Env();
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));
Nan::Set(exports, Nan::New("pointTransferArray").ToLocalChecked(), Uint32Array::New(js_point_transfer_buffer, 0, 2));
auto js_point_transfer_buffer = ArrayBuffer::New(
env,
static_cast<void *>(point_transfer_buffer),
2 * sizeof(uint32_t)
);
exports.Set("pointTransferArray", Uint32Array::New(
env,
2,
js_point_transfer_buffer,
0
));
}

void TransferPoint(const TSPoint &point) {
point_transfer_buffer[0] = point.row;
point_transfer_buffer[1] = point.column / 2;
}

Local<Object> RangeToJS(const TSRange &range) {
Local<Object> result = Nan::New<Object>();
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));
Object RangeToJS(Env env, const TSRange &range) {
Object result = Object::New(env);
result.Set("startPosition", PointToJS(env, range.start_point));
result.Set("startIndex", ByteCountToJS(env, range.start_byte));
result.Set("endPosition", PointToJS(env, range.end_point));
result.Set("endIndex", ByteCountToJS(env, range.end_byte));
return result;
}

Nan::Maybe<TSRange> RangeFromJS(const Local<Value> &arg) {
if (!arg->IsObject()) {
Nan::ThrowTypeError("Range must be a {startPosition, endPosition, startIndex, endIndex} object");
return Nan::Nothing<TSRange>();
optional<TSRange> RangeFromJS(const Value &arg) {
Env env = arg.Env();

if (!arg.IsObject()) {
TypeError::New(env, "Range must be a {startPosition, endPosition, startIndex, endIndex} object").ThrowAsJavaScriptException();
return optional<TSRange>();
}

TSRange result;

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

#define INIT(field, key, Convert) { \
auto value = Nan::Get(js_range, Nan::New(key)); \
auto value = js_range.Get(key); \
if (value.IsEmpty()) { \
Nan::ThrowTypeError("Range must be a {startPosition, endPosition, startIndex, endIndex} object"); \
return Nan::Nothing<TSRange>(); \
TypeError::New(env, "Range must be a {startPosition, endPosition, startIndex, endIndex} object").ThrowAsJavaScriptException(); \
return optional<TSRange>(); \
} \
auto field = Convert(value.ToLocalChecked()); \
if (field.IsJust()) { \
result.field = field.FromJust(); \
auto field = Convert(value); \
if (field) { \
result.field = *field; \
} else { \
return Nan::Nothing<TSRange>(); \
return optional<TSRange>(); \
} \
}

INIT(start_point, start_position_key, PointFromJS);
INIT(end_point, end_position_key, PointFromJS);
INIT(start_byte, start_index_key, ByteCountFromJS);
INIT(end_byte, end_index_key, ByteCountFromJS);
INIT(start_point, "startPosition", PointFromJS);
INIT(end_point, "endPosition", PointFromJS);
INIT(start_byte, "startIndex", ByteCountFromJS);
INIT(end_byte, "endIndex", ByteCountFromJS);

#undef INIT

return Nan::Just(result);
return result;
}

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

Nan::Maybe<TSPoint> PointFromJS(const Local<Value> &arg) {
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>();
Number ByteCountToJS(Env env, uint32_t byte_count) {
return Number::New(env, byte_count / BYTES_PER_CHARACTER);
}

optional<TSPoint> PointFromJS(const Value &arg) {
Env env = arg.Env();

if (!arg.IsObject()) {
TypeError::New(env, "Point must be a {row, column} object").ThrowAsJavaScriptException();
return optional<TSPoint>();
}

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>();
Object js_point = arg.ToObject();

Number js_row = js_point.Get("row").As<Number>();
if (!js_row.IsNumber()) {
TypeError::New(env, "Point must be a {row, column} object").ThrowAsJavaScriptException();
return optional<TSPoint>();
}

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>();
Number js_column = js_point.Get("column").As<Number>();
if (!js_column.IsNumber()) {
TypeError::New(env, "Point must be a {row, column} object").ThrowAsJavaScriptException();
return optional<TSPoint>();
}

uint32_t row;
if (!std::isfinite(Nan::To<double>(js_row).FromMaybe(0))) {
if (!std::isfinite(js_row.DoubleValue())) {
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>();
row = js_row.Uint32Value();
}

uint32_t column;
if (!std::isfinite(Nan::To<double>(js_column).FromMaybe(0))) {
if (!std::isfinite(js_column.DoubleValue())) {
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>();
column = js_column.Uint32Value() * BYTES_PER_CHARACTER;
}

return Nan::Just<TSPoint>({row, column});
return TSPoint{row, column};
}

Local<Number> ByteCountToJS(uint32_t byte_count) {
return Nan::New<Number>(byte_count / BYTES_PER_CHARACTER);
}
optional<uint32_t> ByteCountFromJS(const Value &arg) {
Env env = arg.Env();

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

return Nan::Just<uint32_t>(result.FromJust() * BYTES_PER_CHARACTER);
Number js_number = arg.ToNumber();
if (!std::isfinite(js_number.DoubleValue())) {
return UINT32_MAX;
} else {
return js_number.Uint32Value() * BYTES_PER_CHARACTER;
}
}

} // namespace node_tree_sitter
23 changes: 10 additions & 13 deletions src/conversions.h
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
#ifndef NODE_TREE_SITTER_CONVERSIONS_H_
#define NODE_TREE_SITTER_CONVERSIONS_H_

#include <nan.h>
#include <v8.h>
#include <napi.h>
#include <tree_sitter/api.h>
#include "./optional.h"

namespace node_tree_sitter {

void InitConversions(v8::Local<v8::Object> exports);
v8::Local<v8::Object> RangeToJS(const TSRange &);
v8::Local<v8::Object> PointToJS(const TSPoint &);
void InitConversions(Napi::Object &);
void TransferPoint(const TSPoint &);
v8::Local<v8::Number> ByteCountToJS(uint32_t);
Nan::Maybe<TSPoint> PointFromJS(const v8::Local<v8::Value> &);
Nan::Maybe<uint32_t> ByteCountFromJS(const v8::Local<v8::Value> &);
Nan::Maybe<TSRange> RangeFromJS(const v8::Local<v8::Value> &);

extern Nan::Persistent<v8::String> row_key;
extern Nan::Persistent<v8::String> column_key;
extern Nan::Persistent<v8::String> start_key;
extern Nan::Persistent<v8::String> end_key;
Napi::Object RangeToJS(Napi::Env, const TSRange &);
Napi::Object PointToJS(Napi::Env, const TSPoint &);
Napi::Number ByteCountToJS(Napi::Env, uint32_t);

optional<TSPoint> PointFromJS(const Napi::Value &);
optional<uint32_t> ByteCountFromJS(const Napi::Value &);
optional<TSRange> RangeFromJS(const Napi::Value &);

} // namespace node_tree_sitter

Expand Down
Loading