git.delta.rocks / jrsonnet / refs/commits / 1979dbda1bc0

difftreelog

source

bindings/js/index.js4.8 KiBsourcehistory
1const fs = require('fs');2const path = require('path');3const { WASI } = require('wasi');4const wasi = new WASI({5	args: process.argv,6	env: process.env,7	preopens: {},8});910class JsonnetVM {11	constructor(wasm, vm) {12		this.wasm = wasm;13		this.vm = vm;14		this.wasm.exports.jrsonnet_set_trace_format(this.vm, 1);1516		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		})23	}2425	/**26	 * @param {(from: string, to: string) => {foundHere: string, value: string}} cb27	 */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	}4748	alloc(length) {49		return this.wasm.exports.jsonnet_realloc(this.vm, 0, length);50	}51	allocateString(string) {52		const byteLength = new TextEncoder().encode(string).length;53		const addr = this.alloc(byteLength + 1);54		this.wasm.writeString(addr, string);55		return addr;56	}57	dealloc(addr) {58		return this.wasm.exports.jsonnet_realloc(this.vm, addr, 0);59	}6061	evaluateFile(path) {62		const pathAddr = this.allocateString(path);63		const resultCodeAddr = this.alloc(4);64		const resultAddr = this.wasm.exports.jsonnet_evaluate_file(this.vm, pathAddr, resultCodeAddr);65		this.dealloc(pathAddr);66		const resultCode = this.wasm.memorySliceLen(resultCodeAddr, 4);67		this.dealloc(resultCodeAddr);68		const result = this.wasm.readString(resultAddr).trim();69		this.dealloc(resultAddr);70		if (resultCode[0] === 1) {71			const error = new Error(result);72			throw error;73		} else {74			return result;75		}76	}77	evaluateSnippet(path, snippet) {78		const pathAddr = this.allocateString(path);79		const snippetAddr = this.allocateString(snippet);80		const resultCodeAddr = this.alloc(4);81		const resultAddr = this.wasm.exports.jsonnet_evaluate_snippet(this.vm, pathAddr, snippetAddr, resultCodeAddr);82		this.dealloc(pathAddr);83		this.dealloc(snippetAddr);84		const resultCode = this.wasm.memorySliceLen(resultCodeAddr, 4);85		this.dealloc(resultCodeAddr);86		const result = this.wasm.readString(resultAddr);87		this.dealloc(resultAddr);88		if (resultCode[0] === 1) {89			const error = new Error(result);90			throw error;91		} else {92			return result;93		}94	}9596	/**97	 * Destroys vm, any future call to this object will fail, and all resources will be freed98	 */99	destroy() {100		this.wasm.exports.jsonnet_destroy(this.vm);101		this.wasm.importCbs.delete(this.vm);102	}103}104105class JsonnetWASM {106	constructor() {107		this.importCbs = new Map();108	}109110	async init(buf) {111		const wasm = await WebAssembly.compile(buf);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		});123		wasi.start(instance);124		this.instance = instance;125	}126	/**127	 * @type Record<string, WebAssembly.ExportValue>128	 */129	get exports() {130		return this.instance.exports;131	}132	get memory() {133		return this.exports.memory;134	}135	get memoryBuffer() {136		return this.memory.buffer;137	}138	memorySliceLen(start, length) {139		return new Uint8Array(this.memoryBuffer, start, length);140	}141	memorySlice32Len(start, length) {142		return new Uint32Array(this.memoryBuffer, start, length);143	}144	memorySlice(start, end) {145		return new Uint8Array(this.memoryBuffer, start, start && end && (end - start));146	}147148	readString(addr) {149		let end;150		let slice = this.memorySlice();151		for (end = addr; slice[end]; end++);152		return (new TextDecoder()).decode(this.memorySlice(addr, end));153	}154	writeString(addr, string) {155		let slice = this.memorySlice(addr);156		let result = new TextEncoder().encodeInto(string, slice);157		slice[result.written] = 0;158	}159160	version() {161		return this.readString(this.exports.jsonnet_version());162	}163164	newVM() {165		return new JsonnetVM(this, this.exports.jsonnet_make());166	}167}168169(async () => {170	try {171		const jsonnet = new JsonnetWASM();172		await jsonnet.init(fs.readFileSync(`${__dirname}/../../target/wasm32-wasi/release/jsonnet.wasm`));173		console.log(`Version = ${jsonnet.version()}`);174175		const vm = jsonnet.newVM();176		console.log(vm.evaluateSnippet('./snip.jsonnet', `177			2+2178		`));179		console.log(vm.evaluateFile('./test.jsonnet'));180	} catch (e) {181		console.log(e.stack);182	}183})();