s4s-editor / local-scratch-vm /src /blocks /scratch3_procedures.js
soiz1's picture
Upload 811 files
30c32c8 verified
raw
history blame
4.56 kB
const Cast = require('../util/cast');
class Scratch3ProcedureBlocks {
constructor(runtime) {
/**
* The runtime instantiating this block package.
* @type {Runtime}
*/
this.runtime = runtime;
}
/**
* Retrieve the block primitives implemented by this package.
* @return {object.<string, Function>} Mapping of opcode to Function.
*/
getPrimitives() {
return {
procedures_definition: this.definition,
procedures_call: this.call,
procedures_set: this.set,
argument_reporter_string_number: this.argumentReporterStringNumber,
argument_reporter_boolean: this.argumentReporterBoolean,
argument_reporter_command: this.argumentReporterCommand
};
}
definition() {
// No-op: execute the blocks.
}
call (args, util) {
if (!util.stackFrame.executed) {
const procedureCode = args.mutation.proccode;
const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode);
// If null, procedure could not be found, which can happen if custom
// block is dragged between sprites without the definition.
// Match Scratch 2.0 behavior and noop.
if (paramNamesIdsAndDefaults === null) {
return;
}
const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
// Initialize params for the current stackFrame to {}, even if the procedure does
// not take any arguments. This is so that `getParam` down the line does not look
// at earlier stack frames for the values of a given parameter (#1729)
util.initParams();
for (let i = 0; i < paramIds.length; i++) {
if (args.hasOwnProperty(paramIds[i])) {
util.pushParam(paramNames[i], args[paramIds[i]]);
} else {
util.pushParam(paramNames[i], paramDefaults[i]);
}
}
const addonBlock = util.runtime.getAddonBlock(procedureCode);
if (addonBlock) {
const result = addonBlock.callback(util.thread.getAllparams(), util);
if (util.thread.status === 1 /* STATUS_PROMISE_WAIT */) {
// If the addon block is using STATUS_PROMISE_WAIT to force us to sleep,
// make sure to not re-run this block when we resume.
util.stackFrame.executed = true;
}
return result;
}
util.stackFrame.executed = true;
util.startProcedure(procedureCode);
}
}
set(args, util) {
const contain = util.thread.blockContainer;
const block = contain.getBlock(util.thread.isCompiled ? util.thread.peekStack() : util.thread.peekStackFrame().op.id);
if (!block) return;
const thread = util.thread;
const param = contain.getBlock(block.inputs.PARAM?.block);
if (param) {
try {
const curParams = thread.stackFrames[0].params;
if (curParams !== null) thread.stackFrames[0].params[param.fields.VALUE.value] = args.VALUE;
else thread.stackFrames[0].params = { [param.fields.VALUE.value]: args.VALUE }
} catch { /* shouldn't happen */ }
}
}
argumentReporterStringNumber(args, util) {
const value = util.getParam(args.VALUE);
if (value === null) {
// When the parameter is not found in the most recent procedure
// call, the default is always 0.
return 0;
}
return value;
}
argumentReporterBoolean(args, util) {
const value = util.getParam(args.VALUE);
if (value === null) {
// When the parameter is not found in the most recent procedure
// call, the default is always 0.
return 0;
}
return value;
}
argumentReporterCommand(args, util) {
const branchInfo = util.getParam(args.VALUE) || {};
if (branchInfo.entry === null) return;
const [branchId, target] = util.getBranchAndTarget(
branchInfo.callerId,
branchInfo.entry
) || [];
if (branchId) {
// Push branch ID to the thread's stack.
util.thread.pushStack(branchId, target);
} else {
util.thread.pushStack(null);
}
}
}
module.exports = Scratch3ProcedureBlocks;