mirror of
https://github.com/tree-sitter/tree-sitter.git
synced 2026-09-10 07:26:23 -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
100 lines
3.2 KiB
JavaScript
100 lines
3.2 KiB
JavaScript
import { parentPort, workerData } from 'node:worker_threads';
|
|
|
|
const PAGE_SIZE = 64 * 1024;
|
|
const SIDE_MODULE_DATA_PAGES = 32;
|
|
const SIDE_MODULE_STACK_PAGES = 16;
|
|
const SIDE_MODULE_TABLE_ELEMENTS = 1024;
|
|
const RUNTIME_STACK_PAGES = 16;
|
|
|
|
const { control, runtimeModule, languageModules, memory } = workerData;
|
|
|
|
const runtime = await WebAssembly.instantiate(runtimeModule, {
|
|
env: {
|
|
memory,
|
|
request_parse() {
|
|
throw new Error('parsing worker unexpectedly requested a parse');
|
|
},
|
|
log() {
|
|
throw new Error('parsing worker unexpectedly logged UI progress');
|
|
},
|
|
notify_parsing_started() {
|
|
parentPort.postMessage({ parsing: true });
|
|
Atomics.wait(control, 0, 0);
|
|
},
|
|
},
|
|
});
|
|
|
|
const {
|
|
__stack_pointer: stackPointer,
|
|
__indirect_function_table: table,
|
|
allocate_language_memory: allocateLanguageMemory,
|
|
run_parse: runParse,
|
|
} = runtime.exports;
|
|
|
|
const runtimeStackBase = allocateLanguageMemory(RUNTIME_STACK_PAGES * PAGE_SIZE, PAGE_SIZE);
|
|
if (!runtimeStackBase) {
|
|
throw new Error('Rust test module failed to allocate the worker runtime stack');
|
|
}
|
|
stackPointer.value = runtimeStackBase + RUNTIME_STACK_PAGES * PAGE_SIZE;
|
|
|
|
function loadLanguage(languageModule) {
|
|
const memoryBase = allocateLanguageMemory(
|
|
(SIDE_MODULE_DATA_PAGES + SIDE_MODULE_STACK_PAGES) * PAGE_SIZE,
|
|
PAGE_SIZE,
|
|
);
|
|
const tableBase = table.length;
|
|
table.grow(SIDE_MODULE_TABLE_ELEMENTS);
|
|
|
|
const env = {
|
|
memory,
|
|
__indirect_function_table: table,
|
|
__memory_base: new WebAssembly.Global({ value: 'i32', mutable: false }, memoryBase),
|
|
__table_base: new WebAssembly.Global({ value: 'i32', mutable: false }, tableBase),
|
|
__stack_pointer: new WebAssembly.Global(
|
|
{ value: 'i32', mutable: true },
|
|
memoryBase + (SIDE_MODULE_DATA_PAGES + SIDE_MODULE_STACK_PAGES) * PAGE_SIZE,
|
|
),
|
|
};
|
|
for (const { module, name, kind } of WebAssembly.Module.imports(languageModule)) {
|
|
if (module === 'env' && kind === 'function') {
|
|
const implementation = runtime.exports[name];
|
|
if (typeof implementation !== 'function') {
|
|
throw new Error(`Rust test module does not export ${name}`);
|
|
}
|
|
env[name] = implementation;
|
|
}
|
|
}
|
|
|
|
return WebAssembly.instantiate(languageModule, { env }).then(language => {
|
|
if (typeof language.exports.__wasm_apply_data_relocs === 'function') {
|
|
language.exports.__wasm_apply_data_relocs();
|
|
}
|
|
const languageFunction = Object.entries(language.exports).find(
|
|
([name, value]) => /^tree_sitter_\w+$/.test(name) && typeof value === 'function',
|
|
);
|
|
if (!languageFunction) {
|
|
throw new Error('language module did not export a tree_sitter_* function');
|
|
}
|
|
return languageFunction[1]();
|
|
});
|
|
}
|
|
|
|
const languageAddresses = await Promise.all(languageModules.map(loadLanguage));
|
|
|
|
parentPort.postMessage({ ready: true });
|
|
|
|
parentPort.on('message', message => {
|
|
const languageAddress =
|
|
message.languageId === 0 ? 0 : languageAddresses[message.languageId - 1];
|
|
const tree = runParse(
|
|
languageAddress,
|
|
message.sourceAddress,
|
|
message.sourceLength,
|
|
message.oldTree ?? 0,
|
|
);
|
|
if (tree === 0) {
|
|
throw new Error(`parsing worker failed to parse language ${message.languageId}`);
|
|
}
|
|
parentPort.postMessage({ tree });
|
|
});
|