From e8eb3c5d5a1e7eb2f7c61f27dee659bfe00d7e6a Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Tue, 27 Apr 2021 09:21:38 -0400 Subject: [PATCH 1/2] binding_web: Add Query.didExceedMatchLimit This lets wasm clients check whether a query exceeded its maximum number of in-progress matches. --- lib/binding_web/binding.c | 7 +++++++ lib/binding_web/binding.js | 4 ++++ lib/binding_web/exports.json | 1 + lib/binding_web/test/query-test.js | 20 ++++++++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/lib/binding_web/binding.c b/lib/binding_web/binding.c index d020da7c..c605731e 100644 --- a/lib/binding_web/binding.c +++ b/lib/binding_web/binding.c @@ -670,3 +670,10 @@ void ts_query_captures_wasm( TRANSFER_BUFFER[0] = (const void *)(capture_count); TRANSFER_BUFFER[1] = result.contents; } + +bool ts_query_did_exceed_match_limit_wasm( + const TSQuery *self +) { + if (!scratch_query_cursor) return false; + return ts_query_cursor_did_exceed_match_limit(scratch_query_cursor); +} diff --git a/lib/binding_web/binding.js b/lib/binding_web/binding.js index 62b147f1..a51a7aea 100644 --- a/lib/binding_web/binding.js +++ b/lib/binding_web/binding.js @@ -1048,6 +1048,10 @@ class Query { predicatesForPattern(patternIndex) { return this.predicates[patternIndex] } + + didExceedMatchLimit() { + return C._ts_query_did_exceed_match_limit_wasm(this[0]); + } } function getText(tree, startIndex, endIndex) { diff --git a/lib/binding_web/exports.json b/lib/binding_web/exports.json index b2b4449d..7e73a11a 100644 --- a/lib/binding_web/exports.json +++ b/lib/binding_web/exports.json @@ -76,6 +76,7 @@ "_ts_query_capture_name_for_id", "_ts_query_captures_wasm", "_ts_query_delete", + "_ts_query_did_exceed_match_limit_wasm", "_ts_query_matches_wasm", "_ts_query_new", "_ts_query_pattern_count", diff --git a/lib/binding_web/test/query-test.js b/lib/binding_web/test/query-test.js index 77937a45..b7b2e053 100644 --- a/lib/binding_web/test/query-test.js +++ b/lib/binding_web/test/query-test.js @@ -238,6 +238,26 @@ describe("Query", () => { refutedProperties: { bar: "baz" }, }, ]); + assert.ok(!query.didExceedMatchLimit()); + }); + + it("detects queries with too many permutations to track", () => { + tree = parser.parse(` + [ + hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, + hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, + hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, + hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, + hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, + ]; + `); + + query = JavaScript.query(` + (array (identifier) @pre (identifier) @post) + `); + + const captures = query.captures(tree.rootNode); + assert.ok(query.didExceedMatchLimit()); }); }); From b742e88108e9e79b9e2fe825fb8c6b9dfa0c5a5c Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Wed, 28 Apr 2021 15:46:12 -0400 Subject: [PATCH 2/2] Use TRANSFER_BUFFER for did_exceed_match_limit This bridges the gap between how the C API reports this for a query cursor, but the wasm API defines the method on a query. Whenever you call a query method that might exceed the match limit, we call the C API function and transfer the result across the wasm boundary, storing the result in the JavaScript wrapper class. --- lib/binding_web/binding.c | 13 ++++++------- lib/binding_web/binding.js | 7 ++++++- lib/binding_web/exports.json | 1 - 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/binding_web/binding.c b/lib/binding_web/binding.c index c605731e..8adaec75 100644 --- a/lib/binding_web/binding.c +++ b/lib/binding_web/binding.c @@ -622,8 +622,11 @@ void ts_query_matches_wasm( } } + bool did_exceed_match_limit = + ts_query_cursor_did_exceed_match_limit(scratch_query_cursor); TRANSFER_BUFFER[0] = (const void *)(match_count); TRANSFER_BUFFER[1] = result.contents; + TRANSFER_BUFFER[2] = (const void *)(did_exceed_match_limit); } void ts_query_captures_wasm( @@ -667,13 +670,9 @@ void ts_query_captures_wasm( } } + bool did_exceed_match_limit = + ts_query_cursor_did_exceed_match_limit(scratch_query_cursor); TRANSFER_BUFFER[0] = (const void *)(capture_count); TRANSFER_BUFFER[1] = result.contents; -} - -bool ts_query_did_exceed_match_limit_wasm( - const TSQuery *self -) { - if (!scratch_query_cursor) return false; - return ts_query_cursor_did_exceed_match_limit(scratch_query_cursor); + TRANSFER_BUFFER[2] = (const void *)(did_exceed_match_limit); } diff --git a/lib/binding_web/binding.js b/lib/binding_web/binding.js index a51a7aea..490874ac 100644 --- a/lib/binding_web/binding.js +++ b/lib/binding_web/binding.js @@ -945,6 +945,7 @@ class Query { this.setProperties = setProperties; this.assertedProperties = assertedProperties; this.refutedProperties = refutedProperties; + this.exceededMatchLimit = false; } delete() { @@ -969,7 +970,9 @@ class Query { const rawCount = getValue(TRANSFER_BUFFER, 'i32'); const startAddress = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32'); + const didExceedMatchLimit = getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, 'i32'); const result = new Array(rawCount); + this.exceededMatchLimit = !!didExceedMatchLimit; let filteredCount = 0; let address = startAddress; @@ -1014,7 +1017,9 @@ class Query { const count = getValue(TRANSFER_BUFFER, 'i32'); const startAddress = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32'); + const didExceedMatchLimit = getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, 'i32'); const result = []; + this.exceededMatchLimit = !!didExceedMatchLimit; const captures = []; let address = startAddress; @@ -1050,7 +1055,7 @@ class Query { } didExceedMatchLimit() { - return C._ts_query_did_exceed_match_limit_wasm(this[0]); + return this.exceededMatchLimit; } } diff --git a/lib/binding_web/exports.json b/lib/binding_web/exports.json index 7e73a11a..b2b4449d 100644 --- a/lib/binding_web/exports.json +++ b/lib/binding_web/exports.json @@ -76,7 +76,6 @@ "_ts_query_capture_name_for_id", "_ts_query_captures_wasm", "_ts_query_delete", - "_ts_query_did_exceed_match_limit_wasm", "_ts_query_matches_wasm", "_ts_query_new", "_ts_query_pattern_count",