BlockCached instances are always initialized

This commit is contained in:
Michael "Z" Goddard 2018-05-07 10:31:28 -04:00
parent 5fd749918f
commit 2db2287d12
No known key found for this signature in database
GPG key ID: 762CD40DD5349872
2 changed files with 114 additions and 126 deletions

View file

@ -874,9 +874,10 @@ class Blocks {
* reset. * reset.
* @param {Blocks} blocks Blocks containing the expected blockId * @param {Blocks} blocks Blocks containing the expected blockId
* @param {string} blockId blockId for the desired execute cache * @param {string} blockId blockId for the desired execute cache
* @param {function} CacheType constructor for cached block information
* @return {object} execute cache object * @return {object} execute cache object
*/ */
BlocksExecuteCache.getCached = function (blocks, blockId, CacheType = Object) { BlocksExecuteCache.getCached = function (blocks, blockId, CacheType) {
let cached = blocks._cache._executeCached[blockId]; let cached = blocks._cache._executeCached[blockId];
if (typeof cached !== 'undefined') { if (typeof cached !== 'undefined') {
return cached; return cached;
@ -885,13 +886,22 @@ BlocksExecuteCache.getCached = function (blocks, blockId, CacheType = Object) {
const block = blocks.getBlock(blockId); const block = blocks.getBlock(blockId);
if (typeof block === 'undefined') return null; if (typeof block === 'undefined') return null;
cached = new CacheType({ if (typeof CacheType === 'undefined') {
_initialized: false, cached = {
opcode: blocks.getOpcode(block),
fields: blocks.getFields(block),
inputs: blocks.getInputs(block),
mutation: blocks.getMutation(block)
};
} else {
cached = new CacheType(blocks, {
opcode: blocks.getOpcode(block), opcode: blocks.getOpcode(block),
fields: blocks.getFields(block), fields: blocks.getFields(block),
inputs: blocks.getInputs(block), inputs: blocks.getInputs(block),
mutation: blocks.getMutation(block) mutation: blocks.getMutation(block)
}); });
}
blocks._cache._executeCached[blockId] = cached; blocks._cache._executeCached[blockId] = cached;
return cached; return cached;
}; };

View file

@ -106,66 +106,6 @@ const handleReport = function (
*/ */
const RECURSIVE = true; const RECURSIVE = true;
/**
* A simple description of the kind of information held in the fields of a block.
* @enum {string}
*/
const FieldKind = {
NONE: 'NONE',
VARIABLE: 'VARIABLE',
LIST: 'LIST',
BROADCAST_OPTION: 'BROADCAST_OPTION',
DYNAMIC: 'DYNAMIC'
};
const BlockCached = function (cached) {
this._initialized = false;
this.opcode = cached.opcode;
this.fields = cached.fields;
this.inputs = cached.inputs;
this.mutation = cached.mutation;
this._isHat = false;
this._blockFunction = null;
this._definedBlockFunction = false;
this._isShadowBlock = false;
this._shadowValue = null;
this._fields = null;
this._fieldKind = FieldKind.NONE;
this._fieldVariable = null;
this._fieldList = null;
this._fieldBroadcastOption = null;
this._argValues = null;
this._inputs = null;
};
/**
* Execute a block.
* @param {!Sequencer} sequencer Which sequencer is executing.
* @param {!Thread} thread Thread which to read and execute.
* @param {boolean} recursiveCall is execute called from another execute call?
*/
const execute = function (sequencer, thread, recursiveCall) {
const runtime = sequencer.runtime;
// Current block to execute is the one on the top of the stack.
const currentBlockId = thread.peekStack();
const currentStackFrame = thread.peekStackFrame();
let blockContainer = thread.blockContainer;
let blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached);;
if (blockCached === null) {
blockContainer = runtime.flyoutBlocks;
blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached);
// Stop if block or target no longer exists.
if (blockCached === null) {
// No block found: stop the thread; script no longer exists.
sequencer.retireThread(thread);
return;
}
}
// With the help of the Blocks class create a cached copy of values from // With the help of the Blocks class create a cached copy of values from
// Blocks and the derived values execute needs. These values can be produced // Blocks and the derived values execute needs. These values can be produced
// one time during the first execution of a block so that later executions // one time during the first execution of a block so that later executions
@ -180,73 +120,122 @@ const execute = function (sequencer, thread, recursiveCall) {
// Blocks is modified in the editor these cached objects will be cleaned up // Blocks is modified in the editor these cached objects will be cleaned up
// and new cached copies can be created. This lets us optimize this critical // and new cached copies can be created. This lets us optimize this critical
// path while keeping up to date with editor changes to a project. // path while keeping up to date with editor changes to a project.
if (blockCached._initialized !== true) {
const {opcode, fields, inputs} = blockCached; const BlockCached = function (blockContainer, cached) {
this.opcode = cached.opcode;
this.fields = cached.fields;
this.inputs = cached.inputs;
this.mutation = cached.mutation;
this._isHat = false;
this._blockFunction = null;
this._definedBlockFunction = false;
this._isShadowBlock = false;
this._shadowValue = null;
this._fields = null;
this._argValues = null;
this._inputs = null;
const {runtime} = blockUtility.sequencer;
const {opcode, fields, inputs} = this;
// Assign opcode isHat and blockFunction data to avoid dynamic lookups. // Assign opcode isHat and blockFunction data to avoid dynamic lookups.
blockCached._isHat = runtime.getIsHat(opcode); this._isHat = runtime.getIsHat(opcode);
blockCached._blockFunction = runtime.getOpcodeFunction(opcode); this._blockFunction = runtime.getOpcodeFunction(opcode);
blockCached._definedBlockFunction = typeof blockCached._blockFunction !== 'undefined'; this._definedBlockFunction = typeof this._blockFunction !== 'undefined';
const fieldKeys = Object.keys(fields); const fieldKeys = Object.keys(fields);
// Store the current shadow value if there is a shadow value. // Store the current shadow value if there is a shadow value.
blockCached._isShadowBlock = fieldKeys.length === 1 && Object.keys(inputs).length === 0; this._isShadowBlock = fieldKeys.length === 1 && Object.keys(inputs).length === 0;
blockCached._shadowValue = blockCached._isShadowBlock && fields[fieldKeys[0]].value; this._shadowValue = this._isShadowBlock && fields[fieldKeys[0]].value;
// Store a fields copy. If fields is a VARIABLE, LIST, or // Store a fields copy.
// BROADCAST_OPTION, store the created values so fields assignment to this._fields = Object.assign({}, this.fields);
// argValues does not iterate over fields.
blockCached._fields = Object.assign({}, blockCached.fields); // Create a argValues instance for all executions of this block. Fields are
blockCached._fieldKind = fieldKeys.length > 0 ? FieldKind.DYNAMIC : FieldKind.NONE; // static, store them on args.
if (fieldKeys.length === 1 && fieldKeys.includes('VARIABLE')) { this._argValues = {
blockCached._fieldKind = FieldKind.VARIABLE; mutation: this.mutation
blockCached._argValues = {
VARIABLE: {
id: fields.VARIABLE.id,
name: fields.VARIABLE.value
},
mutation: blockCached.mutation
};
} else if (fieldKeys.length === 1 && fieldKeys.includes('LIST')) {
blockCached._fieldKind = FieldKind.LIST;
blockCached._argValues = {
LIST: {
id: fields.LIST.id,
name: fields.LIST.value
},
mutation: blockCached.mutation
};
} else if (fieldKeys.length === 1 && fieldKeys.includes('BROADCAST_OPTION')) {
blockCached._fieldKind = FieldKind.BROADCAST_OPTION;
blockCached._argValues = {
BROADCAST_OPTION: {
id: fields.BROADCAST_OPTION.id,
name: fields.BROADCAST_OPTION.value
},
mutation: blockCached.mutation
};
} else if (fieldKeys.length > 0) {
// FieldKind.DYNAMIC
blockCached._argValues = {
mutation: blockCached.mutation
}; };
for (const fieldName in fields) { for (const fieldName in fields) {
blockCached._argValues[fieldName] = fields[fieldName].value; if (fieldName === 'VARIABLE') {
} this._argValues.VARIABLE = {
} else { id: fields.VARIABLE.id,
// FieldKind.NONE name: fields.VARIABLE.value
blockCached._argValues = {
mutation: blockCached.mutation
}; };
} else if (fieldName === 'LIST') {
this._argValues.LIST = {
id: fields.LIST.id,
name: fields.LIST.value
};
} else if (fieldName === 'BROADCAST_OPTION') {
this._argValues.BROADCAST_OPTION = {
id: fields.BROADCAST_OPTION.id,
name: fields.BROADCAST_OPTION.value
};
} else {
this._argValues[fieldName] = fields[fieldName].value;
}
} }
// Store a modified inputs. This assures the keys are its own properties // Store a modified inputs. This assures the keys are its own properties
// and that custom_block will not be evaluated. // and that custom_block will not be evaluated.
blockCached._inputs = Object.assign({}, blockCached.inputs); this._inputs = Object.assign({}, this.inputs);
delete blockCached._inputs.custom_block; delete this._inputs.custom_block;
blockCached._initialized = true; if ('BROADCAST_INPUT' in this._inputs) {
this._argValues.BROADCAST_OPTION = {
id: null,
name: null
};
const broadcastInput = this._inputs.BROADCAST_INPUT;
if (broadcastInput.block === broadcastInput.shadow) {
// Shadow dropdown menu is being used.
// Get the appropriate information out of it.
const shadow = blockContainer.getBlock(broadcastInput.shadow);
const broadcastField = shadow.fields.BROADCAST_OPTION;
this._argValues.BROADCAST_OPTION.id = broadcastField.id;
this._argValues.BROADCAST_OPTION.name = broadcastField.name;
delete this._inputs.BROADCAST_INPUT;
}
}
};
/**
* Execute a block.
* @param {!Sequencer} sequencer Which sequencer is executing.
* @param {!Thread} thread Thread which to read and execute.
* @param {boolean} recursiveCall is execute called from another execute call?
*/
const execute = function (sequencer, thread, recursiveCall) {
const runtime = sequencer.runtime;
// sequencer and thread are the same objects during a recursive set of
// execute operations.
if (recursiveCall !== RECURSIVE) {
blockUtility.sequencer = sequencer;
blockUtility.thread = thread;
}
// Current block to execute is the one on the top of the stack.
const currentBlockId = thread.peekStack();
const currentStackFrame = thread.peekStackFrame();
let blockContainer = thread.blockContainer;
let blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached);
if (blockCached === null) {
blockContainer = runtime.flyoutBlocks;
blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached);
// Stop if block or target no longer exists.
if (blockCached === null) {
// No block found: stop the thread; script no longer exists.
sequencer.retireThread(thread);
return;
}
} }
const opcode = blockCached.opcode; const opcode = blockCached.opcode;
@ -281,7 +270,7 @@ const execute = function (sequencer, thread, recursiveCall) {
} }
// Update values for arguments (inputs). // Update values for arguments (inputs).
let argValues = blockCached._argValues; const argValues = blockCached._argValues;
// Fields are set during blockCached initialization. // Fields are set during blockCached initialization.
@ -339,25 +328,16 @@ const execute = function (sequencer, thread, recursiveCall) {
} else if (typeof currentStackFrame.reported[inputName] !== 'undefined') { } else if (typeof currentStackFrame.reported[inputName] !== 'undefined') {
inputValue = currentStackFrame.reported[inputName]; inputValue = currentStackFrame.reported[inputName];
} }
if (inputName === 'BROADCAST_INPUT') { if (inputName === 'BROADCAST_INPUT') {
const broadcastInput = inputs[inputName]; const broadcastInput = inputs[inputName];
// Check if something is plugged into the broadcast block, or // Check if something is plugged into the broadcast block, or
// if the shadow dropdown menu is being used. // if the shadow dropdown menu is being used.
if (broadcastInput.block === broadcastInput.shadow) { if (broadcastInput.block !== broadcastInput.shadow) {
// Shadow dropdown menu is being used.
// Get the appropriate information out of it.
const shadow = blockContainer.getBlock(broadcastInput.shadow);
const broadcastField = shadow.fields.BROADCAST_OPTION;
argValues.BROADCAST_OPTION = {
id: broadcastField.id,
name: broadcastField.value
};
} else {
// Something is plugged into the broadcast input. // Something is plugged into the broadcast input.
// Cast it to a string. We don't need an id here. // Cast it to a string. We don't need an id here.
argValues.BROADCAST_OPTION = { argValues.BROADCAST_OPTION.id = null;
name: cast.toString(inputValue) argValues.BROADCAST_OPTION.name = cast.toString(inputValue);
};
} }
} else { } else {
argValues[inputName] = inputValue; argValues[inputName] = inputValue;
@ -365,8 +345,6 @@ const execute = function (sequencer, thread, recursiveCall) {
} }
let primitiveReportedValue = null; let primitiveReportedValue = null;
blockUtility.sequencer = sequencer;
blockUtility.thread = thread;
if (runtime.profiler !== null) { if (runtime.profiler !== null) {
if (blockFunctionProfilerId === -1) { if (blockFunctionProfilerId === -1) {
blockFunctionProfilerId = runtime.profiler.idByName(blockFunctionProfilerFrame); blockFunctionProfilerId = runtime.profiler.idByName(blockFunctionProfilerFrame);