mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-24 15:02:52 -05:00
Extract handleReport and add arguments to prevent closure alloc
`handleReport` inside `src/engine/execute.js`'s `execute` method needs to allocate a closure to be able to refer to the higher scoped variables. `execute` is called frequently that this has a noticable impact on memory allocation and later collection. Extract handleReport and add arguments to prevent the allocation.
This commit is contained in:
parent
a735459427
commit
78847de660
1 changed files with 59 additions and 52 deletions
|
@ -11,6 +11,62 @@ const isPromise = function (value) {
|
||||||
return value && value.then && typeof value.then === 'function';
|
return value && value.then && typeof value.then === 'function';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle any reported value from the primitive, either directly returned
|
||||||
|
* or after a promise resolves.
|
||||||
|
* @param {*} resolvedValue Value eventually returned from the primitive.
|
||||||
|
* @param {!Sequencer} sequencer Sequencer stepping the thread for the ran
|
||||||
|
* primitive.
|
||||||
|
* @param {!Thread} thread Thread containing the primitive.
|
||||||
|
* @param {!string} currentBlockId Id of the block in its thread for value from
|
||||||
|
* the primitive.
|
||||||
|
* @param {!boolean} isHat Is the current block a hat?
|
||||||
|
*/
|
||||||
|
// @todo move this to callback attached to the thread when we have performance
|
||||||
|
// metrics (dd)
|
||||||
|
const handleReport = function (
|
||||||
|
resolvedValue, sequencer, thread, currentBlockId, isHat) {
|
||||||
|
thread.pushReportedValue(resolvedValue);
|
||||||
|
if (isHat) {
|
||||||
|
// Hat predicate was evaluated.
|
||||||
|
if (sequencer.runtime.getIsEdgeActivatedHat(opcode)) {
|
||||||
|
// If this is an edge-activated hat, only proceed if the value is
|
||||||
|
// true and used to be false, or the stack was activated explicitly
|
||||||
|
// via stack click
|
||||||
|
if (!thread.stackClick) {
|
||||||
|
const oldEdgeValue = sequencer.runtime.updateEdgeActivatedValue(
|
||||||
|
currentBlockId,
|
||||||
|
resolvedValue
|
||||||
|
);
|
||||||
|
const edgeWasActivated = !oldEdgeValue && resolvedValue;
|
||||||
|
if (!edgeWasActivated) {
|
||||||
|
sequencer.retireThread(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!resolvedValue) {
|
||||||
|
// Not an edge-activated hat: retire the thread
|
||||||
|
// if predicate was false.
|
||||||
|
sequencer.retireThread(thread);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In a non-hat, report the value visually if necessary if
|
||||||
|
// at the top of the thread stack.
|
||||||
|
if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) {
|
||||||
|
if (thread.stackClick) {
|
||||||
|
sequencer.runtime.visualReport(currentBlockId, resolvedValue);
|
||||||
|
}
|
||||||
|
if (thread.updateMonitor) {
|
||||||
|
sequencer.runtime.requestUpdateMonitor(Map({
|
||||||
|
id: currentBlockId,
|
||||||
|
value: String(resolvedValue)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Finished any yields.
|
||||||
|
thread.status = Thread.STATUS_RUNNING;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a block.
|
* Execute a block.
|
||||||
* @param {!Sequencer} sequencer Which sequencer is executing.
|
* @param {!Sequencer} sequencer Which sequencer is executing.
|
||||||
|
@ -61,55 +117,6 @@ const execute = function (sequencer, thread) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle any reported value from the primitive, either directly returned
|
|
||||||
* or after a promise resolves.
|
|
||||||
* @param {*} resolvedValue Value eventually returned from the primitive.
|
|
||||||
*/
|
|
||||||
// @todo move this to callback attached to the thread when we have performance
|
|
||||||
// metrics (dd)
|
|
||||||
const handleReport = function (resolvedValue) {
|
|
||||||
thread.pushReportedValue(resolvedValue);
|
|
||||||
if (isHat) {
|
|
||||||
// Hat predicate was evaluated.
|
|
||||||
if (runtime.getIsEdgeActivatedHat(opcode)) {
|
|
||||||
// If this is an edge-activated hat, only proceed if
|
|
||||||
// the value is true and used to be false, or the stack was activated
|
|
||||||
// explicitly via stack click
|
|
||||||
if (!thread.stackClick) {
|
|
||||||
const oldEdgeValue = runtime.updateEdgeActivatedValue(
|
|
||||||
currentBlockId,
|
|
||||||
resolvedValue
|
|
||||||
);
|
|
||||||
const edgeWasActivated = !oldEdgeValue && resolvedValue;
|
|
||||||
if (!edgeWasActivated) {
|
|
||||||
sequencer.retireThread(thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!resolvedValue) {
|
|
||||||
// Not an edge-activated hat: retire the thread
|
|
||||||
// if predicate was false.
|
|
||||||
sequencer.retireThread(thread);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// In a non-hat, report the value visually if necessary if
|
|
||||||
// at the top of the thread stack.
|
|
||||||
if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) {
|
|
||||||
if (thread.stackClick) {
|
|
||||||
runtime.visualReport(currentBlockId, resolvedValue);
|
|
||||||
}
|
|
||||||
if (thread.updateMonitor) {
|
|
||||||
runtime.requestUpdateMonitor(Map({
|
|
||||||
id: currentBlockId,
|
|
||||||
value: String(resolvedValue)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Finished any yields.
|
|
||||||
thread.status = Thread.STATUS_RUNNING;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hats and single-field shadows are implemented slightly differently
|
// Hats and single-field shadows are implemented slightly differently
|
||||||
// from regular blocks.
|
// from regular blocks.
|
||||||
// For hats: if they have an associated block function,
|
// For hats: if they have an associated block function,
|
||||||
|
@ -124,7 +131,7 @@ const execute = function (sequencer, thread) {
|
||||||
const keys = Object.keys(fields);
|
const keys = Object.keys(fields);
|
||||||
if (keys.length === 1 && Object.keys(inputs).length === 0) {
|
if (keys.length === 1 && Object.keys(inputs).length === 0) {
|
||||||
// One field and no inputs - treat as arg.
|
// One field and no inputs - treat as arg.
|
||||||
handleReport(fields[keys[0]].value);
|
handleReport(fields[keys[0]].value, sequencer, thread, currentBlockId, isHat);
|
||||||
} else {
|
} else {
|
||||||
log.warn(`Could not get implementation for opcode: ${opcode}`);
|
log.warn(`Could not get implementation for opcode: ${opcode}`);
|
||||||
}
|
}
|
||||||
|
@ -244,7 +251,7 @@ const execute = function (sequencer, thread) {
|
||||||
}
|
}
|
||||||
// Promise handlers
|
// Promise handlers
|
||||||
primitiveReportedValue.then(resolvedValue => {
|
primitiveReportedValue.then(resolvedValue => {
|
||||||
handleReport(resolvedValue);
|
handleReport(resolvedValue, sequencer, thread, currentBlockId, isHat);
|
||||||
if (typeof resolvedValue === 'undefined') {
|
if (typeof resolvedValue === 'undefined') {
|
||||||
let stackFrame;
|
let stackFrame;
|
||||||
let nextBlockId;
|
let nextBlockId;
|
||||||
|
@ -277,7 +284,7 @@ const execute = function (sequencer, thread) {
|
||||||
thread.popStack();
|
thread.popStack();
|
||||||
});
|
});
|
||||||
} else if (thread.status === Thread.STATUS_RUNNING) {
|
} else if (thread.status === Thread.STATUS_RUNNING) {
|
||||||
handleReport(primitiveReportedValue);
|
handleReport(primitiveReportedValue, sequencer, thread, currentBlockId, isHat);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue