diff --git a/frontend/src/lib/components/apps/components/helpers/eval.ts b/frontend/src/lib/components/apps/components/helpers/eval.ts index 9e80c8a75e..5f338f696e 100644 --- a/frontend/src/lib/components/apps/components/helpers/eval.ts +++ b/frontend/src/lib/components/apps/components/helpers/eval.ts @@ -18,16 +18,20 @@ export function computeGlobalContext(world: World | undefined, extraContext: any } } -function create_context_function_template(eval_string: string, context, noReturn: boolean) { +function create_context_function_template( + eval_string: string, + contextKeys: string[], + noReturn: boolean +) { let lastBreak = eval_string.lastIndexOf('\n') - let lines = lastBreak == -1 ? "" : eval_string.substring(0, lastBreak) + let lines = lastBreak == -1 ? '' : eval_string.substring(0, lastBreak) let lastLine = lastBreak == -1 ? eval_string : eval_string.substring(lastBreak + 1) return ` return async function (context, state, goto, setTab, recompute, getAgGrid, setValue, setSelectedIndex, openModal, closeModal, open, close, validate, invalidate, validateAll, clearFiles, showToast) { "use strict"; ${ - Object.keys(context).length > 0 - ? `let ${Object.keys(context).map((key) => ` ${key} = context['${key}']`)};` + contextKeys && contextKeys.length > 0 + ? `let ${contextKeys.map((key) => ` ${key} = context['${key}']`)};` : `` } ${lines} @@ -40,11 +44,7 @@ ${ ` } -function make_context_evaluator( - eval_string, - context, - noReturn: boolean -): ( +type WmFunctor = ( context, state, goto, @@ -62,11 +62,19 @@ function make_context_evaluator( validateAll, clearFiles, showToast -) => Promise { - let template = create_context_function_template(eval_string, context, noReturn) - let functor = Function(template) +) => Promise - return functor() +let functorCache: Record = {} +function make_context_evaluator(eval_string, contextKeys: string[], noReturn: boolean): WmFunctor { + let cacheKey = hashCode(JSON.stringify({ eval_string, contextKeys, noReturn })) + if (functorCache[cacheKey]) { + return functorCache[cacheKey] + } + let template = create_context_function_template(eval_string, contextKeys, noReturn) + let functor = Function(template) + let r = functor() + functorCache[cacheKey] = r + return r } function isSerializable(obj) { @@ -101,6 +109,18 @@ function isSerializable(obj) { return true } +function hashCode(s: string): number { + var hash = 0, + i, + chr + if (s.length === 0) return hash + for (i = 0; i < s.length; i++) { + chr = s.charCodeAt(i) + hash = (hash << 5) - hash + chr + hash |= 0 // Convert to 32bit integer + } + return hash +} export async function eval_like( text: string, context = {}, @@ -142,8 +162,10 @@ export async function eval_like( return true } }) - let evaluator = make_context_evaluator(text, context, noReturn) - const r = await evaluator( + + let evaluator = make_context_evaluator(text, Object.keys(context ?? {}), noReturn) + // console.log(i, j) + return await evaluator( context, proxiedState, async (x, newTab) => { @@ -201,5 +223,4 @@ export async function eval_like( sendUserToast(message, error) } ) - return r }