mirror of
https://github.com/tree-sitter/tree-sitter.git
synced 2026-09-10 07:36:22 -04:00
* feat(wasm): make syntax trees sendable
* test(wasm): transfer trees across workers
* test(wasm): use JSON grammar for tree transfer
* test(wasm): edit trees across workers
* test(wasm): share dlmalloc with tree-sitter
* test(wasm): simplify worker tree exchange
* test(wasm): drive tree exchange from Rust
* test(wasm): split sendable-tree xtask
* test(wasm): generalize Rust web fixture
* test(wasm): exercise parallel Rust tree access
* fix(wasm): use Rust global allocator for C core
* test(wasm): use default Rust allocator
* feat(wasm): support external scanners in Rust web apps
* Simplify example further, add a readme
* Regenerate wasm-stdlib
* Fix wasm_stdlib check script
* Vendor the Wasm standard library subset
* Test Unicode Ruby scanner behavior in Wasm
* Make Wasm tree languages instance-aware
* Test multi-threaded use of queries in wasm32-unknown
* Refactor reference-counted language storage
* Check ABI version compat before loading rest of language
* 🎨 Remove redundant #ifdef block
* Reject unsupported Rust Wasm builds on 0.26
103 lines
3 KiB
JavaScript
103 lines
3 KiB
JavaScript
import fs from 'node:fs/promises';
|
|
import { Worker } from 'node:worker_threads';
|
|
|
|
const MEMORY_PAGES = 256;
|
|
const MAX_MEMORY_PAGES = 4096;
|
|
const control = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
|
|
const [runtimePath, pythonPath, rubyPath] = process.argv.slice(2);
|
|
if (!runtimePath || !pythonPath || !rubyPath) {
|
|
throw new Error('usage: node run.mjs <runtime.wasm> <python.wasm> <ruby.wasm>');
|
|
}
|
|
|
|
const runtimeModule = await WebAssembly.compile(await fs.readFile(runtimePath));
|
|
const languageModules = await Promise.all(
|
|
[pythonPath, rubyPath].map(async path => WebAssembly.compile(await fs.readFile(path))),
|
|
);
|
|
const memory = new WebAssembly.Memory({
|
|
initial: MEMORY_PAGES,
|
|
maximum: MAX_MEMORY_PAGES,
|
|
shared: true,
|
|
});
|
|
if (!(memory.buffer instanceof SharedArrayBuffer)) {
|
|
throw new Error('WebAssembly memory is not shared');
|
|
}
|
|
|
|
let worker;
|
|
let pendingRequest = 0;
|
|
const uiRuntime = await WebAssembly.instantiate(runtimeModule, {
|
|
env: {
|
|
memory,
|
|
request_parse(languageId, sourceAddress, sourceLength, oldTree, requestAddress) {
|
|
if (pendingRequest !== 0) {
|
|
throw new Error('Rust requested another parse before the prior request completed');
|
|
}
|
|
pendingRequest = requestAddress;
|
|
if (oldTree) {
|
|
Atomics.store(control, 0, 0);
|
|
}
|
|
worker.postMessage(
|
|
oldTree
|
|
? { languageId, sourceAddress, sourceLength, oldTree }
|
|
: { languageId, sourceAddress, sourceLength },
|
|
);
|
|
},
|
|
log(messageAddress, messageLength) {
|
|
console.log(
|
|
new TextDecoder().decode(
|
|
new Uint8Array(memory.buffer, messageAddress, messageLength),
|
|
),
|
|
);
|
|
},
|
|
notify_parsing_started() {
|
|
throw new Error('UI runtime unexpectedly paused for parsing');
|
|
},
|
|
},
|
|
});
|
|
|
|
worker = new Worker(new URL('./worker.mjs', import.meta.url), {
|
|
workerData: { control, runtimeModule, languageModules, memory },
|
|
});
|
|
|
|
try {
|
|
await new Promise((resolve, reject) => {
|
|
let finished = false;
|
|
worker.once('error', reject);
|
|
worker.once('exit', code => {
|
|
if (!finished) {
|
|
reject(new Error(`worker exited before completing the test with code ${code}`));
|
|
}
|
|
});
|
|
worker.on('message', message => {
|
|
try {
|
|
if (message?.ready === true) {
|
|
uiRuntime.exports.start();
|
|
return;
|
|
}
|
|
if (message?.parsing) {
|
|
console.log('parsing...');
|
|
Atomics.store(control, 0, 1);
|
|
Atomics.notify(control, 0);
|
|
return;
|
|
}
|
|
if (!Number.isInteger(message?.tree) || message.tree === 0) {
|
|
throw new Error('worker did not return a tree');
|
|
}
|
|
const request = pendingRequest;
|
|
if (request === 0) {
|
|
throw new Error('worker returned a tree without a pending request');
|
|
}
|
|
pendingRequest = 0;
|
|
if (uiRuntime.exports.tree_ready(request, message.tree)) {
|
|
finished = true;
|
|
resolve();
|
|
}
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
} finally {
|
|
await worker.terminate();
|
|
}
|