Skip to main content

tree_sitter/
wasm_language.rs

1use std::{
2    error,
3    ffi::{CStr, CString},
4    fmt,
5    mem::{self, MaybeUninit},
6    os::raw::c_char,
7};
8
9pub use wasmtime_c_api::wasmtime;
10
11use crate::{Language, LanguageError, Parser, ffi, ts_free};
12
13// Force Cargo to include wasmtime-c-api as a dependency of this crate,
14// even though it is only used by the C code.
15#[expect(unused, reason = "forces Cargo to link wasmtime-c-api")]
16fn use_wasmtime() {
17    wasmtime_c_api::wasm_engine_new();
18}
19
20#[repr(C)]
21#[derive(Clone)]
22#[allow(non_camel_case_types)]
23pub struct wasm_engine_t {
24    pub(crate) engine: wasmtime::Engine,
25}
26
27pub struct WasmStore(*mut ffi::TSWasmStore);
28
29unsafe impl Send for WasmStore {}
30unsafe impl Sync for WasmStore {}
31
32#[derive(Debug, PartialEq, Eq)]
33pub struct WasmError {
34    pub kind: WasmErrorKind,
35    pub message: String,
36}
37
38#[derive(Debug, PartialEq, Eq)]
39pub enum WasmErrorKind {
40    Parse,
41    Compile,
42    Instantiate,
43    Other,
44}
45
46impl WasmStore {
47    pub fn new(engine: &wasmtime::Engine) -> Result<Self, WasmError> {
48        unsafe {
49            let mut error = MaybeUninit::<ffi::TSWasmError>::uninit();
50            let store = ffi::ts_wasm_store_new(
51                std::ptr::from_ref::<wasmtime::Engine>(engine)
52                    .cast_mut()
53                    .cast(),
54                error.as_mut_ptr(),
55            );
56            if store.is_null() {
57                Err(WasmError::new(error.assume_init()))
58            } else {
59                Ok(Self(store))
60            }
61        }
62    }
63
64    pub fn load_language(&mut self, name: &str, bytes: &[u8]) -> Result<Language, WasmError> {
65        let name = CString::new(name).unwrap();
66        unsafe {
67            let mut error = MaybeUninit::<ffi::TSWasmError>::uninit();
68            let language = ffi::ts_wasm_store_load_language(
69                self.0,
70                name.as_ptr(),
71                bytes.as_ptr().cast::<c_char>(),
72                bytes.len() as u32,
73                error.as_mut_ptr(),
74            );
75            if language.is_null() {
76                Err(WasmError::new(error.assume_init()))
77            } else {
78                Ok(Language(language))
79            }
80        }
81    }
82
83    #[must_use]
84    pub fn language_count(&self) -> usize {
85        unsafe { ffi::ts_wasm_store_language_count(self.0) }
86    }
87}
88
89impl WasmError {
90    unsafe fn new(error: ffi::TSWasmError) -> Self {
91        let message = unsafe { CStr::from_ptr(error.message) }
92            .to_str()
93            .unwrap()
94            .to_string();
95        unsafe { ts_free(error.message.cast()) };
96        Self {
97            kind: match error.kind {
98                ffi::TSWasmErrorKindParse => WasmErrorKind::Parse,
99                ffi::TSWasmErrorKindCompile => WasmErrorKind::Compile,
100                ffi::TSWasmErrorKindInstantiate => WasmErrorKind::Instantiate,
101                _ => WasmErrorKind::Other,
102            },
103            message,
104        }
105    }
106}
107
108impl Language {
109    #[must_use]
110    pub fn is_wasm(&self) -> bool {
111        unsafe { ffi::ts_language_is_wasm(self.0) }
112    }
113}
114
115impl Parser {
116    pub fn set_wasm_store(&mut self, store: WasmStore) -> Result<(), LanguageError> {
117        unsafe { ffi::ts_parser_set_wasm_store(self.0.as_ptr(), store.0) };
118        mem::forget(store);
119        Ok(())
120    }
121
122    pub fn take_wasm_store(&mut self) -> Option<WasmStore> {
123        let ptr = unsafe { ffi::ts_parser_take_wasm_store(self.0.as_ptr()) };
124        if ptr.is_null() {
125            None
126        } else {
127            Some(WasmStore(ptr))
128        }
129    }
130}
131
132impl Drop for WasmStore {
133    fn drop(&mut self) {
134        unsafe { ffi::ts_wasm_store_delete(self.0) };
135    }
136}
137
138impl fmt::Display for WasmError {
139    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140        let kind = match self.kind {
141            WasmErrorKind::Parse => "Failed to parse Wasm",
142            WasmErrorKind::Compile => "Failed to compile Wasm",
143            WasmErrorKind::Instantiate => "Failed to instantiate Wasm module",
144            WasmErrorKind::Other => "Unknown error",
145        };
146        write!(f, "{kind}: {}", self.message)
147    }
148}
149
150impl error::Error for WasmError {}