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::{ffi, Language, LanguageError, Parser, FREE_FN};
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#[allow(unused)]
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 = CStr::from_ptr(error.message).to_str().unwrap().to_string();
92        (FREE_FN)(error.message.cast());
93        Self {
94            kind: match error.kind {
95                ffi::TSWasmErrorKindParse => WasmErrorKind::Parse,
96                ffi::TSWasmErrorKindCompile => WasmErrorKind::Compile,
97                ffi::TSWasmErrorKindInstantiate => WasmErrorKind::Instantiate,
98                _ => WasmErrorKind::Other,
99            },
100            message,
101        }
102    }
103}
104
105impl Language {
106    #[must_use]
107    pub fn is_wasm(&self) -> bool {
108        unsafe { ffi::ts_language_is_wasm(self.0) }
109    }
110}
111
112impl Parser {
113    pub fn set_wasm_store(&mut self, store: WasmStore) -> Result<(), LanguageError> {
114        unsafe { ffi::ts_parser_set_wasm_store(self.0.as_ptr(), store.0) };
115        mem::forget(store);
116        Ok(())
117    }
118
119    pub fn take_wasm_store(&mut self) -> Option<WasmStore> {
120        let ptr = unsafe { ffi::ts_parser_take_wasm_store(self.0.as_ptr()) };
121        if ptr.is_null() {
122            None
123        } else {
124            Some(WasmStore(ptr))
125        }
126    }
127}
128
129impl Drop for WasmStore {
130    fn drop(&mut self) {
131        unsafe { ffi::ts_wasm_store_delete(self.0) };
132    }
133}
134
135impl fmt::Display for WasmError {
136    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137        let kind = match self.kind {
138            WasmErrorKind::Parse => "Failed to parse wasm",
139            WasmErrorKind::Compile => "Failed to compile wasm",
140            WasmErrorKind::Instantiate => "Failed to instantiate wasm module",
141            WasmErrorKind::Other => "Unknown error",
142        };
143        write!(f, "{kind}: {}", self.message)
144    }
145}
146
147impl error::Error for WasmError {}