1const fs = require('fs');2const { WASI } = require('wasi');3const wasi = new WASI({4 args: process.argv,5 env: process.env,6 preopens: {},7});8const importObject = { wasi_snapshot_preview1: wasi.wasiImport };910class JsonnetVM {11 constructor(wasm, vm) {12 this.wasm = wasm;13 this.vm = vm;14 }1516 alloc(length) {17 return this.wasm.exports.jsonnet_realloc(this.vm, 0, length);18 }19 allocateString(string) {20 const byteLength = new TextEncoder().encode(string).length;21 const addr = this.alloc(byteLength);22 this.wasm.writeString(addr, string);23 return addr;24 }25 dealloc(addr) {26 return this.wasm.exports.jsonnet_realloc(this.vm, addr, 0);27 }2829 evaluateFile(path) {30 const pathAddr = this.allocateString(path);31 const resultCodeAddr = this.alloc(4);32 const resultAddr = this.wasm.exports.jsonnet_evaluate_file(this.vm, pathAddr, resultCodeAddr);33 this.dealloc(pathAddr);34 const resultCode = this.wasm.memorySliceLen(resultCodeAddr, 4);35 this.dealloc(resultCodeAddr);36 const result = this.wasm.readString(resultAddr).trim();37 this.dealloc(resultAddr);38 if (resultCode[0] === 1) {39 const error = new Error(this.normalizeErrorString(result));40 throw error;41 } else {42 return result;43 }44 }45 evaluateSnippet(path, snippet) {46 const pathAddr = this.allocateString(path);47 const snippetAddr = this.allocateString(snippet);48 const resultCodeAddr = this.alloc(4);49 const resultAddr = this.wasm.exports.jsonnet_evaluate_snippet(this.vm, pathAddr, snippetAddr, resultCodeAddr);50 this.dealloc(pathAddr);51 this.dealloc(snippetAddr);52 const resultCode = this.wasm.memorySliceLen(resultCodeAddr, 4);53 this.dealloc(resultCodeAddr);54 const result = this.wasm.readString(resultAddr);55 this.dealloc(resultAddr);56 if (resultCode[0] === 1) {57 const error = new Error(this.normalizeErrorString(result));58 throw error;59 } else {60 return result;61 }62 }63 normalizeErrorString(str) {64 str = str.trim();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');69 return `${message}\n${trace}`;70 }71}7273class JsonnetWASM {74 constructor() { }7576 async init(buf) {77 const wasm = await WebAssembly.compile(buf);78 const instance = await WebAssembly.instantiate(wasm, importObject);79 wasi.start(instance);80 this.instance = instance;81 }82 get exports() {83 return this.instance.exports;84 }85 get memory() {86 return this.exports.memory;87 }88 get memoryBuffer() {89 return this.memory.buffer;90 }91 memorySliceLen(start, length) {92 return new Uint8Array(this.memoryBuffer, start, length);93 }94 memorySlice(start, end) {95 return new Uint8Array(this.memoryBuffer, start, start && end && (end - start));96 }9798 readString(addr) {99 let end;100 let slice = this.memorySlice();101 for (end = addr; slice[end]; end++);102 return (new TextDecoder()).decode(this.memorySlice(addr, end));103 }104 writeString(addr, string) {105 let slice = this.memorySlice(addr);106 let result = new TextEncoder().encodeInto(string, slice);107 slice[result.written] = 0;108 }109110 version() {111 return this.readString(this.exports.jsonnet_version());112 }113114 newVM() {115 return new JsonnetVM(this, this.exports.jsonnet_make());116 }117}118119(async () => {120 try {121 const jsonnet = new JsonnetWASM();122 await jsonnet.init(fs.readFileSync(`${__dirname}/../../target/wasm32-wasi/release/jsonnet.wasi.wasm`));123 console.log(`Version = ${jsonnet.version()}`);124125 const vm = jsonnet.newVM();126 console.log(vm.evaluateSnippet('./snip.jsonnet', `127 local a(b) = error "sad" + b;128 local c() = a(2 + 2);129 c()130 `))131 console.log(vm.evaluateFile('./test.jsonnet'));132 } catch (e) {133 console.log(e.stack);134 }135})();