From 012adde2ec767456cb6dce7124c37b7aba50190b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 4 Nov 2019 12:45:15 -0800 Subject: [PATCH 01/42] Bump tree-sitter for new EOF API --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index 80008b0b..3214de55 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit 80008b0bccbddffc8e68f66a5f173ef71fd125e3 +Subproject commit 3214de55f06d292babfdc932d2ffbb66c7d16a33 From dc57fe68a9f0456420713384c26325176ae285d0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 4 Nov 2019 13:38:12 -0800 Subject: [PATCH 02/42] 0.15.14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e394dcf3..80ba8abe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.15.13", + "version": "0.15.14", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From 8240213d4a77d43368a471aeb3da6de44fdbd6c3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 6 Dec 2019 09:52:37 -0800 Subject: [PATCH 03/42] :arrow_up: tree-sitter for symbol deduping https://github.com/tree-sitter/tree-sitter/pull/500 --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index 3214de55..1c8aed79 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit 3214de55f06d292babfdc932d2ffbb66c7d16a33 +Subproject commit 1c8aed790d9b98d464f5c7c13333abbecfea2225 From 07c71001f6fbc849d525926efba58fe159c1bb3d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 6 Dec 2019 09:55:40 -0800 Subject: [PATCH 04/42] 0.15.15 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80ba8abe..b4228cec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.15.14", + "version": "0.15.15", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From 048795d68175a94d82bcfa99ef43eaecbdef9f4d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 6 Dec 2019 16:12:59 -0800 Subject: [PATCH 05/42] :arrow_up: tree-sitter for null character bugfixes --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index 1c8aed79..0cb2ef10 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit 1c8aed790d9b98d464f5c7c13333abbecfea2225 +Subproject commit 0cb2ef1082c1ddd05634560ccee643930b11640e From a6631070b3f94e3f1f116a1231049dfec2a70ef1 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 6 Dec 2019 16:13:14 -0800 Subject: [PATCH 06/42] 0.16.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b4228cec..c8ba8034 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.15.15", + "version": "0.16.0", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From 3f8dcb2837ef7bc42874793f8c3c869888a0d4b6 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 3 Apr 2020 12:19:06 -0700 Subject: [PATCH 07/42] Bump tree-sitter library Fixes #61 --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index 0cb2ef10..b6558279 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit 0cb2ef1082c1ddd05634560ccee643930b11640e +Subproject commit b65582797ac1d42576a0193b004a5fa5b147eae2 From ee143c83b829231b19fb0cd51785cca7121af3a4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 3 Apr 2020 12:19:27 -0700 Subject: [PATCH 08/42] 0.16.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c8ba8034..0bf3107b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.16.0", + "version": "0.16.1", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From adebd0dfa943c9af1beaf9ce57e05293305ec7cb Mon Sep 17 00:00:00 2001 From: Thibault Dalban Date: Wed, 20 May 2020 18:17:58 +0200 Subject: [PATCH 09/42] Fix the TypeScript definition of a Range (#64) A Range contains: - startIndex - endIndex - startPosition - endPosition --- tree-sitter.d.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tree-sitter.d.ts b/tree-sitter.d.ts index e366db98..0aefabae 100644 --- a/tree-sitter.d.ts +++ b/tree-sitter.d.ts @@ -14,8 +14,10 @@ declare module "tree-sitter" { }; export type Range = { - start: Point; - end: Point; + startIndex: number, + endIndex: number, + startPosition: Point, + endPosition: Point }; export type Edit = { From c40d598b4e3d587c0be26ac6a780d91815ff731a Mon Sep 17 00:00:00 2001 From: Thibault Dalban Date: Wed, 20 May 2020 18:18:39 +0200 Subject: [PATCH 10/42] Add bufferSize and includedRanges options to parse function in TS def (#63) * Fix the TypeScript definition of a Range A Range contains: - startIndex - endIndex - startPosition - endPosition * Add `bufferSize` and `includedRanges` to TS def The function signature of `Parser.prototype.parse` defined in index.js `Parser.prototype.parse = function(input, oldTree, {bufferSize, includedRanges}={})` accepts a third optional parameter. This commit reflects the signature in TypeScript definition. --- tree-sitter.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree-sitter.d.ts b/tree-sitter.d.ts index 0aefabae..063f24d4 100644 --- a/tree-sitter.d.ts +++ b/tree-sitter.d.ts @@ -1,6 +1,6 @@ declare module "tree-sitter" { class Parser { - parse(input: string | Parser.Input, previousTree?: Parser.Tree): Parser.Tree; + parse(input: string | Parser.Input, previousTree?: Parser.Tree, options?: {bufferSize?: number, includedRanges?: Parser.Range[]}): Parser.Tree; getLanguage(): any; setLanguage(language: any): void; getLogger(): Parser.Logger; From a95bc5c723d094673dbdf622bb6e5d4d7a29614f Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Tue, 18 Aug 2020 17:30:12 -0400 Subject: [PATCH 11/42] Implement Query API (#62) * queries: initial implementation * queries: add predicates * test: setup query tests * package: add build script * query: display TSQueryError name instead of value * tree-sitter: update to latest * query_cursor: remove & move .exec to query.cc * query: continue implementation of .exec * test: adjust query_test.js to match current API * lint: remove unused module * node_methods: expose GetMarshalNodes * lint: add section comments * query: implement Query.matches * query: implement Query.captures * query: implement predicates filtering * query: make Query::GetPredicates warmer * query: fix predicates initialization * tests: unskip query.captures * query: implement range searching * query: remove Query.exec * query: use arrays for marshalling * node: fix #66 free transfer_buffer * tests: fix query_test labels * query: flatten intermediate array * query: dont transfer capture_count * unmarshalNodes: pass nodes as array * lint * query: remove unused values * query: avoid out-of-bound access --- binding.gyp | 2 + index.js | 296 +++++++++++++++++++++++++++++++++++++++--- package.json | 1 + src/binding.cc | 2 + src/node.cc | 21 ++- src/node.h | 4 + src/query.cc | 317 +++++++++++++++++++++++++++++++++++++++++++++ src/query.h | 36 +++++ src/tree.cc | 30 ++++- src/tree.h | 1 + src/util.cc | 14 ++ src/util.h | 8 ++ test/query_test.js | 194 +++++++++++++++++++++++++++ vendor/tree-sitter | 2 +- 14 files changed, 899 insertions(+), 29 deletions(-) create mode 100644 src/query.cc create mode 100644 src/query.h create mode 100644 src/util.cc create mode 100644 test/query_test.js diff --git a/binding.gyp b/binding.gyp index 4dc4cfd0..130ee595 100644 --- a/binding.gyp +++ b/binding.gyp @@ -10,8 +10,10 @@ "src/logger.cc", "src/node.cc", "src/parser.cc", + "src/query.cc", "src/tree.cc", "src/tree_cursor.cc", + "src/util.cc", ], "include_dirs": [ "vendor/tree-sitter/lib/include", diff --git a/index.js b/index.js index f2f927cd..82416503 100644 --- a/index.js +++ b/index.js @@ -9,9 +9,12 @@ try { } } -const vm = require('vm'); const util = require('util') -const {Parser, NodeMethods, Tree, TreeCursor} = binding; +const {Query, Parser, NodeMethods, Tree, TreeCursor} = binding; + +/* + * Tree + */ const {rootNode, edit} = Tree.prototype; @@ -37,6 +40,10 @@ Tree.prototype.walk = function() { return this.rootNode.walk() }; +/* + * Node + */ + class SyntaxNode { constructor(tree) { this.tree = tree; @@ -241,6 +248,10 @@ class SyntaxNode { } } +/* + * Parser + */ + const {parse, parseTextBuffer, parseTextBufferSync, setLanguage} = Parser.prototype; const languageSymbol = Symbol('parser.language'); @@ -325,6 +336,10 @@ Parser.prototype.parseTextBufferSync = function(buffer, oldTree, {includedRanges return tree; }; +/* + * TreeCursor + */ + const {startPosition, endPosition, currentNode, reset} = TreeCursor.prototype; Object.defineProperties(TreeCursor.prototype, { @@ -357,6 +372,225 @@ TreeCursor.prototype.reset = function(node) { reset.call(this); } +/* + * Query + */ + +const {_matches, _captures} = Query.prototype; + +const PREDICATE_STEP_TYPE = { + DONE: 0, + CAPTURE: 1, + STRING: 2, +} + +const ZERO_POINT = { row: 0, column: 0 }; + +Query.prototype._init = function() { + /* + * Initializa predicate functions + * format: [type1, value1, type2, value2, ...] + */ + const predicateDescriptions = this._getPredicates(); + const patternCount = predicateDescriptions.length; + + const setProperties = new Array(patternCount); + const assertedProperties = new Array(patternCount); + const refutedProperties = new Array(patternCount); + const predicates = new Array(patternCount); + + const FIRST = 0 + const SECOND = 2 + const THIRD = 4 + + for (let i = 0; i < predicateDescriptions.length; i++) { + predicates[i] = []; + + for (let j = 0; j < predicateDescriptions[i].length; j++) { + + const steps = predicateDescriptions[i][j]; + const stepsLength = steps.length / 2; + + if (steps[FIRST] !== PREDICATE_STEP_TYPE.STRING) { + throw new Error('Predicates must begin with a literal value'); + } + + const operator = steps[FIRST + 1]; + + let isPositive = true; + + switch (operator) { + case 'not-eq?': + isPositive = false; + case 'eq?': + if (stepsLength !== 3) throw new Error( + `Wrong number of arguments to \`#eq?\` predicate. Expected 2, got ${stepsLength - 1}` + ); + if (steps[SECOND] !== PREDICATE_STEP_TYPE.CAPTURE) throw new Error( + `First argument of \`#eq?\` predicate must be a capture. Got "${steps[SECOND + 1]}"` + ); + if (steps[THIRD] === PREDICATE_STEP_TYPE.CAPTURE) { + const captureName1 = steps[SECOND + 1]; + const captureName2 = steps[THIRD + 1]; + predicates[i].push(function(captures) { + let node1, node2 + for (const c of captures) { + if (c.name === captureName1) node1 = c.node; + if (c.name === captureName2) node2 = c.node; + } + return (node1.text === node2.text) === isPositive; + }); + } else { + const captureName = steps[SECOND + 1]; + const stringValue = steps[THIRD + 1]; + predicates[i].push(function(captures) { + for (const c of captures) { + if (c.name === captureName) { + return (c.node.text === stringValue) === isPositive; + }; + } + return false; + }); + } + break; + + case 'match?': + if (stepsLength !== 3) throw new Error( + `Wrong number of arguments to \`#match?\` predicate. Expected 2, got ${stepsLength - 1}.` + ); + if (steps[SECOND] !== PREDICATE_STEP_TYPE.CAPTURE) throw new Error( + `First argument of \`#match?\` predicate must be a capture. Got "${steps[SECOND + 1]}".` + ); + if (steps[THIRD] !== PREDICATE_STEP_TYPE.STRING) throw new Error( + `Second argument of \`#match?\` predicate must be a string. Got @${steps[THIRD + 1]}.` + ); + const captureName = steps[SECOND + 1]; + const regex = new RegExp(steps[THIRD + 1]); + predicates[i].push(function(captures) { + for (const c of captures) { + if (c.name === captureName) return regex.test(c.node.text); + } + return false; + }); + break; + + case 'set!': + if (stepsLength < 2 || stepsLength > 3) throw new Error( + `Wrong number of arguments to \`#set!\` predicate. Expected 1 or 2. Got ${stepsLength - 1}.` + ); + if (steps.some((s, i) => (i % 2 !== 1) && s !== PREDICATE_STEP_TYPE.STRING)) throw new Error( + `Arguments to \`#set!\` predicate must be a strings.".` + ); + if (!setProperties[i]) setProperties[i] = {}; + setProperties[i][steps[SECOND + 1]] = steps[THIRD] ? steps[THIRD + 1] : null; + break; + + case 'is?': + case 'is-not?': + if (stepsLength < 2 || stepsLength > 3) throw new Error( + `Wrong number of arguments to \`#${operator}\` predicate. Expected 1 or 2. Got ${stepsLength - 1}.` + ); + if (steps.some((s, i) => (i % 2 !== 1) && s !== PREDICATE_STEP_TYPE.STRING)) throw new Error( + `Arguments to \`#${operator}\` predicate must be a strings.".` + ); + const properties = operator === 'is?' ? assertedProperties : refutedProperties; + if (!properties[i]) properties[i] = {}; + properties[i][steps[SECOND + 1]] = steps[THIRD] ? steps[THIRD + 1] : null; + break; + + default: + throw new Error(`Unknown query predicate \`#${steps[FIRST + 1]}\``); + } + } + } + + this.predicates = Object.freeze(predicates); + this.setProperties = Object.freeze(setProperties); + this.assertedProperties = Object.freeze(assertedProperties); + this.refutedProperties = Object.freeze(refutedProperties); +} + +Query.prototype.matches = function(rootNode, start = ZERO_POINT, end = ZERO_POINT) { + marshalNode(rootNode); + const [returnedMatches, returnedNodes] = _matches.call(this, rootNode.tree, + start.row, start.column, + end.row, end.column + ); + const nodes = unmarshalNodes(returnedNodes, rootNode.tree); + const results = []; + + let i = 0 + let nodeIndex = 0; + while (i < returnedMatches.length) { + const patternIndex = returnedMatches[i++]; + const captures = []; + + while (i < returnedMatches.length && typeof returnedMatches[i] === 'string') { + const captureName = returnedMatches[i++]; + captures.push({ + name: captureName, + node: nodes[nodeIndex++], + }) + } + + if (this.predicates[patternIndex].every(p => p(captures))) { + const result = {pattern: patternIndex, captures}; + const setProperties = this.setProperties[patternIndex]; + const assertedProperties = this.assertedProperties[patternIndex]; + const refutedProperties = this.refutedProperties[patternIndex]; + if (setProperties) result.setProperties = setProperties; + if (assertedProperties) result.assertedProperties = assertedProperties; + if (refutedProperties) result.refutedProperties = refutedProperties; + results.push(result); + } + } + + return results; +} + +Query.prototype.captures = function(rootNode, start = ZERO_POINT, end = ZERO_POINT) { + marshalNode(rootNode); + const [returnedMatches, returnedNodes] = _captures.call(this, rootNode.tree, + start.row, start.column, + end.row, end.column + ); + const nodes = unmarshalNodes(returnedNodes, rootNode.tree); + const results = []; + + let i = 0 + let nodeIndex = 0; + while (i < returnedMatches.length) { + const patternIndex = returnedMatches[i++]; + const captureIndex = returnedMatches[i++]; + const captures = []; + + while (i < returnedMatches.length && typeof returnedMatches[i] === 'string') { + const captureName = returnedMatches[i++]; + captures.push({ + name: captureName, + node: nodes[nodeIndex++], + }) + } + + if (this.predicates[patternIndex].every(p => p(captures))) { + const result = captures[captureIndex]; + const setProperties = this.setProperties[patternIndex]; + const assertedProperties = this.assertedProperties[patternIndex]; + const refutedProperties = this.refutedProperties[patternIndex]; + if (setProperties) result.setProperties = setProperties; + if (assertedProperties) result.assertedProperties = assertedProperties; + if (refutedProperties) result.refutedProperties = refutedProperties; + results.push(result); + } + } + + return results; +} + +/* + * Other functions + */ + function getTextFromString (node) { return this.input.substring(node.startIndex, node.endIndex); } @@ -381,37 +615,62 @@ const {pointTransferArray} = binding; const NODE_FIELD_COUNT = 6; const ERROR_TYPE_ID = 0xFFFF -function unmarshalNode(value, tree, offset = 0) { +function getID(buffer, offset) { + const low = BigInt(buffer[offset]); + const high = BigInt(buffer[offset + 1]); + return (high << 32n) + low; +} + +function unmarshalNode(value, tree, offset = 0, cache = null) { + /* case 1: node from the tree cache */ if (typeof value === 'object') { const node = value; return node; - } else { - const nodeTypeId = value; - const NodeClass = nodeTypeId === ERROR_TYPE_ID - ? SyntaxNode - : tree.language.nodeSubclasses[nodeTypeId]; - const {nodeTransferArray} = binding; - if (nodeTransferArray[0] || nodeTransferArray[1]) { - const result = new NodeClass(tree); - for (let i = 0; i < NODE_FIELD_COUNT; i++) { - result[i] = nodeTransferArray[offset + i]; - } - tree._cacheNode(result); - return result; - } + } + + /* case 2: node being transferred */ + const nodeTypeId = value; + const NodeClass = nodeTypeId === ERROR_TYPE_ID + ? SyntaxNode + : tree.language.nodeSubclasses[nodeTypeId]; + + const {nodeTransferArray} = binding; + const id = getID(nodeTransferArray, offset) + if (id === 0n) { return null } + + let cachedResult; + if (cache && (cachedResult = cache.get(id))) + return cachedResult; + + const result = new NodeClass(tree); + for (let i = 0; i < NODE_FIELD_COUNT; i++) { + result[i] = nodeTransferArray[offset + i]; + } + + if (cache) + cache.set(id, result); + else + tree._cacheNode(result); + + return result; } function unmarshalNodes(nodes, tree) { + const cache = new Map(); + let offset = 0; for (let i = 0, {length} = nodes; i < length; i++) { - const node = unmarshalNode(nodes[i], tree, offset); + const node = unmarshalNode(nodes[i], tree, offset, cache); if (node !== nodes[i]) { nodes[i] = node; offset += NODE_FIELD_COUNT } } + + tree._cacheNodes(Array.from(cache.values())); + return nodes; } @@ -490,6 +749,7 @@ function camelCase(name, upperCase) { } module.exports = Parser; +module.exports.Query = Query; module.exports.Tree = Tree; module.exports.SyntaxNode = SyntaxNode; module.exports.TreeCursor = TreeCursor; diff --git a/package.json b/package.json index 0bf3107b..fc27e9e0 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "scripts": { "install": "prebuild-install || node-gyp rebuild", + "build": "node-gyp build", "prebuild": "prebuild -r electron -t 3.0.0 -t 4.0.0 -t 4.0.4 -t 5.0.0 --strip && prebuild -t 10.12.0 -t 12.13.0 --strip", "prebuild:upload": "prebuild --upload-all", "test": "mocha" diff --git a/src/binding.cc b/src/binding.cc index 4b7d2a31..8681223b 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3,6 +3,7 @@ #include "./language.h" #include "./node.h" #include "./parser.h" +#include "./query.h" #include "./tree.h" #include "./tree_cursor.h" #include "./conversions.h" @@ -16,6 +17,7 @@ void InitAll(Local exports) { node_methods::Init(exports); language_methods::Init(exports); Parser::Init(exports); + Query::Init(exports); Tree::Init(exports); TreeCursor::Init(exports); } diff --git a/src/node.cc b/src/node.cc index 14054544..711781e2 100644 --- a/src/node.cc +++ b/src/node.cc @@ -24,6 +24,9 @@ static TSTreeCursor scratch_cursor = {nullptr, nullptr, {0, 0}}; static inline void setup_transfer_buffer(uint32_t node_count) { uint32_t new_length = node_count * FIELD_COUNT_PER_NODE; if (new_length > transfer_buffer_length) { + if (transfer_buffer) { + free(transfer_buffer); + } transfer_buffer_length = new_length; transfer_buffer = static_cast(malloc(transfer_buffer_length * sizeof(uint32_t))); auto js_transfer_buffer = ArrayBuffer::New(Isolate::GetCurrent(), transfer_buffer, transfer_buffer_length * sizeof(uint32_t)); @@ -43,6 +46,15 @@ static inline bool operator<=(const TSPoint &left, const TSPoint &right) { static void MarshalNodes(const Nan::FunctionCallbackInfo &info, const Tree *tree, const TSNode *nodes, uint32_t node_count) { + info.GetReturnValue().Set(GetMarshalNodes(info, tree, nodes, node_count)); +} + +void MarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *tree, TSNode node) { + info.GetReturnValue().Set(GetMarshalNode(info, tree, node)); +} + +Local GetMarshalNodes(const Nan::FunctionCallbackInfo &info, + const Tree *tree, const TSNode *nodes, uint32_t node_count) { auto result = Nan::New(); setup_transfer_buffer(node_count); uint32_t *p = transfer_buffer; @@ -65,10 +77,10 @@ static void MarshalNodes(const Nan::FunctionCallbackInfo &info, Nan::Set(result, i, Nan::New(cache_entry->second->node)); } } - info.GetReturnValue().Set(result); + return result; } -void MarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *tree, TSNode node) { +Local GetMarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *tree, TSNode node) { const auto &cache_entry = tree->cached_nodes_.find(node.id); if (cache_entry == tree->cached_nodes_.end()) { setup_transfer_buffer(1); @@ -80,11 +92,12 @@ void MarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *tree, *(p++) = node.context[2]; *(p++) = node.context[3]; if (node.id) { - info.GetReturnValue().Set(Nan::New(ts_node_symbol(node))); + return Nan::New(ts_node_symbol(node)); } } else { - info.GetReturnValue().Set(Nan::New(cache_entry->second->node)); + return Nan::New(cache_entry->second->node); } + return Nan::Null(); } void MarshalNullNode() { diff --git a/src/node.h b/src/node.h index 6dcc34f0..8c050e20 100644 --- a/src/node.h +++ b/src/node.h @@ -7,11 +7,15 @@ #include #include "./tree.h" +using namespace v8; + namespace node_tree_sitter { namespace node_methods { void Init(v8::Local); void MarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *, TSNode); +Local GetMarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *tree, TSNode node); +Local GetMarshalNodes(const Nan::FunctionCallbackInfo &info, const Tree *tree, const TSNode *nodes, uint32_t node_count); TSNode UnmarshalNode(const Tree *tree); static inline const void *UnmarshalNodeId(const uint32_t *buffer) { diff --git a/src/query.cc b/src/query.cc new file mode 100644 index 00000000..8dac3304 --- /dev/null +++ b/src/query.cc @@ -0,0 +1,317 @@ +#include "./query.h" +#include +#include +#include +#include +#include "./node.h" +#include "./language.h" +#include "./logger.h" +#include "./util.h" +#include "./conversions.h" + +namespace node_tree_sitter { + +using std::vector; +using namespace v8; +using node_methods::UnmarshalNodeId; + +const char *query_error_names[] = { + "TSQueryErrorNone", + "TSQueryErrorSyntax", + "TSQueryErrorNodeType", + "TSQueryErrorField", + "TSQueryErrorCapture", +}; + +TSQueryCursor *Query::ts_query_cursor; +Nan::Persistent Query::constructor; +Nan::Persistent Query::constructor_template; + +void Query::Init(Local exports) { + ts_query_cursor = ts_query_cursor_new(); + + Local tpl = Nan::New(New); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + Local class_name = Nan::New("Query").ToLocalChecked(); + tpl->SetClassName(class_name); + + FunctionPair methods[] = { + {"_matches", Matches}, + {"_captures", Captures}, + {"_getPredicates", GetPredicates}, + }; + + for (size_t i = 0; i < length_of_array(methods); i++) { + Nan::SetPrototypeMethod(tpl, methods[i].name, methods[i].callback); + } + + Local ctor = Nan::GetFunction(tpl).ToLocalChecked(); + + constructor_template.Reset(tpl); + constructor.Reset(ctor); + Nan::Set(exports, class_name, ctor); +} + +Query::Query(TSQuery *query) : query_(query) {} + +Query::~Query() { + ts_query_delete(query_); +} + +Local Query::NewInstance(TSQuery *query) { + if (query) { + Local self; + MaybeLocal maybe_self = Nan::NewInstance(Nan::New(constructor)); + if (maybe_self.ToLocal(&self)) { + (new Query(query))->Wrap(self); + return self; + } + } + return Nan::Null(); +} + +Query *Query::UnwrapQuery(const Local &value) { + if (!value->IsObject()) return nullptr; + Local js_query = Local::Cast(value); + if (!Nan::New(constructor_template)->HasInstance(js_query)) return nullptr; + return ObjectWrap::Unwrap(js_query); +} + +void Query::New(const Nan::FunctionCallbackInfo &info) { + if (!info.IsConstructCall()) { + Local self; + MaybeLocal maybe_self = Nan::New(constructor)->NewInstance(Nan::GetCurrentContext()); + if (maybe_self.ToLocal(&self)) { + info.GetReturnValue().Set(self); + } else { + info.GetReturnValue().Set(Nan::Null()); + } + return; + } + + const TSLanguage *language = language_methods::UnwrapLanguage(info[0]); + const char *source; + uint32_t source_len; + uint32_t error_offset = 0; + TSQueryError error_type = TSQueryErrorNone; + + if (language == nullptr) { + Nan::ThrowError("Missing language argument"); + return; + } + + if (info[1]->IsString()) { + auto string = Nan::To (info[1]).ToLocalChecked(); + source = *Nan::Utf8String(string); + source_len = string->Length(); + } + else if (node::Buffer::HasInstance(info[1])) { + source = node::Buffer::Data(info[1]); + source_len = node::Buffer::Length(info[1]); + } + else { + Nan::ThrowError("Missing source argument"); + return; + } + + TSQuery *query = ts_query_new( + language, source, source_len, &error_offset, &error_type); + + if (error_offset > 0) { + const char *error_name = query_error_names[error_type]; + std::string message = "Query error of type "; + message += error_name; + message += " at position "; + message += std::to_string(error_offset); + Nan::ThrowError(message.c_str()); + return; + } + + auto self = info.This(); + + Query *query_wrapper = new Query(query); + query_wrapper->Wrap(self); + + auto init = + Nan::To( + Nan::Get(self, Nan::New("_init").ToLocalChecked()).ToLocalChecked() + ).ToLocalChecked(); + Nan::Call(init, self, 0, nullptr); + + info.GetReturnValue().Set(self); +} + +void Query::GetPredicates(const Nan::FunctionCallbackInfo &info) { + Query *query = Query::UnwrapQuery(info.This()); + auto ts_query = query->query_; + + auto pattern_len = ts_query_pattern_count(ts_query); + + Local js_predicates = Nan::New(); + + for (size_t pattern_index = 0; pattern_index < pattern_len; pattern_index++) { + uint32_t predicates_len; + const TSQueryPredicateStep *predicates = ts_query_predicates_for_pattern( + ts_query, pattern_index, &predicates_len); + + Local js_pattern_predicates = Nan::New(); + + if (predicates_len > 0) { + Local js_predicate = Nan::New(); + + size_t a_index = 0; + size_t p_index = 0; + for (size_t i = 0; i < predicates_len; i++) { + const TSQueryPredicateStep predicate = predicates[i]; + uint32_t len; + switch (predicate.type) { + case TSQueryPredicateStepTypeCapture: + Nan::Set(js_predicate, p_index++, Nan::New(TSQueryPredicateStepTypeCapture)); + Nan::Set(js_predicate, p_index++, + Nan::New( + ts_query_capture_name_for_id(ts_query, predicate.value_id, &len) + ).ToLocalChecked()); + break; + case TSQueryPredicateStepTypeString: + Nan::Set(js_predicate, p_index++, Nan::New(TSQueryPredicateStepTypeString)); + Nan::Set(js_predicate, p_index++, + Nan::New( + ts_query_string_value_for_id(ts_query, predicate.value_id, &len) + ).ToLocalChecked()); + break; + case TSQueryPredicateStepTypeDone: + Nan::Set(js_pattern_predicates, a_index++, js_predicate); + js_predicate = Nan::New(); + p_index = 0; + break; + } + } + } + + Nan::Set(js_predicates, pattern_index, js_pattern_predicates); + } + + info.GetReturnValue().Set(js_predicates); +} + +void Query::Matches(const Nan::FunctionCallbackInfo &info) { + Query *query = Query::UnwrapQuery(info.This()); + const Tree *tree = Tree::UnwrapTree(info[0]); + uint32_t start_row = Nan::To(info[1]).ToChecked(); + uint32_t start_column = Nan::To(info[2]).ToChecked() << 1; + uint32_t end_row = Nan::To(info[3]).ToChecked(); + uint32_t end_column = Nan::To(info[4]).ToChecked() << 1; + + if (query == nullptr) { + Nan::ThrowError("Missing argument query"); + return; + } + + if (tree == nullptr) { + Nan::ThrowError("Missing argument tree"); + return; + } + + TSQuery *ts_query = query->query_; + TSNode rootNode = node_methods::UnmarshalNode(tree); + TSPoint start_point = {start_row, start_column}; + TSPoint end_point = {end_row, end_column}; + ts_query_cursor_set_point_range(ts_query_cursor, start_point, end_point); + ts_query_cursor_exec(ts_query_cursor, ts_query, rootNode); + + Local js_matches = Nan::New(); + unsigned index = 0; + vector nodes; + TSQueryMatch match; + + while (ts_query_cursor_next_match(ts_query_cursor, &match)) { + Nan::Set(js_matches, index++, Nan::New(match.pattern_index)); + + for (uint16_t i = 0; i < match.capture_count; i++) { + const TSQueryCapture &capture = match.captures[i]; + + uint32_t capture_name_len = 0; + const char *capture_name = ts_query_capture_name_for_id( + ts_query, capture.index, &capture_name_len); + + TSNode node = capture.node; + nodes.push_back(node); + + Local js_capture = Nan::New(capture_name).ToLocalChecked(); + Nan::Set(js_matches, index++, js_capture); + } + } + + auto js_nodes = node_methods::GetMarshalNodes(info, tree, nodes.data(), nodes.size()); + + auto result = Nan::New(); + Nan::Set(result, 0, js_matches); + Nan::Set(result, 1, js_nodes); + info.GetReturnValue().Set(result); +} + +void Query::Captures(const Nan::FunctionCallbackInfo &info) { + Query *query = Query::UnwrapQuery(info.This()); + const Tree *tree = Tree::UnwrapTree(info[0]); + uint32_t start_row = Nan::To(info[1]).ToChecked(); + uint32_t start_column = Nan::To(info[2]).ToChecked() << 1; + uint32_t end_row = Nan::To(info[3]).ToChecked(); + uint32_t end_column = Nan::To(info[4]).ToChecked() << 1; + + if (query == nullptr) { + Nan::ThrowError("Missing argument query"); + return; + } + + if (tree == nullptr) { + Nan::ThrowError("Missing argument tree"); + return; + } + + TSQuery *ts_query = query->query_; + TSNode rootNode = node_methods::UnmarshalNode(tree); + TSPoint start_point = {start_row, start_column}; + TSPoint end_point = {end_row, end_column}; + ts_query_cursor_set_point_range(ts_query_cursor, start_point, end_point); + ts_query_cursor_exec(ts_query_cursor, ts_query, rootNode); + + Local js_matches = Nan::New(); + unsigned index = 0; + vector nodes; + TSQueryMatch match; + uint32_t capture_index; + + while (ts_query_cursor_next_capture( + ts_query_cursor, + &match, + &capture_index + )) { + + Nan::Set(js_matches, index++, Nan::New(match.pattern_index)); + Nan::Set(js_matches, index++, Nan::New(capture_index)); + + for (uint16_t i = 0; i < match.capture_count; i++) { + const TSQueryCapture &capture = match.captures[i]; + + uint32_t capture_name_len = 0; + const char *capture_name = ts_query_capture_name_for_id( + ts_query, capture.index, &capture_name_len); + + TSNode node = capture.node; + nodes.push_back(node); + + Local js_capture = Nan::New(capture_name).ToLocalChecked(); + Nan::Set(js_matches, index++, js_capture); + } + } + + auto js_nodes = node_methods::GetMarshalNodes(info, tree, nodes.data(), nodes.size()); + + auto result = Nan::New(); + Nan::Set(result, 0, js_matches); + Nan::Set(result, 1, js_nodes); + info.GetReturnValue().Set(result); +} + + +} // namespace node_tree_sitter diff --git a/src/query.h b/src/query.h new file mode 100644 index 00000000..955e621f --- /dev/null +++ b/src/query.h @@ -0,0 +1,36 @@ +#ifndef NODE_TREE_SITTER_QUERY_H_ +#define NODE_TREE_SITTER_QUERY_H_ + +#include +#include +#include +#include +#include + +namespace node_tree_sitter { + +class Query : public Nan::ObjectWrap { + public: + static void Init(v8::Local exports); + static v8::Local NewInstance(TSQuery *); + static Query *UnwrapQuery(const v8::Local &); + + TSQuery *query_; + + private: + explicit Query(TSQuery *); + ~Query(); + + static void New(const Nan::FunctionCallbackInfo &); + static void Matches(const Nan::FunctionCallbackInfo &); + static void Captures(const Nan::FunctionCallbackInfo &); + static void GetPredicates(const Nan::FunctionCallbackInfo &); + + static TSQueryCursor *ts_query_cursor; + static Nan::Persistent constructor; + static Nan::Persistent constructor_template; +}; + +} // namespace node_tree_sitter + +#endif // NODE_TREE_SITTER_QUERY_H_ diff --git a/src/tree.cc b/src/tree.cc index bac04a2c..4447d4cc 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -28,6 +28,7 @@ void Tree::Init(Local exports) { {"getChangedRanges", GetChangedRanges}, {"getEditedRange", GetEditedRange}, {"_cacheNode", CacheNode}, + {"_cacheNodes", CacheNodes}, }; for (size_t i = 0; i < length_of_array(methods); i++) { @@ -210,10 +211,7 @@ static void FinalizeNode(const v8::WeakCallbackInfo &info) delete cache_entry; } -void Tree::CacheNode(const Nan::FunctionCallbackInfo &info) { - Tree *tree = ObjectWrap::Unwrap(info.This()); - Local js_node = Local::Cast(info[0]); - +static void CacheNodeForTree(Tree *tree, Isolate *isolate, Local js_node) { Local js_node_field1, js_node_field2; if (!Nan::Get(js_node, 0).ToLocal(&js_node_field1)) return; if (!Nan::Get(js_node, 1).ToLocal(&js_node_field2)) return; @@ -223,8 +221,8 @@ void Tree::CacheNode(const Nan::FunctionCallbackInfo &info) { }; const void *key = UnmarshalNodeId(key_parts); - auto cache_entry = new NodeCacheEntry{tree, key, {}}; - cache_entry->node.Reset(info.GetIsolate(), js_node); + auto cache_entry = new Tree::NodeCacheEntry{tree, key, {}}; + cache_entry->node.Reset(isolate, js_node); cache_entry->node.SetWeak(cache_entry, &FinalizeNode, Nan::WeakCallbackType::kParameter); assert(!tree->cached_nodes_.count(key)); @@ -232,4 +230,24 @@ void Tree::CacheNode(const Nan::FunctionCallbackInfo &info) { tree->cached_nodes_[key] = cache_entry; } +void Tree::CacheNode(const Nan::FunctionCallbackInfo &info) { + Tree *tree = ObjectWrap::Unwrap(info.This()); + Isolate *isolate = info.GetIsolate(); + Local js_node = Local::Cast(info[0]); + + CacheNodeForTree(tree, isolate, js_node); +} + +void Tree::CacheNodes(const Nan::FunctionCallbackInfo &info) { + Tree *tree = ObjectWrap::Unwrap(info.This()); + Isolate *isolate = info.GetIsolate(); + Local js_nodes = Local::Cast(info[0]); + uint32_t length = js_nodes->Length(); + + for (uint32_t i = 0; i < length; i++) { + auto js_node = Local::Cast(Nan::Get(js_nodes, i).ToLocalChecked()); + CacheNodeForTree(tree, isolate, js_node); + } +} + } // namespace node_tree_sitter diff --git a/src/tree.h b/src/tree.h index 1d5962e2..5ed4e316 100644 --- a/src/tree.h +++ b/src/tree.h @@ -35,6 +35,7 @@ class Tree : public Nan::ObjectWrap { static void GetEditedRange(const Nan::FunctionCallbackInfo &); static void GetChangedRanges(const Nan::FunctionCallbackInfo &); static void CacheNode(const Nan::FunctionCallbackInfo &); + static void CacheNodes(const Nan::FunctionCallbackInfo &); static Nan::Persistent constructor; static Nan::Persistent constructor_template; diff --git a/src/util.cc b/src/util.cc new file mode 100644 index 00000000..41133c66 --- /dev/null +++ b/src/util.cc @@ -0,0 +1,14 @@ +#include +#include +#include "./util.h" + +namespace node_tree_sitter { + +bool instance_of(v8::Local value, v8::Local object) { + auto maybe_bool = value->InstanceOf(Nan::GetCurrentContext(), object); + if (maybe_bool.IsNothing()) + return false; + return maybe_bool.FromJust(); +} + +} // namespace node_tree_sitter diff --git a/src/util.h b/src/util.h index 3e2ebbba..eaa7cd99 100644 --- a/src/util.h +++ b/src/util.h @@ -1,3 +1,7 @@ +#ifndef NODE_TREE_SITTER_UTIL_H_ +#define NODE_TREE_SITTER_UTIL_H_ + +#include #include namespace node_tree_sitter { @@ -14,4 +18,8 @@ struct FunctionPair { Nan::FunctionCallback callback; }; +bool instance_of(v8::Local value, v8::Local object); + } // namespace node_tree_sitter + +#endif // NODE_TREE_SITTER_UTIL_H_ diff --git a/test/query_test.js b/test/query_test.js new file mode 100644 index 00000000..35dc3d85 --- /dev/null +++ b/test/query_test.js @@ -0,0 +1,194 @@ +const fs = require("fs"); +const Parser = require(".."); +const JavaScript = require("tree-sitter-javascript"); +const { assert } = require("chai"); +const {Query, QueryCursor} = Parser + +describe("Query", () => { + + const parser = new Parser(); + parser.setLanguage(JavaScript); + + describe("new", () => { + it("works with string", () => { + const query = new Query(JavaScript, ` + (function_declaration name: (identifier) @fn-def) + (call_expression function: (identifier) @fn-ref) + `); + }); + + it("works with Buffer", () => { + const query = new Query(JavaScript, Buffer.from(` + (function_declaration name: (identifier) @fn-def) + (call_expression function: (identifier) @fn-ref) + `)); + }); + }); + + describe(".matches", () => { + it("returns all of the matches for the given query", () => { + const tree = parser.parse("function one() { two(); function three() {} }"); + const query = new Query(JavaScript, ` + (function_declaration name: (identifier) @fn-def) + (call_expression function: (identifier) @fn-ref) + `); + const matches = query.matches(tree.rootNode); + assert.deepEqual(formatMatches(tree, matches), [ + { pattern: 0, captures: [{ name: "fn-def", text: "one" }] }, + { pattern: 1, captures: [{ name: "fn-ref", text: "two" }] }, + { pattern: 0, captures: [{ name: "fn-def", text: "three" }] }, + ]); + }); + + it("can search in a specified ranges", () => { + const tree = parser.parse("[a, b,\nc, d,\ne, f,\ng, h]"); + const query = new Query(JavaScript, "(identifier) @element"); + const matches = query.matches( + tree.rootNode, + { row: 1, column: 1 }, + { row: 3, column: 1 } + ); + assert.deepEqual(formatMatches(tree, matches), [ + { pattern: 0, captures: [{ name: "element", text: "d" }] }, + { pattern: 0, captures: [{ name: "element", text: "e" }] }, + { pattern: 0, captures: [{ name: "element", text: "f" }] }, + { pattern: 0, captures: [{ name: "element", text: "g" }] }, + ]); + }); + }); + + describe(".captures", () => { + it("returns all of the captures for the given query, in order", () => { + const tree = parser.parse(` + a({ + bc: function de() { + const fg = function hi() {} + }, + jk: function lm() { + const no = function pq() {} + }, + }); + `); + const query = new Query(JavaScript, ` + (pair + key: _ @method.def + (function + name: (identifier) @method.alias)) + (variable_declarator + name: _ @function.def + value: (function + name: (identifier) @function.alias)) + ":" @delimiter + "=" @operator + `); + + const captures = query.captures(tree.rootNode); + assert.deepEqual(formatCaptures(tree, captures), [ + { name: "method.def", text: "bc" }, + { name: "delimiter", text: ":" }, + { name: "method.alias", text: "de" }, + { name: "function.def", text: "fg" }, + { name: "operator", text: "=" }, + { name: "function.alias", text: "hi" }, + { name: "method.def", text: "jk" }, + { name: "delimiter", text: ":" }, + { name: "method.alias", text: "lm" }, + { name: "function.def", text: "no" }, + { name: "operator", text: "=" }, + { name: "function.alias", text: "pq" }, + ]); + }); + + it("handles conditions that compare the text of capture to literal strings", () => { + const tree = parser.parse(` + const ab = require('./ab'); + new Cd(EF); + `); + + const query = new Query(JavaScript, ` + (identifier) @variable + ((identifier) @function.builtin + (#eq? @function.builtin "require")) + ((identifier) @constructor + (#match? @constructor "^[A-Z]")) + ((identifier) @constant + (#match? @constant "^[A-Z]{2,}$")) + `); + + const captures = query.captures(tree.rootNode); + assert.deepEqual(formatCaptures(tree, captures), [ + { name: "variable", text: "ab" }, + { name: "variable", text: "require" }, + { name: "function.builtin", text: "require" }, + { name: "variable", text: "Cd" }, + { name: "constructor", text: "Cd" }, + { name: "variable", text: "EF" }, + { name: "constructor", text: "EF" }, + { name: "constant", text: "EF" }, + ]); + }); + + it("handles conditions that compare the text of capture to each other", () => { + const tree = parser.parse(` + ab = abc + 1; + def = de + 1; + ghi = ghi + 1; + `); + + const query = new Query(JavaScript, ` + ( + (assignment_expression + left: (identifier) @id1 + right: (binary_expression + left: (identifier) @id2)) + (#eq? @id1 @id2) + ) + `); + + const captures = query.captures(tree.rootNode); + assert.deepEqual(formatCaptures(tree, captures), [ + { name: "id1", text: "ghi" }, + { name: "id2", text: "ghi" }, + ]); + }); + + it("handles patterns with properties", () => { + const tree = parser.parse(`a(b.c);`); + const query = new Query(JavaScript, ` + ((call_expression (identifier) @func) + (#set! foo) + (#set! bar baz)) + ((property_identifier) @prop + (#is? foo) + (#is-not? bar baz)) + `); + + const captures = query.captures(tree.rootNode); + assert.deepEqual(formatCaptures(tree, captures), [ + { name: "func", text: "a", setProperties: { foo: null, bar: "baz" } }, + { + name: "prop", + text: "c", + assertedProperties: { foo: null }, + refutedProperties: { bar: "baz" }, + }, + ]); + }); + }); +}); + +function formatMatches(tree, matches) { + return matches.map(({ pattern, captures }) => ({ + pattern, + captures: formatCaptures(tree, captures), + })); +} + +function formatCaptures(tree, captures) { + return captures.map((c) => { + const node = c.node; + delete c.node; + c.text = tree.getText(node); + return c; + }); +} diff --git a/vendor/tree-sitter b/vendor/tree-sitter index b6558279..ec870e9e 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit b65582797ac1d42576a0193b004a5fa5b147eae2 +Subproject commit ec870e9e66c34354133ad865dd12fbaceb021083 From 1a5459d1cec14d86f1b619a475106c63fffe04e2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2020 14:32:52 -0700 Subject: [PATCH 12/42] Bump tree-sitter --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index ec870e9e..d5576e30 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit ec870e9e66c34354133ad865dd12fbaceb021083 +Subproject commit d5576e306cee2568730af1a390bd29ffd788fea9 From d395d3d8f32f160465833b607ba91ff5162cf94d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2020 14:33:07 -0700 Subject: [PATCH 13/42] :arrow_up: superstring --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fc27e9e0..801436cf 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "chai": "3.5.x", "mocha": "^5.2.0", "prebuild": "^7.6.0", - "superstring": "^2.4.1", + "superstring": "^2.4.2", "tree-sitter-javascript": "git://github.com/tree-sitter/tree-sitter-javascript.git#master" }, "scripts": { From c52030226d0bcf534809d1d860bbc774b10f9b25 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2020 14:33:12 -0700 Subject: [PATCH 14/42] 0.16.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 801436cf..2fc881a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.16.1", + "version": "0.16.2", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From e41c14b801dcde846120508184b8ea47b23abe8e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 23 Sep 2020 14:04:33 -0700 Subject: [PATCH 15/42] Update tree-sitter library --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index d5576e30..9185262e 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit d5576e306cee2568730af1a390bd29ffd788fea9 +Subproject commit 9185262e48ed67b92bf63a029839bae0758d7429 From 209e734e8df0bc0b79ef0af222843a92ce9c5c8c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 23 Sep 2020 14:04:37 -0700 Subject: [PATCH 16/42] 0.17.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2fc881a2..e3b21192 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.16.2", + "version": "0.17.0", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From 376fc5b8590a182f5873f4e506fdd29164312ffb Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Oct 2020 12:16:15 -0700 Subject: [PATCH 17/42] Bump tree-sitter --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index 9185262e..87fd2f5c 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit 9185262e48ed67b92bf63a029839bae0758d7429 +Subproject commit 87fd2f5ca3c2198c6b11ece3e060aab0e81406ac From 25ae24f1f3958d87cd052051d5c9247146e5e1fe Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Oct 2020 12:16:23 -0700 Subject: [PATCH 18/42] 0.17.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e3b21192..80799216 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.17.0", + "version": "0.17.1", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From 73b94a802d7ad92c9a8c11910031ea1afb4ea5ea Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 19 Feb 2021 13:38:49 -0800 Subject: [PATCH 19/42] :arrow_up: tree-sitter --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index 87fd2f5c..2f28a35e 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit 87fd2f5ca3c2198c6b11ece3e060aab0e81406ac +Subproject commit 2f28a35e1b118e17ab2fb6236a24c7b557e3c8a9 From 9658df8b0b5e807b17732ca3946660a57556ae8a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 19 Feb 2021 13:38:54 -0800 Subject: [PATCH 20/42] 0.17.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80799216..2bccd480 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.17.1", + "version": "0.17.2", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From e867853e298a183ed0a0243a2c03d53e14579a8b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 19 Feb 2021 13:39:26 -0800 Subject: [PATCH 21/42] 0.18.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2bccd480..17cfb1a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.17.2", + "version": "0.18.0", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From d0133d4d6681759a8eeb6e13bcab7b2f351e9ddc Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 19 Feb 2021 15:17:51 -0800 Subject: [PATCH 22/42] Add a string representation of TSQueryErrorStructure --- src/query.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/query.cc b/src/query.cc index 8dac3304..1f0c230e 100644 --- a/src/query.cc +++ b/src/query.cc @@ -21,6 +21,7 @@ const char *query_error_names[] = { "TSQueryErrorNodeType", "TSQueryErrorField", "TSQueryErrorCapture", + "TSQueryErrorStructure", }; TSQueryCursor *Query::ts_query_cursor; From 812cd15cc9648d550c0dd8a39fbde8133b8e4b08 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 19 Feb 2021 15:18:27 -0800 Subject: [PATCH 23/42] 0.18.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 17cfb1a6..3dc27ef2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.18.0", + "version": "0.18.1", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From 738bb127342201a1b74c06c14945908e9ad892f7 Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Wed, 3 Mar 2021 20:07:26 +0200 Subject: [PATCH 24/42] Added missing ts tree-sitter.d.ts declarations (#80) * Fix typo * tree-sitter.d.ts - define missing Parser class fields * tree-sitter.d.ts - define missing SyntaxNode typeId field * tree-sitter.d.ts - define missed TreeCursor currentFieldName field * tree-sitter.d.ts - define missed Tree printDotGraph method * tree-sitter.d.ts - define missed Query class and related types * tree-sitter.d.ts - fix Query.captures return type * tree-sitter.d.ts - sync query parameter names with wasm binding * fix types checking for node types --- index.js | 14 +++++++------- package.json | 1 + tree-sitter.d.ts | 42 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index 82416503..ec0b2b25 100644 --- a/index.js +++ b/index.js @@ -388,7 +388,7 @@ const ZERO_POINT = { row: 0, column: 0 }; Query.prototype._init = function() { /* - * Initializa predicate functions + * Initialize predicate functions * format: [type1, value1, type2, value2, ...] */ const predicateDescriptions = this._getPredicates(); @@ -510,11 +510,11 @@ Query.prototype._init = function() { this.refutedProperties = Object.freeze(refutedProperties); } -Query.prototype.matches = function(rootNode, start = ZERO_POINT, end = ZERO_POINT) { +Query.prototype.matches = function(rootNode, startPosition = ZERO_POINT, endPosition = ZERO_POINT) { marshalNode(rootNode); const [returnedMatches, returnedNodes] = _matches.call(this, rootNode.tree, - start.row, start.column, - end.row, end.column + startPosition.row, startPosition.column, + endPosition.row, endPosition.column ); const nodes = unmarshalNodes(returnedNodes, rootNode.tree); const results = []; @@ -548,11 +548,11 @@ Query.prototype.matches = function(rootNode, start = ZERO_POINT, end = ZERO_POIN return results; } -Query.prototype.captures = function(rootNode, start = ZERO_POINT, end = ZERO_POINT) { +Query.prototype.captures = function(rootNode, startPosition = ZERO_POINT, endPosition = ZERO_POINT) { marshalNode(rootNode); const [returnedMatches, returnedNodes] = _captures.call(this, rootNode.tree, - start.row, start.column, - end.row, end.column + startPosition.row, startPosition.column, + endPosition.row, endPosition.column ); const nodes = unmarshalNodes(returnedNodes, rootNode.tree); const results = []; diff --git a/package.json b/package.json index 3dc27ef2..8aa8dcc8 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "prebuild-install": "^5.0.0" }, "devDependencies": { + "@types/node": "^14.14.31", "chai": "3.5.x", "mocha": "^5.2.0", "prebuild": "^7.6.0", diff --git a/tree-sitter.d.ts b/tree-sitter.d.ts index 063f24d4..3d8bc73c 100644 --- a/tree-sitter.d.ts +++ b/tree-sitter.d.ts @@ -1,10 +1,13 @@ declare module "tree-sitter" { class Parser { - parse(input: string | Parser.Input, previousTree?: Parser.Tree, options?: {bufferSize?: number, includedRanges?: Parser.Range[]}): Parser.Tree; + parse(input: string | Parser.Input | Parser.InputReader, oldTree?: Parser.Tree, options?: { bufferSize?: number, includedRanges?: Parser.Range[] }): Parser.Tree; + parseTextBuffer(buffer: Parser.TextBuffer, oldTree?: Parser.Tree, options?: { syncTimeoutMicros?: number, includedRanges?: Parser.Range[] }): Parser.Tree | Promise; + parseTextBufferSync(buffer: Parser.TextBuffer, oldTree?: Parser.Tree, options?: { includedRanges?: Parser.Range[] }): Parser.Tree; getLanguage(): any; setLanguage(language: any): void; getLogger(): Parser.Logger; setLogger(logFunc: Parser.Logger): void; + printDotGraphs(enabled: boolean): void; } namespace Parser { @@ -35,6 +38,12 @@ declare module "tree-sitter" { type: "parse" | "lex" ) => void; + export type TextBuffer = Buffer; + + export interface InputReader { + (index: any, position: Point): string; + } + export interface Input { seek(index: number): void; read(): any; @@ -43,6 +52,7 @@ declare module "tree-sitter" { export interface SyntaxNode { tree: Tree; type: string; + typeId: string; isNamed: boolean; text: string; startPosition: Point; @@ -94,7 +104,8 @@ declare module "tree-sitter" { endPosition: Point; startIndex: number; endIndex: number; - readonly currentNode: SyntaxNode + readonly currentNode: SyntaxNode; + readonly currentFieldName: string; reset(node: SyntaxNode): void gotoParent(): boolean; @@ -110,6 +121,33 @@ declare module "tree-sitter" { walk(): TreeCursor; getChangedRanges(other: Tree): Range[]; getEditedRange(other: Tree): Range; + printDotGraph(): void; + } + + export interface QueryMatch { + pattern: number, + captures: QueryCapture[], + } + + export interface QueryCapture { + name: string, + text?: string, + node: SyntaxNode, + setProperties?: {[prop: string]: string | null}, + assertedProperties?: {[prop: string]: string | null}, + refutedProperties?: {[prop: string]: string | null}, + } + + export class Query { + readonly predicates: { [name: string]: Function }[]; + readonly setProperties: any[]; + readonly assertedProperties: any[]; + readonly refutedProperties: any[]; + + constructor(language: any, source: string | Buffer); + + matches(rootNode: SyntaxNode, startPosition?: Point, endPosition?: Point): QueryMatch[]; + captures(rootNode: SyntaxNode, startPosition?: Point, endPosition?: Point): QueryCapture[]; } } From c5f3dff5cd44d7049e630ead16608349abd018ad Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 8 Mar 2021 10:46:38 -0800 Subject: [PATCH 25/42] :arrow_up: tree-sitter lib to 0.19 --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index 2f28a35e..841e1608 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit 2f28a35e1b118e17ab2fb6236a24c7b557e3c8a9 +Subproject commit 841e16089820bcf24df2c585340c966e5b6c7377 From 7785d1cd12205a6ac62b80de900676e76b822fc5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 8 Mar 2021 11:43:21 -0800 Subject: [PATCH 26/42] 0.19.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8aa8dcc8..ebd4eb92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.18.1", + "version": "0.19.0", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From 5054899c346853b899516f8b6a12a70afc75ddf3 Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Sun, 7 Mar 2021 19:42:52 +0200 Subject: [PATCH 27/42] fix: prevent Nan::Utf8String destructor call due to out of scope, fixes #72 --- src/query.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/query.cc b/src/query.cc index 1f0c230e..2c722b11 100644 --- a/src/query.cc +++ b/src/query.cc @@ -95,6 +95,7 @@ void Query::New(const Nan::FunctionCallbackInfo &info) { uint32_t source_len; uint32_t error_offset = 0; TSQueryError error_type = TSQueryErrorNone; + TSQuery *query; if (language == nullptr) { Nan::ThrowError("Missing language argument"); @@ -103,21 +104,21 @@ void Query::New(const Nan::FunctionCallbackInfo &info) { if (info[1]->IsString()) { auto string = Nan::To (info[1]).ToLocalChecked(); - source = *Nan::Utf8String(string); - source_len = string->Length(); + Nan::Utf8String utf8_string(string); + source = *utf8_string; + source_len = utf8_string.length(); + query = ts_query_new(language, source, source_len, &error_offset, &error_type); } else if (node::Buffer::HasInstance(info[1])) { source = node::Buffer::Data(info[1]); source_len = node::Buffer::Length(info[1]); + query = ts_query_new(language, source, source_len, &error_offset, &error_type); } else { Nan::ThrowError("Missing source argument"); return; } - TSQuery *query = ts_query_new( - language, source, source_len, &error_offset, &error_type); - if (error_offset > 0) { const char *error_name = query_error_names[error_type]; std::string message = "Query error of type "; From 6b876b7594b3d702d91c128b94105ff85ba812af Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Mon, 8 Mar 2021 02:19:38 +0200 Subject: [PATCH 28/42] Update prebuild, prebuild-install to use newer node-gyp that uses python3 by default --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ebd4eb92..7ab318de 100644 --- a/package.json +++ b/package.json @@ -16,13 +16,13 @@ "types": "tree-sitter.d.ts", "dependencies": { "nan": "^2.14.0", - "prebuild-install": "^5.0.0" + "prebuild-install": "^6.0.1" }, "devDependencies": { "@types/node": "^14.14.31", "chai": "3.5.x", "mocha": "^5.2.0", - "prebuild": "^7.6.0", + "prebuild": "^10.0.1", "superstring": "^2.4.2", "tree-sitter-javascript": "git://github.com/tree-sitter/tree-sitter-javascript.git#master" }, From 596b945bd823240eafc2c0e0dce249a1cc068140 Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Mon, 8 Mar 2021 02:22:00 +0200 Subject: [PATCH 29/42] Update chai and mocha to latest version to resolve npm complains --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7ab318de..3f14b600 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ }, "devDependencies": { "@types/node": "^14.14.31", - "chai": "3.5.x", - "mocha": "^5.2.0", + "chai": "^4.3.3", + "mocha": "^8.3.1", "prebuild": "^10.0.1", "superstring": "^2.4.2", "tree-sitter-javascript": "git://github.com/tree-sitter/tree-sitter-javascript.git#master" From ccd2c9565a737eb8860c1dbc53de3576118e1652 Mon Sep 17 00:00:00 2001 From: anqurvanillapy Date: Wed, 1 Sep 2021 18:23:10 +0800 Subject: [PATCH 30/42] fix #82: Update compiler flag '-std=c++17' Signed-off-by: anqurvanillapy --- binding.gyp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/binding.gyp b/binding.gyp index 130ee595..d0e9b81b 100644 --- a/binding.gyp +++ b/binding.gyp @@ -28,10 +28,10 @@ }] ], "cflags": [ - "-std=c++0x", + "-std=c++17", ], 'xcode_settings': { - 'CLANG_CXX_LANGUAGE_STANDARD': 'c++11', + 'CLANG_CXX_LANGUAGE_STANDARD': 'c++17', }, }, { From e197a7337e9ec7ca1c897a0feca946ed68556b52 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 11 Oct 2021 10:24:20 -0700 Subject: [PATCH 31/42] Upgrade tree-sitter library --- vendor/tree-sitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/tree-sitter b/vendor/tree-sitter index 841e1608..3d554ecf 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit 841e16089820bcf24df2c585340c966e5b6c7377 +Subproject commit 3d554ecf6b68ad2c267c1e90b6ef9aa68ae88bcd From fc999544aa291640081e7e34390a686a8b86fd0c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 11 Oct 2021 10:26:24 -0700 Subject: [PATCH 32/42] 0.20.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f14b600..015a1ead 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter", - "version": "0.19.0", + "version": "0.20.0", "description": "Incremental parsers for node", "author": "Max Brunsfeld", "license": "MIT", From a4c6fc830516c5265c972018c31229a4b04dbb1c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 4 Nov 2019 12:38:13 -0800 Subject: [PATCH 33/42] Convert module to use NAPI instead of Nan --- binding.gyp | 9 +- index.js | 1 + package.json | 1 + src/binding.cc | 20 +- src/conversions.cc | 165 +++++----- src/conversions.h | 23 +- src/language.cc | 101 +++--- src/language.h | 10 +- src/logger.cc | 57 ++-- src/logger.h | 7 +- src/node.cc | 712 +++++++++++++++++++++--------------------- src/node.h | 28 +- src/optional.h | 29 ++ src/parser.cc | 739 +++++++++++++++++++++++--------------------- src/parser.h | 27 +- src/tree.cc | 199 +++++------- src/tree.h | 38 +-- src/tree_cursor.cc | 265 ++++++++-------- src/tree_cursor.h | 35 +-- src/util.h | 56 +++- test/parser_test.js | 4 +- test/tree_test.js | 2 +- 22 files changed, 1251 insertions(+), 1277 deletions(-) create mode 100644 src/optional.h diff --git a/binding.gyp b/binding.gyp index d0e9b81b..f8644a63 100644 --- a/binding.gyp +++ b/binding.gyp @@ -2,7 +2,13 @@ "targets": [ { "target_name": "tree_sitter_runtime_binding", - "dependencies": ["tree_sitter"], + "dependencies": [ + "tree_sitter", + " -#include +#include #include "./language.h" #include "./node.h" #include "./parser.h" @@ -10,18 +9,19 @@ namespace node_tree_sitter { -using namespace v8; +using namespace Napi; -void InitAll(Local exports) { +Object Init(Env env, Object exports) { InitConversions(exports); - node_methods::Init(exports); - language_methods::Init(exports); - Parser::Init(exports); - Query::Init(exports); + InitNode(exports); + InitLanguage(exports); + InitParser(exports); + InitTreeCursor(exports); + InitQuery(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 diff --git a/src/conversions.cc b/src/conversions.cc index 92c92c67..4b0e1c85 100644 --- a/src/conversions.cc +++ b/src/conversions.cc @@ -1,35 +1,30 @@ -#include "./node.h" -#include +#include #include -#include +#include "./node.h" #include "./conversions.h" #include namespace node_tree_sitter { -using namespace v8; - -Nan::Persistent row_key; -Nan::Persistent column_key; -Nan::Persistent start_index_key; -Nan::Persistent start_position_key; -Nan::Persistent end_index_key; -Nan::Persistent end_position_key; +using namespace Napi; static unsigned BYTES_PER_CHARACTER = 2; static uint32_t *point_transfer_buffer; -void InitConversions(Local exports) { - row_key.Reset(Nan::Persistent(Nan::New("row").ToLocalChecked())); - column_key.Reset(Nan::Persistent(Nan::New("column").ToLocalChecked())); - start_index_key.Reset(Nan::Persistent(Nan::New("startIndex").ToLocalChecked())); - start_position_key.Reset(Nan::Persistent(Nan::New("startPosition").ToLocalChecked())); - end_index_key.Reset(Nan::Persistent(Nan::New("endIndex").ToLocalChecked())); - end_position_key.Reset(Nan::Persistent(Nan::New("endPosition").ToLocalChecked())); - +void InitConversions(Object &exports) { + auto env = exports.Env(); point_transfer_buffer = static_cast(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(point_transfer_buffer), + 2 * sizeof(uint32_t) + ); + exports.Set("pointTransferArray", Uint32Array::New( + env, + 2, + js_point_transfer_buffer, + 0 + )); } void TransferPoint(const TSPoint &point) { @@ -37,110 +32,116 @@ void TransferPoint(const TSPoint &point) { point_transfer_buffer[1] = point.column / 2; } -Local RangeToJS(const TSRange &range) { - Local result = Nan::New(); - 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 RangeFromJS(const Local &arg) { - if (!arg->IsObject()) { - Nan::ThrowTypeError("Range must be a {startPosition, endPosition, startIndex, endIndex} object"); - return Nan::Nothing(); +optional 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 result; - - Local js_range = Local::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(); \ + TypeError::New(env, "Range must be a {startPosition, endPosition, startIndex, endIndex} object").ThrowAsJavaScriptException(); \ + return optional(); \ } \ - 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(); \ + return optional(); \ } \ } - 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 PointToJS(const TSPoint &point) { - Local result = Nan::New(); - Nan::Set(result, Nan::New(row_key), Nan::New(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 PointFromJS(const Local &arg) { - Local js_point; - if (!arg->IsObject() || !Nan::To(arg).ToLocal(&js_point)) { - Nan::ThrowTypeError("Point must be a {row, column} object"); - return Nan::Nothing(); +Number ByteCountToJS(Env env, uint32_t byte_count) { + return Number::New(env, byte_count / BYTES_PER_CHARACTER); +} + +optional PointFromJS(const Value &arg) { + Env env = arg.Env(); + + if (!arg.IsObject()) { + TypeError::New(env, "Point must be a {row, column} object").ThrowAsJavaScriptException(); + return optional(); } - Local 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(); + Object js_point = arg.ToObject(); + + Number js_row = js_point.Get("row").As(); + if (!js_row.IsNumber()) { + TypeError::New(env, "Point must be a {row, column} object").ThrowAsJavaScriptException(); + return optional(); } - Local 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(); + Number js_column = js_point.Get("column").As(); + if (!js_column.IsNumber()) { + TypeError::New(env, "Point must be a {row, column} object").ThrowAsJavaScriptException(); + return optional(); } uint32_t row; - if (!std::isfinite(Nan::To(js_row).FromMaybe(0))) { + if (!std::isfinite(js_row.DoubleValue())) { row = UINT32_MAX; - } else if (js_row->IsNumber()) { - row = Nan::To(js_row).FromJust(); } else { - Nan::ThrowTypeError("Point.row must be a number"); - return Nan::Nothing(); + row = js_row.Uint32Value(); } uint32_t column; - if (!std::isfinite(Nan::To(js_column).FromMaybe(0))) { + if (!std::isfinite(js_column.DoubleValue())) { column = UINT32_MAX; - } else if (js_column->IsNumber()) { - column = Nan::To(js_column).FromMaybe(0) * BYTES_PER_CHARACTER; } else { - Nan::ThrowTypeError("Point.column must be a number"); - return Nan::Nothing(); + column = js_column.Uint32Value() * BYTES_PER_CHARACTER; } - return Nan::Just({row, column}); + return TSPoint{row, column}; } -Local ByteCountToJS(uint32_t byte_count) { - return Nan::New(byte_count / BYTES_PER_CHARACTER); -} +optional ByteCountFromJS(const Value &arg) { + Env env = arg.Env(); -Nan::Maybe ByteCountFromJS(const v8::Local &arg) { - auto result = Nan::To(arg); - if (!arg->IsNumber()) { - Nan::ThrowTypeError("Character index must be a number"); - return Nan::Nothing(); + if (!arg.IsNumber()) { + if (!env.IsExceptionPending()) { + TypeError::New(env, "Character index must be a number").ThrowAsJavaScriptException(); + } + return optional(); } - return Nan::Just(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 diff --git a/src/conversions.h b/src/conversions.h index 69bcfb51..649561b5 100644 --- a/src/conversions.h +++ b/src/conversions.h @@ -1,25 +1,22 @@ #ifndef NODE_TREE_SITTER_CONVERSIONS_H_ #define NODE_TREE_SITTER_CONVERSIONS_H_ -#include -#include +#include #include +#include "./optional.h" namespace node_tree_sitter { -void InitConversions(v8::Local exports); -v8::Local RangeToJS(const TSRange &); -v8::Local PointToJS(const TSPoint &); +void InitConversions(Napi::Object &); void TransferPoint(const TSPoint &); -v8::Local ByteCountToJS(uint32_t); -Nan::Maybe PointFromJS(const v8::Local &); -Nan::Maybe ByteCountFromJS(const v8::Local &); -Nan::Maybe RangeFromJS(const v8::Local &); -extern Nan::Persistent row_key; -extern Nan::Persistent column_key; -extern Nan::Persistent start_key; -extern Nan::Persistent end_key; +Napi::Object RangeToJS(Napi::Env, const TSRange &); +Napi::Object PointToJS(Napi::Env, const TSPoint &); +Napi::Number ByteCountToJS(Napi::Env, uint32_t); + +optional PointFromJS(const Napi::Value &); +optional ByteCountFromJS(const Napi::Value &); +optional RangeFromJS(const Napi::Value &); } // namespace node_tree_sitter diff --git a/src/language.cc b/src/language.cc index bdfb90f1..208bd8d6 100644 --- a/src/language.cc +++ b/src/language.cc @@ -1,92 +1,89 @@ #include "./language.h" -#include +#include #include #include #include -#include +#include "./util.h" namespace node_tree_sitter { -namespace language_methods { using std::vector; -using namespace v8; - -const TSLanguage *UnwrapLanguage(const v8::Local &value) { - if (value->IsObject()) { - Local arg = Local::Cast(value); - if (arg->InternalFieldCount() == 1) { - const TSLanguage *language = (const TSLanguage *)Nan::GetInternalFieldPointer(arg, 0); - if (language) { - uint16_t version = ts_language_version(language); - if ( - version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION || - version > TREE_SITTER_LANGUAGE_VERSION - ) { - std::string message = - "Incompatible language version. Compatible range: " + - std::to_string(TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION) + " - " + - std::to_string(TREE_SITTER_LANGUAGE_VERSION) + ". Got: " + - std::to_string(ts_language_version(language)); - Nan::ThrowError(Nan::RangeError(message.c_str())); - return nullptr; - } - return language; - } +using namespace Napi; + + +const TSLanguage *UnwrapLanguage(const Napi::Value &value) { + Env env = value.Env(); + + const TSLanguage *language = static_cast( + GetInternalFieldPointer(value) + ); + + if (language) { + uint16_t version = ts_language_version(language); + if ( + version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION || + version > TREE_SITTER_LANGUAGE_VERSION + ) { + std::string message = + "Incompatible language version. Compatible range: " + + std::to_string(TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION) + " - " + + std::to_string(TREE_SITTER_LANGUAGE_VERSION) + ". Got: " + + std::to_string(ts_language_version(language)); + RangeError::New(env, message.c_str()).ThrowAsJavaScriptException(); + return nullptr; } + return language; } - Nan::ThrowTypeError("Invalid language object"); + + TypeError::New(env, "Invalid language object").ThrowAsJavaScriptException(); return nullptr; } -static void GetNodeTypeNamesById(const Nan::FunctionCallbackInfo &info) { +static Value GetNodeTypeNamesById(const CallbackInfo &info) { + Env env = info.Env(); + const TSLanguage *language = UnwrapLanguage(info[0]); - if (!language) return; + if (!language) return env.Null(); - auto result = Nan::New(); + Array result = Array::New(env); uint32_t length = ts_language_symbol_count(language); for (uint32_t i = 0; i < length; i++) { const char *name = ts_language_symbol_name(language, i); TSSymbolType type = ts_language_symbol_type(language, i); if (type == TSSymbolTypeRegular) { - Nan::Set(result, i, Nan::New(name).ToLocalChecked()); + result[i] = String::New(env, name); } else { - Nan::Set(result, i, Nan::Null()); + result[i] = env.Null(); } } - info.GetReturnValue().Set(result); + return result; } -static void GetNodeFieldNamesById(const Nan::FunctionCallbackInfo &info) { +static Value GetNodeFieldNamesById(const CallbackInfo &info) { + Env env = info.Env(); + const TSLanguage *language = UnwrapLanguage(info[0]); - if (!language) return; + if (!language) return env.Null(); - auto result = Nan::New(); + Array result = Array::New(env); uint32_t length = ts_language_field_count(language); for (uint32_t i = 0; i < length + 1; i++) { const char *name = ts_language_field_name_for_id(language, i); if (name) { - Nan::Set(result, i, Nan::New(name).ToLocalChecked()); + result[i] = String::New(env, name); } else { - Nan::Set(result, i, Nan::Null()); + result[i] = env.Null(); } } - info.GetReturnValue().Set(result); -} -void Init(Local exports) { - Nan::Set( - exports, - Nan::New("getNodeTypeNamesById").ToLocalChecked(), - Nan::GetFunction(Nan::New(GetNodeTypeNamesById)).ToLocalChecked() - ); + return result; +} - Nan::Set( - exports, - Nan::New("getNodeFieldNamesById").ToLocalChecked(), - Nan::GetFunction(Nan::New(GetNodeFieldNamesById)).ToLocalChecked() - ); +void InitLanguage(Object &exports) { + Env env = exports.Env(); + exports["getNodeTypeNamesById"] = Function::New(env, GetNodeTypeNamesById); + exports["getNodeFieldNamesById"] = Function::New(env, GetNodeFieldNamesById); } -} // namespace language_methods } // namespace node_tree_sitter diff --git a/src/language.h b/src/language.h index 76f7ac28..cde411fd 100644 --- a/src/language.h +++ b/src/language.h @@ -1,20 +1,16 @@ #ifndef NODE_TREE_SITTER_LANGUAGE_H_ #define NODE_TREE_SITTER_LANGUAGE_H_ -#include -#include -#include +#include #include #include "./tree.h" namespace node_tree_sitter { -namespace language_methods { -void Init(v8::Local); +void InitLanguage(Napi::Object &); -const TSLanguage *UnwrapLanguage(const v8::Local &); +const TSLanguage *UnwrapLanguage(const Napi::Value &); -} // namespace language_methods } // namespace node_tree_sitter #endif // NODE_TREE_SITTER_LANGUAGE_H_ diff --git a/src/logger.cc b/src/logger.cc index 5f0cacd1..2d1148b1 100644 --- a/src/logger.cc +++ b/src/logger.cc @@ -1,34 +1,34 @@ #include "./logger.h" #include -#include -#include +#include #include namespace node_tree_sitter { -using namespace v8; +using namespace Napi; using std::string; void Logger::Log(void *payload, TSLogType type, const char *message_str) { Logger *debugger = (Logger *)payload; - Local fn = Nan::New(debugger->func); - if (!fn->IsFunction()) - return; + Function fn = debugger->func.Value(); + if (!fn.IsFunction()) return; + Env env = fn.Env(); string message(message_str); string param_sep = " "; size_t param_sep_pos = message.find(param_sep, 0); - Local type_name = Nan::New((type == TSLogTypeParse) ? "parse" : "lex").ToLocalChecked(); - Local name = Nan::New(message.substr(0, param_sep_pos)).ToLocalChecked(); - Local params = Nan::New(); + String type_name = String::New( + env, + type == TSLogTypeParse ? "parse" : "lex" + ); + String name = String::New(env, message.substr(0, param_sep_pos)); + Object params = Object::New(env); while (param_sep_pos != string::npos) { size_t key_pos = param_sep_pos + param_sep.size(); size_t value_sep_pos = message.find(":", key_pos); - - if (value_sep_pos == string::npos) - break; + if (value_sep_pos == string::npos) break; size_t val_pos = value_sep_pos + 1; param_sep = ", "; @@ -36,29 +36,30 @@ 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)); - Nan::Set(params, Nan::New(key).ToLocalChecked(), Nan::New(value).ToLocalChecked()); + params[key] = String::New(env, value); } - Local argv[3] = { name, params, type_name }; - TryCatch try_catch(Isolate::GetCurrent()); - Nan::Call(fn, fn->CreationContext()->Global(), 3, argv); - if (try_catch.HasCaught()) { - Local log_argv[2] = { - Nan::New("Error in debug callback:").ToLocalChecked(), - try_catch.Exception() - }; - - Local console = Local::Cast(Nan::Get(fn->CreationContext()->Global(), Nan::New("console").ToLocalChecked()).ToLocalChecked()); - Local error_fn = Local::Cast(Nan::Get(console, Nan::New("error").ToLocalChecked()).ToLocalChecked()); - Nan::Call(error_fn, console, 2, log_argv); + fn({ name, params, type_name }); + if (env.IsExceptionPending()) { + Error error = env.GetAndClearPendingException(); + Value console = env.Global()["console"]; + if (console.IsObject()) { + Value console_error_fn = console.ToObject()["error"]; + if (console_error_fn.IsFunction()) { + console_error_fn.As()({ + String::New(env, "Error in debug callback:"), + error.Value() + }); + } + } } } -TSLogger Logger::Make(Local func) { +TSLogger Logger::Make(Function func) { TSLogger result; Logger *logger = new Logger(); - logger->func.Reset(Nan::Persistent(func)); - result.payload = (void *)logger; + logger->func.Reset(func, 1); + result.payload = static_cast(logger); result.log = Log; return result; } diff --git a/src/logger.h b/src/logger.h index af68413f..105b4659 100644 --- a/src/logger.h +++ b/src/logger.h @@ -1,16 +1,15 @@ #ifndef NODE_TREE_SITTER_LOGGER_H_ #define NODE_TREE_SITTER_LOGGER_H_ -#include -#include +#include #include namespace node_tree_sitter { class Logger { public: - static TSLogger Make(v8::Local); - Nan::Persistent func; + static TSLogger Make(Napi::Function); + Napi::FunctionReference func; static void Log(void *, TSLogType, const char *); }; diff --git a/src/node.cc b/src/node.cc index 711781e2..d5205ce4 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1,27 +1,25 @@ #include "./node.h" -#include #include +#include #include -#include #include "./util.h" #include "./conversions.h" #include "./tree.h" #include "./tree_cursor.h" namespace node_tree_sitter { -namespace node_methods { using std::vector; -using namespace v8; +using namespace Napi; static const uint32_t FIELD_COUNT_PER_NODE = 6; static uint32_t *transfer_buffer = nullptr; static uint32_t transfer_buffer_length = 0; -static Nan::Persistent module_exports; +static ObjectReference module_exports; static TSTreeCursor scratch_cursor = {nullptr, nullptr, {0, 0}}; -static inline void setup_transfer_buffer(uint32_t node_count) { +static inline void setup_transfer_buffer(Env env, uint32_t node_count) { uint32_t new_length = node_count * FIELD_COUNT_PER_NODE; if (new_length > transfer_buffer_length) { if (transfer_buffer) { @@ -29,11 +27,16 @@ static inline void setup_transfer_buffer(uint32_t node_count) { } transfer_buffer_length = new_length; transfer_buffer = static_cast(malloc(transfer_buffer_length * sizeof(uint32_t))); - auto js_transfer_buffer = ArrayBuffer::New(Isolate::GetCurrent(), transfer_buffer, transfer_buffer_length * sizeof(uint32_t)); - Nan::Set( - Nan::New(module_exports), - Nan::New("nodeTransferArray").ToLocalChecked(), - Uint32Array::New(js_transfer_buffer, 0, transfer_buffer_length) + auto js_transfer_buffer = ArrayBuffer::New( + env, + transfer_buffer, + transfer_buffer_length * sizeof(uint32_t) + ); + module_exports.Value()["nodeTransferArray"] = Uint32Array::New( + env, + transfer_buffer_length, + js_transfer_buffer, + 0 ); } } @@ -44,75 +47,78 @@ static inline bool operator<=(const TSPoint &left, const TSPoint &right) { return left.column <= right.column; } -static void MarshalNodes(const Nan::FunctionCallbackInfo &info, - const Tree *tree, const TSNode *nodes, uint32_t node_count) { - info.GetReturnValue().Set(GetMarshalNodes(info, tree, nodes, node_count)); -} -void MarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *tree, TSNode node) { - info.GetReturnValue().Set(GetMarshalNode(info, tree, node)); -} - -Local GetMarshalNodes(const Nan::FunctionCallbackInfo &info, - const Tree *tree, const TSNode *nodes, uint32_t node_count) { - auto result = Nan::New(); - setup_transfer_buffer(node_count); +static Value MarshalNodes( + Env env, + const Tree *tree, + const TSNode *nodes, + uint32_t node_count +) { + Array result = Array::New(env); + setup_transfer_buffer(env, node_count); uint32_t *p = transfer_buffer; for (unsigned i = 0; i < node_count; i++) { TSNode node = nodes[i]; const auto &cache_entry = tree->cached_nodes_.find(node.id); if (cache_entry == tree->cached_nodes_.end()) { - MarshalNodeId(node.id, p); + MarshalPointer(node.id, p); p += 2; *(p++) = node.context[0]; *(p++) = node.context[1]; *(p++) = node.context[2]; *(p++) = node.context[3]; if (node.id) { - Nan::Set(result, i, Nan::New(ts_node_symbol(node))); + result[i] = Number::New(env, ts_node_symbol(node)); } else { - Nan::Set(result, i, Nan::Null()); + result[i] = env.Null(); } } else { - Nan::Set(result, i, Nan::New(cache_entry->second->node)); + result[i] = cache_entry->second->node.Value(); } } return result; } -Local GetMarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *tree, TSNode node) { +Value MarshalNode( + Env env, + const Tree *tree, + TSNode node +) { const auto &cache_entry = tree->cached_nodes_.find(node.id); if (cache_entry == tree->cached_nodes_.end()) { - setup_transfer_buffer(1); + setup_transfer_buffer(env, 1); uint32_t *p = transfer_buffer; - MarshalNodeId(node.id, p); + MarshalPointer(node.id, p); p += 2; *(p++) = node.context[0]; *(p++) = node.context[1]; *(p++) = node.context[2]; *(p++) = node.context[3]; if (node.id) { - return Nan::New(ts_node_symbol(node)); + return Number::New(env, ts_node_symbol(node)); + } else { + return env.Null(); } } else { - return Nan::New(cache_entry->second->node); + return cache_entry->second->node.Value(); } - return Nan::Null(); + return env.Null(); } -void MarshalNullNode() { +Value MarshalNullNode(Env env) { memset(transfer_buffer, 0, FIELD_COUNT_PER_NODE * sizeof(transfer_buffer[0])); + return env.Null(); } -TSNode UnmarshalNode(const Tree *tree) { +TSNode UnmarshalNode(Env env, const Tree *tree) { TSNode result = {{0, 0, 0, 0}, nullptr, nullptr}; - result.tree = tree->tree_; - if (!result.tree) { - Nan::ThrowTypeError("Argument must be a tree"); + if (!tree) { + TypeError::New(env, "Argument must be a tree").ThrowAsJavaScriptException(); return result; } - result.id = UnmarshalNodeId(&transfer_buffer[0]); + result.tree = tree->tree_; + result.id = UnmarshalPointer(&transfer_buffer[0]); result.context[0] = transfer_buffer[2]; result.context[1] = transfer_buffer[3]; result.context[2] = transfer_buffer[4]; @@ -120,350 +126,375 @@ TSNode UnmarshalNode(const Tree *tree) { return result; } -static void ToString(const Nan::FunctionCallbackInfo &info) { +static Value ToString(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - const char *string = ts_node_string(node); - info.GetReturnValue().Set(Nan::New(string).ToLocalChecked()); - free((char *)string); + char *string = ts_node_string(node); + String result = String::New(env, string); + free(string); + return result; } + return env.Undefined(); } -static void IsMissing(const Nan::FunctionCallbackInfo &info) { +static Value IsMissing(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { bool result = ts_node_is_missing(node); - info.GetReturnValue().Set(Nan::New(result)); + return Boolean::New(env, result); } + return env.Undefined(); } -static void HasChanges(const Nan::FunctionCallbackInfo &info) { +static Value HasChanges(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { bool result = ts_node_has_changes(node); - info.GetReturnValue().Set(Nan::New(result)); + return Boolean::New(env, result); } + return env.Undefined(); } -static void HasError(const Nan::FunctionCallbackInfo &info) { +static Value HasError(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { bool result = ts_node_has_error(node); - info.GetReturnValue().Set(Nan::New(result)); + return Boolean::New(env, result); } + return env.Undefined(); } -static void FirstNamedChildForIndex(const Nan::FunctionCallbackInfo &info) { +static Value FirstNamedChildForIndex(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - Nan::Maybe byte = ByteCountFromJS(info[1]); - if (byte.IsJust()) { - MarshalNode(info, tree, ts_node_first_named_child_for_byte(node, byte.FromJust())); - return; + auto byte = ByteCountFromJS(info[1]); + if (byte) { + return MarshalNode(env, tree, ts_node_first_named_child_for_byte(node, *byte)); } } - MarshalNullNode(); + return MarshalNullNode(env); } -static void FirstChildForIndex(const Nan::FunctionCallbackInfo &info) { +static Value FirstChildForIndex(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id && info.Length() > 1) { - Nan::Maybe byte = ByteCountFromJS(info[1]); - if (byte.IsJust()) { - MarshalNode(info, tree, ts_node_first_child_for_byte(node, byte.FromJust())); - return; + optional byte = ByteCountFromJS(info[1]); + if (byte) { + return MarshalNode(env, tree, ts_node_first_child_for_byte(node, *byte)); } } - MarshalNullNode(); + return MarshalNullNode(env); } -static void NamedDescendantForIndex(const Nan::FunctionCallbackInfo &info) { +static Value NamedDescendantForIndex(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - Nan::Maybe maybe_min = ByteCountFromJS(info[1]); - Nan::Maybe maybe_max = ByteCountFromJS(info[2]); - if (maybe_min.IsJust() && maybe_max.IsJust()) { - uint32_t min = maybe_min.FromJust(); - uint32_t max = maybe_max.FromJust(); - MarshalNode(info, tree, ts_node_named_descendant_for_byte_range(node, min, max)); - return; + optional maybe_min = ByteCountFromJS(info[1]); + if (maybe_min) { + optional maybe_max = ByteCountFromJS(info[2]); + if (maybe_max) { + uint32_t min = *maybe_min; + uint32_t max = *maybe_max; + return MarshalNode(env, tree, ts_node_named_descendant_for_byte_range(node, min, max)); + } } } - MarshalNullNode(); + return MarshalNullNode(env); } -static void DescendantForIndex(const Nan::FunctionCallbackInfo &info) { +static Value DescendantForIndex(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - Nan::Maybe maybe_min = ByteCountFromJS(info[1]); - Nan::Maybe maybe_max = ByteCountFromJS(info[2]); - if (maybe_min.IsJust() && maybe_max.IsJust()) { - uint32_t min = maybe_min.FromJust(); - uint32_t max = maybe_max.FromJust(); - MarshalNode(info, tree, ts_node_descendant_for_byte_range(node, min, max)); - return; + optional maybe_min = ByteCountFromJS(info[1]); + if (maybe_min) { + optional maybe_max = ByteCountFromJS(info[2]); + if (maybe_max) { + uint32_t min = *maybe_min; + uint32_t max = *maybe_max; + return MarshalNode(env, tree, ts_node_descendant_for_byte_range(node, min, max)); + } } } - MarshalNullNode(); + return MarshalNullNode(env); } -static void NamedDescendantForPosition(const Nan::FunctionCallbackInfo &info) { +static Value NamedDescendantForPosition(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - Nan::Maybe maybe_min = PointFromJS(info[1]); - Nan::Maybe maybe_max = PointFromJS(info[2]); - if (maybe_min.IsJust() && maybe_max.IsJust()) { - TSPoint min = maybe_min.FromJust(); - TSPoint max = maybe_max.FromJust(); - MarshalNode(info, tree, ts_node_named_descendant_for_point_range(node, min, max)); - return; + optional maybe_min = PointFromJS(info[1]); + optional maybe_max = PointFromJS(info[2]); + if (maybe_min && maybe_max) { + TSPoint min = *maybe_min; + TSPoint max = *maybe_max; + return MarshalNode(env, tree, ts_node_named_descendant_for_point_range(node, min, max)); } } - MarshalNullNode(); + return MarshalNullNode(env); } -static void DescendantForPosition(const Nan::FunctionCallbackInfo &info) { +static Value DescendantForPosition(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - Nan::Maybe maybe_min = PointFromJS(info[1]); - Nan::Maybe maybe_max = PointFromJS(info[2]); - if (maybe_min.IsJust() && maybe_max.IsJust()) { - TSPoint min = maybe_min.FromJust(); - TSPoint max = maybe_max.FromJust(); - MarshalNode(info, tree, ts_node_descendant_for_point_range(node, min, max)); - return; + optional maybe_min = PointFromJS(info[1]); + if (maybe_min) { + optional maybe_max = PointFromJS(info[2]); + if (maybe_max) { + TSPoint min = *maybe_min; + TSPoint max = *maybe_max; + return MarshalNode(env, tree, ts_node_descendant_for_point_range(node, min, max)); + } } } - MarshalNullNode(); + return MarshalNullNode(env); } -static void Type(const Nan::FunctionCallbackInfo &info) { +static Value Type(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { const char *result = ts_node_type(node); - info.GetReturnValue().Set(Nan::New(result).ToLocalChecked()); + return String::New(env, result); } + return env.Undefined(); } -static void TypeId(const Nan::FunctionCallbackInfo &info) { +static Value TypeId(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { TSSymbol result = ts_node_symbol(node); - info.GetReturnValue().Set(Nan::New(result)); + return Number::New(env, result); } + return env.Undefined(); } -static void IsNamed(const Nan::FunctionCallbackInfo &info) { +static Value IsNamed(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { bool result = ts_node_is_named(node); - info.GetReturnValue().Set(Nan::New(result)); + return Boolean::New(env, result); } + return env.Undefined(); } -static void StartIndex(const Nan::FunctionCallbackInfo &info) { +static Value StartIndex(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - int32_t result = ts_node_start_byte(node) / 2; - info.GetReturnValue().Set(Nan::New(result)); + uint32_t result = ts_node_start_byte(node) / 2; + return Number::New(env, result); } + return env.Undefined(); } -static void EndIndex(const Nan::FunctionCallbackInfo &info) { +static Value EndIndex(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - int32_t result = ts_node_end_byte(node) / 2; - info.GetReturnValue().Set(Nan::New(result)); + uint32_t result = ts_node_end_byte(node) / 2; + return Number::New(env, result); } + return env.Undefined(); } -static void StartPosition(const Nan::FunctionCallbackInfo &info) { +static Value StartPosition(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - + TSNode node = UnmarshalNode(env, tree); if (node.id) { TransferPoint(ts_node_start_point(node)); } + return env.Undefined(); } -static void EndPosition(const Nan::FunctionCallbackInfo &info) { +static Value EndPosition(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - + TSNode node = UnmarshalNode(env, tree); if (node.id) { TransferPoint(ts_node_end_point(node)); } + return env.Undefined(); } -static void Child(const Nan::FunctionCallbackInfo &info) { +static Value Child(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - + TSNode node = UnmarshalNode(env, tree); if (node.id) { - if (!info[1]->IsUint32()) { - Nan::ThrowTypeError("Second argument must be an integer"); - return; + if (info[1].IsNumber()) { + uint32_t index = info[1].As().Uint32Value(); + return MarshalNode(env, tree, ts_node_child(node, index)); } - uint32_t index = Nan::To(info[1]).FromJust(); - MarshalNode(info, tree, ts_node_child(node, index)); - return; + TypeError::New(env, "Second argument must be an integer").ThrowAsJavaScriptException(); } - MarshalNullNode(); + return MarshalNullNode(env); } -static void NamedChild(const Nan::FunctionCallbackInfo &info) { +static Value NamedChild(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - + TSNode node = UnmarshalNode(env, tree); if (node.id) { - if (!info[1]->IsUint32()) { - Nan::ThrowTypeError("Second argument must be an integer"); - return; + if (info[1].IsNumber()) { + uint32_t index = info[1].As().Uint32Value(); + return MarshalNode(env, tree, ts_node_named_child(node, index)); } - uint32_t index = Nan::To(info[1]).FromJust(); - MarshalNode(info, tree, ts_node_named_child(node, index)); - return; + TypeError::New(env, "Second argument must be an integer").ThrowAsJavaScriptException(); } - MarshalNullNode(); + return MarshalNullNode(env); } -static void ChildCount(const Nan::FunctionCallbackInfo &info) { +static Value ChildCount(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - + TSNode node = UnmarshalNode(env, tree); if (node.id) { - info.GetReturnValue().Set(Nan::New(ts_node_child_count(node))); + return Number::New(env, ts_node_child_count(node)); } + return env.Undefined(); } -static void NamedChildCount(const Nan::FunctionCallbackInfo &info) { +static Value NamedChildCount(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - + TSNode node = UnmarshalNode(env, tree); if (node.id) { - info.GetReturnValue().Set(Nan::New(ts_node_named_child_count(node))); + return Number::New(env, ts_node_named_child_count(node)); } + return env.Undefined(); } -static void FirstChild(const Nan::FunctionCallbackInfo &info) { +static Value FirstChild(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - MarshalNode(info, tree, ts_node_child(node, 0)); - return; + return MarshalNode(env, tree, ts_node_child(node, 0)); } - MarshalNullNode(); + return MarshalNullNode(env); } -static void FirstNamedChild(const Nan::FunctionCallbackInfo &info) { +static Value FirstNamedChild(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - MarshalNode(info, tree, ts_node_named_child(node, 0)); - return; + return MarshalNode(env, tree, ts_node_named_child(node, 0)); } - MarshalNullNode(); + return MarshalNullNode(env); } -static void LastChild(const Nan::FunctionCallbackInfo &info) { +static Value LastChild(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { uint32_t child_count = ts_node_child_count(node); if (child_count > 0) { - MarshalNode(info, tree, ts_node_child(node, child_count - 1)); - return; + return MarshalNode(env, tree, ts_node_child(node, child_count - 1)); } } - MarshalNullNode(); + return MarshalNullNode(env); } -static void LastNamedChild(const Nan::FunctionCallbackInfo &info) { +static Value LastNamedChild(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { uint32_t child_count = ts_node_named_child_count(node); if (child_count > 0) { - MarshalNode(info, tree, ts_node_named_child(node, child_count - 1)); - return; + return MarshalNode(env, tree, ts_node_named_child(node, child_count - 1)); } } - MarshalNullNode(); + return MarshalNullNode(env); } -static void Parent(const Nan::FunctionCallbackInfo &info) { +static Value Parent(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - MarshalNode(info, tree, ts_node_parent(node)); - return; + return MarshalNode(env, tree, ts_node_parent(node)); } - MarshalNullNode(); + return MarshalNullNode(env); } -static void NextSibling(const Nan::FunctionCallbackInfo &info) { +static Value NextSibling(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - MarshalNode(info, tree, ts_node_next_sibling(node)); - return; + return MarshalNode(env, tree, ts_node_next_sibling(node)); } - MarshalNullNode(); + return MarshalNullNode(env); } -static void NextNamedSibling(const Nan::FunctionCallbackInfo &info) { +static Value NextNamedSibling(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - MarshalNode(info, tree, ts_node_next_named_sibling(node)); - return; + return MarshalNode(env, tree, ts_node_next_named_sibling(node)); } - MarshalNullNode(); + return MarshalNullNode(env); } -static void PreviousSibling(const Nan::FunctionCallbackInfo &info) { +static Value PreviousSibling(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - MarshalNode(info, tree, ts_node_prev_sibling(node)); - return; + return MarshalNode(env, tree, ts_node_prev_sibling(node)); } - MarshalNullNode(); + return MarshalNullNode(env); } -static void PreviousNamedSibling(const Nan::FunctionCallbackInfo &info) { +static Value PreviousNamedSibling(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - MarshalNode(info, tree, ts_node_prev_named_sibling(node)); - return; + return MarshalNode(env, tree, ts_node_prev_named_sibling(node)); } - MarshalNullNode(); + return MarshalNullNode(env); } struct SymbolSet { @@ -472,62 +503,41 @@ struct SymbolSet { bool contains(TSSymbol symbol) { return symbols.find(symbol) != symbols.npos; } }; -bool symbol_set_from_js(SymbolSet *symbols, const Local &value, const TSLanguage *language) { - if (!value->IsArray()) { - Nan::ThrowTypeError("Argument must be a string or array of strings"); +bool symbol_set_from_js(SymbolSet *symbols, const Value &value, const TSLanguage *language) { + Env env = value.Env(); + if (!value.IsArray()) { + TypeError::New(env, "Argument must be a string or array of strings").ThrowAsJavaScriptException(); return false; } - + Array js_types = value.As(); unsigned symbol_count = ts_language_symbol_count(language); - - Local js_types = Local::Cast(value); - for (unsigned i = 0, n = js_types->Length(); i < n; i++) { - Local js_node_type_value; - if (Nan::Get(js_types, i).ToLocal(&js_node_type_value)) { - Local js_node_type; - if (Nan::To(js_node_type_value).ToLocal(&js_node_type)) { - auto length = js_node_type->Utf8Length( - #if NODE_MAJOR_VERSION >= 12 - Isolate::GetCurrent() - #endif - ); - - std::string node_type(length, '\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(-1)); - } else { - for (TSSymbol j = 0; j < symbol_count; j++) { - if (node_type == ts_language_symbol_name(language, j)) { - symbols->add(j); - } + for (uint32_t i = 0, n = js_types.Length(); i < n; i++) { + Value js_node_type_value = js_types[i]; + if (js_node_type_value.IsString()) { + String js_node_type = js_node_type_value.As(); + std::string node_type = js_node_type.Utf8Value(); + if (node_type == "ERROR") { + symbols->add(static_cast(-1)); + } else { + for (TSSymbol j = 0; j < symbol_count; j++) { + if (node_type == ts_language_symbol_name(language, j)) { + symbols->add(j); } } - - continue; } + } else { + TypeError::New(env, "Argument must be a string or array of strings").ThrowAsJavaScriptException(); + return false; } - - Nan::ThrowTypeError("Argument must be a string or array of strings"); - return false; } - return true; } -static void Children(const Nan::FunctionCallbackInfo &info) { +static Value Children(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - if (!node.id) return; + TSNode node = UnmarshalNode(env, tree); + if (!node.id) return env.Undefined(); vector result; ts_tree_cursor_reset(&scratch_cursor, node); @@ -538,13 +548,14 @@ static void Children(const Nan::FunctionCallbackInfo &info) { } while (ts_tree_cursor_goto_next_sibling(&scratch_cursor)); } - MarshalNodes(info, tree, result.data(), result.size()); + return MarshalNodes(env, tree, result.data(), result.size()); } -static void NamedChildren(const Nan::FunctionCallbackInfo &info) { +static Value NamedChildren(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - if (!node.id) return; + TSNode node = UnmarshalNode(env, tree); + if (!node.id) return env.Undefined(); vector result; ts_tree_cursor_reset(&scratch_cursor, node); @@ -557,30 +568,32 @@ static void NamedChildren(const Nan::FunctionCallbackInfo &info) { } while (ts_tree_cursor_goto_next_sibling(&scratch_cursor)); } - MarshalNodes(info, tree, result.data(), result.size()); + return MarshalNodes(env, tree, result.data(), result.size()); } -static void DescendantsOfType(const Nan::FunctionCallbackInfo &info) { +static Value DescendantsOfType(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - if (!node.id) return; + TSNode node = UnmarshalNode(env, tree); + if (!node.id) return env.Undefined(); SymbolSet symbols; - if (!symbol_set_from_js(&symbols, info[1], ts_tree_language(node.tree))) return; + if (!symbol_set_from_js(&symbols, info[1], ts_tree_language(node.tree))) { + return env.Undefined(); + } TSPoint start_point = {0, 0}; TSPoint end_point = {UINT32_MAX, UINT32_MAX}; - - if (info.Length() > 2 && info[2]->IsObject()) { + 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 (!maybe_start_point) return env.Undefined(); + start_point = *maybe_start_point; } - if (info.Length() > 3 && info[3]->IsObject()) { + 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(); + if (!maybe_end_point) return env.Undefined(); + end_point = *maybe_end_point; } vector found; @@ -623,20 +636,20 @@ static void DescendantsOfType(const Nan::FunctionCallbackInfo &info) { } } - MarshalNodes(info, tree, found.data(), found.size()); + return MarshalNodes(env, tree, found.data(), found.size()); } -static void ChildNodesForFieldId(const Nan::FunctionCallbackInfo &info) { +static Value ChildNodesForFieldId(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - if (!node.id) return; + TSNode node = UnmarshalNode(env, tree); + if (!node.id) return env.Undefined(); - auto maybe_field_id = Nan::To(info[1]); - if (!maybe_field_id.IsJust()) { - Nan::ThrowTypeError("Second argument must be an integer"); - return; + if (!info[1].IsNumber()) { + TypeError::New(env, "Second argument must be an integer").ThrowAsJavaScriptException(); + return env.Undefined(); } - uint32_t field_id = maybe_field_id.FromJust(); + uint32_t field_id = info[1].As().Uint32Value(); vector result; ts_tree_cursor_reset(&scratch_cursor, node); @@ -649,110 +662,111 @@ static void ChildNodesForFieldId(const Nan::FunctionCallbackInfo &info) { } while (ts_tree_cursor_goto_next_sibling(&scratch_cursor)); } - MarshalNodes(info, tree, result.data(), result.size()); + return MarshalNodes(env, tree, result.data(), result.size()); } -static void ChildNodeForFieldId(const Nan::FunctionCallbackInfo &info) { +static Value ChildNodeForFieldId(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); if (node.id) { - auto maybe_field_id = Nan::To(info[1]); - if (!maybe_field_id.IsJust()) { - Nan::ThrowTypeError("Second argument must be an integer"); - return; + if (!info[1].IsNumber()) { + TypeError::New(env, "Second argument must be an integer").ThrowAsJavaScriptException(); + return env.Undefined(); } - uint32_t field_id = maybe_field_id.FromJust(); - MarshalNode(info, tree, ts_node_child_by_field_id(node, field_id)); - return; + uint32_t field_id = info[1].As().Uint32Value(); + return MarshalNode(env, tree, ts_node_child_by_field_id(node, field_id)); } - MarshalNullNode(); + return MarshalNullNode(env); } -static void Closest(const Nan::FunctionCallbackInfo &info) { +static Value Closest(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); - if (!node.id) return; + TSNode node = UnmarshalNode(env, tree); + if (!node.id) return env.Undefined(); SymbolSet symbols; - if (!symbol_set_from_js(&symbols, info[1], ts_tree_language(node.tree))) return; + if (!symbol_set_from_js(&symbols, info[1], ts_tree_language(node.tree))) { + return env.Undefined(); + } for (;;) { TSNode parent = ts_node_parent(node); if (!parent.id) break; if (symbols.contains(ts_node_symbol(parent))) { - MarshalNode(info, tree, parent); - return; + return MarshalNode(env, tree, parent); } node = parent; } - MarshalNullNode(); + return MarshalNullNode(env); } -static void Walk(const Nan::FunctionCallbackInfo &info) { +static Value Walk(const CallbackInfo &info) { + Env env = info.Env(); const Tree *tree = Tree::UnwrapTree(info[0]); - TSNode node = UnmarshalNode(tree); + TSNode node = UnmarshalNode(env, tree); TSTreeCursor cursor = ts_tree_cursor_new(node); - info.GetReturnValue().Set(TreeCursor::NewInstance(cursor)); -} - -void Init(Local exports) { - Local result = Nan::New(); - - FunctionPair methods[] = { - {"startIndex", StartIndex}, - {"endIndex", EndIndex}, - {"type", Type}, - {"typeId", TypeId}, - {"isNamed", IsNamed}, - {"parent", Parent}, - {"child", Child}, - {"namedChild", NamedChild}, - {"children", Children}, - {"namedChildren", NamedChildren}, - {"childCount", ChildCount}, - {"namedChildCount", NamedChildCount}, - {"firstChild", FirstChild}, - {"lastChild", LastChild}, - {"firstNamedChild", FirstNamedChild}, - {"lastNamedChild", LastNamedChild}, - {"nextSibling", NextSibling}, - {"nextNamedSibling", NextNamedSibling}, - {"previousSibling", PreviousSibling}, - {"previousNamedSibling", PreviousNamedSibling}, - {"startPosition", StartPosition}, - {"endPosition", EndPosition}, - {"isMissing", IsMissing}, - {"toString", ToString}, - {"firstChildForIndex", FirstChildForIndex}, - {"firstNamedChildForIndex", FirstNamedChildForIndex}, - {"descendantForIndex", DescendantForIndex}, - {"namedDescendantForIndex", NamedDescendantForIndex}, - {"descendantForPosition", DescendantForPosition}, - {"namedDescendantForPosition", NamedDescendantForPosition}, - {"hasChanges", HasChanges}, - {"hasError", HasError}, - {"descendantsOfType", DescendantsOfType}, - {"walk", Walk}, - {"closest", Closest}, - {"childNodeForFieldId", ChildNodeForFieldId}, - {"childNodesForFieldId", ChildNodesForFieldId}, - }; - - for (size_t i = 0; i < length_of_array(methods); i++) { - Nan::Set( - result, - Nan::New(methods[i].name).ToLocalChecked(), - Nan::GetFunction(Nan::New(methods[i].callback)).ToLocalChecked() - ); - } + return NewTreeCursor(cursor); +} + +class NodeMethods : public ObjectWrap { + public: + NodeMethods(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info) + {} + + static void Init(Napi::Env env, Object &exports) { + exports["NodeMethods"] = DefineClass(env, "NodeMethods", { + StaticMethod("startIndex", StartIndex, napi_writable), + StaticMethod("endIndex", EndIndex, napi_writable), + StaticMethod("type", Type, napi_writable), + StaticMethod("typeId", TypeId, napi_writable), + StaticMethod("isNamed", IsNamed, napi_writable), + StaticMethod("parent", Parent, napi_writable), + StaticMethod("child", Child, napi_writable), + StaticMethod("namedChild", NamedChild, napi_writable), + StaticMethod("children", Children, napi_writable), + StaticMethod("namedChildren", NamedChildren, napi_writable), + StaticMethod("childCount", ChildCount, napi_writable), + StaticMethod("namedChildCount", NamedChildCount, napi_writable), + StaticMethod("firstChild", FirstChild, napi_writable), + StaticMethod("lastChild", LastChild, napi_writable), + StaticMethod("firstNamedChild", FirstNamedChild, napi_writable), + StaticMethod("lastNamedChild", LastNamedChild, napi_writable), + StaticMethod("nextSibling", NextSibling, napi_writable), + StaticMethod("nextNamedSibling", NextNamedSibling, napi_writable), + StaticMethod("previousSibling", PreviousSibling, napi_writable), + StaticMethod("previousNamedSibling", PreviousNamedSibling, napi_writable), + StaticMethod("startPosition", StartPosition, napi_writable), + StaticMethod("endPosition", EndPosition, napi_writable), + StaticMethod("isMissing", IsMissing, napi_writable), + StaticMethod("toString", ToString, napi_writable), + StaticMethod("firstChildForIndex", FirstChildForIndex, napi_writable), + StaticMethod("firstNamedChildForIndex", FirstNamedChildForIndex, napi_writable), + StaticMethod("descendantForIndex", DescendantForIndex, napi_writable), + StaticMethod("namedDescendantForIndex", NamedDescendantForIndex, napi_writable), + StaticMethod("descendantForPosition", DescendantForPosition, napi_writable), + StaticMethod("namedDescendantForPosition", NamedDescendantForPosition, napi_writable), + StaticMethod("hasChanges", HasChanges, napi_writable), + StaticMethod("hasError", HasError, napi_writable), + StaticMethod("descendantsOfType", DescendantsOfType, napi_writable), + StaticMethod("walk", Walk, napi_writable), + StaticMethod("closest", Closest, napi_writable), + StaticMethod("childNodeForFieldId", ChildNodeForFieldId, napi_writable), + StaticMethod("childNodesForFieldId", ChildNodesForFieldId, napi_writable), + }); - module_exports.Reset(exports); - setup_transfer_buffer(1); + } +}; - Nan::Set(exports, Nan::New("NodeMethods").ToLocalChecked(), result); +void InitNode(Object &exports) { + Env env = exports.Env(); + NodeMethods::Init(env, exports); + module_exports.Reset(exports, 1); + setup_transfer_buffer(env, 1); } -} // namespace node_methods } // namespace node_tree_sitter diff --git a/src/node.h b/src/node.h index 8c050e20..1bb95859 100644 --- a/src/node.h +++ b/src/node.h @@ -1,35 +1,17 @@ #ifndef NODE_TREE_SITTER_NODE_H_ #define NODE_TREE_SITTER_NODE_H_ -#include -#include -#include +#include #include #include "./tree.h" - -using namespace v8; +#include "./util.h" namespace node_tree_sitter { -namespace node_methods { - -void Init(v8::Local); -void MarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *, TSNode); -Local GetMarshalNode(const Nan::FunctionCallbackInfo &info, const Tree *tree, TSNode node); -Local GetMarshalNodes(const Nan::FunctionCallbackInfo &info, const Tree *tree, const TSNode *nodes, uint32_t node_count); -TSNode UnmarshalNode(const Tree *tree); - -static inline const void *UnmarshalNodeId(const uint32_t *buffer) { - const void *result; - memcpy(&result, buffer, sizeof(result)); - return result; -} -static inline void MarshalNodeId(const void *id, uint32_t *buffer) { - memset(buffer, 0, sizeof(uint64_t)); - memcpy(buffer, &id, sizeof(id)); -} +void InitNode(Napi::Object &exports); +Napi::Value MarshalNode(Napi::Env, const Tree *, TSNode); +TSNode UnmarshalNode(Napi::Env env, const Tree *tree); -} // namespace node_methods } // namespace node_tree_sitter #endif // NODE_TREE_SITTER_NODE_H_ diff --git a/src/optional.h b/src/optional.h new file mode 100644 index 00000000..31366f2a --- /dev/null +++ b/src/optional.h @@ -0,0 +1,29 @@ +#ifndef NODE_TREE_SITTER_OPTIONAL_H +#define NODE_TREE_SITTER_OPTIONAL_H + +#include + +template class optional { + T value; + bool is_some; + +public: + optional(T &&value) : value(std::move(value)), is_some(true) {} + optional(const T &value) : value(value), is_some(true) {} + optional() : value(T()), is_some(false) {} + + T &operator*() { return value; } + const T &operator*() const { return value; } + const T *operator->() const { return &value; } + T *operator->() { return &value; } + operator bool() const { return is_some; } + bool operator==(const optional &other) { + if (is_some) { + return other.is_some && value == other.value; + } else { + return !other.is_some; + } + } +}; + +#endif // NODE_TREE_SITTER_OPTIONAL_H diff --git a/src/parser.cc b/src/parser.cc index 52e1beac..533f92ee 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -2,11 +2,11 @@ #include #include #include -#include -#include +#include #include "./conversions.h" #include "./language.h" #include "./logger.h" +#include "./node.h" #include "./tree.h" #include "./util.h" #include "text-buffer-snapshot-wrapper.h" @@ -14,445 +14,466 @@ namespace node_tree_sitter { -using namespace v8; +using namespace Napi; using std::vector; using std::pair; -Nan::Persistent Parser::constructor; - -class CallbackInput { +class Parser : public ObjectWrap { public: - CallbackInput(v8::Local callback, v8::Local js_buffer_size) - : callback(callback), - byte_offset(0), - partial_string_offset(0) { - uint32_t buffer_size = Nan::To(js_buffer_size).FromMaybe(0); - if (buffer_size == 0) buffer_size = 32 * 1024; - buffer.resize(buffer_size); - } + static void Init(Object &exports) { + Napi::Env env = exports.Env(); + + Function ctor = DefineClass(env, "Parser", { + InstanceMethod("getLogger", &Parser::GetLogger, napi_writable), + InstanceMethod("setLogger", &Parser::SetLogger, napi_writable), + InstanceMethod("setLanguage", &Parser::SetLanguage, napi_writable), + InstanceMethod("printDotGraphs", &Parser::PrintDotGraphs, napi_writable), + InstanceMethod("parse", &Parser::Parse, napi_writable), + InstanceMethod("parseTextBuffer", &Parser::ParseTextBuffer, napi_writable), + InstanceMethod("parseTextBufferSync", &Parser::ParseTextBufferSync, napi_writable), + }); + + String s = String::New(env, ""); + if (env.IsExceptionPending()) { + return; + } - TSInput Input() { - TSInput result; - result.payload = (void *)this; - result.encoding = TSInputEncodingUTF16; - result.read = Read; - return result; + napi_value value; + napi_valuetype type; + napi_status status = napi_get_property( + env, + s, + String::New(env, "slice"), + &value + ); + assert(status == napi_ok); + status = napi_typeof(env, value, &type); + assert(status == napi_ok); + + constructor.Reset(ctor, 1); + // string_slice.Reset(string_slice_fn.As(), 1); + exports["Parser"] = ctor; + exports["LANGUAGE_VERSION"] = Number::New(env, TREE_SITTER_LANGUAGE_VERSION); } + TSParser *parser_; + bool is_parsing_async_; + + Parser(const CallbackInfo &info) + : Napi::ObjectWrap(info), + parser_(ts_parser_new()), + is_parsing_async_(false) + {} + + ~Parser() { ts_parser_delete(parser_); } + private: - static const char * Read(void *payload, uint32_t byte, TSPoint position, uint32_t *bytes_read) { - CallbackInput *reader = (CallbackInput *)payload; + class CallbackInput { + public: + CallbackInput(Function callback, Napi::Value js_buffer_size) + : byte_offset(0) { + this->callback.Reset(callback, 1); + if (js_buffer_size.IsNumber()) { + buffer.resize(js_buffer_size.As().Uint32Value()); + } else { + buffer.resize(32 * 1024); + } + } - if (byte != reader->byte_offset) { - reader->byte_offset = byte; - reader->partial_string_offset = 0; - reader->partial_string.Reset(); + TSInput Input() { + TSInput result; + result.payload = (void *)this; + result.encoding = TSInputEncodingUTF16; + result.read = Read; + return result; } - *bytes_read = 0; - Local result; - uint32_t start = 0; - if (reader->partial_string_offset) { - result = Nan::New(reader->partial_string); - start = reader->partial_string_offset; - } else { - Local callback = Nan::New(reader->callback); - uint32_t utf16_unit = byte / 2; - Local argv[2] = { Nan::New(utf16_unit), PointToJS(position) }; - TryCatch try_catch(Isolate::GetCurrent()); - auto maybe_result_value = Nan::Call(callback, callback->CreationContext()->Global(), 2, argv); - if (try_catch.HasCaught()) return nullptr; - - Local result_value; - if (!maybe_result_value.ToLocal(&result_value)) return nullptr; - if (!result_value->IsString()) return nullptr; - if (!Nan::To(result_value).ToLocal(&result)) return nullptr; + private: + static String slice(String s, uint32_t offset) { + return string_slice.Call(s, {Number::New(s.Env(), offset)}).As(); } - int utf16_units_read = result->Write( + static const char * Read(void *payload, uint32_t byte, TSPoint position, uint32_t *bytes_read) { + CallbackInput *reader = (CallbackInput *)payload; + Napi::Env env = reader->callback.Env(); - // Nan doesn't wrap this functionality - #if NODE_MAJOR_VERSION >= 12 - Isolate::GetCurrent(), - #endif + if (byte != reader->byte_offset) { + reader->byte_offset = byte; + reader->partial_string.Reset(); + } - reader->buffer.data(), - start, - reader->buffer.size(), - String::NO_NULL_TERMINATION - ); - int end = start + utf16_units_read; - *bytes_read = 2 * utf16_units_read; + *bytes_read = 0; + String result; + if (!reader->partial_string.IsEmpty()) { + result = reader->partial_string.Value().As(); + } else { + Function callback = reader->callback.Value(); + Napi::Value result_value = callback({ + ByteCountToJS(env, byte), + PointToJS(env, position), + }); + if (env.IsExceptionPending()) return nullptr; + if (!result_value.IsString()) return nullptr; + result = result_value.As(); + } - reader->byte_offset += *bytes_read; + size_t length = 0; + size_t utf16_units_read = 0; + napi_status status; + status = napi_get_value_string_utf16( + env, result, nullptr, 0, &length + ); + if (status != napi_ok) return nullptr; + status = napi_get_value_string_utf16( + env, result, (char16_t *)&reader->buffer[0], reader->buffer.size(), &utf16_units_read + ); + if (status != napi_ok) return nullptr; + + *bytes_read = 2 * utf16_units_read; + reader->byte_offset += *bytes_read; + + if (utf16_units_read < length) { + reader->partial_string.Reset(slice(result, utf16_units_read)); + } else { + reader->partial_string.Reset(); + } - if (end < result->Length()) { - reader->partial_string_offset = end; - reader->partial_string.Reset(result); - } else { - reader->partial_string_offset = 0; - reader->partial_string.Reset(); + return (const char *)reader->buffer.data(); } - return (const char *)reader->buffer.data(); - } - - Nan::Persistent callback; - std::vector buffer; - size_t byte_offset; - Nan::Persistent partial_string; - size_t partial_string_offset; -}; + FunctionReference callback; + std::vector buffer; + size_t byte_offset; + Reference partial_string; + }; -class TextBufferInput { -public: - TextBufferInput(const vector> *slices) - : slices_(slices), - byte_offset(0), - slice_index_(0), - slice_offset_(0) {} + class TextBufferInput { + public: + TextBufferInput(const vector> *slices) + : slices_(slices), + byte_offset(0), + slice_index_(0), + slice_offset_(0) {} - TSInput input() { - return TSInput{this, Read, TSInputEncodingUTF16}; - } + TSInput input() { + return TSInput{this, Read, TSInputEncodingUTF16}; + } -private: - void seek(uint32_t byte_offset) { - this->byte_offset = byte_offset; - - uint32_t total_length = 0; - uint32_t goal_index = byte_offset / 2; - for (unsigned i = 0, n = this->slices_->size(); i < n; i++) { - uint32_t next_total_length = total_length + this->slices_->at(i).second; - if (next_total_length > goal_index) { - this->slice_index_ = i; - this->slice_offset_ = goal_index - total_length; - return; + private: + void seek(uint32_t byte_offset) { + this->byte_offset = byte_offset; + + uint32_t total_length = 0; + uint32_t goal_index = byte_offset / 2; + for (unsigned i = 0, n = this->slices_->size(); i < n; i++) { + uint32_t next_total_length = total_length + this->slices_->at(i).second; + if (next_total_length > goal_index) { + this->slice_index_ = i; + this->slice_offset_ = goal_index - total_length; + return; + } + total_length = next_total_length; } - total_length = next_total_length; + + this->slice_index_ = this->slices_->size(); + this->slice_offset_ = 0; } - this->slice_index_ = this->slices_->size(); - this->slice_offset_ = 0; - } + static const char *Read(void *payload, uint32_t byte, TSPoint position, uint32_t *length) { + auto self = static_cast(payload); - static const char *Read(void *payload, uint32_t byte, TSPoint position, uint32_t *length) { - auto self = static_cast(payload); + if (byte != self->byte_offset) self->seek(byte); - if (byte != self->byte_offset) self->seek(byte); + if (self->slice_index_ == self->slices_->size()) { + *length = 0; + return ""; + } - if (self->slice_index_ == self->slices_->size()) { - *length = 0; - return ""; + auto &slice = self->slices_->at(self->slice_index_); + const char16_t *result = slice.first + self->slice_offset_; + *length = 2 * (slice.second - self->slice_offset_); + self->byte_offset += *length; + self->slice_index_++; + self->slice_offset_ = 0; + return reinterpret_cast(result); } - auto &slice = self->slices_->at(self->slice_index_); - const char16_t *result = slice.first + self->slice_offset_; - *length = 2 * (slice.second - self->slice_offset_); - self->byte_offset += *length; - self->slice_index_++; - self->slice_offset_ = 0; - return reinterpret_cast(result); - } - - const vector> *slices_; - uint32_t byte_offset; - uint32_t slice_index_; - uint32_t slice_offset_; -}; - -void Parser::Init(Local exports) { - Local tpl = Nan::New(New); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - Local class_name = Nan::New("Parser").ToLocalChecked(); - tpl->SetClassName(class_name); - - FunctionPair methods[] = { - {"getLogger", GetLogger}, - {"setLogger", SetLogger}, - {"setLanguage", SetLanguage}, - {"printDotGraphs", PrintDotGraphs}, - {"parse", Parse}, - {"parseTextBuffer", ParseTextBuffer}, - {"parseTextBufferSync", ParseTextBufferSync}, + const vector> *slices_; + uint32_t byte_offset; + uint32_t slice_index_; + uint32_t slice_offset_; }; - for (size_t i = 0; i < length_of_array(methods); i++) { - Nan::SetPrototypeMethod(tpl, methods[i].name, methods[i].callback); - } - - constructor.Reset(Nan::Persistent(Nan::GetFunction(tpl).ToLocalChecked())); - Nan::Set(exports, class_name, Nan::New(constructor)); - Nan::Set(exports, Nan::New("LANGUAGE_VERSION").ToLocalChecked(), Nan::New(TREE_SITTER_LANGUAGE_VERSION)); -} - -Parser::Parser() : parser_(ts_parser_new()), is_parsing_async_(false) {} - -Parser::~Parser() { ts_parser_delete(parser_); } - -static bool handle_included_ranges(TSParser *parser, Local arg) { - uint32_t last_included_range_end = 0; - if (arg->IsArray()) { - auto js_included_ranges = Local::Cast(arg); - vector included_ranges; - for (unsigned i = 0; i < js_included_ranges->Length(); i++) { - Local range_value; - if (!Nan::Get(js_included_ranges, i).ToLocal(&range_value)) return false; - auto maybe_range = RangeFromJS(range_value); - if (!maybe_range.IsJust()) return false; - auto range = maybe_range.FromJust(); - if (range.start_byte < last_included_range_end) { - Nan::ThrowRangeError("Overlapping ranges"); - return false; + bool handle_included_ranges(class Value arg) { + Napi::Env env = arg.Env(); + uint32_t last_included_range_end = 0; + if (arg.IsArray()) { + Array js_included_ranges = arg.As(); + vector included_ranges; + for (unsigned i = 0; i < js_included_ranges.Length(); i++) { + Napi::Value range_value = js_included_ranges[i]; + if (!range_value.IsObject()) return false; + optional range = RangeFromJS(range_value); + if (!range) return false; + if (range->start_byte < last_included_range_end) { + RangeError::New(env, "Overlapping ranges").ThrowAsJavaScriptException(); + return false; + } + last_included_range_end = range->end_byte; + included_ranges.push_back(*range); } - last_included_range_end = range.end_byte; - included_ranges.push_back(range); + ts_parser_set_included_ranges(parser_, included_ranges.data(), included_ranges.size()); + } else { + ts_parser_set_included_ranges(parser_, nullptr, 0); } - ts_parser_set_included_ranges(parser, included_ranges.data(), included_ranges.size()); - } else { - ts_parser_set_included_ranges(parser, nullptr, 0); + return true; } - return true; -} - -void Parser::New(const Nan::FunctionCallbackInfo &info) { - if (info.IsConstructCall()) { - Parser *parser = new Parser(); - parser->Wrap(info.This()); - info.GetReturnValue().Set(info.This()); - } else { - Local self; - MaybeLocal maybe_self = Nan::New(constructor)->NewInstance(Nan::GetCurrentContext()); - if (maybe_self.ToLocal(&self)) { - info.GetReturnValue().Set(self); - } else { - info.GetReturnValue().Set(Nan::Null()); + Napi::Value SetLanguage(const CallbackInfo &info) { + auto env = info.Env(); + if (is_parsing_async_) { + Error::New(env, "Parser is in use").ThrowAsJavaScriptException(); + return env.Undefined(); } - } -} -void Parser::SetLanguage(const Nan::FunctionCallbackInfo &info) { - Parser *parser = ObjectWrap::Unwrap(info.This()); - if (parser->is_parsing_async_) { - Nan::ThrowError("Parser is in use"); - return; + const TSLanguage *language = UnwrapLanguage(info[0]); + if (language) ts_parser_set_language(parser_, language); + return info.This(); } - const TSLanguage *language = language_methods::UnwrapLanguage(info[0]); - if (language) { - ts_parser_set_language(parser->parser_, language); - info.GetReturnValue().Set(info.This()); - } -} + Napi::Value Parse(const CallbackInfo &info) { + auto env = info.Env(); -void Parser::Parse(const Nan::FunctionCallbackInfo &info) { - Parser *parser = ObjectWrap::Unwrap(info.This()); - if (parser->is_parsing_async_) { - Nan::ThrowError("Parser is in use"); - return; - } + if (is_parsing_async_) { + Error::New(env, "Parser is in use").ThrowAsJavaScriptException(); + return env.Undefined(); + } - if (!info[0]->IsFunction()) { - Nan::ThrowTypeError("Input must be a function"); - return; - } + if (!info[0].IsFunction()) { + TypeError::New(env, "Input must be a function").ThrowAsJavaScriptException(); + return env.Undefined(); + } - Local callback = Local::Cast(info[0]); + Function callback = info[0].As(); - Local js_old_tree; - const TSTree *old_tree = nullptr; - if (info.Length() > 1 && !info[1]->IsNull() && !info[1]->IsUndefined() && Nan::To(info[1]).ToLocal(&js_old_tree)) { - const Tree *tree = Tree::UnwrapTree(js_old_tree); - if (!tree) { - Nan::ThrowTypeError("Second argument must be a tree"); - return; + const TSTree *old_tree = nullptr; + if (info.Length() > 1 && info[1].IsObject()) { + Object js_old_tree = info[1].As(); + const Tree *tree = Tree::UnwrapTree(js_old_tree); + if (!tree) { + TypeError::New(env, "Second argument must be a tree").ThrowAsJavaScriptException(); + return env.Undefined(); + } + old_tree = tree->tree_; } - old_tree = tree->tree_; + + auto buffer_size = env.Null(); + if (info.Length() > 2) buffer_size = info[2]; + if (!handle_included_ranges(info[3])) return env.Undefined(); + + CallbackInput callback_input(callback, buffer_size); + TSTree *tree = ts_parser_parse(parser_, old_tree, callback_input.Input()); + auto result = Tree::NewInstance(env, tree); + return result; } - Local buffer_size = Nan::Null(); - if (info.Length() > 2) buffer_size = info[2]; + class ParseWorker : public Napi::AsyncWorker { + Parser *parser_; + TSTree *new_tree_; + TextBufferInput *input_; + + public: + ParseWorker(Napi::Function &callback, Parser *parser, TextBufferInput *input) : + Napi::AsyncWorker(callback, "tree-sitter.parseTextBuffer"), + parser_(parser), + new_tree_(nullptr), + input_(input) {} + + void Execute() { + TSLogger logger = ts_parser_logger(parser_->parser_); + ts_parser_set_logger(parser_->parser_, TSLogger{0, 0}); + new_tree_ = ts_parser_parse(parser_->parser_, nullptr, input_->input()); + ts_parser_set_logger(parser_->parser_, logger); + } - if (!handle_included_ranges(parser->parser_, info[3])) return; + void OnOK() { + parser_->is_parsing_async_ = false; + delete input_; + Callback()({Tree::NewInstance(Env(), new_tree_)}); + } + }; - CallbackInput callback_input(callback, buffer_size); - TSTree *tree = ts_parser_parse(parser->parser_, old_tree, callback_input.Input()); - Local result = Tree::NewInstance(tree); - info.GetReturnValue().Set(result); -} + const std::vector> * + TextBufferSnapshotFromJS(Napi::Value value) { + auto env = value.Env(); + if (!value.IsObject()) { + TypeError::New(env, "Expected a snapshot wrapper").ThrowAsJavaScriptException(); + return nullptr; + } -class ParseWorker : public Nan::AsyncWorker { - Parser *parser_; - TSTree *new_tree_; - TextBufferInput *input_; - -public: - ParseWorker(Nan::Callback *callback, Parser *parser, TextBufferInput *input) : - AsyncWorker(callback, "tree-sitter.parseTextBuffer"), - parser_(parser), - new_tree_(nullptr), - input_(input) {} - - void Execute() { - TSLogger logger = ts_parser_logger(parser_->parser_); - ts_parser_set_logger(parser_->parser_, TSLogger{0, 0}); - new_tree_ = ts_parser_parse(parser_->parser_, nullptr, input_->input()); - ts_parser_set_logger(parser_->parser_, logger); + void *internal = GetInternalFieldPointer(value); + if (internal) { + return reinterpret_cast(internal)->slices(); + } else { + TypeError::New(env, "Expected a snapshot wrapper").ThrowAsJavaScriptException(); + return nullptr; + } } - void HandleOKCallback() { - parser_->is_parsing_async_ = false; - delete input_; - Local argv[] = {Tree::NewInstance(new_tree_)}; - callback->Call(1, argv, async_resource); - } -}; + Napi::Value ParseTextBuffer(const CallbackInfo &info) { + auto env = info.Env(); -void Parser::ParseTextBuffer(const Nan::FunctionCallbackInfo &info) { - Parser *parser = ObjectWrap::Unwrap(info.This()); - if (parser->is_parsing_async_) { - Nan::ThrowError("Parser is in use"); - return; - } + if (is_parsing_async_) { + Error::New(env, "Parser is in use").ThrowAsJavaScriptException(); + return env.Undefined(); + } - Local js_old_tree; - const TSTree *old_tree = nullptr; - if (info.Length() > 2 && info[2]->IsObject() && Nan::To(info[2]).ToLocal(&js_old_tree)) { - const Tree *tree = Tree::UnwrapTree(js_old_tree); - if (!tree) { - Nan::ThrowTypeError("Second argument must be a tree"); - return; + auto callback = info[0].As(); + + const TSTree *old_tree = nullptr; + if (info.Length() > 2 && info[2].IsObject()) { + Object js_old_tree = info[2].As(); + const Tree *tree = Tree::UnwrapTree(js_old_tree); + if (!tree) { + TypeError::New(env, "Second argument must be a tree").ThrowAsJavaScriptException(); + return env.Undefined(); + } + old_tree = tree->tree_; } - old_tree = tree->tree_; - } - if (!handle_included_ranges(parser->parser_, info[3])) return; + if (!handle_included_ranges(info[3])) return env.Undefined(); - auto snapshot = Nan::ObjectWrap::Unwrap(info[1].As()); - auto input = new TextBufferInput(snapshot->slices()); + auto snapshot = TextBufferSnapshotFromJS(info[1]); + if (!snapshot) return env.Undefined(); - // If a `syncTimeoutMicros` option is passed, parse synchronously - // for the given amount of time before queuing an async task. - double js_sync_timeout = Nan::To(info[4]).FromMaybe(-1); - if (js_sync_timeout > 0) { - size_t sync_timeout; + auto input = new TextBufferInput(snapshot); - // If the timeout is `Infinity`, then parse synchronously with no timeout. - if (js_sync_timeout && !std::isfinite(js_sync_timeout)) { - sync_timeout = 0; - } else { - auto maybe_sync_timeout = Nan::To(info[4]); - if (maybe_sync_timeout.IsJust()) { - sync_timeout = maybe_sync_timeout.FromJust(); + // If a `syncTimeoutMicros` option is passed, parse synchronously + // for the given amount of time before queuing an async task. + auto js_sync_timeout = info[4]; + if (!js_sync_timeout.IsEmpty() && !js_sync_timeout.IsNull() && !js_sync_timeout.IsUndefined()) { + size_t sync_timeout; + + // If the timeout is `Infinity`, then parse synchronously with no timeout. + if (!std::isfinite(js_sync_timeout.ToNumber().DoubleValue())) { + sync_timeout = 0; + } else if (js_sync_timeout.IsNumber()) { + sync_timeout = js_sync_timeout.As().Uint32Value(); } else { - Nan::ThrowTypeError("The `syncTimeoutMicros` option must be a positive integer."); - return; + TypeError::New(env, "The `syncTimeoutMicros` option must be a positive integer.").ThrowAsJavaScriptException(); + return env.Undefined(); } - } - // Logging is disabled for this method, because we can't call the - // logging callback from an async worker. - TSLogger logger = ts_parser_logger(parser->parser_); - ts_parser_set_timeout_micros(parser->parser_, sync_timeout); - ts_parser_set_logger(parser->parser_, TSLogger{0, 0}); - TSTree *result = ts_parser_parse(parser->parser_, old_tree, input->input()); - ts_parser_set_timeout_micros(parser->parser_, 0); - ts_parser_set_logger(parser->parser_, logger); - - if (result) { - delete input; - Local argv[] = {Tree::NewInstance(result)}; - auto callback = info[0].As(); - Nan::Call(callback, callback->CreationContext()->Global(), 1, argv); - return; + // Logging is disabled for this method, because we can't call the + // logging callback from an async worker. + TSLogger logger = ts_parser_logger(parser_); + ts_parser_set_timeout_micros(parser_, sync_timeout); + ts_parser_set_logger(parser_, TSLogger{0, 0}); + TSTree *result = ts_parser_parse(parser_, old_tree, input->input()); + ts_parser_set_timeout_micros(parser_, 0); + ts_parser_set_logger(parser_, logger); + + if (result) { + delete input; + callback({Tree::NewInstance(env, result)}); + return env.Undefined(); + } } + + is_parsing_async_ = true; + auto worker = new ParseWorker(callback, this, input); + worker->Queue(); + return env.Undefined(); } - auto callback = new Nan::Callback(info[0].As()); - parser->is_parsing_async_ = true; - Nan::AsyncQueueWorker(new ParseWorker( - callback, - parser, - input - )); -} + Napi::Value ParseTextBufferSync(const CallbackInfo &info) { + auto env = info.Env(); + + if (is_parsing_async_) { + Error::New(env, "Parser is in use").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + const TSTree *old_tree = nullptr; + if (info.Length() > 1 && info[1].IsObject()) { + const Tree *tree = Tree::UnwrapTree(info[1].As()); + if (!tree) { + TypeError::New(env, "Second argument must be a tree").ThrowAsJavaScriptException(); + return env.Undefined(); + } + old_tree = ts_tree_copy(tree->tree_); + } + + if (!handle_included_ranges(info[2])) return env.Undefined(); -void Parser::ParseTextBufferSync(const Nan::FunctionCallbackInfo &info) { - Parser *parser = ObjectWrap::Unwrap(info.This()); - if (parser->is_parsing_async_) { - Nan::ThrowError("Parser is in use"); - return; + auto snapshot = TextBufferSnapshotFromJS(info[0]); + if (!snapshot) return env.Undefined(); + + TextBufferInput input(snapshot); + TSTree *result = ts_parser_parse(parser_, old_tree, input.input()); + return Tree::NewInstance(env, result); } - Local js_old_tree; - const TSTree *old_tree = nullptr; - if (info.Length() > 1 && info[1]->IsObject() && Nan::To(info[1]).ToLocal(&js_old_tree)) { - const Tree *tree = Tree::UnwrapTree(js_old_tree); - if (!tree) { - Nan::ThrowTypeError("Second argument must be a tree"); - return; + Napi::Value GetLogger(const CallbackInfo &info) { + auto env = info.Env(); + TSLogger current_logger = ts_parser_logger(parser_); + if (current_logger.payload && current_logger.log == Logger::Log) { + Logger *logger = (Logger *)current_logger.payload; + return logger->func.Value(); + } else { + return env.Null(); } - old_tree = ts_tree_copy(tree->tree_); } - if (!handle_included_ranges(parser->parser_, info[2])) return; + Napi::Value SetLogger(const CallbackInfo &info) { + auto env = info.Env(); - auto snapshot = Nan::ObjectWrap::Unwrap(info[0].As()); - TextBufferInput input(snapshot->slices()); - TSTree *result = ts_parser_parse(parser->parser_, old_tree, input.input()); - info.GetReturnValue().Set(Tree::NewInstance(result)); -} + if (is_parsing_async_) { + Error::New(env, "Parser is in use").ThrowAsJavaScriptException(); + return env.Undefined(); + } -void Parser::GetLogger(const Nan::FunctionCallbackInfo &info) { - Parser *parser = ObjectWrap::Unwrap(info.This()); + TSLogger current_logger = ts_parser_logger(parser_); - TSLogger current_logger = ts_parser_logger(parser->parser_); - if (current_logger.payload && current_logger.log == Logger::Log) { - Logger *logger = (Logger *)current_logger.payload; - info.GetReturnValue().Set(Nan::New(logger->func)); - } else { - info.GetReturnValue().Set(Nan::Null()); - } -} + if (info[0].IsFunction()) { + if (current_logger.payload) delete (Logger *)current_logger.payload; + ts_parser_set_logger(parser_, Logger::Make(info[0].As())); + } else if (info[0].IsEmpty() || info[0].IsNull() || (info[0].IsBoolean() && !info[0].As())) { + if (current_logger.payload) delete (Logger *)current_logger.payload; + ts_parser_set_logger(parser_, { 0, 0 }); + } else { + TypeError::New(env, "Logger callback must either be a function or a falsy value").ThrowAsJavaScriptException(); + } -void Parser::SetLogger(const Nan::FunctionCallbackInfo &info) { - Parser *parser = ObjectWrap::Unwrap(info.This()); - if (parser->is_parsing_async_) { - Nan::ThrowError("Parser is in use"); - return; + return info.This(); } - TSLogger current_logger = ts_parser_logger(parser->parser_); - - if (info[0]->IsFunction()) { - if (current_logger.payload) delete (Logger *)current_logger.payload; - ts_parser_set_logger(parser->parser_, Logger::Make(Local::Cast(info[0]))); - } else if (!Nan::To(info[0]).FromMaybe(true)) { - if (current_logger.payload) delete (Logger *)current_logger.payload; - ts_parser_set_logger(parser->parser_, { 0, 0 }); - } else { - Nan::ThrowTypeError("Logger callback must either be a function or a falsy value"); - return; - } + Napi::Value PrintDotGraphs(const CallbackInfo &info) { + auto env = info.Env(); - info.GetReturnValue().Set(info.This()); -} + if (is_parsing_async_) { + Error::New(env, "Parser is in use").ThrowAsJavaScriptException(); + return env.Undefined(); + } -void Parser::PrintDotGraphs(const Nan::FunctionCallbackInfo &info) { - Parser *parser = ObjectWrap::Unwrap(info.This()); - if (parser->is_parsing_async_) { - Nan::ThrowError("Parser is in use"); - return; - } + if (info[0].IsBoolean() && info[0].As()) { + ts_parser_print_dot_graphs(parser_, 2); + } else { + ts_parser_print_dot_graphs(parser_, -1); + } - if (Nan::To(info[0]).FromMaybe(false)) { - ts_parser_print_dot_graphs(parser->parser_, 2); - } else { - ts_parser_print_dot_graphs(parser->parser_, -1); + return info.This(); } - info.GetReturnValue().Set(info.This()); + static Napi::FunctionReference constructor; + static Napi::FunctionReference string_slice; +}; + +void InitParser(Object &exports) { + Parser::Init(exports); } +Napi::FunctionReference Parser::constructor; +Napi::FunctionReference Parser::string_slice; + } // namespace node_tree_sitter diff --git a/src/parser.h b/src/parser.h index e01e1f11..ac838ead 100644 --- a/src/parser.h +++ b/src/parser.h @@ -1,35 +1,12 @@ #ifndef NODE_TREE_SITTER_PARSER_H_ #define NODE_TREE_SITTER_PARSER_H_ -#include -#include -#include +#include #include namespace node_tree_sitter { -class Parser : public Nan::ObjectWrap { - public: - static void Init(v8::Local exports); - - TSParser *parser_; - bool is_parsing_async_; - - private: - explicit Parser(); - ~Parser(); - - static void New(const Nan::FunctionCallbackInfo &); - static void SetLanguage(const Nan::FunctionCallbackInfo &); - static void GetLogger(const Nan::FunctionCallbackInfo &); - static void SetLogger(const Nan::FunctionCallbackInfo &); - static void Parse(const Nan::FunctionCallbackInfo &); - static void ParseTextBuffer(const Nan::FunctionCallbackInfo &); - static void ParseTextBufferSync(const Nan::FunctionCallbackInfo &); - static void PrintDotGraphs(const Nan::FunctionCallbackInfo &); - - static Nan::Persistent constructor; -}; +void InitParser(Napi::Object &exports); } // namespace node_tree_sitter diff --git a/src/tree.cc b/src/tree.cc index 4447d4cc..40bccef9 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -1,6 +1,5 @@ #include "./tree.h" #include -#include #include #include "./node.h" #include "./logger.h" @@ -9,86 +8,65 @@ namespace node_tree_sitter { -using namespace v8; -using node_methods::UnmarshalNodeId; - -Nan::Persistent Tree::constructor; -Nan::Persistent Tree::constructor_template; - -void Tree::Init(Local exports) { - Local tpl = Nan::New(New); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - Local class_name = Nan::New("Tree").ToLocalChecked(); - tpl->SetClassName(class_name); - - FunctionPair methods[] = { - {"edit", Edit}, - {"rootNode", RootNode}, - {"printDotGraph", PrintDotGraph}, - {"getChangedRanges", GetChangedRanges}, - {"getEditedRange", GetEditedRange}, - {"_cacheNode", CacheNode}, - {"_cacheNodes", CacheNodes}, - }; +using namespace Napi; - for (size_t i = 0; i < length_of_array(methods); i++) { - Nan::SetPrototypeMethod(tpl, methods[i].name, methods[i].callback); - } +FunctionReference Tree::constructor; - Local ctor = Nan::GetFunction(tpl).ToLocalChecked(); +void Tree::Init(Object &exports) { + Napi::Env env = exports.Env(); - constructor_template.Reset(tpl); - constructor.Reset(ctor); - Nan::Set(exports, class_name, ctor); + Function ctor = DefineClass(env, "Tree", { + InstanceMethod("edit", &Tree::Edit, napi_writable), + InstanceMethod("rootNode", &Tree::RootNode, napi_configurable), + InstanceMethod("printDotGraph", &Tree::PrintDotGraph), + InstanceMethod("getChangedRanges", &Tree::GetChangedRanges), + InstanceMethod("getEditedRange", &Tree::GetEditedRange), + InstanceMethod("_cacheNode", &Tree::CacheNode), + }); + + constructor.Reset(ctor, 1); + exports["Tree"] = ctor; } -Tree::Tree(TSTree *tree) : tree_(tree) {} +Tree::Tree(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info), + tree_(nullptr) {} Tree::~Tree() { - ts_tree_delete(tree_); + if (tree_) ts_tree_delete(tree_); for (auto &entry : cached_nodes_) { entry.second->tree = nullptr; } } -Local Tree::NewInstance(TSTree *tree) { +Napi::Value Tree::NewInstance(Napi::Env env, TSTree *tree) { if (tree) { - Local self; - MaybeLocal maybe_self = Nan::NewInstance(Nan::New(constructor)); - if (maybe_self.ToLocal(&self)) { - (new Tree(tree))->Wrap(self); - return self; - } + Object js_tree = constructor.Value().New({}); + Tree::Unwrap(js_tree)->tree_ = tree; + return js_tree; } - return Nan::Null(); + return env.Null(); } -const Tree *Tree::UnwrapTree(const Local &value) { - if (!value->IsObject()) return nullptr; - Local js_tree = Local::Cast(value); - if (!Nan::New(constructor_template)->HasInstance(js_tree)) return nullptr; - return ObjectWrap::Unwrap(js_tree); +const Tree *Tree::UnwrapTree(const Napi::Value &value) { + return Tree::Unwrap(value.As()); } -void Tree::New(const Nan::FunctionCallbackInfo &info) {} - -#define read_number_from_js(out, value, name) \ - maybe_number = Nan::To(value); \ - if (maybe_number.IsNothing()) { \ - Nan::ThrowTypeError(name " must be an integer"); \ - return; \ - } \ - *(out) = maybe_number.FromJust(); +#define read_number_from_js(out, value, name) \ + if (!value.IsNumber()) { \ + TypeError::New(env, name " must be an integer").ThrowAsJavaScriptException(); \ + return env.Undefined(); \ + } \ + *(out) = value.As().Uint32Value(); #define read_byte_count_from_js(out, value, name) \ read_number_from_js(out, value, name); \ (*out) *= 2 -void Tree::Edit(const Nan::FunctionCallbackInfo &info) { - Tree *tree = ObjectWrap::Unwrap(info.This()); +Napi::Value Tree::Edit(const CallbackInfo &info) { + auto env = info.Env(); TSInputEdit edit; - Nan::Maybe maybe_number = Nan::Nothing(); read_number_from_js(&edit.start_point.row, info[0], "startPosition.row"); read_byte_count_from_js(&edit.start_point.column, info[1], "startPosition.column"); read_number_from_js(&edit.old_end_point.row, info[2], "oldEndPosition.row"); @@ -99,57 +77,53 @@ void Tree::Edit(const Nan::FunctionCallbackInfo &info) { read_byte_count_from_js(&edit.old_end_byte, info[7], "oldEndIndex"); read_byte_count_from_js(&edit.new_end_byte, info[8], "newEndIndex"); - ts_tree_edit(tree->tree_, &edit); + ts_tree_edit(tree_, &edit); - for (auto &entry : tree->cached_nodes_) { - Local js_node = Nan::New(entry.second->node); + for (auto &entry : cached_nodes_) { + Object js_node = entry.second->node.Value(); TSNode node; node.id = entry.first; for (unsigned i = 0; i < 4; i++) { - Local node_field; - if (Nan::Get(js_node, i + 2).ToLocal(&node_field)) { - node.context[i] = Nan::To(node_field).FromMaybe(0); + Napi::Value node_field = js_node[i + 2u]; + if (node_field.IsNumber()) { + node.context[i] = node_field.As().Uint32Value(); } } ts_node_edit(&node, &edit); for (unsigned i = 0; i < 4; i++) { - Nan::Set(js_node, i + 2, Nan::New(node.context[i])); + js_node[i + 2u] = Number::New(env, node.context[i]); } } - info.GetReturnValue().Set(info.This()); + return info.This(); } -void Tree::RootNode(const Nan::FunctionCallbackInfo &info) { - Tree *tree = ObjectWrap::Unwrap(info.This()); - node_methods::MarshalNode(info, tree, ts_tree_root_node(tree->tree_)); +Napi::Value Tree::RootNode(const CallbackInfo &info) { + return MarshalNode(info.Env(), this, ts_tree_root_node(tree_)); } -void Tree::GetChangedRanges(const Nan::FunctionCallbackInfo &info) { - const Tree *tree = ObjectWrap::Unwrap(info.This()); +Napi::Value Tree::GetChangedRanges(const CallbackInfo &info) { + auto env = info.Env(); const Tree *other_tree = UnwrapTree(info[0]); - if (!other_tree) { - Nan::ThrowTypeError("Argument must be a tree"); - return; - } + if (!other_tree) return env.Undefined(); uint32_t range_count; - TSRange *ranges = ts_tree_get_changed_ranges(tree->tree_, other_tree->tree_, &range_count); + TSRange *ranges = ts_tree_get_changed_ranges(tree_, other_tree->tree_, &range_count); - Local result = Nan::New(); - for (size_t i = 0; i < range_count; i++) { - Nan::Set(result, i, RangeToJS(ranges[i])); + Array result = Array::New(env); + for (unsigned i = 0; i < range_count; i++) { + result[i] = RangeToJS(env, ranges[i]); } - info.GetReturnValue().Set(result); + return result; } -void Tree::GetEditedRange(const Nan::FunctionCallbackInfo &info) { - Tree *tree = ObjectWrap::Unwrap(info.This()); - TSNode root = ts_tree_root_node(tree->tree_); - if (!ts_node_has_changes(root)) return; +Napi::Value Tree::GetEditedRange(const CallbackInfo &info) { + auto env = info.Env(); + TSNode root = ts_tree_root_node(tree_); + if (!ts_node_has_changes(root)) return env.Undefined(); TSRange result = { ts_node_start_point(root), ts_node_end_point(root), @@ -191,17 +165,15 @@ void Tree::GetEditedRange(const Nan::FunctionCallbackInfo &info) { } ts_tree_cursor_delete(&cursor); - info.GetReturnValue().Set(RangeToJS(result)); + return RangeToJS(env, result); } -void Tree::PrintDotGraph(const Nan::FunctionCallbackInfo &info) { - Tree *tree = ObjectWrap::Unwrap(info.This()); - ts_tree_print_dot_graph(tree->tree_, stderr); - info.GetReturnValue().Set(info.This()); +Napi::Value Tree::PrintDotGraph(const CallbackInfo &info) { + ts_tree_print_dot_graph(tree_, stderr); + return info.This(); } -static void FinalizeNode(const v8::WeakCallbackInfo &info) { - Tree::NodeCacheEntry *cache_entry = info.GetParameter(); +static void FinalizeNode(Env env, Tree::NodeCacheEntry *cache_entry) { assert(!cache_entry->node.IsEmpty()); cache_entry->node.Reset(); if (cache_entry->tree) { @@ -211,43 +183,30 @@ static void FinalizeNode(const v8::WeakCallbackInfo &info) delete cache_entry; } -static void CacheNodeForTree(Tree *tree, Isolate *isolate, Local js_node) { - Local js_node_field1, js_node_field2; - if (!Nan::Get(js_node, 0).ToLocal(&js_node_field1)) return; - if (!Nan::Get(js_node, 1).ToLocal(&js_node_field2)) return; +Napi::Value Tree::CacheNode(const CallbackInfo &info) { + auto env = info.Env(); + Object js_node = info[0].As(); + + Napi::Value js_node_field1 = js_node[0u]; + Napi::Value js_node_field2 = js_node[1u]; + if (!js_node_field1.IsNumber() || !js_node_field2.IsNumber()) { + return env.Undefined(); + } uint32_t key_parts[2] = { - Nan::To(js_node_field1).FromMaybe(0), - Nan::To(js_node_field2).FromMaybe(0) + js_node_field1.As().Uint32Value(), + js_node_field2.As().Uint32Value(), }; - const void *key = UnmarshalNodeId(key_parts); - - auto cache_entry = new Tree::NodeCacheEntry{tree, key, {}}; - cache_entry->node.Reset(isolate, js_node); - cache_entry->node.SetWeak(cache_entry, &FinalizeNode, Nan::WeakCallbackType::kParameter); + const void *key = UnmarshalPointer(key_parts); - assert(!tree->cached_nodes_.count(key)); - - tree->cached_nodes_[key] = cache_entry; -} + auto cache_entry = new NodeCacheEntry{this, key, {}}; + cache_entry->node.Reset(js_node, 0); + js_node.AddFinalizer(&FinalizeNode, cache_entry); -void Tree::CacheNode(const Nan::FunctionCallbackInfo &info) { - Tree *tree = ObjectWrap::Unwrap(info.This()); - Isolate *isolate = info.GetIsolate(); - Local js_node = Local::Cast(info[0]); + assert(!cached_nodes_.count(key)); - CacheNodeForTree(tree, isolate, js_node); + cached_nodes_[key] = cache_entry; + return env.Undefined(); } -void Tree::CacheNodes(const Nan::FunctionCallbackInfo &info) { - Tree *tree = ObjectWrap::Unwrap(info.This()); - Isolate *isolate = info.GetIsolate(); - Local js_nodes = Local::Cast(info[0]); - uint32_t length = js_nodes->Length(); - - for (uint32_t i = 0; i < length; i++) { - auto js_node = Local::Cast(Nan::Get(js_nodes, i).ToLocalChecked()); - CacheNodeForTree(tree, isolate, js_node); - } -} } // namespace node_tree_sitter diff --git a/src/tree.h b/src/tree.h index 5ed4e316..98e4c5eb 100644 --- a/src/tree.h +++ b/src/tree.h @@ -1,44 +1,40 @@ #ifndef NODE_TREE_SITTER_TREE_H_ #define NODE_TREE_SITTER_TREE_H_ -#include -#include -#include +#include #include #include namespace node_tree_sitter { -class Tree : public Nan::ObjectWrap { +class Tree : public Napi::ObjectWrap { public: - static void Init(v8::Local exports); - static v8::Local NewInstance(TSTree *); - static const Tree *UnwrapTree(const v8::Local &); + static void Init(Napi::Object &); + static Napi::Value NewInstance(Napi::Env, TSTree *); + static const Tree *UnwrapTree(const Napi::Value &); + Tree(const Napi::CallbackInfo& info); + ~Tree(); struct NodeCacheEntry { Tree *tree; const void *key; - v8::Persistent node; + Napi::ObjectReference node; }; TSTree *tree_; std::unordered_map cached_nodes_; private: - explicit Tree(TSTree *); - ~Tree(); - static void New(const Nan::FunctionCallbackInfo &); - static void Edit(const Nan::FunctionCallbackInfo &); - static void RootNode(const Nan::FunctionCallbackInfo &); - static void PrintDotGraph(const Nan::FunctionCallbackInfo &); - static void GetEditedRange(const Nan::FunctionCallbackInfo &); - static void GetChangedRanges(const Nan::FunctionCallbackInfo &); - static void CacheNode(const Nan::FunctionCallbackInfo &); - static void CacheNodes(const Nan::FunctionCallbackInfo &); - - static Nan::Persistent constructor; - static Nan::Persistent constructor_template; + Napi::Value New(const Napi::CallbackInfo &); + Napi::Value Edit(const Napi::CallbackInfo &); + Napi::Value RootNode(const Napi::CallbackInfo &); + Napi::Value PrintDotGraph(const Napi::CallbackInfo &); + Napi::Value GetEditedRange(const Napi::CallbackInfo &); + Napi::Value GetChangedRanges(const Napi::CallbackInfo &); + Napi::Value CacheNode(const Napi::CallbackInfo &); + + static Napi::FunctionReference constructor; }; } // namespace node_tree_sitter diff --git a/src/tree_cursor.cc b/src/tree_cursor.cc index a1bb741c..c3f5679c 100644 --- a/src/tree_cursor.cc +++ b/src/tree_cursor.cc @@ -1,7 +1,6 @@ #include "./tree_cursor.h" -#include #include -#include +#include #include "./util.h" #include "./conversions.h" #include "./node.h" @@ -9,163 +8,155 @@ namespace node_tree_sitter { -using namespace v8; - -Nan::Persistent TreeCursor::constructor; - -void TreeCursor::Init(v8::Local exports) { - Local tpl = Nan::New(New); - Local class_name = Nan::New("TreeCursor").ToLocalChecked(); - tpl->SetClassName(class_name); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - - GetterPair getters[] = { - {"startIndex", StartIndex}, - {"endIndex", EndIndex}, - {"nodeType", NodeType}, - {"nodeIsNamed", NodeIsNamed}, - {"currentFieldName", CurrentFieldName}, - }; - - FunctionPair methods[] = { - {"startPosition", StartPosition}, - {"endPosition", EndPosition}, - {"gotoParent", GotoParent}, - {"gotoFirstChild", GotoFirstChild}, - {"gotoFirstChildForIndex", GotoFirstChildForIndex}, - {"gotoNextSibling", GotoNextSibling}, - {"currentNode", CurrentNode}, - {"reset", Reset}, - }; - - for (size_t i = 0; i < length_of_array(getters); i++) { - Nan::SetAccessor( - tpl->InstanceTemplate(), - Nan::New(getters[i].name).ToLocalChecked(), - getters[i].callback); +using namespace Napi; + +class TreeCursor : public Napi::ObjectWrap { + public: + static void Init(Napi::Object &exports) { + Napi::Env env = exports.Env(); + + Function ctor = DefineClass(env, "TreeCursor", { + InstanceAccessor("startIndex", &TreeCursor::StartIndex, nullptr), + InstanceAccessor("endIndex", &TreeCursor::EndIndex, nullptr), + InstanceAccessor("nodeType", &TreeCursor::NodeType, nullptr), + InstanceAccessor("nodeIsNamed", &TreeCursor::NodeIsNamed, nullptr), + InstanceAccessor("currentFieldName", &TreeCursor::CurrentFieldName, nullptr), + + InstanceMethod("startPosition", &TreeCursor::StartPosition, napi_configurable), + InstanceMethod("endPosition", &TreeCursor::EndPosition, napi_configurable), + InstanceMethod("gotoParent", &TreeCursor::GotoParent), + InstanceMethod("gotoFirstChild", &TreeCursor::GotoFirstChild), + InstanceMethod("gotoFirstChildForIndex", &TreeCursor::GotoFirstChildForIndex), + InstanceMethod("gotoNextSibling", &TreeCursor::GotoNextSibling), + InstanceMethod("currentNode", &TreeCursor::CurrentNode, napi_configurable), + InstanceMethod("reset", &TreeCursor::Reset), + }); + + constructor.Reset(ctor, 1); + exports.Set("TreeCursor", ctor); } - for (size_t i = 0; i < length_of_array(methods); i++) { - Nan::SetPrototypeMethod(tpl, methods[i].name, methods[i].callback); - } + TreeCursor(const CallbackInfo &info) + : Napi::ObjectWrap(info), + cursor_({0, 0, {0, 0}}) + {} - Local constructor_local = Nan::GetFunction(tpl).ToLocalChecked(); - Nan::Set(exports, class_name, constructor_local); - constructor.Reset(Nan::Persistent(constructor_local)); -} + ~TreeCursor() { ts_tree_cursor_delete(&cursor_); } -Local TreeCursor::NewInstance(TSTreeCursor cursor) { - Local self; - MaybeLocal maybe_self = Nan::New(constructor)->NewInstance(Nan::GetCurrentContext()); - if (maybe_self.ToLocal(&self)) { - (new TreeCursor(cursor))->Wrap(self); - return self; - } else { - return Nan::Null(); + Napi::Value GotoParent(const CallbackInfo &info) { + auto env = info.Env(); + bool result = ts_tree_cursor_goto_parent(&cursor_); + return Boolean::New(env, result); } -} - -TreeCursor::TreeCursor(TSTreeCursor cursor) : cursor_(cursor) {} -TreeCursor::~TreeCursor() { ts_tree_cursor_delete(&cursor_); } - -void TreeCursor::New(const Nan::FunctionCallbackInfo &info) { - info.GetReturnValue().Set(Nan::Null()); -} + Napi::Value GotoFirstChild(const CallbackInfo &info) { + auto env = info.Env(); + bool result = ts_tree_cursor_goto_first_child(&cursor_); + return Boolean::New(env, result); + } -void TreeCursor::GotoParent(const Nan::FunctionCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - bool result = ts_tree_cursor_goto_parent(&cursor->cursor_); - info.GetReturnValue().Set(Nan::New(result)); -} + Napi::Value GotoFirstChildForIndex(const CallbackInfo &info) { + auto env = info.Env(); + auto js_index = info[0].As(); + if (!js_index.IsNumber()) { + TypeError::New(env, "Argument must be an integer").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + uint32_t goal_byte = js_index.Uint32Value() * 2; + int64_t child_index = ts_tree_cursor_goto_first_child_for_byte(&cursor_, goal_byte); + if (child_index < 0) { + return env.Null(); + } else { + return Number::New(env, child_index); + } + } -void TreeCursor::GotoFirstChild(const Nan::FunctionCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - bool result = ts_tree_cursor_goto_first_child(&cursor->cursor_); - info.GetReturnValue().Set(Nan::New(result)); -} + Napi::Value GotoNextSibling(const CallbackInfo &info) { + auto env = info.Env(); + bool result = ts_tree_cursor_goto_next_sibling(&cursor_); + return Boolean::New(env, result); + } -void TreeCursor::GotoFirstChildForIndex(const Nan::FunctionCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - auto maybe_index = Nan::To(info[0]); - if (maybe_index.IsNothing()) { - Nan::ThrowTypeError("Argument must be an integer"); - return; + Napi::Value StartPosition(const CallbackInfo &info) { + auto env = info.Env(); + TSNode node = ts_tree_cursor_current_node(&cursor_); + TransferPoint(ts_node_start_point(node)); + return env.Undefined(); } - uint32_t goal_byte = maybe_index.FromJust() * 2; - int64_t child_index = ts_tree_cursor_goto_first_child_for_byte(&cursor->cursor_, goal_byte); - if (child_index < 0) { - info.GetReturnValue().Set(Nan::Null()); - } else { - info.GetReturnValue().Set(Nan::New(static_cast(child_index))); + + Napi::Value EndPosition(const CallbackInfo &info) { + auto env = info.Env(); + TSNode node = ts_tree_cursor_current_node(&cursor_); + TransferPoint(ts_node_end_point(node)); + return env.Undefined(); } -} -void TreeCursor::GotoNextSibling(const Nan::FunctionCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - bool result = ts_tree_cursor_goto_next_sibling(&cursor->cursor_); - info.GetReturnValue().Set(Nan::New(result)); -} + Napi::Value CurrentNode(const CallbackInfo &info) { + auto env = info.Env(); + Napi::Value js_tree = info.This().As()["tree"]; + const Tree *tree = Tree::UnwrapTree(js_tree.As()); + TSNode node = ts_tree_cursor_current_node(&cursor_); + return MarshalNode(env, tree, node); + } -void TreeCursor::StartPosition(const Nan::FunctionCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - TSNode node = ts_tree_cursor_current_node(&cursor->cursor_); - TransferPoint(ts_node_start_point(node)); -} + Napi::Value Reset(const CallbackInfo &info) { + auto env = info.Env(); + Napi::Value js_tree = info.This().As()["tree"]; + const Tree *tree = Tree::UnwrapTree(js_tree.As()); + TSNode node = UnmarshalNode(env, tree); + ts_tree_cursor_reset(&cursor_, node); + return env.Undefined(); + } -void TreeCursor::EndPosition(const Nan::FunctionCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - TSNode node = ts_tree_cursor_current_node(&cursor->cursor_); - TransferPoint(ts_node_end_point(node)); -} + Napi::Value NodeType(const CallbackInfo &info) { + auto env = info.Env(); + TSNode node = ts_tree_cursor_current_node(&cursor_); + return String::New(env, ts_node_type(node)); + } -void TreeCursor::CurrentNode(const Nan::FunctionCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - Local key = Nan::New("tree").ToLocalChecked(); - const Tree *tree = Tree::UnwrapTree(Nan::Get(info.This(), key).ToLocalChecked()); - TSNode node = ts_tree_cursor_current_node(&cursor->cursor_); - node_methods::MarshalNode(info, tree, node); -} + Napi::Value NodeIsNamed(const CallbackInfo &info) { + auto env = info.Env(); + TSNode node = ts_tree_cursor_current_node(&cursor_); + return Boolean::New(env, ts_node_is_named(node)); + } -void TreeCursor::Reset(const Nan::FunctionCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - Local key = Nan::New("tree").ToLocalChecked(); - const Tree *tree = Tree::UnwrapTree(Nan::Get(info.This(), key).ToLocalChecked()); - TSNode node = node_methods::UnmarshalNode(tree); - ts_tree_cursor_reset(&cursor->cursor_, node); -} + Napi::Value CurrentFieldName(const CallbackInfo &info) { + auto env = info.Env(); + const char *field_name = ts_tree_cursor_current_field_name(&cursor_); + if (field_name) { + return String::New(env, field_name); + } else { + return env.Undefined(); + } + } -void TreeCursor::NodeType(v8::Local prop, const Nan::PropertyCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - TSNode node = ts_tree_cursor_current_node(&cursor->cursor_); - info.GetReturnValue().Set(Nan::New(ts_node_type(node)).ToLocalChecked()); -} + Napi::Value StartIndex(const CallbackInfo &info) { + auto env = info.Env(); + TSNode node = ts_tree_cursor_current_node(&cursor_); + return ByteCountToJS(env, ts_node_start_byte(node)); + } -void TreeCursor::NodeIsNamed(v8::Local prop, const Nan::PropertyCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - TSNode node = ts_tree_cursor_current_node(&cursor->cursor_); + Napi::Value EndIndex(const CallbackInfo &info) { + auto env = info.Env(); + TSNode node = ts_tree_cursor_current_node(&cursor_); + return ByteCountToJS(env, ts_node_end_byte(node)); + } - info.GetReturnValue().Set(Nan::New(ts_node_is_named(node))); -} + TSTreeCursor cursor_; + static Napi::FunctionReference constructor; +}; -void TreeCursor::CurrentFieldName(v8::Local prop, const Nan::PropertyCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - const char *field_name = ts_tree_cursor_current_field_name(&cursor->cursor_); - if (field_name) { - info.GetReturnValue().Set(Nan::New(field_name).ToLocalChecked()); - } +void InitTreeCursor(Object &exports) { + TreeCursor::Init(exports); } -void TreeCursor::StartIndex(v8::Local prop, const Nan::PropertyCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - TSNode node = ts_tree_cursor_current_node(&cursor->cursor_); - info.GetReturnValue().Set(ByteCountToJS(ts_node_start_byte(node))); +Napi::Value NewTreeCursor(TSTreeCursor cursor) { + Object js_cursor = TreeCursor::constructor.Value().New({}); + TreeCursor::Unwrap(js_cursor)->cursor_ = cursor; + return js_cursor; } -void TreeCursor::EndIndex(v8::Local prop, const Nan::PropertyCallbackInfo &info) { - TreeCursor *cursor = Nan::ObjectWrap::Unwrap(info.This()); - TSNode node = ts_tree_cursor_current_node(&cursor->cursor_); - info.GetReturnValue().Set(ByteCountToJS(ts_node_end_byte(node))); -} +FunctionReference TreeCursor::constructor; -} +} // namespace node_tree_sitter diff --git a/src/tree_cursor.h b/src/tree_cursor.h index cd54e4bb..1bff0660 100644 --- a/src/tree_cursor.h +++ b/src/tree_cursor.h @@ -1,42 +1,13 @@ #ifndef NODE_TREE_SITTER_TREE_CURSOR_H_ #define NODE_TREE_SITTER_TREE_CURSOR_H_ -#include -#include -#include +#include #include namespace node_tree_sitter { -class TreeCursor : public Nan::ObjectWrap { - public: - static void Init(v8::Local exports); - static v8::Local NewInstance(TSTreeCursor); - - private: - explicit TreeCursor(TSTreeCursor); - ~TreeCursor(); - - static void New(const Nan::FunctionCallbackInfo &); - static void GotoParent(const Nan::FunctionCallbackInfo &); - static void GotoFirstChild(const Nan::FunctionCallbackInfo &); - static void GotoFirstChildForIndex(const Nan::FunctionCallbackInfo &); - static void GotoNextSibling(const Nan::FunctionCallbackInfo &); - static void StartPosition(const Nan::FunctionCallbackInfo &); - static void EndPosition(const Nan::FunctionCallbackInfo &); - static void CurrentNode(const Nan::FunctionCallbackInfo &); - static void Reset(const Nan::FunctionCallbackInfo &); - - static void NodeType(v8::Local, const Nan::PropertyCallbackInfo &); - static void NodeIsNamed(v8::Local, const Nan::PropertyCallbackInfo &); - static void CurrentFieldName(v8::Local, const Nan::PropertyCallbackInfo &); - static void StartIndex(v8::Local, const Nan::PropertyCallbackInfo &); - static void EndIndex(v8::Local, const Nan::PropertyCallbackInfo &); - - TSTreeCursor cursor_; - static Nan::Persistent constructor; - static Nan::Persistent constructor_template; -}; +void InitTreeCursor(Napi::Object &); +Napi::Value NewTreeCursor(TSTreeCursor); } // namespace node_tree_sitter diff --git a/src/util.h b/src/util.h index eaa7cd99..1d78b06b 100644 --- a/src/util.h +++ b/src/util.h @@ -1,22 +1,54 @@ #ifndef NODE_TREE_SITTER_UTIL_H_ #define NODE_TREE_SITTER_UTIL_H_ +#include #include -#include namespace node_tree_sitter { -#define length_of_array(a) (sizeof(a) / sizeof(a[0])) - -struct GetterPair { - const char *name; - Nan::GetterCallback callback; -}; - -struct FunctionPair { - const char *name; - Nan::FunctionCallback callback; -}; +static inline const void *UnmarshalPointer(const uint32_t *buffer) { + const void *result; + memcpy(&result, buffer, sizeof(result)); + return result; +} + +static inline void MarshalPointer(const void *id, uint32_t *buffer) { + memset(buffer, 0, sizeof(uint64_t)); + memcpy(buffer, &id, sizeof(id)); +} + +//========================================================================= +// This library must be able to load parsers that were generated +// using older versions of Tree-sitter, which did not use `napi`. +// So we need to use the V8 APIs directly here. +// +// The following assertion and function were taken from Node's `napi` implementation: +// github.com/nodejs/node/blob/53ca0b9ae145c430842bf78e553e3b6cbd2823aa/src/js_native_api_v8.h + +static_assert( + sizeof(v8::Local) == sizeof(napi_value), + "Cannot convert between v8::Local and napi_value" +); + +static inline v8::Local V8LocalValueFromJsValue(napi_value v) { + v8::Local local; + memcpy(static_cast(&local), &v, sizeof(v)); + return local; +} + +//========================================================================= + +static inline void *GetInternalFieldPointer(Napi::Value value) { + if (value.IsObject()) { + v8::Local object = v8::Local::Cast( + V8LocalValueFromJsValue(value) + ); + if (object->InternalFieldCount() == 1) { + return object->GetAlignedPointerFromInternalField(0); + } + } + return nullptr; +} bool instance_of(v8::Local value, v8::Local object); diff --git a/test/parser_test.js b/test/parser_test.js index a1f6d179..b45f1dfa 100644 --- a/test/parser_test.js +++ b/test/parser_test.js @@ -178,7 +178,9 @@ describe("Parser", () => { const sourceCode = "[" + "0,".repeat(elementCount) + "]"; const buffer = new TextBuffer(sourceCode) - const tree = await parser.parseTextBuffer(buffer); + const promise = parser.parseTextBuffer(buffer); + assert.equal(promise.constructor, Promise); + const tree = await promise; const arrayNode = tree.rootNode.firstChild.firstChild; assert.equal(arrayNode.type, "array"); assert.equal(arrayNode.namedChildCount, elementCount); diff --git a/test/tree_test.js b/test/tree_test.js index fe4729ad..660c4d3b 100644 --- a/test/tree_test.js +++ b/test/tree_test.js @@ -147,7 +147,7 @@ describe("Tree", () => { assert.throws(() => { tree1.getChangedRanges({}); - }, /Argument must be a tree/); + }, /Invalid argument/); }) }); From d219bbe3f3e324c96411fb0252308404f912cece Mon Sep 17 00:00:00 2001 From: Michael Belousov Date: Sun, 6 Mar 2022 00:42:02 -0500 Subject: [PATCH 34/42] first pass at converting query from nan to napi --- src/binding.cc | 2 +- src/query.cc | 243 ++++++++++++++++++------------------------------- src/query.h | 26 +++--- src/tree.cc | 2 +- 4 files changed, 103 insertions(+), 170 deletions(-) diff --git a/src/binding.cc b/src/binding.cc index 2f88ff18..ee05f659 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -17,7 +17,7 @@ Object Init(Env env, Object exports) { InitLanguage(exports); InitParser(exports); InitTreeCursor(exports); - InitQuery(exports); + Query::Init(exports); Tree::Init(exports); return exports; } diff --git a/src/query.cc b/src/query.cc index 2c722b11..a79e020b 100644 --- a/src/query.cc +++ b/src/query.cc @@ -1,8 +1,7 @@ #include "./query.h" #include #include -#include -#include +#include #include "./node.h" #include "./language.h" #include "./logger.h" @@ -12,7 +11,7 @@ namespace node_tree_sitter { using std::vector; -using namespace v8; +using namespace Napi; using node_methods::UnmarshalNodeId; const char *query_error_names[] = { @@ -25,97 +24,69 @@ const char *query_error_names[] = { }; TSQueryCursor *Query::ts_query_cursor; -Nan::Persistent Query::constructor; -Nan::Persistent Query::constructor_template; - -void Query::Init(Local exports) { - ts_query_cursor = ts_query_cursor_new(); - - Local tpl = Nan::New(New); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - Local class_name = Nan::New("Query").ToLocalChecked(); - tpl->SetClassName(class_name); - - FunctionPair methods[] = { - {"_matches", Matches}, - {"_captures", Captures}, - {"_getPredicates", GetPredicates}, - }; - - for (size_t i = 0; i < length_of_array(methods); i++) { - Nan::SetPrototypeMethod(tpl, methods[i].name, methods[i].callback); - } - - Local ctor = Nan::GetFunction(tpl).ToLocalChecked(); - - constructor_template.Reset(tpl); - constructor.Reset(ctor); - Nan::Set(exports, class_name, ctor); +Napi::FunctionReference Query::constructor; + +void Query::Init(Napi::Object &exports) { + Napi::Env env = exports.Env(); + Function ctor = DefineClass(env, "Query", { + InstanceMethod("matches", &Query::Matches), + InstanceMethod("captures", &Query::Captures), + InstanceMethod("getPredicates", &Query::GetPredicates), + }); + + constructor.Reset(ctor, 1); + exports["Query"] = ctor; } -Query::Query(TSQuery *query) : query_(query) {} - Query::~Query() { ts_query_delete(query_); } -Local Query::NewInstance(TSQuery *query) { +/* +Napi::Value Query::NewInstance(Napi::Env env, TSQuery *query) { if (query) { - Local self; - MaybeLocal maybe_self = Nan::NewInstance(Nan::New(constructor)); - if (maybe_self.ToLocal(&self)) { - (new Query(query))->Wrap(self); - return self; - } + Object js_query = constructor.Value().New({}); + Query::Unwrap(js_query)->query_ = query; + return js_query; } - return Nan::Null(); + return env.Null(); } +*/ -Query *Query::UnwrapQuery(const Local &value) { - if (!value->IsObject()) return nullptr; - Local js_query = Local::Cast(value); - if (!Nan::New(constructor_template)->HasInstance(js_query)) return nullptr; - return ObjectWrap::Unwrap(js_query); +Query *Query::UnwrapQuery(const Napi::Value &value) { + return Query::Unwrap(value.As()); } -void Query::New(const Nan::FunctionCallbackInfo &info) { - if (!info.IsConstructCall()) { - Local self; - MaybeLocal maybe_self = Nan::New(constructor)->NewInstance(Nan::GetCurrentContext()); - if (maybe_self.ToLocal(&self)) { - info.GetReturnValue().Set(self); - } else { - info.GetReturnValue().Set(Nan::Null()); - } - return; - } +Query::Query(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , query_(nullptr) { +//Napi::Value Query::New(const Napi::CallbackInfo &info) { const TSLanguage *language = language_methods::UnwrapLanguage(info[0]); const char *source; uint32_t source_len; uint32_t error_offset = 0; TSQueryError error_type = TSQueryErrorNone; - TSQuery *query; if (language == nullptr) { - Nan::ThrowError("Missing language argument"); + Napi::TypeError::New(info.Env(), "Missing language argument").ThrowAsJavaScriptException(); return; } - if (info[1]->IsString()) { - auto string = Nan::To (info[1]).ToLocalChecked(); - Nan::Utf8String utf8_string(string); - source = *utf8_string; - source_len = utf8_string.length(); - query = ts_query_new(language, source, source_len, &error_offset, &error_type); + if (info[1].IsString()) { + auto&& utf8_string = info[1].As().Utf8Value(); + source = utf8_string.data(); + source_len = utf8_string.size(); + query_ = ts_query_new(language, source, source_len, &error_offset, &error_type); } - else if (node::Buffer::HasInstance(info[1])) { - source = node::Buffer::Data(info[1]); - source_len = node::Buffer::Length(info[1]); - query = ts_query_new(language, source, source_len, &error_offset, &error_type); + else if (info[1].IsBuffer()) { + auto&& buffer = info[1].As>(); + source = buffer.Data(); + source_len = buffer.Length(); + query_ = ts_query_new(language, source, source_len, &error_offset, &error_type); } else { - Nan::ThrowError("Missing source argument"); + Napi::TypeError::New(info.Env(), "Missing source argument").ThrowAsJavaScriptException(); return; } @@ -125,41 +96,27 @@ void Query::New(const Nan::FunctionCallbackInfo &info) { message += error_name; message += " at position "; message += std::to_string(error_offset); - Nan::ThrowError(message.c_str()); + Napi::TypeError::New(info.Env(),message.c_str()).ThrowAsJavaScriptException(); return; } - auto self = info.This(); - - Query *query_wrapper = new Query(query); - query_wrapper->Wrap(self); - - auto init = - Nan::To( - Nan::Get(self, Nan::New("_init").ToLocalChecked()).ToLocalChecked() - ).ToLocalChecked(); - Nan::Call(init, self, 0, nullptr); - - info.GetReturnValue().Set(self); + info.This().As().Get("_init").As().Call({}); } -void Query::GetPredicates(const Nan::FunctionCallbackInfo &info) { - Query *query = Query::UnwrapQuery(info.This()); - auto ts_query = query->query_; +Napi::Value Query::GetPredicates(const Napi::CallbackInfo &info) { + auto pattern_len = ts_query_pattern_count(query_); - auto pattern_len = ts_query_pattern_count(ts_query); - - Local js_predicates = Nan::New(); + Napi::Array js_predicates = Napi::Array::New(info.Env()); for (size_t pattern_index = 0; pattern_index < pattern_len; pattern_index++) { uint32_t predicates_len; const TSQueryPredicateStep *predicates = ts_query_predicates_for_pattern( - ts_query, pattern_index, &predicates_len); + query_, pattern_index, &predicates_len); - Local js_pattern_predicates = Nan::New(); + Napi::Array js_pattern_predicates = Napi::Array::New(info.Env()); if (predicates_len > 0) { - Local js_predicate = Nan::New(); + Napi::Array js_predicate = Napi::Array::New(info.Env()); size_t a_index = 0; size_t p_index = 0; @@ -168,116 +125,96 @@ void Query::GetPredicates(const Nan::FunctionCallbackInfo &info) { uint32_t len; switch (predicate.type) { case TSQueryPredicateStepTypeCapture: - Nan::Set(js_predicate, p_index++, Nan::New(TSQueryPredicateStepTypeCapture)); - Nan::Set(js_predicate, p_index++, - Nan::New( - ts_query_capture_name_for_id(ts_query, predicate.value_id, &len) - ).ToLocalChecked()); + js_predicate[p_index++] = Napi::Number::New(info.Env(), TSQueryPredicateStepTypeCapture); + js_predicate[p_index++] = Napi::String::New(info.Env(), ts_query_capture_name_for_id(query_, predicate.value_id, &len)); break; case TSQueryPredicateStepTypeString: - Nan::Set(js_predicate, p_index++, Nan::New(TSQueryPredicateStepTypeString)); - Nan::Set(js_predicate, p_index++, - Nan::New( - ts_query_string_value_for_id(ts_query, predicate.value_id, &len) - ).ToLocalChecked()); + js_predicate[p_index++] = Napi::Number::New(info.Env(), TSQueryPredicateStepTypeString); + js_predicate[p_index++] = Napi::String::New(info.Env(), ts_query_string_value_for_id(query_, predicate.value_id, &len)); break; case TSQueryPredicateStepTypeDone: - Nan::Set(js_pattern_predicates, a_index++, js_predicate); - js_predicate = Nan::New(); + js_pattern_predicates[a_index++] = js_predicate; + js_predicate = Napi::Array::New(info.Env()); p_index = 0; break; } } } - Nan::Set(js_predicates, pattern_index, js_pattern_predicates); + js_predicates[pattern_index] = js_pattern_predicates; } - info.GetReturnValue().Set(js_predicates); + return js_predicates; } -void Query::Matches(const Nan::FunctionCallbackInfo &info) { - Query *query = Query::UnwrapQuery(info.This()); +Napi::Value Query::Matches(const Napi::CallbackInfo &info) { const Tree *tree = Tree::UnwrapTree(info[0]); - uint32_t start_row = Nan::To(info[1]).ToChecked(); - uint32_t start_column = Nan::To(info[2]).ToChecked() << 1; - uint32_t end_row = Nan::To(info[3]).ToChecked(); - uint32_t end_column = Nan::To(info[4]).ToChecked() << 1; - - if (query == nullptr) { - Nan::ThrowError("Missing argument query"); - return; - } + uint32_t start_row = info[1].As().Uint32Value(); + uint32_t start_column = info[2].As().Uint32Value() << 1; + uint32_t end_row = info[3].As().Uint32Value(); + uint32_t end_column = info[4].As().Uint32Value() << 1; if (tree == nullptr) { - Nan::ThrowError("Missing argument tree"); - return; + Napi::TypeError::New(info.Env(), "Missing argument tree").ThrowAsJavaScriptException(); + return Env().Null(); } - TSQuery *ts_query = query->query_; TSNode rootNode = node_methods::UnmarshalNode(tree); TSPoint start_point = {start_row, start_column}; TSPoint end_point = {end_row, end_column}; ts_query_cursor_set_point_range(ts_query_cursor, start_point, end_point); - ts_query_cursor_exec(ts_query_cursor, ts_query, rootNode); + ts_query_cursor_exec(ts_query_cursor, query_, rootNode); - Local js_matches = Nan::New(); + Napi::Array js_matches = Napi::Array::New(info.Env()); unsigned index = 0; vector nodes; TSQueryMatch match; while (ts_query_cursor_next_match(ts_query_cursor, &match)) { - Nan::Set(js_matches, index++, Nan::New(match.pattern_index)); + js_matches[index++] = Napi::Number::New(info.Env(), match.pattern_index); for (uint16_t i = 0; i < match.capture_count; i++) { const TSQueryCapture &capture = match.captures[i]; uint32_t capture_name_len = 0; const char *capture_name = ts_query_capture_name_for_id( - ts_query, capture.index, &capture_name_len); + query_, capture.index, &capture_name_len); TSNode node = capture.node; nodes.push_back(node); - Local js_capture = Nan::New(capture_name).ToLocalChecked(); - Nan::Set(js_matches, index++, js_capture); + Napi::Value js_capture = Napi::String::New(info.Env(), capture_name); + js_matches[index++] = js_capture; } } auto js_nodes = node_methods::GetMarshalNodes(info, tree, nodes.data(), nodes.size()); - auto result = Nan::New(); - Nan::Set(result, 0, js_matches); - Nan::Set(result, 1, js_nodes); - info.GetReturnValue().Set(result); + auto result = Napi::Array::New(info.Env()); + result[0u] = js_matches; + result[1u] = js_nodes; + return result; } -void Query::Captures(const Nan::FunctionCallbackInfo &info) { - Query *query = Query::UnwrapQuery(info.This()); +Napi::Value Query::Captures(const Napi::CallbackInfo &info) { const Tree *tree = Tree::UnwrapTree(info[0]); - uint32_t start_row = Nan::To(info[1]).ToChecked(); - uint32_t start_column = Nan::To(info[2]).ToChecked() << 1; - uint32_t end_row = Nan::To(info[3]).ToChecked(); - uint32_t end_column = Nan::To(info[4]).ToChecked() << 1; - - if (query == nullptr) { - Nan::ThrowError("Missing argument query"); - return; - } + uint32_t start_row = info[1].As().Uint32Value(); + uint32_t start_column = info[2].As().Uint32Value() << 1; + uint32_t end_row = info[3].As().Uint32Value(); + uint32_t end_column = info[4].As().Uint32Value() << 1; if (tree == nullptr) { - Nan::ThrowError("Missing argument tree"); - return; + Napi::TypeError::New(info.Env(), "Missing argument tree").ThrowAsJavaScriptException(); + return info.Env().Null(); } - TSQuery *ts_query = query->query_; TSNode rootNode = node_methods::UnmarshalNode(tree); TSPoint start_point = {start_row, start_column}; TSPoint end_point = {end_row, end_column}; ts_query_cursor_set_point_range(ts_query_cursor, start_point, end_point); - ts_query_cursor_exec(ts_query_cursor, ts_query, rootNode); + ts_query_cursor_exec(ts_query_cursor, query_, rootNode); - Local js_matches = Nan::New(); + Napi::Array js_matches = Napi::Array::New(info.Env()); unsigned index = 0; vector nodes; TSQueryMatch match; @@ -289,30 +226,30 @@ void Query::Captures(const Nan::FunctionCallbackInfo &info) { &capture_index )) { - Nan::Set(js_matches, index++, Nan::New(match.pattern_index)); - Nan::Set(js_matches, index++, Nan::New(capture_index)); + js_matches[index++] = Napi::Number::New(info.Env(), match.pattern_index); + js_matches[index++] = Napi::Number::New(info.Env(), capture_index); for (uint16_t i = 0; i < match.capture_count; i++) { const TSQueryCapture &capture = match.captures[i]; uint32_t capture_name_len = 0; const char *capture_name = ts_query_capture_name_for_id( - ts_query, capture.index, &capture_name_len); + query_, capture.index, &capture_name_len); TSNode node = capture.node; nodes.push_back(node); - Local js_capture = Nan::New(capture_name).ToLocalChecked(); - Nan::Set(js_matches, index++, js_capture); + Napi::String js_capture = Napi::String::New(info.Env(), capture_name); + js_matches[index++] = js_capture; } } auto js_nodes = node_methods::GetMarshalNodes(info, tree, nodes.data(), nodes.size()); - auto result = Nan::New(); - Nan::Set(result, 0, js_matches); - Nan::Set(result, 1, js_nodes); - info.GetReturnValue().Set(result); + auto result = Napi::Array::New(info.Env()); + result[0u] = js_matches; + result[1u] = js_nodes; + return result; } diff --git a/src/query.h b/src/query.h index 955e621f..f97be129 100644 --- a/src/query.h +++ b/src/query.h @@ -1,34 +1,30 @@ #ifndef NODE_TREE_SITTER_QUERY_H_ #define NODE_TREE_SITTER_QUERY_H_ -#include -#include -#include -#include +#include #include namespace node_tree_sitter { -class Query : public Nan::ObjectWrap { +class Query : public Napi::ObjectWrap { public: - static void Init(v8::Local exports); - static v8::Local NewInstance(TSQuery *); - static Query *UnwrapQuery(const v8::Local &); + static void Init(Napi::Object &); + //static Napi::Value NewInstance(Napi::Env, TSQuery *); + static Query *UnwrapQuery(const Napi::Value &); TSQuery *query_; private: - explicit Query(TSQuery *); + explicit Query(const Napi::CallbackInfo &); ~Query(); - static void New(const Nan::FunctionCallbackInfo &); - static void Matches(const Nan::FunctionCallbackInfo &); - static void Captures(const Nan::FunctionCallbackInfo &); - static void GetPredicates(const Nan::FunctionCallbackInfo &); + //static Napi::Value New(const Napi::CallbackInfo &); + Napi::Value Matches(const Napi::CallbackInfo &); + Napi::Value Captures(const Napi::CallbackInfo &); + Napi::Value GetPredicates(const Napi::CallbackInfo &); static TSQueryCursor *ts_query_cursor; - static Nan::Persistent constructor; - static Nan::Persistent constructor_template; + static Napi::FunctionReference constructor; }; } // namespace node_tree_sitter diff --git a/src/tree.cc b/src/tree.cc index 40bccef9..d1f5b378 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -1,6 +1,6 @@ #include "./tree.h" #include -#include +#include #include "./node.h" #include "./logger.h" #include "./util.h" From a98f61ba35991988bf79eec97d5cda270dceb35a Mon Sep 17 00:00:00 2001 From: Michael Belousov Date: Sun, 6 Mar 2022 01:09:28 -0500 Subject: [PATCH 35/42] repair rebase and finish first draft of query api conversion to napi --- src/node.cc | 7 +++++-- src/node.h | 2 ++ src/query.cc | 12 +++++------- src/query.h | 3 +-- src/tree.cc | 6 +++--- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/node.cc b/src/node.cc index d5205ce4..f95af9a1 100644 --- a/src/node.cc +++ b/src/node.cc @@ -48,7 +48,7 @@ static inline bool operator<=(const TSPoint &left, const TSPoint &right) { } -static Value MarshalNodes( +Value GetMarshalNodes( Env env, const Tree *tree, const TSNode *nodes, @@ -79,7 +79,10 @@ static Value MarshalNodes( return result; } -Value MarshalNode( +Value MarshalNode(Env env, const Tree *tree, TSNode node) { return GetMarshalNode(env, tree, node); } +Value MarshalNodes(Env env, const Tree *tree, const TSNode *nodes, uint32_t node_count) { return GetMarshalNodes(env, tree, nodes, node_count); } + +Value GetMarshalNode( Env env, const Tree *tree, TSNode node diff --git a/src/node.h b/src/node.h index 1bb95859..09f715b1 100644 --- a/src/node.h +++ b/src/node.h @@ -10,6 +10,8 @@ namespace node_tree_sitter { void InitNode(Napi::Object &exports); Napi::Value MarshalNode(Napi::Env, const Tree *, TSNode); +Napi::Value GetMarshalNode(Napi::Env env, const Tree*, TSNode); +Napi::Value GetMarshalNodes(Napi::Env env, const Tree*, const TSNode*, uint32_t); TSNode UnmarshalNode(Napi::Env env, const Tree *tree); } // namespace node_tree_sitter diff --git a/src/query.cc b/src/query.cc index a79e020b..ffbbff9a 100644 --- a/src/query.cc +++ b/src/query.cc @@ -12,7 +12,6 @@ namespace node_tree_sitter { using std::vector; using namespace Napi; -using node_methods::UnmarshalNodeId; const char *query_error_names[] = { "TSQueryErrorNone", @@ -61,8 +60,7 @@ Query::Query(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) , query_(nullptr) { -//Napi::Value Query::New(const Napi::CallbackInfo &info) { - const TSLanguage *language = language_methods::UnwrapLanguage(info[0]); + const TSLanguage *language = UnwrapLanguage(info[0]); const char *source; uint32_t source_len; uint32_t error_offset = 0; @@ -159,7 +157,7 @@ Napi::Value Query::Matches(const Napi::CallbackInfo &info) { return Env().Null(); } - TSNode rootNode = node_methods::UnmarshalNode(tree); + TSNode rootNode = UnmarshalNode(info.Env(), tree); TSPoint start_point = {start_row, start_column}; TSPoint end_point = {end_row, end_column}; ts_query_cursor_set_point_range(ts_query_cursor, start_point, end_point); @@ -188,7 +186,7 @@ Napi::Value Query::Matches(const Napi::CallbackInfo &info) { } } - auto js_nodes = node_methods::GetMarshalNodes(info, tree, nodes.data(), nodes.size()); + auto js_nodes = GetMarshalNodes(info.Env(), tree, nodes.data(), nodes.size()); auto result = Napi::Array::New(info.Env()); result[0u] = js_matches; @@ -208,7 +206,7 @@ Napi::Value Query::Captures(const Napi::CallbackInfo &info) { return info.Env().Null(); } - TSNode rootNode = node_methods::UnmarshalNode(tree); + TSNode rootNode = UnmarshalNode(info.Env(), tree); TSPoint start_point = {start_row, start_column}; TSPoint end_point = {end_row, end_column}; ts_query_cursor_set_point_range(ts_query_cursor, start_point, end_point); @@ -244,7 +242,7 @@ Napi::Value Query::Captures(const Napi::CallbackInfo &info) { } } - auto js_nodes = node_methods::GetMarshalNodes(info, tree, nodes.data(), nodes.size()); + auto js_nodes = GetMarshalNodes(info.Env(), tree, nodes.data(), nodes.size()); auto result = Napi::Array::New(info.Env()); result[0u] = js_matches; diff --git a/src/query.h b/src/query.h index f97be129..73a66f11 100644 --- a/src/query.h +++ b/src/query.h @@ -14,11 +14,10 @@ class Query : public Napi::ObjectWrap { TSQuery *query_; - private: explicit Query(const Napi::CallbackInfo &); ~Query(); - //static Napi::Value New(const Napi::CallbackInfo &); + private: Napi::Value Matches(const Napi::CallbackInfo &); Napi::Value Captures(const Napi::CallbackInfo &); Napi::Value GetPredicates(const Napi::CallbackInfo &); diff --git a/src/tree.cc b/src/tree.cc index d1f5b378..d0bedc75 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -174,10 +174,10 @@ Napi::Value Tree::PrintDotGraph(const CallbackInfo &info) { } static void FinalizeNode(Env env, Tree::NodeCacheEntry *cache_entry) { - assert(!cache_entry->node.IsEmpty()); + //assert(!cache_entry->node.IsEmpty()); cache_entry->node.Reset(); if (cache_entry->tree) { - assert(cache_entry->tree->cached_nodes_.count(cache_entry->key)); + //assert(cache_entry->tree->cached_nodes_.count(cache_entry->key)); cache_entry->tree->cached_nodes_.erase(cache_entry->key); } delete cache_entry; @@ -202,7 +202,7 @@ Napi::Value Tree::CacheNode(const CallbackInfo &info) { cache_entry->node.Reset(js_node, 0); js_node.AddFinalizer(&FinalizeNode, cache_entry); - assert(!cached_nodes_.count(key)); + //assert(!cached_nodes_.count(key)); cached_nodes_[key] = cache_entry; return env.Undefined(); From 6881821e560491b7f95266a8d54a0e0d26b24e92 Mon Sep 17 00:00:00 2001 From: Michael Belousov Date: Sun, 6 Mar 2022 12:37:00 -0500 Subject: [PATCH 36/42] fix hidden method names that I incorrectly fixed --- src/query.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/query.cc b/src/query.cc index ffbbff9a..7158d638 100644 --- a/src/query.cc +++ b/src/query.cc @@ -28,9 +28,9 @@ Napi::FunctionReference Query::constructor; void Query::Init(Napi::Object &exports) { Napi::Env env = exports.Env(); Function ctor = DefineClass(env, "Query", { - InstanceMethod("matches", &Query::Matches), - InstanceMethod("captures", &Query::Captures), - InstanceMethod("getPredicates", &Query::GetPredicates), + InstanceMethod("_matches", &Query::Matches), + InstanceMethod("_captures", &Query::Captures), + InstanceMethod("_getPredicates", &Query::GetPredicates), }); constructor.Reset(ctor, 1); From f9d263c3e794de8222513bedf3ac3c23b693d740 Mon Sep 17 00:00:00 2001 From: Michael Belousov Date: Sun, 6 Mar 2022 13:27:37 -0500 Subject: [PATCH 37/42] add loading of napi-compiled language native addons --- src/language.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/language.cc b/src/language.cc index 208bd8d6..1871b1fd 100644 --- a/src/language.cc +++ b/src/language.cc @@ -14,9 +14,13 @@ using namespace Napi; const TSLanguage *UnwrapLanguage(const Napi::Value &value) { Env env = value.Env(); - const TSLanguage *language = static_cast( - GetInternalFieldPointer(value) - ); + const TSLanguage *language + = value.IsString() + && value.As().Has("_language") + && value.As().Get("_language").IsExternal() + ? value.As().Get("_language").As>().Data() + : static_cast(GetInternalFieldPointer(value)) + ; if (language) { uint16_t version = ts_language_version(language); From 464865029668f71d31ffac00b8c06cb715a25bc0 Mon Sep 17 00:00:00 2001 From: Michael Belousov Date: Sun, 6 Mar 2022 16:20:40 -0500 Subject: [PATCH 38/42] fix query impl rebase --- src/language.cc | 2 +- src/query.cc | 15 +++------------ src/tree.cc | 29 +++++++++++++++++++++-------- src/tree.h | 1 + 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/language.cc b/src/language.cc index 1871b1fd..d24491b5 100644 --- a/src/language.cc +++ b/src/language.cc @@ -15,7 +15,7 @@ const TSLanguage *UnwrapLanguage(const Napi::Value &value) { Env env = value.Env(); const TSLanguage *language - = value.IsString() + = value.IsObject() && value.As().Has("_language") && value.As().Get("_language").IsExternal() ? value.As().Get("_language").As>().Data() diff --git a/src/query.cc b/src/query.cc index 7158d638..4bcd446a 100644 --- a/src/query.cc +++ b/src/query.cc @@ -7,6 +7,7 @@ #include "./logger.h" #include "./util.h" #include "./conversions.h" +#include "tree_sitter/api.h" namespace node_tree_sitter { @@ -26,6 +27,7 @@ TSQueryCursor *Query::ts_query_cursor; Napi::FunctionReference Query::constructor; void Query::Init(Napi::Object &exports) { + ts_query_cursor = ts_query_cursor_new(); Napi::Env env = exports.Env(); Function ctor = DefineClass(env, "Query", { InstanceMethod("_matches", &Query::Matches), @@ -41,17 +43,6 @@ Query::~Query() { ts_query_delete(query_); } -/* -Napi::Value Query::NewInstance(Napi::Env env, TSQuery *query) { - if (query) { - Object js_query = constructor.Value().New({}); - Query::Unwrap(js_query)->query_ = query; - return js_query; - } - return env.Null(); -} -*/ - Query *Query::UnwrapQuery(const Napi::Value &value) { return Query::Unwrap(value.As()); } @@ -98,7 +89,7 @@ Query::Query(const Napi::CallbackInfo& info) return; } - info.This().As().Get("_init").As().Call({}); + info.This().As().Get("_init").As().Call(info.This(), {}); } Napi::Value Query::GetPredicates(const Napi::CallbackInfo &info) { diff --git a/src/tree.cc b/src/tree.cc index d0bedc75..90aeae87 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -22,6 +22,7 @@ void Tree::Init(Object &exports) { InstanceMethod("getChangedRanges", &Tree::GetChangedRanges), InstanceMethod("getEditedRange", &Tree::GetEditedRange), InstanceMethod("_cacheNode", &Tree::CacheNode), + InstanceMethod("_cacheNodes", &Tree::CacheNodes), }); constructor.Reset(ctor, 1); @@ -183,14 +184,11 @@ static void FinalizeNode(Env env, Tree::NodeCacheEntry *cache_entry) { delete cache_entry; } -Napi::Value Tree::CacheNode(const CallbackInfo &info) { - auto env = info.Env(); - Object js_node = info[0].As(); - +static void CacheNodeForTree(Tree *tree, Napi::Env env, Object js_node) { Napi::Value js_node_field1 = js_node[0u]; Napi::Value js_node_field2 = js_node[1u]; if (!js_node_field1.IsNumber() || !js_node_field2.IsNumber()) { - return env.Undefined(); + return; } uint32_t key_parts[2] = { js_node_field1.As().Uint32Value(), @@ -198,14 +196,29 @@ Napi::Value Tree::CacheNode(const CallbackInfo &info) { }; const void *key = UnmarshalPointer(key_parts); - auto cache_entry = new NodeCacheEntry{this, key, {}}; + auto cache_entry = new Tree::NodeCacheEntry{tree, key, {}}; cache_entry->node.Reset(js_node, 0); js_node.AddFinalizer(&FinalizeNode, cache_entry); //assert(!cached_nodes_.count(key)); - cached_nodes_[key] = cache_entry; - return env.Undefined(); + tree->cached_nodes_[key] = cache_entry; +} + +Napi::Value Tree::CacheNode(const CallbackInfo &info) { + Object js_node = info[0].As(); + CacheNodeForTree(this, info.Env(), js_node); + return info.Env().Undefined(); +} + +Napi::Value Tree::CacheNodes(const CallbackInfo &info) { + Array js_nodes = info[0].As(); + uint32_t length = js_nodes.Length(); + + for (uint32_t i = 0; i < length; i++) { + CacheNodeForTree(this, info.Env(), js_nodes.Get(i).As()); + } + return info.Env().Undefined(); } diff --git a/src/tree.h b/src/tree.h index 98e4c5eb..e177af36 100644 --- a/src/tree.h +++ b/src/tree.h @@ -33,6 +33,7 @@ class Tree : public Napi::ObjectWrap { Napi::Value GetEditedRange(const Napi::CallbackInfo &); Napi::Value GetChangedRanges(const Napi::CallbackInfo &); Napi::Value CacheNode(const Napi::CallbackInfo &); + Napi::Value CacheNodes(const Napi::CallbackInfo &); static Napi::FunctionReference constructor; }; From efa2c041dc03f3683907fdd3978df41c8eb44bc1 Mon Sep 17 00:00:00 2001 From: Michael Belousov Date: Sun, 6 Mar 2022 17:18:25 -0500 Subject: [PATCH 39/42] suppress destruct of static function refs --- src/parser.cc | 1 + src/query.cc | 1 + src/tree.cc | 1 + src/tree_cursor.cc | 1 + 4 files changed, 4 insertions(+) diff --git a/src/parser.cc b/src/parser.cc index 533f92ee..ee9d454c 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -51,6 +51,7 @@ class Parser : public ObjectWrap { assert(status == napi_ok); constructor.Reset(ctor, 1); + constructor.SuppressDestruct(); // statics should not destruct // string_slice.Reset(string_slice_fn.As(), 1); exports["Parser"] = ctor; exports["LANGUAGE_VERSION"] = Number::New(env, TREE_SITTER_LANGUAGE_VERSION); diff --git a/src/query.cc b/src/query.cc index 4bcd446a..a556a150 100644 --- a/src/query.cc +++ b/src/query.cc @@ -36,6 +36,7 @@ void Query::Init(Napi::Object &exports) { }); constructor.Reset(ctor, 1); + constructor.SuppressDestruct(); // statics should not destruct exports["Query"] = ctor; } diff --git a/src/tree.cc b/src/tree.cc index 90aeae87..4ab49cbd 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -26,6 +26,7 @@ void Tree::Init(Object &exports) { }); constructor.Reset(ctor, 1); + constructor.SuppressDestruct(); // statics should not destruct exports["Tree"] = ctor; } diff --git a/src/tree_cursor.cc b/src/tree_cursor.cc index c3f5679c..e4801b4f 100644 --- a/src/tree_cursor.cc +++ b/src/tree_cursor.cc @@ -33,6 +33,7 @@ class TreeCursor : public Napi::ObjectWrap { }); constructor.Reset(ctor, 1); + constructor.SuppressDestruct(); // statics should not destruct exports.Set("TreeCursor", ctor); } From 9115155199c31ceafabaee66019110ef31dd3865 Mon Sep 17 00:00:00 2001 From: Michael Belousov Date: Thu, 9 Mar 2023 15:41:11 -0500 Subject: [PATCH 40/42] fix passing stderr, the FILE*, to an api that expects the file descriptor 2 --- src/tree.cc | 2 +- vendor/tree-sitter | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tree.cc b/src/tree.cc index 4ab49cbd..9763b082 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -171,7 +171,7 @@ Napi::Value Tree::GetEditedRange(const CallbackInfo &info) { } Napi::Value Tree::PrintDotGraph(const CallbackInfo &info) { - ts_tree_print_dot_graph(tree_, stderr); + ts_tree_print_dot_graph(tree_, fileno(stderr)); return info.This(); } diff --git a/vendor/tree-sitter b/vendor/tree-sitter index 3d554ecf..c51896d3 160000 --- a/vendor/tree-sitter +++ b/vendor/tree-sitter @@ -1 +1 @@ -Subproject commit 3d554ecf6b68ad2c267c1e90b6ef9aa68ae88bcd +Subproject commit c51896d32dcc11a38e41f36e3deb1a6a9c4f4b14 From 6f3725994db68240963a0de2b728aef6a5212013 Mon Sep 17 00:00:00 2001 From: Michael Belousov Date: Thu, 9 Mar 2023 21:43:04 -0500 Subject: [PATCH 41/42] disable exceptions in binding.gyp and tests pass (but segfaults on close) --- binding.gyp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/binding.gyp b/binding.gyp index f8644a63..f19ba0db 100644 --- a/binding.gyp +++ b/binding.gyp @@ -7,7 +7,7 @@ " Date: Fri, 10 Mar 2023 12:29:10 -0500 Subject: [PATCH 42/42] suppress destruction of static Napi::ObjectReference --- src/node.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node.cc b/src/node.cc index f95af9a1..afece1a8 100644 --- a/src/node.cc +++ b/src/node.cc @@ -769,6 +769,7 @@ void InitNode(Object &exports) { Env env = exports.Env(); NodeMethods::Init(env, exports); module_exports.Reset(exports, 1); + module_exports.SuppressDestruct(); setup_transfer_buffer(env, 1); }