tree-sitter / tree-sitter/node-tree-sitter
Possible progress callback leak in `Parser.parse()`
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 876
- Forks
- 172
- PR merge metrics
- No merged PRs in 30d
Description
Possible progress callback leak in Parser.parse()
I found a possible native and JavaScript reference leak when Parser.parse() is called with progressCallback.
File: src/parser.cc
Functions: CallbackProgress::Make, Parser::Parse
Relevant JavaScript API:
Parser.prototype.parse = function(input, oldTree, {
bufferSize,
includedRanges,
progressCallback
} = {}) {
const tree = parse.call(
this,
input,
oldTree,
bufferSize,
includedRanges,
progressCallback,
);
// ...
}
The native wrapper allocates a callback payload and stores a persistent function
reference:
class CallbackProgress final {
public:
static TSParseOptions Make(const Napi::Function &func) {
TSParseOptions options;
auto *callback = new CallbackProgress();
callback->func = Napi::Persistent(func);
options.payload = static_cast<void *>(callback);
options.progress_callback = Cancel;
return options;
}
private:
Napi::FunctionReference func;
// ...
};
Parser::Parse() passes the options to Tree-sitter:
if (info.Length() > 4 && info[4].IsFunction()) {
TSParseOptions options = CallbackProgress::Make(info[4].As<Function>());
tree = ts_parser_parse_with_options(parser_, old_tree, callback_input.Input(), options);
} else {
tree = ts_parser_parse(parser_, old_tree, callback_input.Input());
}
return Tree::NewInstance(env, tree);
There is no matching delete or func.Reset() after
ts_parser_parse_with_options() returns. TSParseOptions is stack-local, and
Tree-sitter only receives the raw payload pointer for the parse operation.
For comparison, logger payload ownership is handled explicitly:
if (current_logger.payload != nullptr) {
delete static_cast<Logger *>(current_logger.payload);
}
Suggested fix: keep the progress callback payload owned by Parser::Parse()
and release it after ts_parser_parse_with_options() returns, for example by
using a stack object or std::unique_ptr<CallbackProgress>.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/parser.cc, reading CallbackProgress::Make and Parser::Parse, then compare their ownership handling with the logger payload cleanup shown in the issue. Verify the progress callback payload and persistent function reference are released after ts_parser_parse_with_options() returns, without affecting parsing or callback behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, javascript
- Domain
- api, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100