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
108 lines
3.2 KiB
Rust
108 lines
3.2 KiB
Rust
//! C allocation functions backed by Rust's application-selected global allocator.
|
|
//!
|
|
//! This is intentionally a Tree-sitter compatibility shim rather than a
|
|
//! general-purpose implementation of the WebAssembly C allocation ABI.
|
|
//! Tree-sitter and its supported external scanners only allocate types whose
|
|
//! alignment is at most eight bytes.
|
|
|
|
use alloc::alloc::{Layout, alloc, alloc_zeroed, dealloc, realloc as rust_realloc};
|
|
use core::{ffi::c_void, mem, ptr};
|
|
|
|
const C_ALIGNMENT: usize = 8;
|
|
|
|
#[repr(C, align(8))]
|
|
struct Header {
|
|
payload_size: usize,
|
|
}
|
|
|
|
const HEADER_SIZE: usize = mem::size_of::<Header>();
|
|
|
|
const _: () = assert!(HEADER_SIZE == C_ALIGNMENT);
|
|
|
|
fn layout(payload_size: usize) -> Option<Layout> {
|
|
let allocation_size = HEADER_SIZE.checked_add(payload_size)?;
|
|
Layout::from_size_align(allocation_size, C_ALIGNMENT).ok()
|
|
}
|
|
|
|
unsafe fn header_from_payload(payload: *mut c_void) -> *mut Header {
|
|
unsafe { payload.cast::<u8>().sub(HEADER_SIZE).cast() }
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
unsafe extern "C" fn malloc(size: usize) -> *mut c_void {
|
|
let Some(layout) = layout(size).filter(|_| size != 0) else {
|
|
return ptr::null_mut();
|
|
};
|
|
let header = unsafe { alloc(layout).cast::<Header>() };
|
|
if header.is_null() {
|
|
return ptr::null_mut();
|
|
}
|
|
unsafe {
|
|
header.write(Header { payload_size: size });
|
|
header.add(1).cast()
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
unsafe extern "C" fn calloc(count: usize, size: usize) -> *mut c_void {
|
|
let Some(payload_size) = count.checked_mul(size) else {
|
|
return ptr::null_mut();
|
|
};
|
|
let Some(layout) = layout(payload_size).filter(|_| payload_size != 0) else {
|
|
return ptr::null_mut();
|
|
};
|
|
let header = unsafe { alloc_zeroed(layout).cast::<Header>() };
|
|
if header.is_null() {
|
|
return ptr::null_mut();
|
|
}
|
|
unsafe {
|
|
header.write(Header { payload_size });
|
|
header.add(1).cast()
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
unsafe extern "C" fn realloc(payload: *mut c_void, new_size: usize) -> *mut c_void {
|
|
if payload.is_null() {
|
|
return unsafe { malloc(new_size) };
|
|
}
|
|
if new_size == 0 {
|
|
unsafe { free(payload) };
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let old_header = unsafe { header_from_payload(payload) };
|
|
let old_size = unsafe { (*old_header).payload_size };
|
|
let Some(old_layout) = layout(old_size) else {
|
|
return ptr::null_mut();
|
|
};
|
|
let Some(new_layout) = layout(new_size) else {
|
|
return ptr::null_mut();
|
|
};
|
|
let new_header =
|
|
unsafe { rust_realloc(old_header.cast(), old_layout, new_layout.size()).cast::<Header>() };
|
|
if new_header.is_null() {
|
|
return ptr::null_mut();
|
|
}
|
|
unsafe {
|
|
(*new_header).payload_size = new_size;
|
|
new_header.add(1).cast()
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
unsafe extern "C" fn free(payload: *mut c_void) {
|
|
if payload.is_null() {
|
|
return;
|
|
}
|
|
let header = unsafe { header_from_payload(payload) };
|
|
let payload_size = unsafe { (*header).payload_size };
|
|
let allocation_layout = layout(payload_size).unwrap();
|
|
unsafe { dealloc(header.cast(), allocation_layout) };
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn abort() -> ! {
|
|
core::arch::wasm32::unreachable()
|
|
}
|