From 9dfbdba2d339729b1528bde166d0de4d9be50342 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 13 Jul 2020 23:52:18 -0500 Subject: [PATCH] Factory variables Variables that are evaluated once, at compile-time. Makes every variable access even faster than before. Implementation a bit dirty, will clean up later. --- src/compiler/blocks/compiler_scratch3_data.js | 11 ++++---- src/compiler/compiler.js | 27 +++++++++++++++++-- src/compiler/execute.js | 22 ++++++++++----- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/compiler/blocks/compiler_scratch3_data.js b/src/compiler/blocks/compiler_scratch3_data.js index f8f1103f..6278e39b 100644 --- a/src/compiler/blocks/compiler_scratch3_data.js +++ b/src/compiler/blocks/compiler_scratch3_data.js @@ -48,11 +48,11 @@ const findVariable = (util, id, name, type) => { const stage = util.stage; // Search for it by ID if (target.variables.hasOwnProperty(id)) { - return `targetVariables["${util.safe(id)}"]`; + return util.compiler.getOrCreateFactoryVariable(`targetVariables["${util.safe(id)}"]`); } - if (target.runtime && !target.isStage) { + if (!target.isStage) { if (stage && stage.variables.hasOwnProperty(id)) { - return `stageVariables["${util.safe(id)}"]`; + return util.compiler.getOrCreateFactoryVariable(`stageVariables["${util.safe(id)}"]`); } } // Search for it by name and type @@ -60,16 +60,17 @@ const findVariable = (util, id, name, type) => { if (target.variables.hasOwnProperty(varId)) { const currVar = target.variables[varId]; if (currVar.name === name && currVar.type === type) { - return `targetVariables["${util.safe(varId)}"]`; + return util.compiler.getOrCreateFactoryVariable(`targetVariables["${util.safe(varId)}"]`); } } } - if (target.runtime && !target.isStage) { + if (!target.isStage) { if (stage) { for (const varId in stage.variables) { if (stage.variables.hasOwnProperty(varId)) { const currVar = stage.variables[varId]; if (currVar.name === name && currVar.type === type) { + // FIXME: return the JS to evaluate it not the variable itself return currVar; } } diff --git a/src/compiler/compiler.js b/src/compiler/compiler.js index f61693cb..78fd1254 100644 --- a/src/compiler/compiler.js +++ b/src/compiler/compiler.js @@ -392,6 +392,15 @@ class Compiler { */ this.procedures = Object.assign({}, this.blocks._cache.compiledProcedures); + /** + * Factory variables. + * These variables will be setup once when the script factory runs. + * This is a map of Value to variable name. + * It may seem backwards but it makes tracking identical values very easy and efficient. + * @type {Object.} + */ + this.factoryVariables = {}; + /** * Number of local variables created. */ @@ -498,8 +507,9 @@ class Compiler { * @param {CompilerHints} hints */ compileScript(topBlock, hints) { - // blocks will read hints from here + // reset some data this.hints = hints; + this.factoryVariables = {}; let script = ''; @@ -527,7 +537,7 @@ class Compiler { script += '}'; - const fn = execute.createScriptFactory(script); + const fn = execute.createScriptFactory(script, this); log.info(`[${this.target.getName()}] compiled script`, script); return fn; } @@ -540,6 +550,19 @@ class Compiler { return 'a' + this.variableCount; } + /** + * Create or get a factory variable. + * @param {string} value The value of the factory variable. + */ + getOrCreateFactoryVariable(value) { + if (this.factoryVariables.hasOwnProperty(value)) { + return this.factoryVariables[value]; + } + const variableName = this.nextVariable(); + this.factoryVariables[value] = variableName; + return variableName; + } + /** * @returns {CompilationResult} */ diff --git a/src/compiler/execute.js b/src/compiler/execute.js index 7196ecc6..c231317e 100644 --- a/src/compiler/execute.js +++ b/src/compiler/execute.js @@ -390,16 +390,26 @@ const execute = (_thread) => { * Evaluate a continuation from its source code. * Prepares the necessary environment. * @param {string} source + * @param {Compiler} compiler */ -const createScriptFactory = (source) => { - // we cache some very frequently accessed variables up here, it does a lot to help performance. - source = `(function f(target) { +const createScriptFactory = (_source, compiler) => { + let source = `(function f(target) { var runtime = target.runtime; var stage = runtime.getTargetForStage(); var stageVariables = stage.variables; -var targetVariables = target.variables; -return ${source}; -});`; +var targetVariables = target.variables;`; + + for (const data of Object.keys(compiler.factoryVariables)) { + const varName = compiler.factoryVariables[data]; + source += `var ${varName} = ${data};\n`; + } + + source += `return ${_source}; });`; + + // try to get the GC to drop references to these variables, they have no reason to stick around at runtime and can be rather large + // TODO: see if this works or is necessary + _source = null; + compiler = null; try { return eval(source);