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>.
Possible progress callback leak in
Parser.parse()I found a possible native and JavaScript reference leak when
Parser.parse()is called withprogressCallback.File:
src/parser.ccFunctions:
CallbackProgress::Make,Parser::ParseRelevant JavaScript API:
The native wrapper allocates a callback payload and stores a persistent function
reference:
Parser::Parse()passes the options to Tree-sitter:There is no matching
deleteorfunc.Reset()afterts_parser_parse_with_options()returns.TSParseOptionsis stack-local, andTree-sitter only receives the raw payload pointer for the parse operation.
For comparison, logger payload ownership is handled explicitly:
Suggested fix: keep the progress callback payload owned by
Parser::Parse()and release it after
ts_parser_parse_with_options()returns, for example byusing a stack object or
std::unique_ptr<CallbackProgress>.