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:
Michael "Z" Goddard 2017-11-03 11:43:19 -04:00
parent a735459427
commit 78847de660
No known key found for this signature in database
GPG key ID: 762CD40DD5349872

View file

@ -11,6 +11,62 @@ const isPromise = function (value) {
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.
* @param {!Sequencer} sequencer Which sequencer is executing.
@ -61,55 +117,6 @@ const execute = function (sequencer, thread) {
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
// from regular blocks.
// For hats: if they have an associated block function,
@ -124,7 +131,7 @@ const execute = function (sequencer, thread) {
const keys = Object.keys(fields);
if (keys.length === 1 && Object.keys(inputs).length === 0) {
// One field and no inputs - treat as arg.
handleReport(fields[keys[0]].value);
handleReport(fields[keys[0]].value, sequencer, thread, currentBlockId, isHat);
} else {
log.warn(`Could not get implementation for opcode: ${opcode}`);
}
@ -244,7 +251,7 @@ const execute = function (sequencer, thread) {
}
// Promise handlers
primitiveReportedValue.then(resolvedValue => {
handleReport(resolvedValue);
handleReport(resolvedValue, sequencer, thread, currentBlockId, isHat);
if (typeof resolvedValue === 'undefined') {
let stackFrame;
let nextBlockId;
@ -277,7 +284,7 @@ const execute = function (sequencer, thread) {
thread.popStack();
});
} else if (thread.status === Thread.STATUS_RUNNING) {
handleReport(primitiveReportedValue);
handleReport(primitiveReportedValue, sequencer, thread, currentBlockId, isHat);
}
};