Fix some smaller bugs
This commit is contained in:
8 files changed
+99
-76
No files matched your search
+25
-23
@@ -27,6 +27,14 @@ const compatBlocks = require('./compat-blocks');
|
||||
* @property {string} kind
|
||||
*/
|
||||
|
||||
const createVariableData = (scope, varObj) => ({
|
||||
scope,
|
||||
// todo: maybe just return varObj
|
||||
id: varObj.id,
|
||||
name: varObj.name,
|
||||
isCloud: varObj.isCloud
|
||||
});
|
||||
|
||||
class ScriptTreeGenerator {
|
||||
constructor (thread) {
|
||||
this.thread = thread;
|
||||
@@ -1029,29 +1037,32 @@ class ScriptTreeGenerator {
|
||||
return result;
|
||||
}
|
||||
|
||||
descendVariable (block, name, type) {
|
||||
// todo: cache variables by ID
|
||||
const variable = block.fields[name];
|
||||
descendVariable (block, fieldName, type) {
|
||||
const variable = block.fields[fieldName];
|
||||
const id = variable.id;
|
||||
|
||||
if (this.variableCache.hasOwnProperty(id)) {
|
||||
return this.variableCache[id];
|
||||
}
|
||||
|
||||
const data = this.variableCache[id] = this._descendVariable(id, variable.value, type);
|
||||
this.variableCache[id] = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
_descendVariable (id, name, type) {
|
||||
const target = this.target;
|
||||
const stage = this.stage;
|
||||
|
||||
// Look for by ID in target...
|
||||
if (target.variables.hasOwnProperty(id)) {
|
||||
return {
|
||||
scope: 'target',
|
||||
id
|
||||
};
|
||||
return createVariableData('target', target.variables[id]);
|
||||
}
|
||||
|
||||
// Look for by ID in stage...
|
||||
if (!target.isStage) {
|
||||
if (stage && stage.variables.hasOwnProperty(id)) {
|
||||
return {
|
||||
scope: 'stage',
|
||||
id
|
||||
};
|
||||
return createVariableData('stage', stage.variables[id]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1060,10 +1071,7 @@ class ScriptTreeGenerator {
|
||||
if (target.variables.hasOwnProperty(varId)) {
|
||||
const currVar = target.variables[varId];
|
||||
if (currVar.name === name && currVar.type === type) {
|
||||
return {
|
||||
scope: 'target',
|
||||
id: varId
|
||||
};
|
||||
return createVariableData('target', currVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1074,10 +1082,7 @@ class ScriptTreeGenerator {
|
||||
if (stage.variables.hasOwnProperty(varId)) {
|
||||
const currVar = stage.variables[varId];
|
||||
if (currVar.name === name && currVar.type === type) {
|
||||
return {
|
||||
sccope: 'stage',
|
||||
id: varId
|
||||
};
|
||||
return createVariableData('stage', currVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1086,10 +1091,7 @@ class ScriptTreeGenerator {
|
||||
// Create it locally...
|
||||
const newVariable = new Variable(id, name, type, false);
|
||||
target.variables[id] = newVariable;
|
||||
return {
|
||||
scope: 'target',
|
||||
id: id
|
||||
};
|
||||
return createVariableData('target', newVariable);
|
||||
}
|
||||
|
||||
descendCompatLayer (block) {
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
const stacked = [
|
||||
'control_clear_counter',
|
||||
'control_get_counter',
|
||||
'control_incr_counter',
|
||||
'control_clear_counter',
|
||||
'motion_glideto',
|
||||
'motion_glidesecstoxy',
|
||||
'motion_goto',
|
||||
'sound_play',
|
||||
'sound_playuntildone',
|
||||
'sound_stopallsounds',
|
||||
'sound_seteffectto',
|
||||
'sound_changeeffectby',
|
||||
'sound_cleareffects',
|
||||
'sound_setvolumeto',
|
||||
'sensing_askandwait',
|
||||
'sound_seteffectto',
|
||||
'sound_changeeffectby',
|
||||
'sound_changevolumeby',
|
||||
'looks_nextcostume',
|
||||
'looks_changeeffectby',
|
||||
'looks_nextbackdrop',
|
||||
'looks_nextcostume',
|
||||
'looks_say',
|
||||
'looks_sayforsecs',
|
||||
'looks_think',
|
||||
'looks_changeeffectby',
|
||||
'looks_seteffectto',
|
||||
'looks_think',
|
||||
'looks_thinkforsecs',
|
||||
'motion_glidesecstoxy',
|
||||
'motion_glideto',
|
||||
'motion_goto',
|
||||
'motion_pointtowards',
|
||||
'sensing_askandwait',
|
||||
'sensing_setdragmode',
|
||||
'motion_pointtowards'
|
||||
'sound_changeeffectby',
|
||||
'sound_changeeffectby',
|
||||
'sound_changevolumeby',
|
||||
'sound_cleareffects',
|
||||
'sound_play',
|
||||
'sound_playuntildone',
|
||||
'sound_seteffectto',
|
||||
'sound_seteffectto',
|
||||
'sound_setvolumeto',
|
||||
'sound_stopallsounds'
|
||||
];
|
||||
|
||||
const inputs = [
|
||||
'sound_volume',
|
||||
'sensing_of',
|
||||
'sensing_distanceto',
|
||||
'sensing_answer',
|
||||
'sensing_current',
|
||||
'sensing_dayssince2000',
|
||||
'sensing_loudness',
|
||||
'sensing_distanceto',
|
||||
'sensing_loud',
|
||||
'sensing_answer'
|
||||
'sensing_loudness',
|
||||
'sensing_of',
|
||||
'sound_volume'
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -5,7 +5,7 @@ const compile = thread => {
|
||||
const astGenerator = new ASTGenerator(thread);
|
||||
const ast = astGenerator.generate();
|
||||
|
||||
const jsGenerator = new JSGenerator(ast);
|
||||
const jsGenerator = new JSGenerator(ast, thread.target);
|
||||
const js = jsGenerator.compile();
|
||||
|
||||
return js;
|
||||
|
||||
@@ -3,8 +3,17 @@ const Timer = require('../util/timer');
|
||||
const Cast = require('../util/cast');
|
||||
const log = require('../util/log');
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
const compatibilityLayerBlockUtility = require('./compat-block-utility');
|
||||
|
||||
|
||||
/**
|
||||
* The currently running thread.
|
||||
* @type {Thread}
|
||||
*/
|
||||
var thread;
|
||||
|
||||
// All the functions defined here will be available to compiled scripts.
|
||||
// The JSDoc annotations define the function's contract.
|
||||
// Most of these functions are only used at runtime by generated scripts. Despite what your editor may say, they are not unused.
|
||||
@@ -443,12 +452,6 @@ const mod = (n, modulus) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* The currently running thread.
|
||||
* @type {Thread}
|
||||
*/
|
||||
var thread;
|
||||
|
||||
/**
|
||||
* Step a compiled thread.
|
||||
* @param {Thread} _thread
|
||||
|
||||
+36
-18
@@ -120,9 +120,10 @@ class ConstantInput {
|
||||
}
|
||||
|
||||
class ScriptCompiler {
|
||||
constructor (script, ast) {
|
||||
constructor (script, ast, target) {
|
||||
this.script = script;
|
||||
this.ast = ast;
|
||||
this.target = target;
|
||||
this.source = '';
|
||||
this.localVariables = new VariablePool('a');
|
||||
this._setupVariablesPool = new VariablePool('b');
|
||||
@@ -131,7 +132,7 @@ class ScriptCompiler {
|
||||
|
||||
/**
|
||||
* @param {object} node Input node to compile.
|
||||
* @returns {Input}
|
||||
* @returns {Input} Compiled input.
|
||||
*/
|
||||
descendInput (node) {
|
||||
switch (node.kind) {
|
||||
@@ -144,8 +145,7 @@ class ScriptCompiler {
|
||||
return new TypedInput(`(${this.generateCompatCall(node)})`, TYPE_UNKNOWN);
|
||||
|
||||
case 'constant':
|
||||
// todo: converting to number sometimes break things, need to check for those conditions
|
||||
return new ConstantInput(node.value);
|
||||
return this.safeConstantInput(node.value);
|
||||
|
||||
case 'list.contains':
|
||||
return new TypedInput(`listContains(${this.referenceVariable(node.list)}, ${this.descendInput(node.item).asUnknown()})`, TYPE_BOOLEAN);
|
||||
@@ -287,7 +287,6 @@ class ScriptCompiler {
|
||||
this.source += `runtime.ext_scratch3_control._createClone(${this.descendInput(node.target).asString()}, target);\n`;
|
||||
break;
|
||||
case 'control.deleteClone':
|
||||
// TODO: actually stop thread
|
||||
this.source += 'if (!target.isOriginal) {\n';
|
||||
this.source += ' runtime.disposeTarget(target);\n';
|
||||
this.source += ' runtime.stopForTarget(target);\n';
|
||||
@@ -373,16 +372,16 @@ class ScriptCompiler {
|
||||
this.source += `yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: ${this.descendInput(node.broadcast).asString()} }));\n`;
|
||||
break;
|
||||
|
||||
case 'list.add':
|
||||
this.source += `${this.referenceVariable(node.list)}.value.push(${this.descendInput(node.item).asUnknown()});\n`;
|
||||
// todo _monitorUpToDate
|
||||
case 'list.add': {
|
||||
const list = this.referenceVariable(node.list);
|
||||
this.source += `${list}._monitorUpToDate = false;\n`;
|
||||
break;
|
||||
}
|
||||
case 'list.delete':
|
||||
this.source += `listDelete(${this.referenceVariable(node.list)}, ${this.descendInput(node.index).asUnknown()});\n`;
|
||||
break;
|
||||
case 'list.deleteAll':
|
||||
this.source += `${this.referenceVariable(node.list)}.value = [];\n`;
|
||||
// todo _monitorUpToDate
|
||||
break;
|
||||
case 'list.hide':
|
||||
this.source += `runtime.monitorBlocks.changeBlock({ id: "${sanitize(node.list.id)}", element: "checkbox", value: false }, runtime);\n`;
|
||||
@@ -424,7 +423,7 @@ class ScriptCompiler {
|
||||
this.source += 'runtime.ext_scratch3_looks._renderBubble(target);\n';
|
||||
break;
|
||||
case 'looks.switchBackdrop':
|
||||
this.source += `runtime.ext_scratch3_looks._setBackdrop(stage, ${this.descendInput(node.backdrop)});\n`;
|
||||
this.source += `runtime.ext_scratch3_looks._setBackdrop(stage, ${this.descendInput(node.backdrop).asUnknown()});\n`;
|
||||
break;
|
||||
case 'looks.switchCostume':
|
||||
this.source += `runtime.ext_scratch3_looks._setCostume(target, ${this.descendInput(node.costume).asUnknown()});\n`;
|
||||
@@ -512,18 +511,23 @@ class ScriptCompiler {
|
||||
|
||||
case 'var.change': {
|
||||
const variable = this.referenceVariable(node.variable);
|
||||
// todo: cloud
|
||||
this.source += `${variable}.value = (+${variable}.value || 0) + ${this.descendInput(node.value).asNumber()};\n`;
|
||||
if (node.variable.isCloud) {
|
||||
this.source += `ioQuery("cloud", "requestUpdateVariable", ["${sanitize(node.variable.name)}", ${variable}.value]);\n`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'var.hide':
|
||||
this.source += `runtime.monitorBlocks.changeBlock({ id: "${sanitize(node.variable.id)}", element: "checkbox", value: false }, runtime);\n`;
|
||||
break;
|
||||
case 'var.set':
|
||||
// todo: cloud
|
||||
this.source += `${this.referenceVariable(node.variable)}.value = ${this.descendInput(node.value).asUnknown()};\n`;
|
||||
case 'var.set': {
|
||||
const variable = this.referenceVariable(node.variable);
|
||||
this.source += `${variable}.value = ${this.descendInput(node.value).asUnknown()};\n`;
|
||||
if (node.variable.isCloud) {
|
||||
this.source += `ioQuery("cloud", "requestUpdateVariable", ["${sanitize(node.variable.name)}", ${variable}.value]);\n`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'var.show':
|
||||
this.source += `runtime.monitorBlocks.changeBlock({ id: "${sanitize(node.variable.id)}", element: "checkbox", value: true }, runtime);\n`;
|
||||
break;
|
||||
@@ -541,7 +545,6 @@ class ScriptCompiler {
|
||||
}
|
||||
|
||||
referenceVariable (variable) {
|
||||
// todo: factoryVariables
|
||||
if (variable.scope === 'target') {
|
||||
return this.evaluateOnce(`target.variables["${sanitize(variable.id)}"]`);
|
||||
}
|
||||
@@ -567,6 +570,20 @@ class ScriptCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
safeConstantInput (value) {
|
||||
if (typeof value === 'string') {
|
||||
if (this.isNameOfCostume(value)) {
|
||||
return new TypedInput(`"${sanitize(value)}"`, TYPE_STRING);
|
||||
}
|
||||
}
|
||||
return new ConstantInput(value);
|
||||
}
|
||||
|
||||
isNameOfCostume (stringValue) {
|
||||
// todo: also check backdrop
|
||||
return this.target.getCostumeIndexByName(stringValue) !== -1;
|
||||
}
|
||||
|
||||
generateCompatCall (node) {
|
||||
const opcode = node.opcode;
|
||||
|
||||
@@ -650,8 +667,9 @@ disableToString(TypedInput.prototype.asBoolean);
|
||||
disableToString(TypedInput.prototype.asUnknown);
|
||||
|
||||
class JSCompiler {
|
||||
constructor (ast) {
|
||||
constructor (ast, target) {
|
||||
this.ast = ast;
|
||||
this.target = target;
|
||||
this.compilingProcedures = [];
|
||||
this.compiledProcedures = {};
|
||||
}
|
||||
@@ -676,7 +694,7 @@ class JSCompiler {
|
||||
this.compilingProcedures.pop();
|
||||
}
|
||||
|
||||
const compiler = new ScriptCompiler(script, this.ast);
|
||||
const compiler = new ScriptCompiler(script, this.ast, this.target);
|
||||
return compiler.compile();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ class VariablePool {
|
||||
/**
|
||||
* @param {string} prefix The prefix at the start of the variable name.
|
||||
*/
|
||||
constructor(prefix) {
|
||||
constructor (prefix) {
|
||||
if (prefix.trim().length === 0) {
|
||||
throw new Error('prefix cannot be empty');
|
||||
}
|
||||
@@ -13,7 +13,7 @@ class VariablePool {
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
next() {
|
||||
next () {
|
||||
return `${this.prefix}${this.count++}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ class Sequencer {
|
||||
thread.requestScriptGlowInFrame = false;
|
||||
thread.status = Thread.STATUS_DONE;
|
||||
if (thread.isCompiled) {
|
||||
thread.procedures = {};
|
||||
thread.procedures = null;
|
||||
thread.generator = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ class Thread {
|
||||
/**
|
||||
* @type {Object.<string, import('../compiler/compiler').CompiledScript>}
|
||||
*/
|
||||
this.procedures = {};
|
||||
this.procedures = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user