Block args set by fields are static and never change. Inputs that do
change are always set onto args. With these two assumptions we can
reuse the same objects for each execution of the same block instead of
constantly creating them and letting them be garbage collected.
Use a private StackFrame class to help internally manage use of Stack
Frame values and memory. Create params, reported, and executionContext
on demand.
Explain how BlocksExecuteCache helps speed up execute by storing values from
Blocks and one-time derived values on an object that is released when Blocks is
edited in the editor. The one time created cache object for a block then
simplifies the complexity of later javascript operations to use these stored
values.
The new `maybeFormatMessage` function detects whether its argument
looks like a message descriptor object and, if so, will call
`formatMessage` on it. This is now used for all user-visible text fields
in extensions.
Also, messages may use "select" to check the target type with a message
like this: '{targetType, select, stage {text for stage} sprite {text for
sprite} other {text for other}'. Note that the "other" clause is
required by `formatMessage`.
A LOOP block is like a conditional, but the LOOP block will be
re-evaluated after any child branch runs.
Also:
- Support using '---' as a block separator
- Refactor common code from `_registerExtensionPrimitives` and
`_refreshExtensionPrimitives` into new `_fillExtensionCategory`
- Improve error reporting during block conversion
This involves adding a new opcode, event_whenstageclicked, and adding a
method to the blocks container to update the opcode pair depending on
whether the target is the stage. This method is then called in two
places: first after the sb2 import parses the blocks (not done in the
sb2 parsing itself because no other blocks are target specific) and then
again when blocks are shared between targets.
Also added tests for the block container method, and a fixture project
that tests the opcode splitting on sb2 import.
The previous logic seemed to be expecting `peekStack` to return 0 when
the stack was empty, but its return type is `?string` and it returns
`undefined` when the stack is empty. The `!== 0` check would never pass,
then, leading to problems with (for example) the "say/think and wait"
blocks.
Towards fixing #865. This adds an IO class for detecting the mouse wheel
being scrolled. Basic tests are included; they mock the runtime to see
what blocks are activated by scrolling.
Add a Blocks cache available only to execute. This cache lets execute
get a blocks inputs, fields, opcode, and mutation in one request and an
object execute can further modify to store derivative values it use
this and every later execute iteration to perform its duties quicker.
Before calling execute, if a thread's target is null, retire that
thread.
This saves repeatedly checking if the thread's target is null in
recursive calls where even if scratch-gui or blocks, or some other
related library set the target to null, that will not happen during
block execution. It will happen at some time outside of the sequencer
letting the sequencer check once instead of execute checking at every
recursive level.
Store the thread's blocks at blockContainer letting execute quickly
determine the block source. Monitor threads are a monitor thread. They
do not become a target thread suddenly.
When execute calls itself to step into the next stack level, pass
RECURSIVE to the recursiveCall level. This argument provides
opportunity to recursed calls in execute to reduce the time needed for
some checks.
- Reduce time for checking around setting
thread.requestScriptGlowInFrame
- Call thread.pushReportedValue at end of execute when in a recursive
call
A boolean check is faster than a type lookup, command blocks will only
be executed in non-recursive calls. This saves a minor amount of time
for any result reporting blocks like blocks that get variables or add
two inputs together.
If the block is not returning a promise and execute is a recursed call,
that implies that the block is neither a hat or that the thread is not
at the top. That reduces handleReport to pushReportedValue. Using the
recursiveCall argument the final block calling handleReport can
shortcut the extra work in handleReport and reduce it to immediately
calling pushReportedValue.
- Add stackFrame.justReported
Report the block result to a known key `justReported` instead of a
dynamic key on `reported`. Assuming blocks with a promised value are
relatively infrequent the most common recursive input flow immediately
reads the value "just" reported. In the assumed uncommon case of a
promised thread status, empty the already argValues assigned values
onto the currentStackFrame's reported member. In the next execute call
on this stackFrame, values assigned to reported are read back off onto
argValues, and execute will returned to the assumed common case. This
is a safe assumption since a thread in the promise state will not exit
that state until the next frame when javascript has a chance to call
the resolve handle, setting the thread's state back to another
executable state.
Using direct assignment to `justReported` saves building an object
dynamically. Instead of always building `reported` and `argValues` only
`argValues` is built until a promised state is reached. This also gives
a known time when `reported` is used, allowing cleanup of a
stackFrame's reported to only happen when it was used to persist
already reported values.