Return informative error when load_language fails

This commit is contained in:
Max Brunsfeld 2023-11-26 10:41:33 -08:00
parent e9289d3b30
commit 6fd7a1e44e
8 changed files with 246 additions and 108 deletions

View file

@ -1,4 +1,4 @@
/* automatically generated by rust-bindgen 0.66.1 */
/* automatically generated by rust-bindgen 0.69.1 */
pub const TREE_SITTER_LANGUAGE_VERSION: u32 = 14;
pub const TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION: u32 = 13;
@ -715,53 +715,6 @@ extern "C" {
#[doc = " Get the ABI version number for this language. This version number is used\n to ensure that languages were generated by a compatible version of\n Tree-sitter.\n\n See also [`ts_parser_set_language`]."]
pub fn ts_language_version(self_: *const TSLanguage) -> u32;
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct wasm_engine_t {
_unused: [u8; 0],
}
pub type TSWasmEngine = wasm_engine_t;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct TSWasmStore {
_unused: [u8; 0],
}
extern "C" {
#[doc = " Create a Wasm store."]
pub fn ts_wasm_store_new(engine: *mut TSWasmEngine) -> *mut TSWasmStore;
}
extern "C" {
#[doc = " Free the memory associated with the given Wasm store."]
pub fn ts_wasm_store_delete(arg1: *mut TSWasmStore);
}
extern "C" {
#[doc = " Create a language from a buffer of Wasm. The resulting language behaves"]
#[doc = " like any other Tree-sitter language, except that in order to use it with"]
#[doc = " a parser, that parser must have a Wasm store. Note that the language"]
#[doc = " can be used with any Wasm store, it doesn't need to be the same store that"]
#[doc = " was used to originally load it."]
pub fn ts_wasm_store_load_language(
arg1: *mut TSWasmStore,
name: *const ::std::os::raw::c_char,
wasm: *const ::std::os::raw::c_char,
wasm_len: u32,
) -> *const TSLanguage;
}
extern "C" {
#[doc = " Check if the language came from a Wasm module. If so, then in order to use"]
#[doc = " this langauge with a Parser, that parser must have a Wasm store assigned."]
pub fn ts_language_is_wasm(arg1: *const TSLanguage) -> bool;
}
extern "C" {
#[doc = " Assign the given Wasm store to the parser. A parser must have a Wasm store"]
#[doc = " in order to use Wasm languages."]
pub fn ts_parser_set_wasm_store(arg1: *mut TSParser, arg2: *mut TSWasmStore);
}
extern "C" {
#[doc = " Remove the parser's current Wasm store and return it. This returns NULL if"]
#[doc = " the parser doesn't have a Wasm store."]
pub fn ts_parser_take_wasm_store(arg1: *mut TSParser) -> *mut TSWasmStore;
}
extern "C" {
#[doc = " Get the next parse state. Combine this with lookahead iterators to generate\n completion suggestions or valid symbols in error nodes. Use\n [`ts_node_grammar_symbol`] for valid symbols."]
pub fn ts_language_next_state(
@ -814,6 +767,52 @@ extern "C" {
self_: *const TSLookaheadIterator,
) -> *const ::std::os::raw::c_char;
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct wasm_engine_t {
_unused: [u8; 0],
}
pub type TSWasmEngine = wasm_engine_t;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct TSWasmStore {
_unused: [u8; 0],
}
pub const TSWasmErrorParse: TSWasmError = 0;
pub const TSWasmErrorCompile: TSWasmError = 1;
pub const TSWasmErrorInstantiate: TSWasmError = 2;
pub type TSWasmError = ::std::os::raw::c_uint;
extern "C" {
#[doc = " Create a Wasm store."]
pub fn ts_wasm_store_new(engine: *mut TSWasmEngine) -> *mut TSWasmStore;
}
extern "C" {
#[doc = " Free the memory associated with the given Wasm store."]
pub fn ts_wasm_store_delete(arg1: *mut TSWasmStore);
}
extern "C" {
#[doc = " Create a language from a buffer of Wasm. The resulting language behaves\n like any other Tree-sitter language, except that in order to use it with\n a parser, that parser must have a Wasm store. Note that the language\n can be used with any Wasm store, it doesn't need to be the same store that\n was used to originally load it."]
pub fn ts_wasm_store_load_language(
arg1: *mut TSWasmStore,
name: *const ::std::os::raw::c_char,
wasm: *const ::std::os::raw::c_char,
wasm_len: u32,
error: *mut TSWasmError,
message: *mut *mut ::std::os::raw::c_char,
) -> *const TSLanguage;
}
extern "C" {
#[doc = " Check if the language came from a Wasm module. If so, then in order to use\n this langauge with a Parser, that parser must have a Wasm store assigned."]
pub fn ts_language_is_wasm(arg1: *const TSLanguage) -> bool;
}
extern "C" {
#[doc = " Assign the given Wasm store to the parser. A parser must have a Wasm store\n in order to use Wasm languages."]
pub fn ts_parser_set_wasm_store(arg1: *mut TSParser, arg2: *mut TSWasmStore);
}
extern "C" {
#[doc = " Remove the parser's current Wasm store and return it. This returns NULL if\n the parser doesn't have a Wasm store."]
pub fn ts_parser_take_wasm_store(arg1: *mut TSParser) -> *mut TSWasmStore;
}
extern "C" {
#[doc = " Set the allocation functions used by the library.\n\n By default, Tree-sitter uses the standard libc allocation functions,\n but aborts the process when an allocation fails. This function lets\n you supply alternative allocation functions at runtime.\n\n If you pass `NULL` for any parameter, Tree-sitter will switch back to\n its default implementation of that function.\n\n If you call this function after the library has already been used, then\n you must ensure that either:\n 1. All the existing objects have been freed.\n 2. The new allocator shares its state with the old one, so it is capable\n of freeing memory that was allocated by the old allocator."]
pub fn ts_set_allocator(

View file

@ -1,5 +1,11 @@
use crate::{ffi, Language, LanguageError, Parser};
use std::{ffi::CString, mem, os::raw::c_char};
use std::{
error,
ffi::CString,
fmt,
mem::{self, MaybeUninit},
os::raw::c_char,
};
pub use wasmtime;
#[cfg(feature = "wasm")]
@ -18,6 +24,20 @@ pub struct wasm_engine_t {
pub struct WasmStore(*mut ffi::TSWasmStore);
#[derive(Debug, PartialEq, Eq)]
pub struct WasmError {
pub kind: WasmErrorKind,
pub message: String,
}
#[derive(Debug, PartialEq, Eq)]
pub enum WasmErrorKind {
Parse,
Compile,
Instantiate,
Other,
}
impl WasmStore {
pub fn new(engine: wasmtime::Engine) -> Self {
let engine = Box::new(wasm_engine_t { engine });
@ -26,16 +46,37 @@ impl WasmStore {
})
}
pub fn load_language(&mut self, name: &str, bytes: &[u8]) -> Language {
pub fn load_language(&mut self, name: &str, bytes: &[u8]) -> Result<Language, WasmError> {
let name = CString::new(name).unwrap();
Language(unsafe {
ffi::ts_wasm_store_load_language(
unsafe {
let mut error = MaybeUninit::<ffi::TSWasmError>::uninit();
let mut message = MaybeUninit::<*mut c_char>::uninit();
let language = ffi::ts_wasm_store_load_language(
self.0,
name.as_ptr(),
bytes.as_ptr() as *const c_char,
bytes.len() as u32,
)
})
error.as_mut_ptr(),
message.as_mut_ptr(),
);
if language.is_null() {
let error = error.assume_init();
let message = message.assume_init();
let message = CString::from_raw(message);
Err(WasmError {
kind: match error {
ffi::TSWasmErrorParse => WasmErrorKind::Parse,
ffi::TSWasmErrorCompile => WasmErrorKind::Compile,
ffi::TSWasmErrorInstantiate => WasmErrorKind::Instantiate,
_ => WasmErrorKind::Other,
},
message: message.into_string().unwrap(),
})
} else {
Ok(Language(language))
}
}
}
}
@ -67,3 +108,17 @@ impl Drop for WasmStore {
unsafe { ffi::ts_wasm_store_delete(self.0) };
}
}
impl fmt::Display for WasmError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let kind = match self.kind {
WasmErrorKind::Parse => "Failed to parse wasm",
WasmErrorKind::Compile => "Failed to compile wasm",
WasmErrorKind::Instantiate => "Failed to instantiate wasm module",
WasmErrorKind::Other => "Unknown error",
};
write!(f, "{kind} {}", self.message)
}
}
impl error::Error for WasmError {}