From 663abde785a6af0294a5e561d979c687badcbc5c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 13 Mar 2024 12:48:55 +0000 Subject: [PATCH] Optimise isolated-vm snippet performance by using a map and by caching evaluated snippets --- .../jsRunner/bundles/snippets.ivm.bundle.js | 6 +++--- .../server/src/jsRunner/bundles/snippets.ts | 19 ++++++++++++------- .../server/src/jsRunner/vm/isolated-vm.ts | 8 +++++++- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/server/src/jsRunner/bundles/snippets.ivm.bundle.js b/packages/server/src/jsRunner/bundles/snippets.ivm.bundle.js index 5adb19eaf7..85fb8bbd5a 100644 --- a/packages/server/src/jsRunner/bundles/snippets.ivm.bundle.js +++ b/packages/server/src/jsRunner/bundles/snippets.ivm.bundle.js @@ -1,3 +1,3 @@ -"use strict";var snippets=(()=>{var u=Object.create;var p=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var m=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty;var l=(e,n)=>()=>(n||e((n={exports:{}}).exports,n),n.exports),W=(e,n)=>{for(var i in n)p(e,i,{get:n[i],enumerable:!0})},o=(e,n,i,t)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of d(n))!x.call(e,r)&&r!==i&&p(e,r,{get:()=>n[r],enumerable:!(t=c(n,r))||t.enumerable});return e};var g=(e,n,i)=>(i=e!=null?u(m(e)):{},o(n||!e||!e.__esModule?p(i,"default",{value:e,enumerable:!0}):i,e)),v=e=>o(p({},"__esModule",{value:!0}),e);var a=l((P,f)=>{f.exports.iifeWrapper=e=>`(function(){ -${e} -})();`});var y={};W(y,{default:()=>w});var s=g(a()),w=new Proxy({},{get:function(e,n){let i=(snippetDefinitions||[]).find(t=>t.name===n);return[eval][0]((0,s.iifeWrapper)(i.code))}});return v(y);})(); +"use strict";var snippets=(()=>{var a=Object.create;var r=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var l=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty;var C=(i,e)=>()=>(e||i((e={exports:{}}).exports,e),e.exports),y=(i,e)=>{for(var n in e)r(i,n,{get:e[n],enumerable:!0})},f=(i,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let p of h(e))!x.call(i,p)&&p!==n&&r(i,p,{get:()=>e[p],enumerable:!(t=c(e,p))||t.enumerable});return i};var W=(i,e,n)=>(n=i!=null?a(l(i)):{},f(e||!i||!i.__esModule?r(n,"default",{value:i,enumerable:!0}):n,i)),d=i=>f(r({},"__esModule",{value:!0}),i);var s=C((D,o)=>{o.exports.iifeWrapper=i=>`(function(){ +${i} +})();`});var v={};y(v,{default:()=>g});var u=W(s()),g=new Proxy({},{get:function(i,e){if(!(e in snippetCache))snippetCache[e]=[eval][0]((0,u.iifeWrapper)(snippetDefinitions[e]));else return n=>n*2;return snippetCache[e]}});return d(v);})(); diff --git a/packages/server/src/jsRunner/bundles/snippets.ts b/packages/server/src/jsRunner/bundles/snippets.ts index f473aaf7b4..258d501a27 100644 --- a/packages/server/src/jsRunner/bundles/snippets.ts +++ b/packages/server/src/jsRunner/bundles/snippets.ts @@ -6,14 +6,19 @@ export default new Proxy( {}, { get: function (_, name) { - // Snippet definitions are injected to the isolate global scope before - // this bundle is loaded, so we can access it from there. - // https://esbuild.github.io/content-types/#direct-eval for info on why - // eval is being called this way. + // Both snippetDefinitions and snippetCache are injected to the isolate + // global scope before this bundle is loaded, so we can access it from + // there. + // See https://esbuild.github.io/content-types/#direct-eval for info on + // why eval is being called this way. + // Snippets are cached and reused once they have been evaluated. // @ts-ignore - // eslint-disable-next-line no-undef - const snippet = (snippetDefinitions || []).find(x => x.name === name) - return [eval][0](iifeWrapper(snippet.code)) + if (!(name in snippetCache)) { + // @ts-ignore + snippetCache[name] = [eval][0](iifeWrapper(snippetDefinitions[name])) + } + // @ts-ignore + return snippetCache[name] }, } ) diff --git a/packages/server/src/jsRunner/vm/isolated-vm.ts b/packages/server/src/jsRunner/vm/isolated-vm.ts index 64e68c296d..e89d420ec5 100644 --- a/packages/server/src/jsRunner/vm/isolated-vm.ts +++ b/packages/server/src/jsRunner/vm/isolated-vm.ts @@ -99,9 +99,15 @@ export class IsolatedVM implements VM { } withSnippets(snippets?: Snippet[]) { + // Transform snippets into a map for faster access + let snippetMap: Record = {} + for (let snippet of snippets || []) { + snippetMap[snippet.name] = snippet.code + } const snippetsSource = loadBundle(BundleType.SNIPPETS) const script = this.isolate.compileScriptSync(` - const snippetDefinitions = ${JSON.stringify(snippets || [])}; + const snippetDefinitions = ${JSON.stringify(snippetMap)}; + const snippetCache = {}; ${snippetsSource}; snippets = snippets.default; `)