Open ncake opened 1 year ago
This adds an option called functionClass, which purpose is to allow to bring your own execution runtime.
functionClass
Here's a basic example of how one could integrate quickjs-emscripten module to execute code in a WASM-powered virtual machine.
// index.js import LoadSafeEJS from "./safe-ejs.js"; const safeEJS = await LoadSafeEJS(); const res = safeEJS.render("<%- 'hello ' + world %>", {world: 'world'}); console.log(res); safeEJS.dispose();
// safe-ejs.js import ejs from 'ejs/ejs.js'; import { getQuickJS } from "quickjs-emscripten" /** @param {import('quickjs-emscripten/dist').QuickJSWASMModule} QuickJS */ function SafeEJS(QuickJS){ const vm = QuickJS.newContext(); this.dispose = function(){ vm.dispose(); }; this.render = function(source, data, ejsOptions){ return ejs.render(source, data, {...ejsOptions, functionClass}) }; const functionClass = function(argNames, funcBody){ // not implemented here: // - caching the function between calls (thus useless for ejs.compile) // - async, include(), probably more return (locals, escapeFn, include, rethrow) => { // create function from source const ctor = vm.getProp(vm.global, 'Function'); const argNamesHandle = vm.newString(argNames); const funcBodyHandle = vm.newString(funcBody); const newFuncRet = vm.callFunction(ctor, vm.undefined, argNamesHandle, funcBodyHandle); ctor.dispose(); argNamesHandle.dispose(); funcBodyHandle.dispose(); const newFunc = vm.unwrapResult(newFuncRet); // wrap user-passed data into quickjs values const handleList = []; const makeDisposable = handle => (handleList.unshift(handle), handle); const localsHandle = wrapValue(locals, makeDisposable); // wrap the rest of the arguments const escapeFnHandle = vm.newFunction('escapeFn', strHandle => { const str = vm.getString(strHandle); const res = escapeFn(str); return vm.newString(res); }); const includeHandle = vm.undefined; const rethrowHandle = vm.newFunction('rethrow', (errHandle, strHandle, flnmHandle, linenoHandle) => { const str = vm.getString(strHandle); const flnm = vm.getString(flnmHandle); const lineno = vm.getNumber(linenoHandle); const errMsg = vm.getProp(errHandle, 'message'); const errName = vm.getProp(errHandle, 'name'); const errStack = vm.getProp(errHandle, 'stack'); const err = new Error(); if(errMsg !== vm.undefined) err.message = vm.getString(errMsg); if(errName !== vm.undefined) err.name = vm.getString(errName); if(errStack !== vm.undefined) err.stack = vm.getString(errStack); rethrow(err, str, flnm, lineno, escapeFn); } ); // execute our function const ret = vm.callFunction( newFunc, vm.undefined, localsHandle, escapeFnHandle, includeHandle, rethrowHandle ); // dispose of everything to prevent memory leaks escapeFnHandle.dispose(); rethrowHandle.dispose(); for(const handle of handleList){ handle.dispose(); } newFunc.dispose(); // return or throw an error const res = vm.unwrapResult(ret); const str = vm.getString(res); res.dispose(); return str; }; }; const wrapValue = function(value, makeDisposable){ if(value === undefined) return vm.undefined; if(value === null) return vm.null; if(typeof value === "boolean") return value ? vm.true : vm.false; if(typeof value === "number") return makeDisposable( vm.newNumber(value) ); if(Array.isArray(value)){ const arr = makeDisposable( vm.newArray() ); for(let i = (value.length - 1); i >= 0; i--) vm.setProp(arr, i, wrapValue(value[i], makeDisposable)); return arr; } if(typeof value === "object"){ const obj = makeDisposable( vm.newObject() ); for(const key in value) vm.setProp(obj, key, wrapValue(value[key], makeDisposable)); return obj; } if(value.toString) return makeDisposable( vm.newString(value.toString()) ); return vm.undefined; }; } async function LoadSafeEJS(){ return new SafeEJS(await getQuickJS()); } export default LoadSafeEJS;
can we get this merged?
Can this get merged? I would love to use quickjs-sandboxed templates in my app
@mde pls
This adds an option called
functionClass
, which purpose is to allow to bring your own execution runtime.Here's a basic example of how one could integrate quickjs-emscripten module to execute code in a WASM-powered virtual machine.