git.delta.rocks / jrsonnet / refs/commits / dc0a0bacae12

difftreelog

refactor split libjsonnet code into modules

Лач2020-07-19parent: #3943cce.patch.diff
in: master

11 files changed

addedbindings/c/.gitignorediffbeforeafterboth
--- /dev/null
+++ b/bindings/c/.gitignore
@@ -0,0 +1 @@
+libjsonnet_test_file
modifiedbindings/js/index.jsdiffbeforeafterboth
1const fs = require('fs');1const fs = require('fs');
2const path = require('path');
2const { WASI } = require('wasi');3const { WASI } = require('wasi');
3const wasi = new WASI({4const wasi = new WASI({
4 args: process.argv,5 args: process.argv,
5 env: process.env,6 env: process.env,
6 preopens: {},7 preopens: {},
7});8});
8const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
99
10class JsonnetVM {10class JsonnetVM {
11 constructor(wasm, vm) {11 constructor(wasm, vm) {
12 this.wasm = wasm;12 this.wasm = wasm;
13 this.vm = vm;13 this.vm = vm;
14 this.wasm.exports.jrsonnet_set_trace_format(this.vm, 1);
15
16 this.setImportCallback((from, to) => {
17 const resolved = path.resolve(from, to);
18 return {
19 value: fs.readFileSync(resolved).toString('utf-8'),
20 foundHere: resolved,
21 };
22 })
14 }23 }
24
25 /**
26 * @param {(from: string, to: string) => {foundHere: string, value: string}} cb
27 */
28 setImportCallback(cb) {
29 this.wasm.importCbs.set(this.vm, (base, rel, foundHere, success) => {
30 const baseStr = this.wasm.readString(base);
31 const relStr = this.wasm.readString(rel);
32 try {
33 const value = cb(baseStr, relStr);
34 this.wasm.memorySlice32Len(foundHere, 1)[0] = this.allocateString(value.foundHere);
35 this.wasm.memorySlice32Len(success, 1)[0] = 1;
36 return this.allocateString(value.value);
37 } catch (e) {
38 this.wasm.memorySlice32Len(success, 1)[0] = 0;
39 return this.allocateString(e.stack)
40 }
41 });
42 this.wasm.exports.jrsonnet_apply_static_import_callback(
43 this.vm,
44 this.vm,
45 );
46 }
1547
16 alloc(length) {48 alloc(length) {
17 return this.wasm.exports.jsonnet_realloc(this.vm, 0, length);49 return this.wasm.exports.jsonnet_realloc(this.vm, 0, length);
18 }50 }
19 allocateString(string) {51 allocateString(string) {
20 const byteLength = new TextEncoder().encode(string).length;52 const byteLength = new TextEncoder().encode(string).length;
21 const addr = this.alloc(byteLength);53 const addr = this.alloc(byteLength + 1);
22 this.wasm.writeString(addr, string);54 this.wasm.writeString(addr, string);
23 return addr;55 return addr;
24 }56 }
36 const result = this.wasm.readString(resultAddr).trim();68 const result = this.wasm.readString(resultAddr).trim();
37 this.dealloc(resultAddr);69 this.dealloc(resultAddr);
38 if (resultCode[0] === 1) {70 if (resultCode[0] === 1) {
39 const error = new Error(this.normalizeErrorString(result));71 const error = new Error(result);
40 throw error;72 throw error;
41 } else {73 } else {
42 return result;74 return result;
54 const result = this.wasm.readString(resultAddr);86 const result = this.wasm.readString(resultAddr);
55 this.dealloc(resultAddr);87 this.dealloc(resultAddr);
56 if (resultCode[0] === 1) {88 if (resultCode[0] === 1) {
57 const error = new Error(this.normalizeErrorString(result));89 const error = new Error(result);
58 throw error;90 throw error;
59 } else {91 } else {
60 return result;92 return result;
61 }93 }
62 }94 }
95
96 /**
97 * Destroys vm, any future call to this object will fail, and all resources will be freed
98 */
63 normalizeErrorString(str) {99 destroy() {
64 str = str.trim();100 this.wasm.exports.jsonnet_destroy(this.vm);
65 const newLine = str.indexOf('\n');
66 if (newLine === -1) return str;
67 let message = str.slice(0, newLine);
68 let trace = str.slice(newLine + 1).split('\n').map(s => s.split(' ---- ')).map(([p, v]) => ` at ${v} (${p})`).join('\n');101 this.wasm.importCbs.delete(this.vm);
69 return `${message}\n${trace}`;
70 }102 }
71}103}
72104
73class JsonnetWASM {105class JsonnetWASM {
74 constructor() { }106 constructor() {
107 this.importCbs = new Map();
108 }
75109
76 async init(buf) {110 async init(buf) {
77 const wasm = await WebAssembly.compile(buf);111 const wasm = await WebAssembly.compile(buf);
78 const instance = await WebAssembly.instantiate(wasm, importObject);112 const instance = await WebAssembly.instantiate(wasm, {
113 wasi_snapshot_preview1: wasi.wasiImport,
114 env: {
115 _jrsonnet_static_import_callback: (ctx, base, rel, found_here, success) => {
116 if (!this.importCbs.has(ctx)) {
117 throw new Error(`Got unknown ctx callback: ${ctx}`);
118 }
119 return this.importCbs.get(ctx)(base, rel, found_here, success);
120 }
121 }
122 });
79 wasi.start(instance);123 wasi.start(instance);
80 this.instance = instance;124 this.instance = instance;
81 }125 }
126 /**
127 * @type Record<string, WebAssembly.ExportValue>
128 */
82 get exports() {129 get exports() {
83 return this.instance.exports;130 return this.instance.exports;
84 }131 }
91 memorySliceLen(start, length) {138 memorySliceLen(start, length) {
92 return new Uint8Array(this.memoryBuffer, start, length);139 return new Uint8Array(this.memoryBuffer, start, length);
93 }140 }
141 memorySlice32Len(start, length) {
142 return new Uint32Array(this.memoryBuffer, start, length);
143 }
94 memorySlice(start, end) {144 memorySlice(start, end) {
95 return new Uint8Array(this.memoryBuffer, start, start && end && (end - start));145 return new Uint8Array(this.memoryBuffer, start, start && end && (end - start));
96 }146 }
119(async () => {169(async () => {
120 try {170 try {
121 const jsonnet = new JsonnetWASM();171 const jsonnet = new JsonnetWASM();
122 await jsonnet.init(fs.readFileSync(`${__dirname}/../../target/wasm32-wasi/release/jsonnet.wasi.wasm`));172 await jsonnet.init(fs.readFileSync(`${__dirname}/../../target/wasm32-wasi/release/jsonnet.wasm`));
123 console.log(`Version = ${jsonnet.version()}`);173 console.log(`Version = ${jsonnet.version()}`);
124174
125 const vm = jsonnet.newVM();175 const vm = jsonnet.newVM();
126 console.log(vm.evaluateSnippet('./snip.jsonnet', `176 console.log(vm.evaluateSnippet('./snip.jsonnet', `
127 local a(b) = error "sad" + b;177 2+2
128 local c() = a(2 + 2);
129 c()
130 `))178 `));
131 console.log(vm.evaluateFile('./test.jsonnet'));179 console.log(vm.evaluateFile('./test.jsonnet'));
132 } catch (e) {180 } catch (e) {
133 console.log(e.stack);181 console.log(e.stack);
modifiedbindings/jsonnet/Cargo.tomldiffbeforeafterboth
--- a/bindings/jsonnet/Cargo.toml
+++ b/bindings/jsonnet/Cargo.toml
@@ -9,7 +9,6 @@
 [dependencies]
 jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "1.0.0" }
 jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "1.0.0" }
-libc = "0.2.71"
 
 [lib]
 crate-type = ["cdylib"]
addedbindings/jsonnet/src/interop.rsdiffbeforeafterboth
--- /dev/null
+++ b/bindings/jsonnet/src/interop.rs
@@ -0,0 +1,36 @@
+//! Jrsonnet specific additional binding helpers
+
+use crate::import::jsonnet_import_callback;
+use jrsonnet_evaluator::EvaluationState;
+use std::{
+	ffi::c_void,
+	os::raw::{c_char, c_int},
+};
+
+extern "C" {
+	pub fn _jrsonnet_static_import_callback(
+		ctx: *mut c_void,
+		base: *const c_char,
+		rel: *const c_char,
+		found_here: *mut *const c_char,
+		success: &mut c_int,
+	) -> *const c_char;
+}
+
+/// # Safety
+#[no_mangle]
+pub unsafe extern "C" fn jrsonnet_apply_static_import_callback(
+	vm: &EvaluationState,
+	ctx: *mut c_void,
+) {
+	jsonnet_import_callback(vm, _jrsonnet_static_import_callback, ctx)
+}
+
+#[no_mangle]
+pub extern "C" fn jrsonnet_set_trace_format(vm: &EvaluationState, format: u8) {
+	use jrsonnet_evaluator::trace::JSFormat;
+	match format {
+		1 => vm.set_trace_format(Box::new(JSFormat)),
+		_ => panic!("unknown trace format"),
+	}
+}
modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -1,23 +1,24 @@
-use jrsonnet_evaluator::{
-	create_error, create_error_result, Error, EvaluationState, ImportResolver, LazyBinding,
-	LazyVal, ObjMember, ObjValue, Result, Val,
-};
-use jrsonnet_parser::Visibility;
-use libc::{c_char, c_double, c_int, c_uint};
+#![feature(custom_inner_attributes)]
+
+pub mod import;
+pub mod interop;
+pub mod val_extract;
+pub mod val_make;
+pub mod val_modify;
+pub mod vars_tlas;
+
+use import::NativeImportResolver;
+use jrsonnet_evaluator::{EvaluationState, ManifestFormat, Val};
 use std::{
 	alloc::Layout,
-	any::Any,
-	cell::RefCell,
-	collections::HashMap,
 	ffi::{CStr, CString},
-	fs::File,
-	io::Read,
+	os::raw::{c_char, c_double, c_int, c_uint},
 	path::PathBuf,
 	rc::Rc,
 };
 
+/// WASM stub
 #[no_mangle]
-#[cfg(target = "wasm32-wasi")]
 pub extern "C" fn _start() {}
 
 #[no_mangle]
@@ -25,45 +26,6 @@
 	b"v0.16.0\0"
 }
 
-#[derive(Default)]
-struct NativeImportResolver {
-	library_paths: RefCell<Vec<PathBuf>>,
-}
-impl NativeImportResolver {
-	fn add_jpath(&self, path: PathBuf) {
-		self.library_paths.borrow_mut().push(path);
-	}
-}
-impl ImportResolver for NativeImportResolver {
-	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
-		let mut new_path = from.clone();
-		new_path.push(path);
-		if new_path.exists() {
-			Ok(Rc::new(new_path))
-		} else {
-			for library_path in self.library_paths.borrow().iter() {
-				let mut cloned = library_path.clone();
-				cloned.push(path);
-				if cloned.exists() {
-					return Ok(Rc::new(cloned));
-				}
-			}
-			create_error_result(Error::ImportFileNotFound(from.clone(), path.clone()))
-		}
-	}
-	fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {
-		let mut file =
-			File::open(id).map_err(|_e| create_error(Error::ResolvedFileNotFound(id.clone())))?;
-		let mut out = String::new();
-		file.read_to_string(&mut out)
-			.map_err(|_e| create_error(Error::ImportBadFileUtf8(id.clone())))?;
-		Ok(out.into())
-	}
-	unsafe fn as_any(&self) -> &dyn Any {
-		self
-	}
-}
-
 #[no_mangle]
 pub extern "C" fn jsonnet_make() -> *mut EvaluationState {
 	let state = EvaluationState::default();
@@ -89,134 +51,18 @@
 pub extern "C" fn jsonnet_gc_min_objects(_vm: &EvaluationState, _v: c_uint) {}
 #[no_mangle]
 pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &EvaluationState, _v: c_double) {}
-
-// TODO
-#[no_mangle]
-pub extern "C" fn jsonnet_string_output(_vm: &EvaluationState, _v: c_int) {
-	todo!()
-}
 
 #[no_mangle]
-pub extern "C" fn jsonnet_json_extract_string(_vm: &EvaluationState, v: &Val) -> *mut c_char {
-	match v.unwrap_if_lazy().unwrap() {
-		Val::Str(s) => CString::new(&*s as &str).unwrap().into_raw(),
-		_ => std::ptr::null_mut(),
-	}
-}
-#[no_mangle]
-pub extern "C" fn jsonnet_json_extract_number(
-	_vm: &EvaluationState,
-	v: &Val,
-	out: &mut c_double,
-) -> c_int {
-	match v.unwrap_if_lazy().unwrap() {
-		Val::Num(n) => {
-			*out = n;
-			1
-		}
-		_ => 0,
-	}
-}
-#[no_mangle]
-pub extern "C" fn jsonnet_json_extract_bool(_vm: &EvaluationState, v: &Val) -> c_int {
-	match v.unwrap_if_lazy().unwrap() {
-		Val::Bool(false) => 0,
-		Val::Bool(true) => 1,
-		_ => 2,
-	}
-}
-#[no_mangle]
-pub extern "C" fn jsonnet_json_extract_null(_vm: &EvaluationState, v: &Val) -> c_int {
-	match v.unwrap_if_lazy().unwrap() {
-		Val::Null => 1,
-		_ => 0,
-	}
-}
-
-/// # Safety
-///
-/// This function is safe, if received v is a pointer to normal C string
-#[no_mangle]
-pub unsafe extern "C" fn jsonnet_json_make_string(
-	_vm: &EvaluationState,
-	v: *const c_char,
-) -> *mut Val {
-	let cstr = CStr::from_ptr(v);
-	let str = cstr.to_str().unwrap();
-	Box::into_raw(Box::new(Val::Str(str.into())))
-}
-
-#[no_mangle]
-pub extern "C" fn jsonnet_json_make_number(_vm: &EvaluationState, v: c_double) -> *mut Val {
-	Box::into_raw(Box::new(Val::Num(v)))
-}
-
-#[no_mangle]
-pub extern "C" fn jsonnet_json_make_bool(_vm: &EvaluationState, v: c_int) -> *mut Val {
-	assert!(v == 0 || v == 1);
-	Box::into_raw(Box::new(Val::Bool(v == 1)))
-}
-
-#[no_mangle]
-pub extern "C" fn jsonnet_json_make_null(_vm: &EvaluationState) -> *mut Val {
-	Box::into_raw(Box::new(Val::Null))
-}
-
-#[no_mangle]
-pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> *mut Val {
-	Box::into_raw(Box::new(Val::Arr(Rc::new(Vec::new()))))
-}
-
-#[no_mangle]
-pub extern "C" fn jsonnet_json_array_append(_vm: &EvaluationState, arr: &mut Val, val: &Val) {
-	match arr {
-		Val::Arr(old) => {
-			// TODO: Mutate array, instead of recreating them
-			let mut new = Vec::new();
-			new.extend(old.iter().cloned());
-			new.push(val.clone());
-			*arr = Val::Arr(Rc::new(new));
-		}
-		_ => panic!("should receive array"),
+pub extern "C" fn jsonnet_string_output(vm: &EvaluationState, v: c_int) {
+	match v {
+		1 => vm.set_manifest_format(ManifestFormat::None),
+		0 => vm.set_manifest_format(ManifestFormat::Json(4)),
+		_ => panic!("incorrect output format"),
 	}
-}
-
-#[no_mangle]
-pub extern "C" fn jsonnet_json_make_object(_vm: &EvaluationState) -> *mut Val {
-	Box::into_raw(Box::new(Val::Obj(ObjValue::new_empty())))
 }
 
 /// # Safety
 ///
-/// This function is safe if passed name is ok
-#[no_mangle]
-pub unsafe extern "C" fn jsonnet_json_object_append(
-	_vm: &EvaluationState,
-	obj: &mut Val,
-	name: *const c_char,
-	val: &Val,
-) {
-	match obj {
-		Val::Obj(old) => {
-			let mut new = HashMap::new();
-			new.insert(
-				CStr::from_ptr(name).to_str().unwrap().into(),
-				ObjMember {
-					add: false,
-					visibility: Visibility::Normal,
-					invoke: LazyBinding::Bound(LazyVal::new_resolved(val.clone())),
-					location: None,
-				},
-			);
-			let new_obj = ObjValue::new(Some(old.clone()), Rc::new(new));
-			*obj = Val::Obj(new_obj);
-		}
-		_ => panic!("should receive array"),
-	}
-}
-
-/// # Safety
-///
 /// This function is most definitely broken, but it works somehow, see TODO inside
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_realloc(
@@ -247,48 +93,14 @@
 	Box::from_raw(v);
 }
 
-#[no_mangle]
-pub extern "C" fn jsonnet_import_callback() {
-	todo!()
-}
 #[no_mangle]
 pub extern "C" fn jsonnet_native_callback() {
-	todo!()
-}
-#[no_mangle]
-pub extern "C" fn jsonnet_ext_var() {
 	todo!()
 }
-#[no_mangle]
-pub extern "C" fn jsonnet_ext_code() {
-	todo!()
-}
-#[no_mangle]
-pub extern "C" fn jsonnet_tla_var() {
-	todo!()
-}
-#[no_mangle]
-pub extern "C" fn jsonnet_tla_code() {
-	todo!()
-}
-#[no_mangle]
-pub extern "C" fn jsonnet_max_trace() {
-	todo!()
-}
 
-/// # Safety
-///
-/// This function is safe, if received v is a pointer to normal C string
 #[no_mangle]
-pub unsafe extern "C" fn jsonnet_jpath_add(vm: &EvaluationState, v: *const c_char) {
-	let cstr = CStr::from_ptr(v);
-	let path = PathBuf::from(cstr.to_str().unwrap());
-	let any_resolver = &vm.settings().import_resolver;
-	let resolver = any_resolver
-		.as_any()
-		.downcast_ref::<NativeImportResolver>()
-		.unwrap();
-	resolver.add_jpath(path);
+pub extern "C" fn jsonnet_max_trace(vm: &EvaluationState, v: c_uint) {
+	vm.set_max_trace(v as usize)
 }
 
 /// # Safety
@@ -301,20 +113,19 @@
 	error: &mut c_int,
 ) -> *const c_char {
 	vm.run_in_state(|| {
-		use std::fmt::Write;
 		let filename = CStr::from_ptr(filename);
-		match vm.evaluate_file_to_json(&PathBuf::from(filename.to_str().unwrap())) {
+		match vm
+			.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))
+			.and_then(|v| vm.with_tla(v))
+			.and_then(|v| vm.manifest(v))
+		{
 			Ok(v) => {
 				*error = 0;
 				CString::new(&*v as &str).unwrap().into_raw()
 			}
 			Err(e) => {
 				*error = 1;
-				let mut out = String::new();
-				writeln!(out, "{:?}", e.0).unwrap();
-				for i in (e.1).0.iter() {
-					writeln!(out, "{:?}", i.0).unwrap();
-				}
+				let out = vm.stringify_err(&e);
 				CString::new(&out as &str).unwrap().into_raw()
 			}
 		}
@@ -332,24 +143,23 @@
 	error: &mut c_int,
 ) -> *const c_char {
 	vm.run_in_state(|| {
-		use std::fmt::Write;
 		let filename = CStr::from_ptr(filename);
 		let snippet = CStr::from_ptr(snippet);
-		match vm.evaluate_snippet_to_json(
-			&PathBuf::from(filename.to_str().unwrap()),
-			&snippet.to_str().unwrap(),
-		) {
+		match vm
+			.evaluate_snippet_raw(
+				Rc::new(PathBuf::from(filename.to_str().unwrap())),
+				snippet.to_str().unwrap().into(),
+			)
+			.and_then(|v| vm.with_tla(v))
+			.and_then(|v| vm.manifest(v))
+		{
 			Ok(v) => {
 				*error = 0;
 				CString::new(&*v as &str).unwrap().into_raw()
 			}
 			Err(e) => {
 				*error = 1;
-				let mut out = String::new();
-				writeln!(out, "{:?}", e.0).unwrap();
-				for i in (e.1).0.iter() {
-					writeln!(out, "{:?} ---- {}", i.0, i.1).unwrap();
-				}
+				let out = vm.stringify_err(&e);
 				CString::new(&out as &str).unwrap().into_raw()
 			}
 		}
addedbindings/jsonnet/src/val_extract.rsdiffbeforeafterboth
--- /dev/null
+++ b/bindings/jsonnet/src/val_extract.rs
@@ -0,0 +1,45 @@
+//! Extract values from VM
+
+use jrsonnet_evaluator::{EvaluationState, Val};
+
+use std::{
+	ffi::CString,
+	os::raw::{c_char, c_double, c_int},
+};
+
+#[no_mangle]
+pub extern "C" fn jsonnet_json_extract_string(_vm: &EvaluationState, v: &Val) -> *mut c_char {
+	match v.unwrap_if_lazy().unwrap() {
+		Val::Str(s) => CString::new(&*s as &str).unwrap().into_raw(),
+		_ => std::ptr::null_mut(),
+	}
+}
+#[no_mangle]
+pub extern "C" fn jsonnet_json_extract_number(
+	_vm: &EvaluationState,
+	v: &Val,
+	out: &mut c_double,
+) -> c_int {
+	match v.unwrap_if_lazy().unwrap() {
+		Val::Num(n) => {
+			*out = n;
+			1
+		}
+		_ => 0,
+	}
+}
+#[no_mangle]
+pub extern "C" fn jsonnet_json_extract_bool(_vm: &EvaluationState, v: &Val) -> c_int {
+	match v.unwrap_if_lazy().unwrap() {
+		Val::Bool(false) => 0,
+		Val::Bool(true) => 1,
+		_ => 2,
+	}
+}
+#[no_mangle]
+pub extern "C" fn jsonnet_json_extract_null(_vm: &EvaluationState, v: &Val) -> c_int {
+	match v.unwrap_if_lazy().unwrap() {
+		Val::Null => 1,
+		_ => 0,
+	}
+}
addedbindings/jsonnet/src/val_make.rsdiffbeforeafterboth
--- /dev/null
+++ b/bindings/jsonnet/src/val_make.rs
@@ -0,0 +1,47 @@
+//! Create values in VM
+
+use jrsonnet_evaluator::{EvaluationState, ObjValue, Val};
+use std::{
+	ffi::CStr,
+	os::raw::{c_char, c_double, c_int},
+	rc::Rc,
+};
+
+/// # Safety
+///
+/// This function is safe, if received v is a pointer to normal C string
+#[no_mangle]
+pub unsafe extern "C" fn jsonnet_json_make_string(
+	_vm: &EvaluationState,
+	v: *const c_char,
+) -> *mut Val {
+	let cstr = CStr::from_ptr(v);
+	let str = cstr.to_str().unwrap();
+	Box::into_raw(Box::new(Val::Str(str.into())))
+}
+
+#[no_mangle]
+pub extern "C" fn jsonnet_json_make_number(_vm: &EvaluationState, v: c_double) -> *mut Val {
+	Box::into_raw(Box::new(Val::Num(v)))
+}
+
+#[no_mangle]
+pub extern "C" fn jsonnet_json_make_bool(_vm: &EvaluationState, v: c_int) -> *mut Val {
+	assert!(v == 0 || v == 1);
+	Box::into_raw(Box::new(Val::Bool(v == 1)))
+}
+
+#[no_mangle]
+pub extern "C" fn jsonnet_json_make_null(_vm: &EvaluationState) -> *mut Val {
+	Box::into_raw(Box::new(Val::Null))
+}
+
+#[no_mangle]
+pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> *mut Val {
+	Box::into_raw(Box::new(Val::Arr(Rc::new(Vec::new()))))
+}
+
+#[no_mangle]
+pub extern "C" fn jsonnet_json_make_object(_vm: &EvaluationState) -> *mut Val {
+	Box::into_raw(Box::new(Val::Obj(ObjValue::new_empty())))
+}
addedbindings/jsonnet/src/val_modify.rsdiffbeforeafterboth
--- /dev/null
+++ b/bindings/jsonnet/src/val_modify.rs
@@ -0,0 +1,55 @@
+//! Modify VM values
+//! Only tested with variables, which haven't altered by code before appearing here
+//! In jrsonnet every value is immutable, and this code is probally broken
+
+use jrsonnet_evaluator::{EvaluationState, LazyBinding, LazyVal, ObjMember, ObjValue, Val};
+use jrsonnet_parser::Visibility;
+use std::{collections::HashMap, ffi::CStr, os::raw::c_char, rc::Rc};
+
+/// # Safety
+///
+/// Received arr value should be correct pointer to array allocated by make_array
+#[no_mangle]
+pub unsafe extern "C" fn jsonnet_json_array_append(
+	_vm: &EvaluationState,
+	arr: *mut Val,
+	val: &Val,
+) {
+	match *Box::from_raw(arr) {
+		Val::Arr(old) => {
+			let mut new = Rc::try_unwrap(old).expect("arr with no refs");
+			new.push(val.clone());
+			*arr = Val::Arr(Rc::new(new));
+		}
+		_ => panic!("should receive array"),
+	}
+}
+
+/// # Safety
+///
+/// This function is safe if passed name is ok
+#[no_mangle]
+pub unsafe extern "C" fn jsonnet_json_object_append(
+	_vm: &EvaluationState,
+	obj: &mut Val,
+	name: *const c_char,
+	val: &Val,
+) {
+	match obj {
+		Val::Obj(old) => {
+			let mut new = HashMap::new();
+			new.insert(
+				CStr::from_ptr(name).to_str().unwrap().into(),
+				ObjMember {
+					add: false,
+					visibility: Visibility::Normal,
+					invoke: LazyBinding::Bound(LazyVal::new_resolved(val.clone())),
+					location: None,
+				},
+			);
+			let new_obj = ObjValue::new(Some(old.clone()), Rc::new(new));
+			*obj = Val::Obj(new_obj);
+		}
+		_ => panic!("should receive object"),
+	}
+}
addedbindings/jsonnet/src/vars_tlas.rsdiffbeforeafterboth
--- /dev/null
+++ b/bindings/jsonnet/src/vars_tlas.rs
@@ -0,0 +1,64 @@
+//! Manipulate external variables and top level arguments
+
+use jrsonnet_evaluator::EvaluationState;
+use std::{ffi::CStr, os::raw::c_char};
+
+/// # Safety
+#[no_mangle]
+pub unsafe extern "C" fn jsonnet_ext_var(
+	vm: &EvaluationState,
+	name: *const c_char,
+	value: *const c_char,
+) {
+	let name = CStr::from_ptr(name);
+	let value = CStr::from_ptr(value);
+	vm.add_ext_str(
+		name.to_str().unwrap().into(),
+		value.to_str().unwrap().into(),
+	)
+}
+
+/// # Safety
+#[no_mangle]
+pub unsafe extern "C" fn jsonnet_ext_code(
+	vm: &EvaluationState,
+	name: *const c_char,
+	value: *const c_char,
+) {
+	let name = CStr::from_ptr(name);
+	let value = CStr::from_ptr(value);
+	vm.add_ext_code(
+		name.to_str().unwrap().into(),
+		value.to_str().unwrap().into(),
+	)
+	.unwrap()
+}
+/// # Safety
+#[no_mangle]
+pub unsafe extern "C" fn jsonnet_tla_var(
+	vm: &EvaluationState,
+	name: *const c_char,
+	value: *const c_char,
+) {
+	let name = CStr::from_ptr(name);
+	let value = CStr::from_ptr(value);
+	vm.add_tla_str(
+		name.to_str().unwrap().into(),
+		value.to_str().unwrap().into(),
+	)
+}
+/// # Safety
+#[no_mangle]
+pub unsafe extern "C" fn jsonnet_tla_code(
+	vm: &EvaluationState,
+	name: *const c_char,
+	value: *const c_char,
+) {
+	let name = CStr::from_ptr(name);
+	let value = CStr::from_ptr(value);
+	vm.add_tla_code(
+		name.to_str().unwrap().into(),
+		value.to_str().unwrap().into(),
+	)
+	.unwrap()
+}
deletedbindings/test.jsonnetdiffbeforeafterboth
--- a/bindings/test.jsonnet
+++ /dev/null
@@ -1 +0,0 @@
-2 + 2
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -56,10 +56,17 @@
 	StackOverflow,
 	FractionalIndex,
 	DivisionByZero,
+
+	StringManifestOutputIsNotAString,
+
+	ImportCallbackError(String),
 }
 
 #[derive(Clone, Debug)]
-pub struct StackTraceElement(pub ExprLocation, pub String);
+pub struct StackTraceElement {
+	pub location: ExprLocation,
+	pub desc: String,
+}
 #[derive(Debug, Clone)]
 pub struct StackTrace(pub Vec<StackTraceElement>);