git.delta.rocks / jrsonnet / refs/commits / 4ea8dc2d6da9

difftreelog

feat(bindings) implement WASM

Лач2020-06-28parent: #85533fe.patch.diff
in: master

3 files changed

modifiedREADME.mddiffbeforeafterboth
--- a/README.md
+++ b/README.md
@@ -20,8 +20,7 @@
 ## Bindings
 
 C bindings (libjsonnet.so) are WIP
-
-Something is implemented, but mostly unusable for now
+JS bindings are WIP too, but also available in WASM form
 
 See them in `./bindings/jsonnet/`
 
modifiedbindings/Makefilediffbeforeafterboth
--- a/bindings/Makefile
+++ b/bindings/Makefile
@@ -10,3 +10,8 @@
 	ldd ./c/libjsonnet_test_file
 	valgrind --leak-check=full ./c/libjsonnet_test_file test.jsonnet
 
+../target/wasm32-wasi/release/jsonnet.wasm:
+	cd jsonnet && cargo wasi build --release -p jsonnet && cd ..
+
+test-js: ../target/wasm32-wasi/release/jsonnet.wasm
+	node --experimental-wasi-unstable-preview1 --experimental-wasm-bigint js/index
modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
before · bindings/jsonnet/src/lib.rs
1use jrsonnet_evaluator::{2	create_error, create_error_result, Error, EvaluationState, ImportResolver, LazyBinding,3	LazyVal, ObjMember, ObjValue, Result, Val,4};5use jrsonnet_parser::Visibility;6use libc::{c_char, c_double, c_int, c_uint};7use std::{8	alloc::Layout,9	any::Any,10	cell::RefCell,11	collections::BTreeMap,12	ffi::{CStr, CString},13	fs::File,14	io::Read,15	path::PathBuf,16	rc::Rc,17};1819#[no_mangle]20pub extern "C" fn jsonnet_version() -> &'static [u8; 8] {21	b"v0.16.0\0"22}2324#[derive(Default)]25struct NativeImportResolver {26	library_paths: RefCell<Vec<PathBuf>>,27}28impl NativeImportResolver {29	fn add_jpath(&self, path: PathBuf) {30		self.library_paths.borrow_mut().push(path);31	}32}33impl ImportResolver for NativeImportResolver {34	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {35		let mut new_path = from.clone();36		new_path.push(path);37		if new_path.exists() {38			Ok(Rc::new(new_path))39		} else {40			for library_path in self.library_paths.borrow().iter() {41				let mut cloned = library_path.clone();42				cloned.push(path);43				if cloned.exists() {44					return Ok(Rc::new(cloned));45				}46			}47			create_error_result(Error::ImportFileNotFound(from.clone(), path.clone()))48		}49	}50	fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {51		let mut file =52			File::open(id).map_err(|_e| create_error(Error::ResolvedFileNotFound(id.clone())))?;53		let mut out = String::new();54		file.read_to_string(&mut out)55			.map_err(|_e| create_error(Error::ImportBadFileUtf8(id.clone())))?;56		Ok(out.into())57	}58	unsafe fn as_any(&self) -> &dyn Any {59		self60	}61}6263#[no_mangle]64pub extern "C" fn jsonnet_make() -> Box<EvaluationState> {65	let state = EvaluationState::default();66	state.with_stdlib();67	state.set_import_resolver(Box::new(NativeImportResolver::default()));68	Box::new(state)69}7071#[no_mangle]72pub extern "C" fn jsonnet_max_stack(vm: &EvaluationState, v: c_uint) {73	vm.set_max_stack(v as usize);74}7576// jrsonnet currently have no GC, so these functions is no-op77#[no_mangle]78pub extern "C" fn jsonnet_gc_min_objects(_vm: &EvaluationState, _v: c_uint) {}79#[no_mangle]80pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &EvaluationState, _v: c_double) {}8182// TODO83#[no_mangle]84pub extern "C" fn jsonnet_string_output(_vm: &EvaluationState, _v: c_int) {}8586#[no_mangle]87pub extern "C" fn jsonnet_json_extract_string(_vm: &EvaluationState, v: &Val) -> *mut c_char {88	match v.unwrap_if_lazy().unwrap() {89		Val::Str(s) => CString::new(&*s as &str).unwrap().into_raw(),90		_ => std::ptr::null_mut(),91	}92}93#[no_mangle]94pub extern "C" fn jsonnet_json_extract_number(95	_vm: &EvaluationState,96	v: &Val,97	out: &mut c_double,98) -> c_int {99	match v.unwrap_if_lazy().unwrap() {100		Val::Num(n) => {101			*out = n;102			1103		}104		_ => 0,105	}106}107#[no_mangle]108pub extern "C" fn jsonnet_json_extract_bool(_vm: &EvaluationState, v: &Val) -> c_int {109	match v.unwrap_if_lazy().unwrap() {110		Val::Bool(false) => 0,111		Val::Bool(true) => 1,112		_ => 2,113	}114}115#[no_mangle]116pub extern "C" fn jsonnet_json_extract_null(_vm: &EvaluationState, v: &Val) -> c_int {117	match v.unwrap_if_lazy().unwrap() {118		Val::Null => 1,119		_ => 0,120	}121}122123/// # Safety124///125/// This function is safe, if received v is a pointer to normal C string126#[no_mangle]127pub unsafe extern "C" fn jsonnet_json_make_string(128	_vm: &EvaluationState,129	v: *const c_char,130) -> Box<Val> {131	let cstr = CStr::from_ptr(v);132	let str = cstr.to_str().unwrap();133	Box::new(Val::Str(str.into()))134}135136#[no_mangle]137pub extern "C" fn jsonnet_json_make_number(_vm: &EvaluationState, v: c_double) -> Box<Val> {138	Box::new(Val::Num(v))139}140141#[no_mangle]142pub extern "C" fn jsonnet_json_make_bool(_vm: &EvaluationState, v: c_int) -> Box<Val> {143	assert!(v == 0 || v == 1);144	Box::new(Val::Bool(v == 1))145}146147#[no_mangle]148pub extern "C" fn jsonnet_json_make_null(_vm: &EvaluationState) -> Box<Val> {149	Box::new(Val::Null)150}151152#[no_mangle]153pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> Box<Val> {154	Box::new(Val::Arr(Rc::new(Vec::new())))155}156157#[no_mangle]158pub extern "C" fn jsonnet_json_array_append(_vm: &EvaluationState, arr: &mut Val, val: &Val) {159	match arr {160		Val::Arr(old) => {161			// TODO: Mutate array, instead of recreating them162			let mut new = Vec::new();163			new.extend(old.iter().cloned());164			new.push(val.clone());165			*arr = Val::Arr(Rc::new(new));166		}167		_ => panic!("should receive array"),168	}169}170171#[no_mangle]172pub extern "C" fn jsonnet_json_make_object(_vm: &EvaluationState) -> Box<Val> {173	Box::new(Val::Obj(ObjValue::new_empty()))174}175176/// # Safety177///178/// This function is safe if passed name is ok179#[no_mangle]180pub unsafe extern "C" fn jsonnet_json_object_append(181	_vm: &EvaluationState,182	obj: &mut Val,183	name: *const c_char,184	val: &Val,185) {186	match obj {187		Val::Obj(old) => {188			let mut new = BTreeMap::new();189			new.insert(190				CStr::from_ptr(name).to_str().unwrap().into(),191				ObjMember {192					add: false,193					visibility: Visibility::Normal,194					invoke: LazyBinding::Bound(LazyVal::new_resolved(val.clone())),195				},196			);197			let new_obj = ObjValue::new(Some(old.clone()), Rc::new(new));198			*obj = Val::Obj(new_obj);199		}200		_ => panic!("should receive array"),201	}202}203204/// # Safety205///206/// This function is most definitely broken, but it works somehow, see TODO inside207#[no_mangle]208pub unsafe extern "C" fn jsonnet_realloc(209	_vm: &EvaluationState,210	buf: *mut u8,211	sz: usize,212) -> *mut u8 {213	if buf.is_null() {214		assert!(sz != 0);215		return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());216	}217	// TODO: Somehow store size of allocation, because its real size is probally not 16 :D218	// OR (Alternative way of fixing this TODO)219	// TODO: Standard allocator uses malloc, and it doesn't uses allocation size,220	// TODO: so it should work in normal cases. Maybe force allocator for this library?221	let old_layout = Layout::from_size_align(16, std::mem::align_of::<u8>()).unwrap();222	if sz == 0 {223		std::alloc::dealloc(buf, old_layout);224		return std::ptr::null_mut();225	}226	std::alloc::realloc(buf, old_layout, sz)227}228229#[no_mangle]230#[allow(clippy::boxed_local)]231pub extern "C" fn jsonnet_json_destroy(_vm: &EvaluationState, _v: Box<Val>) {}232233#[no_mangle]234pub extern "C" fn jsonnet_import_callback() {235	todo!()236}237#[no_mangle]238pub extern "C" fn jsonnet_native_callback() {239	todo!()240}241#[no_mangle]242pub extern "C" fn jsonnet_ext_var() {243	todo!()244}245#[no_mangle]246pub extern "C" fn jsonnet_ext_code() {247	todo!()248}249#[no_mangle]250pub extern "C" fn jsonnet_tla_var() {251	todo!()252}253#[no_mangle]254pub extern "C" fn jsonnet_tla_code() {255	todo!()256}257#[no_mangle]258pub extern "C" fn jsonnet_max_trace() {259	todo!()260}261262/// # Safety263///264/// This function is safe, if received v is a pointer to normal C string265#[no_mangle]266pub unsafe extern "C" fn jsonnet_jpath_add(vm: &EvaluationState, v: *const c_char) {267	let cstr = CStr::from_ptr(v);268	let path = PathBuf::from(cstr.to_str().unwrap());269	let any_resolver = vm.import_resolver();270	let resolver = any_resolver271		.as_any()272		.downcast_ref::<NativeImportResolver>()273		.unwrap();274	resolver.add_jpath(path);275}276277/// # Safety278///279/// This function is safe, if received v is a pointer to normal C string280#[no_mangle]281pub unsafe extern "C" fn jsonnet_evaluate_file(282	vm: &EvaluationState,283	filename: *const c_char,284	error: &mut c_int,285) -> *const c_char {286	vm.run_in_state(|| {287		use std::fmt::Write;288		let filename = CStr::from_ptr(filename);289		match vm.evaluate_file_to_json(&PathBuf::from(filename.to_str().unwrap())) {290			Ok(v) => {291				*error = 0;292				CString::new(&*v as &str).unwrap().into_raw()293			}294			Err(e) => {295				*error = 1;296				let mut out = String::new();297				writeln!(out, "{:?}", e.0).unwrap();298				for i in (e.1).0.iter() {299					writeln!(out, "{:?}", i).unwrap();300				}301				CString::new(&out as &str).unwrap().into_raw()302			}303		}304	})305}306#[no_mangle]307pub extern "C" fn jsonnet_evaluate_snippet() {308	todo!()309}310#[no_mangle]311pub extern "C" fn jsonnet_evaluate_file_multi() {312	todo!()313}314#[no_mangle]315pub extern "C" fn jsonnet_evaluate_snippet_multi() {316	todo!()317}318#[no_mangle]319pub extern "C" fn jsonnet_evaluate_file_stream() {320	todo!()321}322#[no_mangle]323pub extern "C" fn jsonnet_evaluate_snippet_stream() {324	todo!()325}326327#[no_mangle]328#[allow(clippy::boxed_local)]329pub extern "C" fn jsonnet_destroy(_vm: Box<EvaluationState>) {}
after · bindings/jsonnet/src/lib.rs
1use jrsonnet_evaluator::{2	create_error, create_error_result, Error, EvaluationState, ImportResolver, LazyBinding,3	LazyVal, ObjMember, ObjValue, Result, Val,4};5use jrsonnet_parser::Visibility;6use libc::{c_char, c_double, c_int, c_uint};7use std::{8	alloc::Layout,9	any::Any,10	cell::RefCell,11	collections::BTreeMap,12	ffi::{CStr, CString},13	fs::File,14	io::Read,15	path::PathBuf,16	rc::Rc,17};1819#[no_mangle]20pub extern "C" fn _start() {}2122#[no_mangle]23pub extern "C" fn jsonnet_version() -> &'static [u8; 8] {24	b"v0.16.0\0"25}2627#[derive(Default)]28struct NativeImportResolver {29	library_paths: RefCell<Vec<PathBuf>>,30}31impl NativeImportResolver {32	fn add_jpath(&self, path: PathBuf) {33		self.library_paths.borrow_mut().push(path);34	}35}36impl ImportResolver for NativeImportResolver {37	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {38		let mut new_path = from.clone();39		new_path.push(path);40		if new_path.exists() {41			Ok(Rc::new(new_path))42		} else {43			for library_path in self.library_paths.borrow().iter() {44				let mut cloned = library_path.clone();45				cloned.push(path);46				if cloned.exists() {47					return Ok(Rc::new(cloned));48				}49			}50			create_error_result(Error::ImportFileNotFound(from.clone(), path.clone()))51		}52	}53	fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {54		let mut file =55			File::open(id).map_err(|_e| create_error(Error::ResolvedFileNotFound(id.clone())))?;56		let mut out = String::new();57		file.read_to_string(&mut out)58			.map_err(|_e| create_error(Error::ImportBadFileUtf8(id.clone())))?;59		Ok(out.into())60	}61	unsafe fn as_any(&self) -> &dyn Any {62		self63	}64}6566#[no_mangle]67pub extern "C" fn jsonnet_make() -> Box<EvaluationState> {68	let state = EvaluationState::default();69	state.with_stdlib();70	state.set_import_resolver(Box::new(NativeImportResolver::default()));71	Box::new(state)72}7374#[no_mangle]75pub extern "C" fn jsonnet_max_stack(vm: &EvaluationState, v: c_uint) {76	vm.set_max_stack(v as usize);77}7879// jrsonnet currently have no GC, so these functions is no-op80#[no_mangle]81pub extern "C" fn jsonnet_gc_min_objects(_vm: &EvaluationState, _v: c_uint) {}82#[no_mangle]83pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &EvaluationState, _v: c_double) {}8485// TODO86#[no_mangle]87pub extern "C" fn jsonnet_string_output(_vm: &EvaluationState, _v: c_int) {}8889#[no_mangle]90pub extern "C" fn jsonnet_json_extract_string(_vm: &EvaluationState, v: &Val) -> *mut c_char {91	match v.unwrap_if_lazy().unwrap() {92		Val::Str(s) => CString::new(&*s as &str).unwrap().into_raw(),93		_ => std::ptr::null_mut(),94	}95}96#[no_mangle]97pub extern "C" fn jsonnet_json_extract_number(98	_vm: &EvaluationState,99	v: &Val,100	out: &mut c_double,101) -> c_int {102	match v.unwrap_if_lazy().unwrap() {103		Val::Num(n) => {104			*out = n;105			1106		}107		_ => 0,108	}109}110#[no_mangle]111pub extern "C" fn jsonnet_json_extract_bool(_vm: &EvaluationState, v: &Val) -> c_int {112	match v.unwrap_if_lazy().unwrap() {113		Val::Bool(false) => 0,114		Val::Bool(true) => 1,115		_ => 2,116	}117}118#[no_mangle]119pub extern "C" fn jsonnet_json_extract_null(_vm: &EvaluationState, v: &Val) -> c_int {120	match v.unwrap_if_lazy().unwrap() {121		Val::Null => 1,122		_ => 0,123	}124}125126/// # Safety127///128/// This function is safe, if received v is a pointer to normal C string129#[no_mangle]130pub unsafe extern "C" fn jsonnet_json_make_string(131	_vm: &EvaluationState,132	v: *const c_char,133) -> Box<Val> {134	let cstr = CStr::from_ptr(v);135	let str = cstr.to_str().unwrap();136	Box::new(Val::Str(str.into()))137}138139#[no_mangle]140pub extern "C" fn jsonnet_json_make_number(_vm: &EvaluationState, v: c_double) -> Box<Val> {141	Box::new(Val::Num(v))142}143144#[no_mangle]145pub extern "C" fn jsonnet_json_make_bool(_vm: &EvaluationState, v: c_int) -> Box<Val> {146	assert!(v == 0 || v == 1);147	Box::new(Val::Bool(v == 1))148}149150#[no_mangle]151pub extern "C" fn jsonnet_json_make_null(_vm: &EvaluationState) -> Box<Val> {152	Box::new(Val::Null)153}154155#[no_mangle]156pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> Box<Val> {157	Box::new(Val::Arr(Rc::new(Vec::new())))158}159160#[no_mangle]161pub extern "C" fn jsonnet_json_array_append(_vm: &EvaluationState, arr: &mut Val, val: &Val) {162	match arr {163		Val::Arr(old) => {164			// TODO: Mutate array, instead of recreating them165			let mut new = Vec::new();166			new.extend(old.iter().cloned());167			new.push(val.clone());168			*arr = Val::Arr(Rc::new(new));169		}170		_ => panic!("should receive array"),171	}172}173174#[no_mangle]175pub extern "C" fn jsonnet_json_make_object(_vm: &EvaluationState) -> Box<Val> {176	Box::new(Val::Obj(ObjValue::new_empty()))177}178179/// # Safety180///181/// This function is safe if passed name is ok182#[no_mangle]183pub unsafe extern "C" fn jsonnet_json_object_append(184	_vm: &EvaluationState,185	obj: &mut Val,186	name: *const c_char,187	val: &Val,188) {189	match obj {190		Val::Obj(old) => {191			let mut new = BTreeMap::new();192			new.insert(193				CStr::from_ptr(name).to_str().unwrap().into(),194				ObjMember {195					add: false,196					visibility: Visibility::Normal,197					invoke: LazyBinding::Bound(LazyVal::new_resolved(val.clone())),198				},199			);200			let new_obj = ObjValue::new(Some(old.clone()), Rc::new(new));201			*obj = Val::Obj(new_obj);202		}203		_ => panic!("should receive array"),204	}205}206207/// # Safety208///209/// This function is most definitely broken, but it works somehow, see TODO inside210#[no_mangle]211pub unsafe extern "C" fn jsonnet_realloc(212	_vm: &EvaluationState,213	buf: *mut u8,214	sz: usize,215) -> *mut u8 {216	if buf.is_null() {217		assert!(sz != 0);218		return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());219	}220	// TODO: Somehow store size of allocation, because its real size is probally not 16 :D221	// OR (Alternative way of fixing this TODO)222	// TODO: Standard allocator uses malloc, and it doesn't uses allocation size,223	// TODO: so it should work in normal cases. Maybe force allocator for this library?224	let old_layout = Layout::from_size_align(16, std::mem::align_of::<u8>()).unwrap();225	if sz == 0 {226		std::alloc::dealloc(buf, old_layout);227		return std::ptr::null_mut();228	}229	std::alloc::realloc(buf, old_layout, sz)230}231232#[no_mangle]233#[allow(clippy::boxed_local)]234pub extern "C" fn jsonnet_json_destroy(_vm: &EvaluationState, _v: Box<Val>) {}235236#[no_mangle]237pub extern "C" fn jsonnet_import_callback() {238	todo!()239}240#[no_mangle]241pub extern "C" fn jsonnet_native_callback() {242	todo!()243}244#[no_mangle]245pub extern "C" fn jsonnet_ext_var() {246	todo!()247}248#[no_mangle]249pub extern "C" fn jsonnet_ext_code() {250	todo!()251}252#[no_mangle]253pub extern "C" fn jsonnet_tla_var() {254	todo!()255}256#[no_mangle]257pub extern "C" fn jsonnet_tla_code() {258	todo!()259}260#[no_mangle]261pub extern "C" fn jsonnet_max_trace() {262	todo!()263}264265/// # Safety266///267/// This function is safe, if received v is a pointer to normal C string268#[no_mangle]269pub unsafe extern "C" fn jsonnet_jpath_add(vm: &EvaluationState, v: *const c_char) {270	let cstr = CStr::from_ptr(v);271	let path = PathBuf::from(cstr.to_str().unwrap());272	let any_resolver = vm.import_resolver();273	let resolver = any_resolver274		.as_any()275		.downcast_ref::<NativeImportResolver>()276		.unwrap();277	resolver.add_jpath(path);278}279280/// # Safety281///282/// This function is safe, if received v is a pointer to normal C string283#[no_mangle]284pub unsafe extern "C" fn jsonnet_evaluate_file(285	vm: &EvaluationState,286	filename: *const c_char,287	error: &mut c_int,288) -> *const c_char {289	vm.run_in_state(|| {290		use std::fmt::Write;291		let filename = CStr::from_ptr(filename);292		match vm.evaluate_file_to_json(&PathBuf::from(filename.to_str().unwrap())) {293			Ok(v) => {294				*error = 0;295				CString::new(&*v as &str).unwrap().into_raw()296			}297			Err(e) => {298				*error = 1;299				let mut out = String::new();300				writeln!(out, "{:?}", e.0).unwrap();301				for i in (e.1).0.iter() {302					writeln!(out, "{:?}", i).unwrap();303				}304				CString::new(&out as &str).unwrap().into_raw()305			}306		}307	})308}309#[no_mangle]310pub extern "C" fn jsonnet_evaluate_snippet() {311	todo!()312}313#[no_mangle]314pub extern "C" fn jsonnet_evaluate_file_multi() {315	todo!()316}317#[no_mangle]318pub extern "C" fn jsonnet_evaluate_snippet_multi() {319	todo!()320}321#[no_mangle]322pub extern "C" fn jsonnet_evaluate_file_stream() {323	todo!()324}325#[no_mangle]326pub extern "C" fn jsonnet_evaluate_snippet_stream() {327	todo!()328}329330#[no_mangle]331#[allow(clippy::boxed_local)]332pub extern "C" fn jsonnet_destroy(_vm: Box<EvaluationState>) {}