Spaces:
Running
Running
File size: 8,038 Bytes
30c32c8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
const Thread = require('./thread');
const Timer = require('../util/timer');
/**
* @fileoverview
* Interface provided to block primitive functions for interacting with the
* runtime, thread, target, and convenient methods.
*/
class BlockUtility {
constructor (sequencer = null, thread = null) {
/**
* A sequencer block primitives use to branch or start procedures with
* @type {?Sequencer}
*/
this.sequencer = sequencer;
/**
* The block primitives thread with the block's target, stackFrame and
* modifiable status.
* @type {?Thread}
*/
this.thread = thread;
this._nowObj = {
now: () => this.sequencer.runtime.currentMSecs
};
}
/**
* The target the primitive is working on.
* @type {Target}
*/
get target () {
return this.thread.target;
}
/**
* The runtime the block primitive is running in.
* @type {Runtime}
*/
get runtime () {
return this.sequencer.runtime;
}
/**
* Use the runtime's currentMSecs value as a timestamp value for now
* This is useful in some cases where we need compatibility with Scratch 2
* @type {function}
*/
get nowObj () {
if (this.runtime) {
return this._nowObj;
}
return null;
}
/**
* The stack frame used by loop and other blocks to track internal state.
* @type {object}
*/
get stackFrame () {
const frame = this.thread.peekStackFrame();
if (frame.executionContext === null) {
frame.executionContext = {};
}
return frame.executionContext;
}
/**
* Check the stack timer and return a boolean based on whether it has finished or not.
* @return {boolean} - true if the stack timer has finished.
*/
stackTimerFinished () {
const timeElapsed = this.stackFrame.timer.timeElapsed();
if (timeElapsed < this.stackFrame.duration) {
return false;
}
return true;
}
/**
* Check if the stack timer needs initialization.
* @return {boolean} - true if the stack timer needs to be initialized.
*/
stackTimerNeedsInit () {
return !this.stackFrame.timer;
}
/**
* Create and start a stack timer
* @param {number} duration - a duration in milliseconds to set the timer for.
*/
startStackTimer (duration) {
if (this.nowObj) {
this.stackFrame.timer = new Timer(this.nowObj);
} else {
this.stackFrame.timer = new Timer();
}
this.stackFrame.timer.start();
this.stackFrame.duration = duration;
}
/**
* Set the thread to yield.
*/
yield () {
this.thread.status = Thread.STATUS_YIELD;
}
/**
* pm: Set the thread to the running state.
*/
defaultStatus () {
this.thread.status = Thread.STATUS_RUNNING;
}
/**
* Set the thread to yield until the next tick of the runtime.
*/
yieldTick () {
this.thread.status = Thread.STATUS_YIELD_TICK;
}
/**
* Start a branch in the current block.
* @param {number} branchNum Which branch to step to (i.e., 1, 2).
* @param {boolean} isLoop Whether this block is a loop.
*/
startBranch (branchNum, isLoop) {
this.sequencer.stepToBranch(this.thread, branchNum, isLoop);
}
/**
* Get the branch for a particular C-shaped block, and it's target.
* @param {string} id ID for block to get the branch for.
* @param {string} branchId Which branch to select (e.g. for if-else).
* @return {string} ID of block in the branch.
*/
getBranchAndTarget (id, branchId) {
const result = this.thread.blockContainer.getBranch(id, branchId);
if (result) {
return [result, this.thread.target];
}
return this.sequencer.runtime.getBranchAndTarget(id, branchId);
}
/**
* Stop all threads.
*/
stopAll () {
this.sequencer.runtime.stopAll();
}
/**
* Stop threads other on this target other than the thread holding the
* executed block.
*/
stopOtherTargetThreads () {
this.sequencer.runtime.stopForTarget(this.thread.target, this.thread);
}
/**
* Stop this thread.
*/
stopThisScript () {
this.thread.stopThisScript();
}
/**
* Start a specified procedure on this thread.
* @param {string} procedureCode Procedure code for procedure to start.
*/
startProcedure (procedureCode) {
this.sequencer.stepToProcedure(this.thread, procedureCode);
}
/**
* Get names and ids of parameters for the given procedure.
* @param {string} procedureCode Procedure code for procedure to query.
* @return {Array.<string>} List of param names for a procedure.
*/
getProcedureParamNamesAndIds (procedureCode) {
return this.thread.target.blocks.getProcedureParamNamesAndIds(procedureCode);
}
/**
* Get names, ids, and defaults of parameters for the given procedure.
* @param {string} procedureCode Procedure code for procedure to query.
* @return {Array.<string>} List of param names for a procedure.
*/
getProcedureParamNamesIdsAndDefaults (procedureCode) {
return this.thread.target.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode);
}
/**
* Initialize procedure parameters in the thread before pushing parameters.
*/
initParams () {
this.thread.initParams();
}
/**
* Store a procedure parameter value by its name.
* @param {string} paramName The procedure's parameter name.
* @param {*} paramValue The procedure's parameter value.
*/
pushParam (paramName, paramValue) {
this.thread.pushParam(paramName, paramValue);
}
/**
* Retrieve the stored parameter value for a given parameter name.
* @param {string} paramName The procedure's parameter name.
* @return {*} The parameter's current stored value.
*/
getParam (paramName) {
return this.thread.getParam(paramName);
}
/**
* Start all relevant hats.
* @param {!string} requestedHat Opcode of hats to start.
* @param {object=} optMatchFields Optionally, fields to match on the hat.
* @param {Target=} optTarget Optionally, a target to restrict to.
* @return {Array.<Thread>} List of threads started by this function.
*/
startHats (requestedHat, optMatchFields, optTarget) {
// Store thread and sequencer to ensure we can return to the calling block's context.
// startHats may execute further blocks and dirty the BlockUtility's execution context
// and confuse the calling block when we return to it.
const callerThread = this.thread;
const callerSequencer = this.sequencer;
const result = this.sequencer.runtime.startHats(requestedHat, optMatchFields, optTarget);
// Restore thread and sequencer to prior values before we return to the calling block.
this.thread = callerThread;
this.sequencer = callerSequencer;
return result;
}
/**
* Query a named IO device.
* @param {string} device The name of like the device, like keyboard.
* @param {string} func The name of the device's function to query.
* @param {Array.<*>} args Arguments to pass to the device's function.
* @return {*} The expected output for the device's function.
*/
ioQuery (device, func, args) {
// Find the I/O device and execute the query/function call.
if (
this.sequencer.runtime.ioDevices[device] &&
this.sequencer.runtime.ioDevices[device][func]) {
const devObject = this.sequencer.runtime.ioDevices[device];
return devObject[func].apply(devObject, args);
}
}
}
module.exports = BlockUtility;
|