From fffb72b09d549bb37bbbc64ef7508a4a2eafa13d Mon Sep 17 00:00:00 2001 From: Tacodiva <27910867+Tacodiva@users.noreply.github.com> Date: Sun, 16 Jun 2024 13:10:14 +1000 Subject: [PATCH] Upgraded Type Analysis --- src/compiler/compat-block-utility.js | 7 +- src/compiler/compat-blocks.js | 2 + src/compiler/compile.js | 12 +- src/compiler/enums.js | 295 ++++ src/compiler/environment.js | 1 + src/compiler/intermediate.js | 253 ++- src/compiler/irgen.js | 1507 +++++++---------- src/compiler/iroptimizer.js | 703 ++++++++ src/compiler/jsexecute.js | 13 +- src/compiler/jsgen.js | 1390 ++++++--------- src/compiler/variable-pool.js | 2 + .../order-library-reverse.sb3.tw-snapshot | 4 +- .../order-library.sb3.tw-snapshot | 4 +- .../__snapshots__/tw-NaN.sb3.tw-snapshot | 24 +- ...-does-not-use-rounded-size.sb3.tw-snapshot | 2 +- ...tw-color-input-returns-hex.sb3.tw-snapshot | 2 +- ...w-comparison-matrix-inline.sb3.tw-snapshot | 186 +- ...-comparison-matrix-runtime.sb3.tw-snapshot | 14 +- .../tw-custom-report-repeat.sb3.tw-snapshot | 2 +- ...-boolean-number-comparison.sb3.tw-snapshot | 4 +- ...-name-desync-name-fallback.sb3.tw-snapshot | 4 +- ...-zero-seconds-in-warp-mode.sb3.tw-snapshot | 6 +- ...s-not-reevaluate-arguments.sb3.tw-snapshot | 4 +- .../__snapshots__/tw-list-any.sb3.tw-snapshot | 2 +- ...w-one-divide-negative-zero.sb3.tw-snapshot | 2 +- ...nce-of-procedure-387608267.sb3.tw-snapshot | 2 +- ...e-arguments-with-same-name.sb3.tw-snapshot | 4 +- ...able-input-types-430811055.sb3.tw-snapshot | 4 +- ...cedure-return-non-existant.sb3.tw-snapshot | 4 +- ...cedure-return-non-existent.sb3.tw-snapshot | 2 +- ...procedure-return-recursion.sb3.tw-snapshot | 32 +- ...tw-procedure-return-simple.sb3.tw-snapshot | 20 +- ...edure-return-stops-scripts.sb3.tw-snapshot | 8 +- .../tw-procedure-return-warp.sb3.tw-snapshot | 6 +- ...procedure-argument-casting.sb3.tw-snapshot | 2 +- .../tw-sensing-of.sb3.tw-snapshot | 4 +- .../tw-tab-equals-zero.sb3.tw-snapshot | 2 +- .../__snapshots__/tw-tangent.sb3.tw-snapshot | 14 +- .../tw-unsafe-equals.sb3.tw-snapshot | 26 +- ...t-until-timer-greater-than.sb3.tw-snapshot | 6 +- ...mbie-cube-escape-284516654.sb3.tw-snapshot | 4 +- .../order-library-reverse.sb3.tw-snapshot | 4 +- .../warp-timer/order-library.sb3.tw-snapshot | 4 +- .../warp-timer/tw-NaN.sb3.tw-snapshot | 24 +- ...-does-not-use-rounded-size.sb3.tw-snapshot | 2 +- ...tw-color-input-returns-hex.sb3.tw-snapshot | 2 +- ...w-comparison-matrix-inline.sb3.tw-snapshot | 186 +- ...-comparison-matrix-runtime.sb3.tw-snapshot | 14 +- .../tw-custom-report-repeat.sb3.tw-snapshot | 2 +- ...-boolean-number-comparison.sb3.tw-snapshot | 4 +- ...-name-desync-name-fallback.sb3.tw-snapshot | 4 +- ...-zero-seconds-in-warp-mode.sb3.tw-snapshot | 6 +- ...s-not-reevaluate-arguments.sb3.tw-snapshot | 4 +- .../warp-timer/tw-list-any.sb3.tw-snapshot | 2 +- ...w-one-divide-negative-zero.sb3.tw-snapshot | 2 +- ...nce-of-procedure-387608267.sb3.tw-snapshot | 2 +- ...e-arguments-with-same-name.sb3.tw-snapshot | 4 +- ...able-input-types-430811055.sb3.tw-snapshot | 4 +- ...cedure-return-non-existant.sb3.tw-snapshot | 4 +- ...cedure-return-non-existent.sb3.tw-snapshot | 2 +- ...procedure-return-recursion.sb3.tw-snapshot | 32 +- ...tw-procedure-return-simple.sb3.tw-snapshot | 20 +- ...edure-return-stops-scripts.sb3.tw-snapshot | 6 +- .../tw-procedure-return-warp.sb3.tw-snapshot | 6 +- ...procedure-argument-casting.sb3.tw-snapshot | 2 +- .../warp-timer/tw-sensing-of.sb3.tw-snapshot | 4 +- .../tw-tab-equals-zero.sb3.tw-snapshot | 2 +- .../warp-timer/tw-tangent.sb3.tw-snapshot | 14 +- .../tw-unsafe-equals.sb3.tw-snapshot | 26 +- ...t-until-timer-greater-than.sb3.tw-snapshot | 6 +- ...mbie-cube-escape-284516654.sb3.tw-snapshot | 4 +- 71 files changed, 2860 insertions(+), 2123 deletions(-) create mode 100644 src/compiler/enums.js create mode 100644 src/compiler/iroptimizer.js diff --git a/src/compiler/compat-block-utility.js b/src/compiler/compat-block-utility.js index 6fedba1d..b662867d 100644 --- a/src/compiler/compat-block-utility.js +++ b/src/compiler/compat-block-utility.js @@ -1,13 +1,16 @@ +// @ts-check + const BlockUtility = require('../engine/block-utility'); class CompatibilityLayerBlockUtility extends BlockUtility { constructor () { super(); + this._stackFrame = {}; this._startedBranch = null; } get stackFrame () { - return this.thread.compatibilityStackFrame; + return this._stackFrame; } startBranch (branchNumber, isLoop) { @@ -32,9 +35,9 @@ class CompatibilityLayerBlockUtility extends BlockUtility { init (thread, fakeBlockId, stackFrame) { this.thread = thread; this.sequencer = thread.target.runtime.sequencer; + this._stackFrame = stackFrame; this._startedBranch = null; thread.stack[0] = fakeBlockId; - thread.compatibilityStackFrame = stackFrame; } } diff --git a/src/compiler/compat-blocks.js b/src/compiler/compat-blocks.js index 1c9d8f5a..ba9d5b88 100644 --- a/src/compiler/compat-blocks.js +++ b/src/compiler/compat-blocks.js @@ -1,3 +1,5 @@ +// @ts-check + /** * @fileoverview List of blocks to be supported in the compiler compatibility layer. * This is only for native blocks. Extensions should not be listed here. diff --git a/src/compiler/compile.js b/src/compiler/compile.js index b5edb813..4a5c5029 100644 --- a/src/compiler/compile.js +++ b/src/compiler/compile.js @@ -1,14 +1,20 @@ -const {IRGenerator} = require('./irgen'); +// @ts-check + +const IRGenerator = require('./irgen'); +const {IROptimizer} = require('./iroptimizer'); const JSGenerator = require('./jsgen'); -const compile = thread => { +const compile = (/** @type {import("../engine/thread")} */ thread) => { const irGenerator = new IRGenerator(thread); const ir = irGenerator.generate(); + const irOptimizer = new IROptimizer(ir); + irOptimizer.optimize(); + const procedures = {}; const target = thread.target; - const compileScript = script => { + const compileScript = (/** @type {import("./intermediate").IntermediateScript} */ script) => { if (script.cachedCompileResult) { return script.cachedCompileResult; } diff --git a/src/compiler/enums.js b/src/compiler/enums.js new file mode 100644 index 00000000..c1a4e90f --- /dev/null +++ b/src/compiler/enums.js @@ -0,0 +1,295 @@ +// @ts-check + +/** + * @fileoverview Common enums shared amongst parts of the compiler. + */ + + +/** + * Enum for the type of the value that is returned by reporter blocks and stored in constants. + * + * At compile time, often we don't know exactly type a value will be but we can tell it must be one of a + * set of types. For this reason, the number value of each type represents a possibility space, where set + * bits indicate that their corropoding type *could* be encountered at runtime. + * For example, a type of InputType.NUMBER | InputType.STRING means the value will be either a number or + * a string at runtime, the compiler can't tell which, but we do know that it's not a boolean or NaN as + * those bits are not set. + * + * @readonly + * @enum {number} + */ +const InputType = { + /** The value Infinity */ + NUMBER_POS_INF: 0x001, + /** Any natural number */ + NUMBER_POS_INT: 0x002, + /** Any positive fractional number, excluding integers. */ + NUMBER_POS_FRACT: 0x004, + /** Any positive number excluding 0 and Infinity. Equal to NUMBER_POS_INT | NUMBER_POS_FRACT */ + NUMBER_POS_REAL: 0x006, + /** The value 0 */ + NUMBER_ZERO: 0x008, + /** The value -0 */ + NUMBER_NEG_ZERO: 0x010, + /** Any negitive integer excluding -0 */ + NUMBER_NEG_INT: 0x020, + /** Any negitive fractional number, excluding integers. */ + NUMBER_NEG_FRACT: 0x040, + /** Any negitive number excluding -0 and -Infinity. Equal to NUMBER_NEG_INT | NUMBER_NEG_FRACT */ + NUMBER_NEG_REAL: 0x060, + /** The value -Infinity */ + NUMBER_NEG_INF: 0x080, + + /** The value NaN */ + NUMBER_NAN: 0x100, + + /** Either 0 or -0. Equal to NUMBER_ZERO | NUMBER_NEG_ZERO */ + NUMBER_ANY_ZERO: 0x018, + /** Either Infinity or -Infinity. Equal to NUMBER_POS_INF | NUMBER_NEG_INF */ + NUMBER_INF: 0x081, + /** Any positive number, excluding 0. Equal to NUMBER_POS_REAL | NUMBER_POS_INF */ + NUMBER_POS: 0x007, + /** Any negitive number, excluding -0. Equal to NUMBER_NEG_REAL | NUMBER_NEG_INF */ + NUMBER_NEG: 0x0E0, + /** Any whole number. Equal to NUMBER_POS_INT | NUMBER_ZERO */ + NUMBER_WHOLE: 0x00A, + /** Any integer. Equal to NUMBER_POS_INT | NUMBER_ANY_ZERO | NUMBER_NEG_INT */ + NUMBER_INT: 0x03A, + /** Any number that works as an array index. Equal to NUMBER_INT | NUMBER_INF | NUMBER_NAN */ + NUMBER_INDEX: 0x1BB, + /** Any fractional non-integer numbers. Equal to NUMBER_POS_FRACT | NUMBER_NEG_FRACT */ + NUMBER_FRACT: 0x44, + /** Any real number. Equal to NUMBER_POS_REAL | NUMBER_ANY_ZERO | NUMBER_NEG_REAL */ + NUMBER_REAL: 0x07E, + + /** Any number, excluding NaN. Equal to NUMBER_REAL | NUMBER_INF */ + NUMBER: 0x0FF, + /** Any number, including NaN. Equal to NUMBER | NUMBER_NAN */ + NUMBER_OR_NAN: 0x1FF, + /** Anything that can be interperated as a number. Equal to NUMBER | STRING_NUM | BOOLEAN */ + NUMBER_INTERPRETABLE: 0x12FF, + + /** Any string which as a non-NaN neumeric interpretation, excluding ''. */ + STRING_NUM: 0x200, + /** Any string which has no non-NaN neumeric interpretation, including ''. */ + STRING_NAN: 0x400, + /** Either of the strings 'true' or 'false'. */ + STRING_BOOLEAN: 0x800, + + /** Any string. Equal to STRING_NUM | STRING_NAN | STRING_BOOLEAN */ + STRING: 0xE00, + + /** Any boolean. */ + BOOLEAN: 0x1000, + /** Any input that can be interperated as a boolean. Equal to BOOLEAN | STRING_BOOLEAN */ + BOOLEAN_INTERPRETABLE: 0x1800, + + /** Any type. Equal to NUMBER_OR_NAN | STRING | BOOLEAN */ + ANY: 0x1FFF +}; + +/** + * Enum for the opcodes of the stackable blocks used in the IR AST. + * @readonly + * @enum {string} + */ +const StackOpcode = { + NOP: 'noop', + + ADDON_CALL: 'addons.call', + DEBUGGER: 'tw.debugger', + VISUAL_REPORT: 'visualReport', + COMPATIBILITY_LAYER: 'compat', + + HAT_EDGE: 'hat.edge', + HAT_PREDICATE: 'hat.predicate', + + CONTROL_IF_ELSE: 'control.if', + CONTROL_CLONE_CREATE: 'control.createClone', + CONTROL_CLONE_DELETE: 'control.deleteClone', + CONTROL_WHILE: 'control.while', + CONTROL_FOR: 'control.for', + CONTROL_REPEAT: 'control.repeat', + CONTROL_STOP_ALL: 'control.stopAll', + CONTROL_STOP_OTHERS: 'control.stopOthers', + CONTROL_STOP_SCRIPT: 'control.stopScript', + CONTROL_WAIT: 'control.wait', + CONTROL_WAIT_UNTIL: 'control.waitUntil', + CONTROL_CLEAR_COUNTER: 'control.counterClear', + CONTORL_INCR_COUNTER: 'control.counterIncr', + + LIST_ADD: 'list.add', + LIST_INSERT: 'list.instert', + LIST_REPLACE: 'list.replace', + LIST_DELETE_ALL: 'list.deleteAll', + LIST_DELETE: 'list.delete', + LIST_SHOW: 'list.show', + LIST_HIDE: 'list.hide', + + VAR_SET: 'var.set', + VAR_SHOW: 'var.show', + VAR_HIDE: 'var.hide', + + EVENT_BROADCAST: 'event.broadcast', + EVENT_BROADCAST_AND_WAIT: 'event.broadcastAndWait', + + LOOKS_EFFECT_SET: 'looks.setEffect', + LOOKS_EFFECT_CHANGE: 'looks.changeEffect', + LOOKS_EFFECT_CLEAR: 'looks.clearEffects', + LOOKS_SIZE_CHANGE: 'looks.changeSize', + LOOKS_SIZE_SET: 'looks.setSize', + LOOKS_LAYER_FORWARD: 'looks.forwardLayers', + LOOKS_LAYER_BACKWARD: 'looks.backwardLayers', + LOOKS_LAYER_FRONT: 'looks.goToFront', + LOOKS_LAYER_BACK: 'looks.goToBack', + LOOKS_HIDE: 'looks.hide', + LOOKS_SHOW: 'looks.show', + LOOKS_BACKDROP_NEXT: 'looks.nextBackdrop', + LOOKS_BACKDROP_SET: 'looks.switchBackdrop', + LOOKS_COSTUME_NEXT: 'looks.nextCostume', + LOOKS_COSTUME_SET: 'looks.switchCostume', + + MOTION_X_SET: 'motion.setX', + MOTION_X_CHANGE: 'motion.changeX', + MOTION_Y_SET: 'motion.setY', + MOTION_Y_CHANGE: 'motion.changeY', + MOTION_XY_SET: 'motion.setXY', + MOTION_IF_ON_EDGE_BOUNCE: 'motion.ifOnEdgeBounce', + MOTION_STEP: 'motion.step', + MOTION_ROTATION_STYLE_SET: 'motion.setRotationStyle', + MOTION_DIRECTION_SET: 'motion.setDirection', + + PEN_UP: 'pen.up', + PEN_DOWN: 'pen.down', + PEN_CLEAR: 'pen.clear', + PEN_COLOR_PARAM_SET: 'pen.setParam', + PEN_COLOR_PARAM_CHANGE: 'pen.changeParam', + PEN_COLOR_HUE_CHANGE_LEGACY: 'pen.legacyChangeHue', + PEN_COLOR_HUE_SET_LEGACY: 'pen_setPenHueToNumber', + PEN_COLOR_SHADE_CHANGE_LEGACY: 'pen.legacyChangeShade', + PEN_COLOR_SHADE_SET_LEGACY: 'pen.legacySetShade', + PEN_COLOR_SET: 'pen.setColor', + PEN_SIZE_SET: 'pen.setSize', + PEN_SIZE_CHANGE: 'pen.changeSize', + PEN_STAMP: 'pen.stamp', + + SENSING_TIMER_RESET: 'timer.reset', + + PROCEDURE_RETURN: 'procedures.return', + PROCEDURE_CALL: 'procedures.call' +}; + +/** + * Enum for the opcodes of the reporter blocks used in the IR AST. + * @readonly + * @enum {string} + */ +const InputOpcode = { + NOP: 'noop', + + ADDON_CALL: 'addons.call', + CONSTANT: 'constant', + + CAST_NUMBER: 'cast.toNumber', + CAST_NUMBER_INDEX: 'cast.toInteger', + CAST_NUMBER_OR_NAN: 'cast.toNumberOrNaN', + CAST_STRING: 'cast.toString', + CAST_BOOLEAN: 'cast.toBoolean', + + COMPATIBILITY_LAYER: 'compat', + + LOOKS_BACKDROP_NUMBER: 'looks.backdropNumber', + LOOKS_BACKDROP_NAME: 'looks.backdropName', + LOOKS_COSTUME_NUMBER: 'looks.costumeNumber', + LOOKS_COSTUME_NAME: 'looks.costumeName', + LOOKS_SIZE_GET: 'looks.size', + + VAR_GET: 'var.get', + + LIST_GET: 'list.get', + LIST_LENGTH: 'list.length', + LIST_CONTAINS: 'list.contains', + LIST_INDEX_OF: 'list.indexOf', + LIST_CONTENTS: 'list.contents', + + MOTION_X_GET: 'motion.x', + MOTION_Y_GET: 'motion.y', + MOTION_DIRECTION_GET: 'motion.direction', + + OP_ADD: 'op.add', + OP_AND: 'op.and', + OP_CONTAINS: 'op.contains', + OP_DIVIDE: 'op.divide', + OP_EQUALS: 'op.equals', + OP_GREATER: 'op.greater', + OP_LESS: 'op.less', + OP_JOIN: 'op.join', + OP_LENGTH: 'op.length', + OP_LETTER_OF: 'op.letterOf', + OP_ABS: 'op.abs', + OP_FLOOR: 'op.floor', + OP_CEILING: 'op.ceiling', + OP_SQRT: 'op.sqrt', + OP_SIN: 'op.sin', + OP_COS: 'op.cos', + OP_TAN: 'op.tan', + OP_ASIN: 'op.asin', + OP_ACOS: 'op.acos', + OP_ATAN: 'op.atan', + OP_LOG_E: 'op.ln', + OP_LOG_10: 'op.log', + OP_POW_E: 'op.e^', + OP_POW_10: 'op.10^', + OP_MOD: 'op.mod', + OP_MULTIPLY: 'op.multiply', + OP_NOT: 'op.not', + OP_OR: 'op.or', + OP_RANDOM: 'op.random', + OP_ROUND: 'op.round', + OP_SUBTRACT: 'op.subtract', + + SENSING_ANSWER: 'sensing.answer', + SENSING_COLOR_TOUCHING_COLOR: 'sensing.colorTouchingColor', + SENSING_TIME_YEAR: 'sensing.year', + SENSING_TIME_MONTH: 'sensing.month', + SENSING_TIME_DATE: 'sensing.date', + SENSING_TIME_WEEKDAY: 'sensing.dayofweek', + SENSING_TIME_HOUR: 'sensing.hour', + SENSING_TIME_MINUTE: 'sensing.minute', + SENSING_TIME_SECOND: 'sensing.second', + SENSING_TIME_DAYS_SINCE_2000: 'sensing.daysSince2000', + SENSING_DISTANCE: 'sensing.distance', + SENSING_KEY_DOWN: 'keyboard.pressed', + SENSING_MOUSE_DOWN: 'mouse.down', + SENSING_MOUSE_X: 'mouse.x', + SENSING_MOUSE_Y: 'mouse.y', + SENSING_OF: 'sensing.of', + SENSING_OF_BACKDROP_NAME: 'sensing.of.backdropName', + SENSING_OF_BACKDROP_NUMBER: 'sensing.of.backdropNumber', + SENSING_OF_COSTUME_NAME: 'sensing.of.costumeName', + SENSING_OF_COSTUME_NUMBER: 'sensing.of.costumeNumber', + SENSING_OF_VOLUME: 'sensing.of.volume', + SENSING_OF_POS_X: 'sensing.of.x', + SENSING_OF_POS_Y: 'sensing.of.y', + SENSING_OF_DIRECTION: 'sensing.of.direction', + SENSING_OF_SIZE: 'sensing.of.size', + SENSING_OF_VAR: 'sensing.of.var', + SENSING_TIMER_GET: 'timer.get', + SENSING_TOUCHING_COLOR: 'sensing.touchingColor', + SENSING_TOUCHING_OBJECT: 'sensing.touching', + SENSING_USERNAME: 'sensing.username', + + PROCEDURE_CALL: 'procedures.call', + PROCEDURE_ARG_STRING_NUMBER: 'args.stringNumber', + PROCEDURE_ARG_BOOLEAN: 'args.boolean', + + CONTROL_COUNTER: 'control.counter', + + TW_KEY_LAST_PRESSED: 'tw.lastKeyPressed' +}; + +module.exports = { + StackOpcode, + InputOpcode, + InputType +}; diff --git a/src/compiler/environment.js b/src/compiler/environment.js index 75b300a6..a7b49c31 100644 --- a/src/compiler/environment.js +++ b/src/compiler/environment.js @@ -1,3 +1,4 @@ +// @ts-check /* eslint-disable no-eval */ /** diff --git a/src/compiler/intermediate.js b/src/compiler/intermediate.js index 9882cefc..dc5f4ea3 100644 --- a/src/compiler/intermediate.js +++ b/src/compiler/intermediate.js @@ -1,7 +1,220 @@ +// @ts-check + +const Cast = require('../util/cast'); +const {InputOpcode, InputType} = require('./enums.js'); +const log = require('../util/log'); + /** * @fileoverview Common intermediates shared amongst parts of the compiler. */ +/** + * Describes a 'stackable' block (eg. show) + */ +class IntermediateStackBlock { + /** + * @param {import("./enums").StackOpcode} opcode + * @param {Object} inputs + * @param {boolean} yields + */ + constructor (opcode, inputs = {}, yields = false) { + /** + * The type of the stackable block. + * @type {import("./enums").StackOpcode} + */ + this.opcode = opcode; + + /** + * The inputs of this block. + * @type {Object} + */ + this.inputs = inputs; + + /** + * Does this block cause a yield + * @type {boolean} + */ + this.yields = yields; + + /** + * Should state changes made by this stack block be ignored? Used for testing. + * @type {boolean} + */ + this.ignoreState = false; + + /** + * @type {import("./iroptimizer").TypeState?} + */ + this.entryState = null; + + /** + * @type {import("./iroptimizer").TypeState?} + */ + this.exitState = null; + } +} + +/** + * Describes an input to a block. + * This could be a constant, variable or math operation. + */ +class IntermediateInput { + + static getNumberInputType (number) { + if (typeof number !== 'number') throw new Error('Expected a number.'); + if (number === Infinity) return InputType.NUMBER_POS_INF; + if (number === -Infinity) return InputType.NUMBER_NEG_INF; + if (number < 0) return Number.isInteger(number) ? InputType.NUMBER_NEG_INT : InputType.NUMBER_NEG_FRACT; + if (number > 0) return Number.isInteger(number) ? InputType.NUMBER_POS_INT : InputType.NUMBER_POS_FRACT; + if (Number.isNaN(number)) return InputType.NUMBER_NAN; + if (Object.is(number, -0)) return InputType.NUMBER_NEG_ZERO; + return InputType.NUMBER_ZERO; + } + + /** + * @param {InputOpcode} opcode + * @param {InputType} type + * @param {Object} inputs + * @param {boolean} yields + */ + constructor (opcode, type, inputs = {}, yields = false) { + /** + * @type {InputOpcode} + */ + this.opcode = opcode; + + /** + * @type {InputType} + */ + this.type = type; + + /** + * @type {Object} + */ + this.inputs = inputs; + + /** + * @type {boolean} + */ + this.yields = yields; + } + + /** + * Is this input a constant whos value equals value. + * @param {*} value The value + * @returns {boolean} + */ + isConstant (value) { + if (this.opcode !== InputOpcode.CONSTANT) return false; + let equal = this.inputs.value === value; + if (!equal && typeof value === 'number') equal = (+this.inputs.value) === value; + return equal; + } + + /** + * Is the type of this input guaranteed to always be the type at runtime. + * @param {InputType} type + * @returns {boolean} + */ + isAlwaysType (type) { + return (this.type & type) === this.type; + } + + /** + * Is it possible for this input to be the type at runtime. + * @param {InputType} type + * @returns + */ + isSometimesType (type) { + return (this.type & type) !== 0; + } + + /** + * Converts this input to a target type. + * If this input is a constant the conversion is performed now, at compile time. + * If the input changes, the conversion is performed at runtime. + * @param {InputType} targetType + * @returns {IntermediateInput} An input with the new type. + */ + toType (targetType) { + let castOpcode; + switch (targetType) { + case InputType.BOOLEAN: + castOpcode = InputOpcode.CAST_BOOLEAN; + break; + case InputType.NUMBER: + castOpcode = InputOpcode.CAST_NUMBER; + break; + case InputType.NUMBER_INDEX: + castOpcode = InputOpcode.CAST_NUMBER_INDEX; + break; + case InputType.NUMBER_OR_NAN: + castOpcode = InputOpcode.CAST_NUMBER_OR_NAN; + break; + case InputType.STRING: + castOpcode = InputOpcode.CAST_STRING; + break; + default: + log.warn(`Cannot cast to type: ${targetType}`, this); + throw new Error(`Cannot cast to type: ${targetType}`); + } + + if (this.isAlwaysType(targetType)) return this; + + if (this.opcode === InputOpcode.CONSTANT) { + // If we are a constant, we can do the cast here at compile time + switch (castOpcode) { + case InputOpcode.CAST_BOOLEAN: + this.inputs.value = Cast.toBoolean(this.inputs.value); + this.type = InputType.BOOLEAN; + break; + case InputOpcode.CAST_NUMBER: + case InputOpcode.CAST_NUMBER_INDEX: + case InputOpcode.CAST_NUMBER_OR_NAN: + if (this.isAlwaysType(InputType.BOOLEAN_INTERPRETABLE)) { + this.type = InputType.NUMBER; + this.inputs.value = +Cast.toBoolean(this.inputs.value); + } + var numberValue = +this.inputs.value; + if (numberValue) { + this.inputs.value = numberValue; + } else { + // numberValue is one of 0, -0, or NaN + if (Object.is(numberValue, -0)) this.inputs.value = -0; + else this.inputs.value = 0; // Convert NaN to 0 + } + if (castOpcode === InputOpcode.CAST_NUMBER_INDEX) { + // Round numberValue to an integer + numberValue |= 0; + } + this.type = IntermediateInput.getNumberInputType(this.inputs.value); + break; + case InputOpcode.CAST_STRING: + this.inputs.value += ''; + this.type = InputType.STRING; + break; + } + return this; + } + + return new IntermediateInput(castOpcode, targetType, {target: this}); + } +} + +/** + * A 'stack' of blocks, like the contents of a script or the inside + * of a C block. + */ +class IntermediateStack { + /** + * @param {IntermediateStackBlock[]} [blocks] + */ + constructor (blocks) { + /** @type {IntermediateStackBlock[]} */ + this.blocks = blocks ?? []; + } +} + /** * An IntermediateScript describes a single script. * Scripts do not necessarily have hats. @@ -10,13 +223,13 @@ class IntermediateScript { constructor () { /** * The ID of the top block of this script. - * @type {string} + * @type {string?} */ this.topBlockId = null; /** * List of nodes that make up this script. - * @type {Array|null} + * @type {IntermediateStack?} */ this.stack = null; @@ -26,12 +239,6 @@ class IntermediateScript { */ this.isProcedure = false; - /** - * This procedure's variant, if any. - * @type {string} - */ - this.procedureVariant = ''; - /** * This procedure's code, if any. * @type {string} @@ -76,11 +283,18 @@ class IntermediateScript { */ this.cachedCompileResult = null; + /** + * Cached result of analysing this script. + * @type {import("./iroptimizer").TypeState|null} + */ + this.cachedAnalysisEndState = null; + /** * Whether the top block of this script is an executable hat. * @type {boolean} */ this.executableHat = false; + } } @@ -88,22 +302,39 @@ class IntermediateScript { * An IntermediateRepresentation contains scripts. */ class IntermediateRepresentation { - constructor () { + /** + * + * @param {IntermediateScript} entry + * @param {Object.} procedures + */ + constructor (entry, procedures) { /** * The entry point of this IR. * @type {IntermediateScript} */ - this.entry = null; + this.entry = entry; /** * Maps procedure variants to their intermediate script. * @type {Object.} */ - this.procedures = {}; + this.procedures = procedures; + } + + /** + * Gets the first procedure with the given proccode. + * @param {string} proccode + * @returns {IntermediateScript | undefined} + */ + getProcedure (proccode) { + return Object.values(this.procedures).find(procedure => procedure.procedureCode === proccode); } } module.exports = { + IntermediateStackBlock, + IntermediateInput, + IntermediateStack, IntermediateScript, IntermediateRepresentation }; diff --git a/src/compiler/irgen.js b/src/compiler/irgen.js index 39707ef5..91a576db 100644 --- a/src/compiler/irgen.js +++ b/src/compiler/irgen.js @@ -1,10 +1,13 @@ +// @ts-check + const Cast = require('../util/cast'); const StringUtil = require('../util/string-util'); const BlockType = require('../extension-support/block-type'); const Variable = require('../engine/variable'); const log = require('../util/log'); -const {IntermediateScript, IntermediateRepresentation} = require('./intermediate'); +const {IntermediateStackBlock, IntermediateInput, IntermediateStack, IntermediateScript, IntermediateRepresentation} = require('./intermediate'); const compatBlocks = require('./compat-blocks'); +const {StackOpcode, InputOpcode, InputType} = require('./enums.js'); /** * @fileoverview Generate intermediate representations from Scratch blocks. @@ -13,11 +16,6 @@ const compatBlocks = require('./compat-blocks'); const SCALAR_TYPE = ''; const LIST_TYPE = 'list'; -/** - * @typedef {Object.} Node - * @property {string} kind - */ - /** * Create a variable codegen object. * @param {'target'|'stage'} scope The scope of this variable -- which object owns it. @@ -82,12 +80,24 @@ class ScriptTreeGenerator { this.variableCache = {}; this.usesTimer = false; + + this.namesOfCostumesAndSounds = new Set(); + for (const target of this.runtime.targets) { + if (target.isOriginal) { + const sprite = target.sprite; + for (const costume of sprite.costumes) { + this.namesOfCostumesAndSounds.add(costume.name); + } + for (const sound of sprite.sounds) { + this.namesOfCostumesAndSounds.add(sound.name); + } + } + } } setProcedureVariant (procedureVariant) { const procedureCode = parseProcedureCode(procedureVariant); - this.script.procedureVariant = procedureVariant; this.script.procedureCode = procedureCode; this.script.isProcedure = true; this.script.yields = false; @@ -126,63 +136,76 @@ class ScriptTreeGenerator { return blockInfo; } + createConstantInput (constant, preserveStrings = false) { + if (constant == null) throw new Error('IR: Constant cannot have a null value.'); + + constant += ''; + const numConstant = +constant; + const preserve = preserveStrings && this.namesOfCostumesAndSounds.has(constant); + + if (!Number.isNaN(numConstant) && (constant.trim() !== '' || constant.includes('\t'))) { + if (!preserve && numConstant.toString() === constant) { + return new IntermediateInput(InputOpcode.CONSTANT, IntermediateInput.getNumberInputType(numConstant), {value: numConstant}); + } + return new IntermediateInput(InputOpcode.CONSTANT, InputType.STRING_NUM, {value: constant}); + } + + if (!preserve) { + if (constant === 'true') { + return new IntermediateInput(InputOpcode.CONSTANT, InputType.STRING_BOOLEAN, {value: constant}); + } else if (constant === 'false') { + return new IntermediateInput(InputOpcode.CONSTANT, InputType.STRING_BOOLEAN, {value: constant}); + } + } + + return new IntermediateInput(InputOpcode.CONSTANT, InputType.STRING_NAN, {value: constant}); + } + /** * Descend into a child input of a block. (eg. the input STRING of "length of ( )") * @param {*} parentBlock The parent Scratch block that contains the input. * @param {string} inputName The name of the input to descend into. + * @param {boolean} preserveStrings Should this input keep the names of costumes and sounds at strings. * @private - * @returns {Node} Compiled input node for this input. + * @returns {IntermediateInput} Compiled input node for this input. */ - descendInputOfBlock (parentBlock, inputName) { + descendInputOfBlock (parentBlock, inputName, preserveStrings = false) { const input = parentBlock.inputs[inputName]; if (!input) { log.warn(`IR: ${parentBlock.opcode}: missing input ${inputName}`, parentBlock); - return { - kind: 'constant', - value: 0 - }; + return this.createConstantInput(0); } const inputId = input.block; const block = this.getBlockById(inputId); if (!block) { log.warn(`IR: ${parentBlock.opcode}: could not find input ${inputName} with ID ${inputId}`); - return { - kind: 'constant', - value: 0 - }; + return this.createConstantInput(0); } - return this.descendInput(block); + const intermediate = this.descendInput(block, preserveStrings); + this.script.yields = this.script.yields || intermediate.yields; + return intermediate; } /** * Descend into an input. (eg. "length of ( )") * @param {*} block The parent Scratch block input. + * @param {boolean} preserveStrings Should this input keep the names of costumes and sounds at strings. * @private - * @returns {Node} Compiled input node for this input. + * @returns {IntermediateInput} Compiled input node for this input. */ - descendInput (block) { + descendInput (block, preserveStrings = false) { switch (block.opcode) { case 'colour_picker': - return { - kind: 'constant', - value: block.fields.COLOUR.value - }; + return this.createConstantInput(block.fields.COLOUR.value, true); case 'math_angle': case 'math_integer': case 'math_number': case 'math_positive_number': case 'math_whole_number': - return { - kind: 'constant', - value: block.fields.NUM.value - }; + return this.createConstantInput(block.fields.NUM.value, preserveStrings); case 'text': - return { - kind: 'constant', - value: block.fields.TEXT.value - }; - + return this.createConstantInput(block.fields.TEXT.value, preserveStrings); case 'argument_reporter_string_number': { const name = block.fields.VALUE.value; // lastIndexOf because multiple parameters with the same name will use the value of the last definition @@ -190,21 +213,13 @@ class ScriptTreeGenerator { if (index === -1) { // Legacy support if (name.toLowerCase() === 'last key pressed') { - return { - kind: 'tw.lastKeyPressed' - }; + return new IntermediateInput(InputOpcode.TW_KEY_LAST_PRESSED, InputType.STRING); } } if (index === -1) { - return { - kind: 'constant', - value: 0 - }; + return this.createConstantInput(0); } - return { - kind: 'args.stringNumber', - index: index - }; + return new IntermediateInput(InputOpcode.PROCEDURE_ARG_STRING_NUMBER, InputType.ANY, {index}); } case 'argument_reporter_boolean': { // see argument_reporter_string_number above @@ -212,460 +227,343 @@ class ScriptTreeGenerator { const index = this.script.arguments.lastIndexOf(name); if (index === -1) { if (name.toLowerCase() === 'is compiled?' || name.toLowerCase() === 'is turbowarp?') { - return { - kind: 'constant', - value: true - }; + return this.createConstantInput(true).toType(InputType.BOOLEAN); } - return { - kind: 'constant', - value: 0 - }; + return this.createConstantInput(0); } - return { - kind: 'args.boolean', - index: index - }; + return new IntermediateInput(InputOpcode.PROCEDURE_ARG_BOOLEAN, InputType.BOOLEAN, {index}); } - case 'control_get_counter': - return { - kind: 'counter.get' - }; - case 'data_variable': - return { - kind: 'var.get', + return new IntermediateInput(InputOpcode.VAR_GET, InputType.ANY, { variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE) - }; + }); case 'data_itemoflist': - return { - kind: 'list.get', + return new IntermediateInput(InputOpcode.LIST_GET, InputType.ANY, { list: this.descendVariable(block, 'LIST', LIST_TYPE), index: this.descendInputOfBlock(block, 'INDEX') - }; + }); case 'data_lengthoflist': - return { - kind: 'list.length', + return new IntermediateInput(InputOpcode.LIST_LENGTH, InputType.NUMBER_POS_REAL | InputType.NUMBER_ZERO, { list: this.descendVariable(block, 'LIST', LIST_TYPE) - }; + }); case 'data_listcontainsitem': - return { - kind: 'list.contains', + return new IntermediateInput(InputOpcode.LIST_CONTAINS, InputType.BOOLEAN, { list: this.descendVariable(block, 'LIST', LIST_TYPE), item: this.descendInputOfBlock(block, 'ITEM') - }; + }); case 'data_itemnumoflist': - return { - kind: 'list.indexOf', + return new IntermediateInput(InputOpcode.LIST_INDEX_OF, InputType.NUMBER_POS_REAL | InputType.NUMBER_ZERO, { list: this.descendVariable(block, 'LIST', LIST_TYPE), item: this.descendInputOfBlock(block, 'ITEM') - }; + }); case 'data_listcontents': - return { - kind: 'list.contents', + return new IntermediateInput(InputOpcode.LIST_CONTENTS, InputType.STRING, { list: this.descendVariable(block, 'LIST', LIST_TYPE) - }; + }); case 'event_broadcast_menu': { const broadcastOption = block.fields.BROADCAST_OPTION; const broadcastVariable = this.target.lookupBroadcastMsg(broadcastOption.id, broadcastOption.value); // TODO: empty string probably isn't the correct fallback const broadcastName = broadcastVariable ? broadcastVariable.name : ''; - return { - kind: 'constant', - value: broadcastName - }; + return this.createConstantInput(broadcastName); } case 'looks_backdropnumbername': if (block.fields.NUMBER_NAME.value === 'number') { - return { - kind: 'looks.backdropNumber' - }; + return new IntermediateInput(InputOpcode.LOOKS_BACKDROP_NUMBER, InputType.NUMBER_POS_REAL); } - return { - kind: 'looks.backdropName' - }; + return new IntermediateInput(InputOpcode.LOOKS_BACKDROP_NAME, InputType.STRING); case 'looks_costumenumbername': if (block.fields.NUMBER_NAME.value === 'number') { - return { - kind: 'looks.costumeNumber' - }; + return new IntermediateInput(InputOpcode.LOOKS_COSTUME_NUMBER, InputType.NUMBER_POS_REAL); } - return { - kind: 'looks.costumeName' - }; + return new IntermediateInput(InputOpcode.LOOKS_COSTUME_NAME, InputType.STRING); case 'looks_size': - return { - kind: 'looks.size' - }; + return new IntermediateInput(InputOpcode.LOOKS_SIZE_GET, InputType.NUMBER_POS_REAL); case 'motion_direction': - return { - kind: 'motion.direction' - }; + return new IntermediateInput(InputOpcode.MOTION_DIRECTION_GET, InputType.NUMBER_REAL); case 'motion_xposition': - return { - kind: 'motion.x' - }; + return new IntermediateInput(InputOpcode.MOTION_X_GET, InputType.NUMBER_REAL); case 'motion_yposition': - return { - kind: 'motion.y' - }; + return new IntermediateInput(InputOpcode.MOTION_Y_GET, InputType.NUMBER_REAL); case 'operator_add': - return { - kind: 'op.add', - left: this.descendInputOfBlock(block, 'NUM1'), - right: this.descendInputOfBlock(block, 'NUM2') - }; + return new IntermediateInput(InputOpcode.OP_ADD, InputType.NUMBER_OR_NAN, { + left: this.descendInputOfBlock(block, 'NUM1').toType(InputType.NUMBER), + right: this.descendInputOfBlock(block, 'NUM2').toType(InputType.NUMBER) + }); case 'operator_and': - return { - kind: 'op.and', - left: this.descendInputOfBlock(block, 'OPERAND1'), - right: this.descendInputOfBlock(block, 'OPERAND2') - }; + return new IntermediateInput(InputOpcode.OP_AND, InputType.BOOLEAN, { + left: this.descendInputOfBlock(block, 'OPERAND1').toType(InputType.BOOLEAN), + right: this.descendInputOfBlock(block, 'OPERAND2').toType(InputType.BOOLEAN) + }); case 'operator_contains': - return { - kind: 'op.contains', - string: this.descendInputOfBlock(block, 'STRING1'), - contains: this.descendInputOfBlock(block, 'STRING2') - }; + return new IntermediateInput(InputOpcode.OP_CONTAINS, InputType.BOOLEAN, { + string: this.descendInputOfBlock(block, 'STRING1').toType(InputType.STRING), + contains: this.descendInputOfBlock(block, 'STRING2').toType(InputType.STRING) + }); case 'operator_divide': - return { - kind: 'op.divide', - left: this.descendInputOfBlock(block, 'NUM1'), - right: this.descendInputOfBlock(block, 'NUM2') - }; + return new IntermediateInput(InputOpcode.OP_DIVIDE, InputType.NUMBER_OR_NAN, { + left: this.descendInputOfBlock(block, 'NUM1').toType(InputType.NUMBER), + right: this.descendInputOfBlock(block, 'NUM2').toType(InputType.NUMBER) + }); case 'operator_equals': - return { - kind: 'op.equals', + return new IntermediateInput(InputOpcode.OP_EQUALS, InputType.BOOLEAN, { left: this.descendInputOfBlock(block, 'OPERAND1'), right: this.descendInputOfBlock(block, 'OPERAND2') - }; + }); case 'operator_gt': - return { - kind: 'op.greater', + return new IntermediateInput(InputOpcode.OP_GREATER, InputType.BOOLEAN, { left: this.descendInputOfBlock(block, 'OPERAND1'), right: this.descendInputOfBlock(block, 'OPERAND2') - }; + }); case 'operator_join': - return { - kind: 'op.join', - left: this.descendInputOfBlock(block, 'STRING1'), - right: this.descendInputOfBlock(block, 'STRING2') - }; + return new IntermediateInput(InputOpcode.OP_JOIN, InputType.STRING, { + left: this.descendInputOfBlock(block, 'STRING1').toType(InputType.STRING), + right: this.descendInputOfBlock(block, 'STRING2').toType(InputType.STRING) + }); case 'operator_length': - return { - kind: 'op.length', - string: this.descendInputOfBlock(block, 'STRING') - }; + return new IntermediateInput(InputOpcode.OP_LENGTH, InputType.NUMBER_REAL, { + string: this.descendInputOfBlock(block, 'STRING').toType(InputType.STRING) + }); case 'operator_letter_of': - return { - kind: 'op.letterOf', - letter: this.descendInputOfBlock(block, 'LETTER'), - string: this.descendInputOfBlock(block, 'STRING') - }; + return new IntermediateInput(InputOpcode.OP_LETTER_OF, InputType.STRING, { + letter: this.descendInputOfBlock(block, 'LETTER').toType(InputType.NUMBER_INDEX), + string: this.descendInputOfBlock(block, 'STRING').toType(InputType.STRING) + }); case 'operator_lt': - return { - kind: 'op.less', + return new IntermediateInput(InputOpcode.OP_LESS, InputType.BOOLEAN, { left: this.descendInputOfBlock(block, 'OPERAND1'), right: this.descendInputOfBlock(block, 'OPERAND2') - }; + }); case 'operator_mathop': { - const value = this.descendInputOfBlock(block, 'NUM'); + const value = this.descendInputOfBlock(block, 'NUM').toType(InputType.NUMBER); const operator = block.fields.OPERATOR.value.toLowerCase(); switch (operator) { - case 'abs': return { - kind: 'op.abs', - value - }; - case 'floor': return { - kind: 'op.floor', - value - }; - case 'ceiling': return { - kind: 'op.ceiling', - value - }; - case 'sqrt': return { - kind: 'op.sqrt', - value - }; - case 'sin': return { - kind: 'op.sin', - value - }; - case 'cos': return { - kind: 'op.cos', - value - }; - case 'tan': return { - kind: 'op.tan', - value - }; - case 'asin': return { - kind: 'op.asin', - value - }; - case 'acos': return { - kind: 'op.acos', - value - }; - case 'atan': return { - kind: 'op.atan', - value - }; - case 'ln': return { - kind: 'op.ln', - value - }; - case 'log': return { - kind: 'op.log', - value - }; - case 'e ^': return { - kind: 'op.e^', - value - }; - case '10 ^': return { - kind: 'op.10^', - value - }; - default: return { - kind: 'constant', - value: 0 - }; + case 'abs': return new IntermediateInput(InputOpcode.OP_ABS, InputType.NUMBER_POS | InputType.NUMBER_ZERO, {value}); + case 'floor': return new IntermediateInput(InputOpcode.OP_FLOOR, InputType.NUMBER, {value}); + case 'ceiling': return new IntermediateInput(InputOpcode.OP_CEILING, InputType.NUMBER, {value}); + case 'sqrt': return new IntermediateInput(InputOpcode.OP_SQRT, InputType.NUMBER_OR_NAN, {value}); + case 'sin': return new IntermediateInput(InputOpcode.OP_SIN, InputType.NUMBER_OR_NAN, {value}); + case 'cos': return new IntermediateInput(InputOpcode.OP_COS, InputType.NUMBER_OR_NAN, {value}); + case 'tan': return new IntermediateInput(InputOpcode.OP_TAN, InputType.NUMBER_OR_NAN, {value}); + case 'asin': return new IntermediateInput(InputOpcode.OP_ASIN, InputType.NUMBER_OR_NAN, {value}); + case 'acos': return new IntermediateInput(InputOpcode.OP_ACOS, InputType.NUMBER_OR_NAN, {value}); + case 'atan': return new IntermediateInput(InputOpcode.OP_ATAN, InputType.NUMBER, {value}); + case 'ln': return new IntermediateInput(InputOpcode.OP_LOG_E, InputType.NUMBER_OR_NAN, {value}); + case 'log': return new IntermediateInput(InputOpcode.OP_LOG_10, InputType.NUMBER_OR_NAN, {value}); + case 'e ^': return new IntermediateInput(InputOpcode.OP_POW_E, InputType.NUMBER, {value}); + case '10 ^': return new IntermediateInput(InputOpcode.OP_POW_10, InputType.NUMBER, {value}); + default: this.createConstantInput(0); } } case 'operator_mod': - return { - kind: 'op.mod', - left: this.descendInputOfBlock(block, 'NUM1'), - right: this.descendInputOfBlock(block, 'NUM2') - }; + return new IntermediateInput(InputOpcode.OP_MOD, InputType.NUMBER_OR_NAN, { + left: this.descendInputOfBlock(block, 'NUM1').toType(InputType.NUMBER), + right: this.descendInputOfBlock(block, 'NUM2').toType(InputType.NUMBER) + }); case 'operator_multiply': - return { - kind: 'op.multiply', - left: this.descendInputOfBlock(block, 'NUM1'), - right: this.descendInputOfBlock(block, 'NUM2') - }; + return new IntermediateInput(InputOpcode.OP_MULTIPLY, InputType.NUMBER_OR_NAN, { + left: this.descendInputOfBlock(block, 'NUM1').toType(InputType.NUMBER), + right: this.descendInputOfBlock(block, 'NUM2').toType(InputType.NUMBER) + }); case 'operator_not': - return { - kind: 'op.not', - operand: this.descendInputOfBlock(block, 'OPERAND') - }; + return new IntermediateInput(InputOpcode.OP_NOT, InputType.BOOLEAN, { + operand: this.descendInputOfBlock(block, 'OPERAND').toType(InputType.BOOLEAN) + }); case 'operator_or': - return { - kind: 'op.or', - left: this.descendInputOfBlock(block, 'OPERAND1'), - right: this.descendInputOfBlock(block, 'OPERAND2') - }; + return new IntermediateInput(InputOpcode.OP_OR, InputType.BOOLEAN, { + left: this.descendInputOfBlock(block, 'OPERAND1').toType(InputType.BOOLEAN), + right: this.descendInputOfBlock(block, 'OPERAND2').toType(InputType.BOOLEAN) + }); case 'operator_random': { const from = this.descendInputOfBlock(block, 'FROM'); const to = this.descendInputOfBlock(block, 'TO'); // If both values are known at compile time, we can do some optimizations. // TODO: move optimizations to jsgen? - if (from.kind === 'constant' && to.kind === 'constant') { - const sFrom = from.value; - const sTo = to.value; + if (from.opcode === InputOpcode.CONSTANT && to.opcode === InputOpcode.CONSTANT) { + const sFrom = from.inputs.value; + const sTo = to.inputs.value; const nFrom = Cast.toNumber(sFrom); const nTo = Cast.toNumber(sTo); // If both numbers are the same, random is unnecessary. // todo: this probably never happens so consider removing if (nFrom === nTo) { - return { - kind: 'constant', - value: nFrom - }; + return this.createConstantInput(nFrom); } // If both are ints, hint this to the compiler if (Cast.isInt(sFrom) && Cast.isInt(sTo)) { - return { - kind: 'op.random', - low: nFrom <= nTo ? from : to, - high: nFrom <= nTo ? to : from, + // Both inputs are ints, so we know neither are NaN + return new IntermediateInput(InputOpcode.OP_RANDOM, InputType.NUMBER, { + low: (nFrom <= nTo ? from : to).toType(InputType.NUMBER), + high: (nFrom <= nTo ? to : from).toType(InputType.NUMBER), useInts: true, useFloats: false - }; + }); } // Otherwise hint that these are floats - return { - kind: 'op.random', - low: nFrom <= nTo ? from : to, - high: nFrom <= nTo ? to : from, + return new IntermediateInput(InputOpcode.OP_RANDOM, InputType.NUMBER_OR_NAN, { + low: (nFrom <= nTo ? from : to).toType(InputType.NUMBER), + high: (nFrom <= nTo ? to : from).toType(InputType.NUMBER), useInts: false, useFloats: true - }; - } else if (from.kind === 'constant') { + }); + } else if (from.opcode === InputOpcode.CONSTANT) { // If only one value is known at compile-time, we can still attempt some optimizations. - if (!Cast.isInt(Cast.toNumber(from.value))) { - return { - kind: 'op.random', - low: from, - high: to, + if (!Cast.isInt(Cast.toNumber(from.inputs.value))) { + return new IntermediateInput(InputOpcode.OP_RANDOM, InputType.NUMBER_OR_NAN, { + low: from.toType(InputType.NUMBER), + high: to.toType(InputType.NUMBER), useInts: false, useFloats: true - }; + }); } - } else if (to.kind === 'constant') { - if (!Cast.isInt(Cast.toNumber(to.value))) { - return { - kind: 'op.random', - low: from, - high: to, + } else if (to.opcode === InputOpcode.CONSTANT) { + if (!Cast.isInt(Cast.toNumber(from.inputs.value))) { + return new IntermediateInput(InputOpcode.OP_RANDOM, InputType.NUMBER_OR_NAN, { + low: from.toType(InputType.NUMBER), + high: to.toType(InputType.NUMBER), useInts: false, useFloats: true - }; + }); } } // No optimizations possible - return { - kind: 'op.random', + return new IntermediateInput(InputOpcode.OP_RANDOM, InputType.NUMBER_OR_NAN, { low: from, high: to, useInts: false, useFloats: false - }; + }); } case 'operator_round': - return { - kind: 'op.round', - value: this.descendInputOfBlock(block, 'NUM') - }; + return new IntermediateInput(InputOpcode.OP_ROUND, InputType.NUMBER, { + value: this.descendInputOfBlock(block, 'NUM').toType(InputType.NUMBER) + }); case 'operator_subtract': - return { - kind: 'op.subtract', - left: this.descendInputOfBlock(block, 'NUM1'), - right: this.descendInputOfBlock(block, 'NUM2') - }; + return new IntermediateInput(InputOpcode.OP_SUBTRACT, InputType.NUMBER_OR_NAN, { + left: this.descendInputOfBlock(block, 'NUM1').toType(InputType.NUMBER), + right: this.descendInputOfBlock(block, 'NUM2').toType(InputType.NUMBER) + }); - case 'procedures_call': - return this.descendProcedure(block); + case 'procedures_call': { + const procedureInfo = this.getProcedureInfo(block); + return new IntermediateInput(procedureInfo.opcode, InputType.ANY, procedureInfo.inputs, procedureInfo.yields); + } case 'sensing_answer': - return { - kind: 'sensing.answer' - }; + return new IntermediateInput(InputOpcode.SENSING_ANSWER, InputType.STRING); + case 'sensing_coloristouchingcolor': - return { - kind: 'sensing.colorTouchingColor', + return new IntermediateInput(InputOpcode.SENSING_COLOR_TOUCHING_COLOR, InputType.BOOLEAN, { target: this.descendInputOfBlock(block, 'COLOR2'), mask: this.descendInputOfBlock(block, 'COLOR') - }; + }); case 'sensing_current': switch (block.fields.CURRENTMENU.value.toLowerCase()) { - case 'year': - return { - kind: 'sensing.year' - }; - case 'month': - return { - kind: 'sensing.month' - }; - case 'date': - return { - kind: 'sensing.date' - }; - case 'dayofweek': - return { - kind: 'sensing.dayofweek' - }; - case 'hour': - return { - kind: 'sensing.hour' - }; - case 'minute': - return { - kind: 'sensing.minute' - }; - case 'second': - return { - kind: 'sensing.second' - }; + case 'year': return new IntermediateInput(InputOpcode.SENSING_TIME_YEAR, InputType.NUMBER_POS_REAL | InputType.NUMBER_ZERO); + case 'month': return new IntermediateInput(InputOpcode.SENSING_TIME_MONTH, InputType.NUMBER_POS_REAL); + case 'date': return new IntermediateInput(InputOpcode.SENSING_TIME_DATE, InputType.NUMBER_POS_REAL); + case 'dayofweek': return new IntermediateInput(InputOpcode.SENSING_TIME_WEEKDAY, InputType.NUMBER_POS_REAL); + case 'hour': return new IntermediateInput(InputOpcode.SENSING_TIME_HOUR, InputType.NUMBER_POS_REAL | InputType.NUMBER_ZERO); + case 'minute': return new IntermediateInput(InputOpcode.SENSING_TIME_MINUTE, InputType.NUMBER_POS_REAL | InputType.NUMBER_ZERO); + case 'second': return new IntermediateInput(InputOpcode.SENSING_TIME_SECOND, InputType.NUMBER_POS_REAL | InputType.NUMBER_ZERO); + default: return this.createConstantInput(0); } - return { - kind: 'constant', - value: 0 - }; case 'sensing_dayssince2000': - return { - kind: 'sensing.daysSince2000' - }; + return new IntermediateInput(InputOpcode.SENSING_TIME_DAYS_SINCE_2000, InputType.NUMBER); case 'sensing_distanceto': - return { - kind: 'sensing.distance', - target: this.descendInputOfBlock(block, 'DISTANCETOMENU') - }; + return new IntermediateInput(InputOpcode.SENSING_DISTANCE, InputType.NUMBER_POS_REAL | InputType.NUMBER_ZERO, { + target: this.descendInputOfBlock(block, 'DISTANCETOMENU').toType(InputType.STRING) + }); case 'sensing_keypressed': - return { - kind: 'keyboard.pressed', - key: this.descendInputOfBlock(block, 'KEY_OPTION') - }; + return new IntermediateInput(InputOpcode.SENSING_KEY_DOWN, InputType.BOOLEAN, { + key: this.descendInputOfBlock(block, 'KEY_OPTION', true) + }); case 'sensing_mousedown': - return { - kind: 'mouse.down' - }; + return new IntermediateInput(InputOpcode.SENSING_MOUSE_DOWN, InputType.BOOLEAN); case 'sensing_mousex': - return { - kind: 'mouse.x' - }; + return new IntermediateInput(InputOpcode.SENSING_MOUSE_X, InputType.NUMBER); case 'sensing_mousey': - return { - kind: 'mouse.y' - }; + return new IntermediateInput(InputOpcode.SENSING_MOUSE_Y, InputType.NUMBER); case 'sensing_of': - return { - kind: 'sensing.of', - property: block.fields.PROPERTY.value, - object: this.descendInputOfBlock(block, 'OBJECT') - }; + const property = block.fields.PROPERTY.value; + const object = this.descendInputOfBlock(block, 'OBJECT').toType(InputType.STRING); + + if (object.opcode !== InputOpcode.CONSTANT) { + return new IntermediateInput(InputOpcode.SENSING_OF, InputType.ANY, {object, property}); + } + + if (property === 'volume') { + return new IntermediateInput(InputOpcode.SENSING_OF_VOLUME, InputType.NUMBER_POS_REAL | InputType.NUMBER_ZERO, {object, property}); + } + + if (object.isConstant('_stage_')) { + switch (property) { + case 'background #': // fallthrough for scratch 1.0 compatibility + case 'backdrop #': + return new IntermediateInput(InputOpcode.SENSING_OF_BACKDROP_NUMBER, InputType.NUMBER_POS_REAL); + case 'backdrop name': + return new IntermediateInput(InputOpcode.SENSING_OF_BACKDROP_NAME, InputType.STRING); + } + } else { + switch (property) { + case 'x position': + return new IntermediateInput(InputOpcode.SENSING_OF_POS_X, InputType.NUMBER_REAL, {object}); + case 'y position': + return new IntermediateInput(InputOpcode.SENSING_OF_POS_Y, InputType.NUMBER_REAL, {object}); + case 'direction': + return new IntermediateInput(InputOpcode.SENSING_OF_DIRECTION, InputType.NUMBER_REAL, {object}); + case 'costume #': + return new IntermediateInput(InputOpcode.SENSING_OF_COSTUME_NUMBER, InputType.NUMBER_POS_REAL, {object}); + case 'costume name': + return new IntermediateInput(InputOpcode.SENSING_OF_COSTUME_NAME, InputType.STRING, {object}); + case 'size': + return new IntermediateInput(InputOpcode.SENSING_OF_SIZE, InputType.NUMBER_POS_REAL, {object}); + } + } + + return new IntermediateInput(InputOpcode.SENSING_OF_VAR, InputType.ANY, {object, property}); case 'sensing_timer': this.usesTimer = true; - return { - kind: 'timer.get' - }; + return new IntermediateInput(InputOpcode.SENSING_TIMER_GET, InputType.NUMBER_POS_REAL | InputType.NUMBER_ZERO); case 'sensing_touchingcolor': - return { - kind: 'sensing.touchingColor', - color: this.descendInputOfBlock(block, 'COLOR') - }; + return new IntermediateInput(InputOpcode.SENSING_TOUCHING_COLOR, InputType.BOOLEAN, { + color: this.descendInputOfBlock(block, 'COLOR').toType(InputType.NUMBER) + }); case 'sensing_touchingobject': - return { - kind: 'sensing.touching', + return new IntermediateInput(InputOpcode.SENSING_TOUCHING_OBJECT, InputType.BOOLEAN, { object: this.descendInputOfBlock(block, 'TOUCHINGOBJECTMENU') - }; + }); case 'sensing_username': - return { - kind: 'sensing.username' - }; + return new IntermediateInput(InputOpcode.SENSING_USERNAME, InputType.STRING); case 'sound_sounds_menu': // This menu is special compared to other menus -- it actually has an opcode function. - return { - kind: 'constant', - value: block.fields.SOUND_MENU.value - }; + return this.createConstantInput(block.fields.SOUND_MENU.value); + + case 'control_get_counter': + return new IntermediateInput(InputOpcode.CONTROL_COUNTER, InputType.NUMBER_POS_INT | InputType.NUMBER_ZERO); case 'tw_getLastKeyPressed': - return { - kind: 'tw.lastKeyPressed' - }; + return new IntermediateInput(InputOpcode.TW_KEY_LAST_PRESSED, InputType.STRING); default: { const opcodeFunction = this.runtime.getOpcodeFunction(block.opcode); if (opcodeFunction) { // It might be a non-compiled primitive from a standard category if (compatBlocks.inputs.includes(block.opcode)) { - return this.descendCompatLayer(block); + return this.descendCompatLayerInput(block); } // It might be an extension block. const blockInfo = this.getBlockInfo(block.opcode); if (blockInfo) { const type = blockInfo.info.blockType; if (type === BlockType.REPORTER || type === BlockType.BOOLEAN) { - return this.descendCompatLayer(block); + return this.descendCompatLayerInput(block); } } } @@ -674,10 +572,7 @@ class ScriptTreeGenerator { const inputs = Object.keys(block.inputs); const fields = Object.keys(block.fields); if (inputs.length === 0 && fields.length === 1) { - return { - kind: 'constant', - value: block.fields[fields[0]].value - }; + return this.createConstantInput(block.fields[fields[0]].value); } log.warn(`IR: Unknown input: ${block.opcode}`, block); @@ -690,477 +585,356 @@ class ScriptTreeGenerator { * Descend into a stacked block. (eg. "move ( ) steps") * @param {*} block The Scratch block to parse. * @private - * @returns {Node} Compiled node for this block. + * @returns {IntermediateStackBlock} Compiled node for this block. */ descendStackedBlock (block) { switch (block.opcode) { case 'control_all_at_once': // In Scratch 3, this block behaves like "if 1 = 1" - return { - kind: 'control.if', - condition: { - kind: 'constant', - value: true - }, + return new IntermediateStackBlock(StackOpcode.CONTROL_IF_ELSE, { + condition: this.createConstantInput(true).toType(InputType.BOOLEAN), whenTrue: this.descendSubstack(block, 'SUBSTACK'), - whenFalse: [] - }; - case 'control_clear_counter': - return { - kind: 'counter.clear' - }; + whenFalse: new IntermediateStack() + }); case 'control_create_clone_of': - return { - kind: 'control.createClone', - target: this.descendInputOfBlock(block, 'CLONE_OPTION') - }; + return new IntermediateStackBlock(StackOpcode.CONTROL_CLONE_CREATE, { + target: this.descendInputOfBlock(block, 'CLONE_OPTION').toType(InputType.STRING) + }); case 'control_delete_this_clone': - this.script.yields = true; - return { - kind: 'control.deleteClone' - }; + return new IntermediateStackBlock(StackOpcode.CONTROL_CLONE_DELETE, {}, true); case 'control_forever': - this.analyzeLoop(); - return { - kind: 'control.while', - condition: { - kind: 'constant', - value: true - }, + return new IntermediateStackBlock(StackOpcode.CONTROL_WHILE, { + condition: this.createConstantInput(true).toType(InputType.BOOLEAN), do: this.descendSubstack(block, 'SUBSTACK') - }; + }, this.analyzeLoop()); case 'control_for_each': - this.analyzeLoop(); - return { - kind: 'control.for', + return new IntermediateStackBlock(StackOpcode.CONTROL_FOR, { variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE), - count: this.descendInputOfBlock(block, 'VALUE'), + count: this.descendInputOfBlock(block, 'VALUE').toType(InputType.NUMBER), do: this.descendSubstack(block, 'SUBSTACK') - }; + }, this.analyzeLoop()); case 'control_if': - return { - kind: 'control.if', - condition: this.descendInputOfBlock(block, 'CONDITION'), + return new IntermediateStackBlock(StackOpcode.CONTROL_IF_ELSE, { + condition: this.descendInputOfBlock(block, 'CONDITION').toType(InputType.BOOLEAN), whenTrue: this.descendSubstack(block, 'SUBSTACK'), - whenFalse: [] - }; + whenFalse: new IntermediateStack() + }); case 'control_if_else': - return { - kind: 'control.if', - condition: this.descendInputOfBlock(block, 'CONDITION'), + return new IntermediateStackBlock(StackOpcode.CONTROL_IF_ELSE, { + condition: this.descendInputOfBlock(block, 'CONDITION').toType(InputType.BOOLEAN), whenTrue: this.descendSubstack(block, 'SUBSTACK'), whenFalse: this.descendSubstack(block, 'SUBSTACK2') - }; - case 'control_incr_counter': - return { - kind: 'counter.increment' - }; + }); case 'control_repeat': - this.analyzeLoop(); - return { - kind: 'control.repeat', - times: this.descendInputOfBlock(block, 'TIMES'), + return new IntermediateStackBlock(StackOpcode.CONTROL_REPEAT, { + times: this.descendInputOfBlock(block, 'TIMES').toType(InputType.NUMBER), do: this.descendSubstack(block, 'SUBSTACK') - }; + }, this.analyzeLoop()); case 'control_repeat_until': { - this.analyzeLoop(); // Dirty hack: automatically enable warp timer for this block if it uses timer // This fixes project that do things like "repeat until timer > 0.5" this.usesTimer = false; const condition = this.descendInputOfBlock(block, 'CONDITION'); const needsWarpTimer = this.usesTimer; - if (needsWarpTimer) { - this.script.yields = true; - } - return { - kind: 'control.while', - condition: { - kind: 'op.not', + return new IntermediateStackBlock(StackOpcode.CONTROL_WHILE, { + condition: new IntermediateInput(InputOpcode.OP_NOT, InputType.BOOLEAN, { operand: condition - }, + }), do: this.descendSubstack(block, 'SUBSTACK'), warpTimer: needsWarpTimer - }; + }, this.analyzeLoop() || needsWarpTimer); } case 'control_stop': { const level = block.fields.STOP_OPTION.value; if (level === 'all') { - this.script.yields = true; - return { - kind: 'control.stopAll' - }; + return new IntermediateStackBlock(StackOpcode.CONTROL_STOP_ALL, {}, true); } else if (level === 'other scripts in sprite' || level === 'other scripts in stage') { - return { - kind: 'control.stopOthers' - }; + return new IntermediateStackBlock(StackOpcode.CONTROL_STOP_OTHERS); } else if (level === 'this script') { - return { - kind: 'control.stopScript' - }; + return new IntermediateStackBlock(StackOpcode.CONTROL_STOP_SCRIPT); } - return { - kind: 'noop' - }; + return new IntermediateStackBlock(StackOpcode.NOP); } case 'control_wait': - this.script.yields = true; - return { - kind: 'control.wait', - seconds: this.descendInputOfBlock(block, 'DURATION') - }; + return new IntermediateStackBlock(StackOpcode.CONTROL_WAIT, { + seconds: this.descendInputOfBlock(block, 'DURATION').toType(InputType.NUMBER) + }, true); case 'control_wait_until': - this.script.yields = true; - return { - kind: 'control.waitUntil', - condition: this.descendInputOfBlock(block, 'CONDITION') - }; + return new IntermediateStackBlock(StackOpcode.CONTROL_WAIT_UNTIL, { + condition: this.descendInputOfBlock(block, 'CONDITION').toType(InputType.BOOLEAN) + }, true); case 'control_while': - this.analyzeLoop(); - return { - kind: 'control.while', - condition: this.descendInputOfBlock(block, 'CONDITION'), + return new IntermediateStackBlock(StackOpcode.CONTROL_WHILE, { + condition: this.descendInputOfBlock(block, 'CONDITION').toType(InputType.BOOLEAN), do: this.descendSubstack(block, 'SUBSTACK'), // We should consider analyzing this like we do for control_repeat_until warpTimer: false - }; + }, this.analyzeLoop()); + case 'control_clear_counter': + return new IntermediateStackBlock(StackOpcode.CONTROL_CLEAR_COUNTER); + case 'control_incr_counter': + return new IntermediateStackBlock(StackOpcode.CONTORL_INCR_COUNTER); case 'data_addtolist': - return { - kind: 'list.add', + return new IntermediateStackBlock(StackOpcode.LIST_ADD, { list: this.descendVariable(block, 'LIST', LIST_TYPE), - item: this.descendInputOfBlock(block, 'ITEM') - }; + item: this.descendInputOfBlock(block, 'ITEM', true) + }); case 'data_changevariableby': { const variable = this.descendVariable(block, 'VARIABLE', SCALAR_TYPE); - return { - kind: 'var.set', + return new IntermediateStackBlock(StackOpcode.VAR_SET, { variable, - value: { - kind: 'op.add', - left: { - kind: 'var.get', - variable - }, - right: this.descendInputOfBlock(block, 'VALUE') - } - }; + value: new IntermediateInput(InputOpcode.OP_ADD, InputType.NUMBER_OR_NAN, { + left: new IntermediateInput(InputOpcode.VAR_GET, InputType.ANY, {variable}).toType(InputType.NUMBER), + right: this.descendInputOfBlock(block, 'VALUE').toType(InputType.NUMBER) + }) + }); } case 'data_deletealloflist': - return { - kind: 'list.deleteAll', + return new IntermediateStackBlock(StackOpcode.LIST_DELETE_ALL, { list: this.descendVariable(block, 'LIST', LIST_TYPE) - }; + }); case 'data_deleteoflist': { const index = this.descendInputOfBlock(block, 'INDEX'); - if (index.kind === 'constant' && index.value === 'all') { - return { - kind: 'list.deleteAll', + if (index.isConstant('all')) { + return new IntermediateStackBlock(StackOpcode.LIST_DELETE_ALL, { list: this.descendVariable(block, 'LIST', LIST_TYPE) - }; + }); } - return { - kind: 'list.delete', + return new IntermediateStackBlock(StackOpcode.LIST_DELETE, { list: this.descendVariable(block, 'LIST', LIST_TYPE), index: index - }; + }); } case 'data_hidelist': - return { - kind: 'list.hide', + return new IntermediateStackBlock(StackOpcode.LIST_HIDE, { list: this.descendVariable(block, 'LIST', LIST_TYPE) - }; + }); case 'data_hidevariable': - return { - kind: 'var.hide', + return new IntermediateStackBlock(StackOpcode.VAR_HIDE, { variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE) - }; + }); case 'data_insertatlist': - return { - kind: 'list.insert', + return new IntermediateStackBlock(StackOpcode.LIST_INSERT, { list: this.descendVariable(block, 'LIST', LIST_TYPE), index: this.descendInputOfBlock(block, 'INDEX'), - item: this.descendInputOfBlock(block, 'ITEM') - }; + item: this.descendInputOfBlock(block, 'ITEM', true) + }); case 'data_replaceitemoflist': - return { - kind: 'list.replace', + return new IntermediateStackBlock(StackOpcode.LIST_REPLACE, { list: this.descendVariable(block, 'LIST', LIST_TYPE), index: this.descendInputOfBlock(block, 'INDEX'), - item: this.descendInputOfBlock(block, 'ITEM') - }; + item: this.descendInputOfBlock(block, 'ITEM', true) + }); case 'data_setvariableto': - return { - kind: 'var.set', + return new IntermediateStackBlock(StackOpcode.VAR_SET, { variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE), - value: this.descendInputOfBlock(block, 'VALUE') - }; + value: this.descendInputOfBlock(block, 'VALUE', true) + }); case 'data_showlist': - return { - kind: 'list.show', + return new IntermediateStackBlock(StackOpcode.LIST_SHOW, { list: this.descendVariable(block, 'LIST', LIST_TYPE) - }; + }); case 'data_showvariable': - return { - kind: 'var.show', + return new IntermediateStackBlock(StackOpcode.VAR_SHOW, { variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE) - }; + }); case 'event_broadcast': - return { - kind: 'event.broadcast', - broadcast: this.descendInputOfBlock(block, 'BROADCAST_INPUT') - }; + return new IntermediateStackBlock(StackOpcode.EVENT_BROADCAST, { + broadcast: this.descendInputOfBlock(block, 'BROADCAST_INPUT').toType(InputType.STRING) + }); case 'event_broadcastandwait': - this.script.yields = true; - return { - kind: 'event.broadcastAndWait', - broadcast: this.descendInputOfBlock(block, 'BROADCAST_INPUT') - }; + return new IntermediateStackBlock(StackOpcode.EVENT_BROADCAST_AND_WAIT, { + broadcast: this.descendInputOfBlock(block, 'BROADCAST_INPUT').toType(InputType.STRING) + }, true); case 'looks_changeeffectby': - return { - kind: 'looks.changeEffect', + return new IntermediateStackBlock(StackOpcode.LOOKS_EFFECT_CHANGE, { effect: block.fields.EFFECT.value.toLowerCase(), - value: this.descendInputOfBlock(block, 'CHANGE') - }; + value: this.descendInputOfBlock(block, 'CHANGE').toType(InputType.NUMBER) + }); case 'looks_changesizeby': - return { - kind: 'looks.changeSize', - size: this.descendInputOfBlock(block, 'CHANGE') - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_SIZE_CHANGE, { + size: this.descendInputOfBlock(block, 'CHANGE').toType(InputType.NUMBER) + }); case 'looks_cleargraphiceffects': - return { - kind: 'looks.clearEffects' - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_EFFECT_CLEAR); case 'looks_goforwardbackwardlayers': if (block.fields.FORWARD_BACKWARD.value === 'forward') { - return { - kind: 'looks.forwardLayers', - layers: this.descendInputOfBlock(block, 'NUM') - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_LAYER_FORWARD, { + layers: this.descendInputOfBlock(block, 'NUM').toType(InputType.NUMBER) + }); } - return { - kind: 'looks.backwardLayers', - layers: this.descendInputOfBlock(block, 'NUM') - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_LAYER_BACKWARD, { + layers: this.descendInputOfBlock(block, 'NUM').toType(InputType.NUMBER) + }); case 'looks_gotofrontback': if (block.fields.FRONT_BACK.value === 'front') { - return { - kind: 'looks.goToFront' - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_LAYER_FRONT); } - return { - kind: 'looks.goToBack' - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_LAYER_BACK); case 'looks_hide': - return { - kind: 'looks.hide' - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_HIDE); case 'looks_nextbackdrop': - return { - kind: 'looks.nextBackdrop' - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_BACKDROP_NEXT); case 'looks_nextcostume': - return { - kind: 'looks.nextCostume' - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_COSTUME_NEXT); case 'looks_seteffectto': - return { - kind: 'looks.setEffect', + return new IntermediateStackBlock(StackOpcode.LOOKS_EFFECT_SET, { effect: block.fields.EFFECT.value.toLowerCase(), - value: this.descendInputOfBlock(block, 'VALUE') - }; + value: this.descendInputOfBlock(block, 'VALUE').toType(InputType.NUMBER) + }); case 'looks_setsizeto': - return { - kind: 'looks.setSize', - size: this.descendInputOfBlock(block, 'SIZE') - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_SIZE_SET, { + size: this.descendInputOfBlock(block, 'SIZE').toType(InputType.NUMBER) + }); case 'looks_show': - return { - kind: 'looks.show' - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_SHOW); case 'looks_switchbackdropto': - return { - kind: 'looks.switchBackdrop', - backdrop: this.descendInputOfBlock(block, 'BACKDROP') - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_BACKDROP_SET, { + backdrop: this.descendInputOfBlock(block, 'BACKDROP', true) + }); case 'looks_switchcostumeto': - return { - kind: 'looks.switchCostume', - costume: this.descendInputOfBlock(block, 'COSTUME') - }; + return new IntermediateStackBlock(StackOpcode.LOOKS_COSTUME_SET, { + costume: this.descendInputOfBlock(block, 'COSTUME', true) + }); case 'motion_changexby': - return { - kind: 'motion.changeX', - dx: this.descendInputOfBlock(block, 'DX') - }; + return new IntermediateStackBlock(StackOpcode.MOTION_X_CHANGE, { + dx: this.descendInputOfBlock(block, 'DX').toType(InputType.NUMBER) + }); case 'motion_changeyby': - return { - kind: 'motion.changeY', - dy: this.descendInputOfBlock(block, 'DY') - }; + return new IntermediateStackBlock(StackOpcode.MOTION_Y_CHANGE, { + dy: this.descendInputOfBlock(block, 'DY').toType(InputType.NUMBER) + }); case 'motion_gotoxy': - return { - kind: 'motion.setXY', - x: this.descendInputOfBlock(block, 'X'), - y: this.descendInputOfBlock(block, 'Y') - }; + return new IntermediateStackBlock(StackOpcode.MOTION_XY_SET, { + x: this.descendInputOfBlock(block, 'X').toType(InputType.NUMBER), + y: this.descendInputOfBlock(block, 'Y').toType(InputType.NUMBER) + }); case 'motion_ifonedgebounce': - return { - kind: 'motion.ifOnEdgeBounce' - }; + return new IntermediateStackBlock(StackOpcode.MOTION_IF_ON_EDGE_BOUNCE); case 'motion_movesteps': - return { - kind: 'motion.step', - steps: this.descendInputOfBlock(block, 'STEPS') - }; + return new IntermediateStackBlock(StackOpcode.MOTION_STEP, { + steps: this.descendInputOfBlock(block, 'STEPS').toType(InputType.NUMBER) + }); case 'motion_pointindirection': - return { - kind: 'motion.setDirection', - direction: this.descendInputOfBlock(block, 'DIRECTION') - }; + return new IntermediateStackBlock(StackOpcode.MOTION_DIRECTION_SET, { + direction: this.descendInputOfBlock(block, 'DIRECTION').toType(InputType.NUMBER) + }); case 'motion_setrotationstyle': - return { - kind: 'motion.setRotationStyle', + return new IntermediateStackBlock(StackOpcode.MOTION_ROTATION_STYLE_SET, { style: block.fields.STYLE.value - }; + }); case 'motion_setx': - return { - kind: 'motion.setX', - x: this.descendInputOfBlock(block, 'X') - }; + return new IntermediateStackBlock(StackOpcode.MOTION_X_SET, { + x: this.descendInputOfBlock(block, 'X').toType(InputType.NUMBER) + }); case 'motion_sety': - return { - kind: 'motion.setY', - y: this.descendInputOfBlock(block, 'Y') - }; + return new IntermediateStackBlock(StackOpcode.MOTION_Y_SET, { + y: this.descendInputOfBlock(block, 'Y').toType(InputType.NUMBER) + }); case 'motion_turnleft': - return { - kind: 'motion.setDirection', - direction: { - kind: 'op.subtract', - left: { - kind: 'motion.direction' - }, + return new IntermediateStackBlock(StackOpcode.MOTION_DIRECTION_SET, { + direction: new IntermediateInput(InputOpcode.OP_SUBTRACT, InputType.NUMBER, { + left: new IntermediateInput(InputOpcode.MOTION_DIRECTION_GET, InputType.NUMBER), right: this.descendInputOfBlock(block, 'DEGREES') - } - }; + }) + }); case 'motion_turnright': - return { - kind: 'motion.setDirection', - direction: { - kind: 'op.add', - left: { - kind: 'motion.direction' - }, + return new IntermediateStackBlock(StackOpcode.MOTION_DIRECTION_SET, { + direction: new IntermediateInput(InputOpcode.OP_ADD, InputType.NUMBER, { + left: new IntermediateInput(InputOpcode.MOTION_DIRECTION_GET, InputType.NUMBER), right: this.descendInputOfBlock(block, 'DEGREES') - } - }; + }) + }); case 'pen_clear': - return { - kind: 'pen.clear' - }; + return new IntermediateStackBlock(StackOpcode.PEN_CLEAR); case 'pen_changePenColorParamBy': - return { - kind: 'pen.changeParam', - param: this.descendInputOfBlock(block, 'COLOR_PARAM'), - value: this.descendInputOfBlock(block, 'VALUE') - }; + return new IntermediateStackBlock(StackOpcode.PEN_COLOR_PARAM_CHANGE, { + param: this.descendInputOfBlock(block, 'COLOR_PARAM').toType(InputType.STRING), + value: this.descendInputOfBlock(block, 'VALUE').toType(InputType.NUMBER) + }); case 'pen_changePenHueBy': - return { - kind: 'pen.legacyChangeHue', - hue: this.descendInputOfBlock(block, 'HUE') - }; + return new IntermediateStackBlock(StackOpcode.PEN_COLOR_HUE_CHANGE_LEGACY, { + hue: this.descendInputOfBlock(block, 'HUE').toType(InputType.NUMBER) + }); case 'pen_changePenShadeBy': - return { - kind: 'pen.legacyChangeShade', - shade: this.descendInputOfBlock(block, 'SHADE') - }; + return new IntermediateStackBlock(StackOpcode.PEN_COLOR_SHADE_CHANGE_LEGACY, { + shade: this.descendInputOfBlock(block, 'SHADE').toType(InputType.NUMBER) + }); case 'pen_penDown': - return { - kind: 'pen.down' - }; + return new IntermediateStackBlock(StackOpcode.PEN_DOWN); case 'pen_penUp': - return { - kind: 'pen.up' - }; + return new IntermediateStackBlock(StackOpcode.PEN_UP); case 'pen_setPenColorParamTo': - return { - kind: 'pen.setParam', - param: this.descendInputOfBlock(block, 'COLOR_PARAM'), - value: this.descendInputOfBlock(block, 'VALUE') - }; + return new IntermediateStackBlock(StackOpcode.PEN_COLOR_PARAM_SET, { + param: this.descendInputOfBlock(block, 'COLOR_PARAM').toType(InputType.STRING), + value: this.descendInputOfBlock(block, 'VALUE').toType(InputType.NUMBER) + }); case 'pen_setPenColorToColor': - return { - kind: 'pen.setColor', + return new IntermediateStackBlock(StackOpcode.PEN_COLOR_SET, { color: this.descendInputOfBlock(block, 'COLOR') - }; + }); case 'pen_setPenHueToNumber': - return { - kind: 'pen.legacySetHue', - hue: this.descendInputOfBlock(block, 'HUE') - }; + return new IntermediateStackBlock(StackOpcode.PEN_COLOR_HUE_SET_LEGACY, { + hue: this.descendInputOfBlock(block, 'HUE').toType(InputType.NUMBER) + }); case 'pen_setPenShadeToNumber': - return { - kind: 'pen.legacySetShade', - shade: this.descendInputOfBlock(block, 'SHADE') - }; + return new IntermediateStackBlock(StackOpcode.PEN_COLOR_SHADE_SET_LEGACY, { + shade: this.descendInputOfBlock(block, 'SHADE').toType(InputType.NUMBER) + }); case 'pen_setPenSizeTo': - return { - kind: 'pen.setSize', - size: this.descendInputOfBlock(block, 'SIZE') - }; + return new IntermediateStackBlock(StackOpcode.PEN_SIZE_SET, { + size: this.descendInputOfBlock(block, 'SIZE').toType(InputType.NUMBER) + }); case 'pen_changePenSizeBy': - return { - kind: 'pen.changeSize', - size: this.descendInputOfBlock(block, 'SIZE') - }; + return new IntermediateStackBlock(StackOpcode.PEN_SIZE_CHANGE, { + size: this.descendInputOfBlock(block, 'SIZE').toType(InputType.NUMBER) + }); case 'pen_stamp': - return { - kind: 'pen.stamp' - }; + return new IntermediateStackBlock(StackOpcode.PEN_STAMP); case 'procedures_call': { const procedureCode = block.mutation.proccode; + if (block.mutation.return) { const visualReport = this.descendVisualReport(block); if (visualReport) { return visualReport; } } + if (procedureCode === 'tw:debugger;') { - return { - kind: 'tw.debugger' - }; + return new IntermediateStackBlock(StackOpcode.DEBUGGER); } - return this.descendProcedure(block); + + const procedure = this.getProcedureInfo(block); + return new IntermediateStackBlock(procedure.opcode, procedure.inputs, procedure.yields); } case 'procedures_return': - return { - kind: 'procedures.return', + return new IntermediateStackBlock(StackOpcode.PROCEDURE_RETURN, { value: this.descendInputOfBlock(block, 'VALUE') - }; + }); case 'sensing_resettimer': - return { - kind: 'timer.reset' - }; + return new IntermediateStackBlock(StackOpcode.SENSING_TIMER_RESET); default: { const opcodeFunction = this.runtime.getOpcodeFunction(block.opcode); if (opcodeFunction) { // It might be a non-compiled primitive from a standard category if (compatBlocks.stacked.includes(block.opcode)) { - return this.descendCompatLayer(block); + return this.descendCompatLayerStack(block); } // It might be an extension block. const blockInfo = this.getBlockInfo(block.opcode); if (blockInfo) { const type = blockInfo.info.blockType; if (type === BlockType.COMMAND || type === BlockType.CONDITIONAL || type === BlockType.LOOP) { - return this.descendCompatLayer(block); + return this.descendCompatLayerStack(block); } } } @@ -1179,14 +953,14 @@ class ScriptTreeGenerator { /** * Descend into a stack of blocks (eg. the blocks contained within an "if" block) * @param {*} parentBlock The parent Scratch block that contains the stack to parse. - * @param {*} substackName The name of the stack to descend into. + * @param {string} substackName The name of the stack to descend into. * @private - * @returns {Node[]} List of stacked block nodes. + * @returns {IntermediateStack} Stacked blocks. */ descendSubstack (parentBlock, substackName) { const input = parentBlock.inputs[substackName]; if (!input) { - return []; + return new IntermediateStack(); } const stackId = input.block; return this.walkStack(stackId); @@ -1196,10 +970,10 @@ class ScriptTreeGenerator { * Descend into and walk the siblings of a stack. * @param {string} startingBlockId The ID of the first block of a stack. * @private - * @returns {Node[]} List of stacked block nodes. + * @returns {IntermediateStack} List of stacked block nodes. */ walkStack (startingBlockId) { - const result = []; + const result = new IntermediateStack(); let blockId = startingBlockId; while (blockId !== null) { @@ -1209,7 +983,8 @@ class ScriptTreeGenerator { } const node = this.descendStackedBlock(block); - result.push(node); + this.script.yields = this.script.yields || node.yields; + result.blocks.push(node); blockId = block.next; } @@ -1218,130 +993,51 @@ class ScriptTreeGenerator { } /** - * Descend into a variable. - * @param {*} block The block that has the variable. - * @param {string} fieldName The name of the field that the variable is stored in. - * @param {''|'list'} type Variable type, '' for scalar and 'list' for list. - * @private - * @returns {*} A parsed variable object. + * @param {*} block + * @returns {{ + * opcode: StackOpcode & InputOpcode, + * inputs?: *, + * yields: boolean + * }} */ - descendVariable (block, fieldName, type) { - const variable = block.fields[fieldName]; - const id = variable.id; - - if (Object.prototype.hasOwnProperty.call(this.variableCache, id)) { - return this.variableCache[id]; - } - - const data = this._descendVariable(id, variable.value, type); - this.variableCache[id] = data; - return data; - } - - /** - * @param {string} id The ID of the variable. - * @param {string} name The name of the variable. - * @param {''|'list'} type The variable type. - * @private - * @returns {*} A parsed variable object. - */ - _descendVariable (id, name, type) { - const target = this.target; - const stage = this.stage; - - // Look for by ID in target... - if (Object.prototype.hasOwnProperty.call(target.variables, id)) { - return createVariableData('target', target.variables[id]); - } - - // Look for by ID in stage... - if (!target.isStage) { - if (stage && Object.prototype.hasOwnProperty.call(stage.variables, id)) { - return createVariableData('stage', stage.variables[id]); - } - } - - // Look for by name and type in target... - for (const varId in target.variables) { - if (Object.prototype.hasOwnProperty.call(target.variables, varId)) { - const currVar = target.variables[varId]; - if (currVar.name === name && currVar.type === type) { - return createVariableData('target', currVar); - } - } - } - - // Look for by name and type in stage... - if (!target.isStage && stage) { - for (const varId in stage.variables) { - if (Object.prototype.hasOwnProperty.call(stage.variables, varId)) { - const currVar = stage.variables[varId]; - if (currVar.name === name && currVar.type === type) { - return createVariableData('stage', currVar); - } - } - } - } - - // Create it locally... - const newVariable = new Variable(id, name, type, false); - target.variables[id] = newVariable; - - if (target.sprite) { - // Create the variable in all instances of this sprite. - // This is necessary because the script cache is shared between clones. - // sprite.clones has all instances of this sprite including the original and all clones - for (const clone of target.sprite.clones) { - if (!Object.prototype.hasOwnProperty.call(clone.variables, id)) { - clone.variables[id] = new Variable(id, name, type, false); - } - } - } - - return createVariableData('target', newVariable); - } - - descendProcedure (block) { + getProcedureInfo (block) { const procedureCode = block.mutation.proccode; const paramNamesIdsAndDefaults = this.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode); + if (paramNamesIdsAndDefaults === null) { - return { - kind: 'noop' - }; + return {opcode: StackOpcode.NOP, yields: false}; } const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults; const addonBlock = this.runtime.getAddonBlock(procedureCode); if (addonBlock) { - this.script.yields = true; const args = {}; for (let i = 0; i < paramIds.length; i++) { let value; if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) { - value = this.descendInputOfBlock(block, paramIds[i]); + value = this.descendInputOfBlock(block, paramIds[i], true); } else { - value = { - kind: 'constant', - value: paramDefaults[i] - }; + value = this.createConstantInput(paramDefaults[i], true); } args[paramNames[i]] = value; } + return { - kind: 'addons.call', - code: procedureCode, - arguments: args, - blockId: block.id + opcode: StackOpcode.ADDON_CALL, + inputs: { + code: procedureCode, + arguments: args, + blockId: block.id + }, + yields: true }; } const definitionId = this.blocks.getProcedureDefinition(procedureCode); const definitionBlock = this.blocks.getBlock(definitionId); if (!definitionBlock) { - return { - kind: 'noop' - }; + return {opcode: StackOpcode.NOP, yields: false}; } const innerDefinition = this.blocks.getBlock(definitionBlock.inputs.custom_block.block); @@ -1363,48 +1059,163 @@ class ScriptTreeGenerator { this.script.dependedProcedures.push(variant); } - // Non-warp direct recursion yields. - if (!this.script.isWarp) { - if (procedureCode === this.script.procedureCode) { - this.script.yields = true; - } - } - const args = []; for (let i = 0; i < paramIds.length; i++) { let value; if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) { - value = this.descendInputOfBlock(block, paramIds[i]); + value = this.descendInputOfBlock(block, paramIds[i], true); } else { - value = { - kind: 'constant', - value: paramDefaults[i] - }; + value = this.createConstantInput(paramDefaults[i], true); } args.push(value); } return { - kind: 'procedures.call', - code: procedureCode, - variant, - arguments: args + opcode: StackOpcode.PROCEDURE_CALL, + inputs: { + code: procedureCode, + variant, + arguments: args + }, + yields: !this.script.isWarp && procedureCode === this.script.procedureCode }; } /** - * Descend into a block that uses the compatibility layer. + * @param {*} block + * @returns {IntermediateStackBlock | null} + */ + descendVisualReport (block) { + if (!this.thread.stackClick || block.next) { + return null; + } + try { + return new IntermediateStackBlock(StackOpcode.VISUAL_REPORT, { + input: this.descendInput(block) + }); + } catch (e) { + return null; + } + } + + /** + * Descend into a variable. + * @param {*} block The block that has the variable. + * @param {string} fieldName The name of the field that the variable is stored in. + * @param {''|'list'} type Variable type, '' for scalar and 'list' for list. + * @private + * @returns {*} A parsed variable object. + */ + descendVariable (block, fieldName, type) { + const variable = block.fields[fieldName]; + const id = variable.id; + + if (this.variableCache.hasOwnProperty(id)) { + return this.variableCache[id]; + } + + const data = this._descendVariable(id, variable.value, type); + this.variableCache[id] = data; + return data; + } + + /** + * @param {string} id The ID of the variable. + * @param {string} name The name of the variable. + * @param {''|'list'} type The variable type. + * @private + * @returns {*} A parsed variable object. + */ + _descendVariable (id, name, type) { + const target = this.target; + const stage = this.stage; + + // Look for by ID in target... + if (target.variables.hasOwnProperty(id)) { + return createVariableData('target', target.variables[id]); + } + + // Look for by ID in stage... + if (!target.isStage) { + if (stage && stage.variables.hasOwnProperty(id)) { + return createVariableData('stage', stage.variables[id]); + } + } + + // Look for by name and type in target... + for (const varId in target.variables) { + if (target.variables.hasOwnProperty(varId)) { + const currVar = target.variables[varId]; + if (currVar.name === name && currVar.type === type) { + return createVariableData('target', currVar); + } + } + } + + // Look for by name and type in stage... + if (!target.isStage && stage) { + for (const varId in stage.variables) { + if (stage.variables.hasOwnProperty(varId)) { + const currVar = stage.variables[varId]; + if (currVar.name === name && currVar.type === type) { + return createVariableData('stage', currVar); + } + } + } + } + + // Create it locally... + const newVariable = new Variable(id, name, type, false); + target.variables[id] = newVariable; + + if (target.sprite) { + // Create the variable in all instances of this sprite. + // This is necessary because the script cache is shared between clones. + // sprite.clones has all instances of this sprite including the original and all clones + for (const clone of target.sprite.clones) { + if (!clone.variables.hasOwnProperty(id)) { + clone.variables[id] = new Variable(id, name, type, false); + } + } + } + + return createVariableData('target', newVariable); + } + + /** + * Descend into an input block that uses the compatibility layer. * @param {*} block The block to use the compatibility layer for. * @private - * @returns {Node} The parsed node. + * @returns {IntermediateInput} The parsed node. */ - descendCompatLayer (block) { - this.script.yields = true; + descendCompatLayerInput (block) { + const inputs = {}; + const fields = {}; + for (const name of Object.keys(block.inputs)) { + inputs[name] = this.descendInputOfBlock(block, name, true); + } + for (const name of Object.keys(block.fields)) { + fields[name] = block.fields[name].value; + } + return new IntermediateInput(InputOpcode.COMPATIBILITY_LAYER, InputType.ANY, { + opcode: block.opcode, + id: block.id, + inputs, + fields + }, true); + } + /** + * Descend into a stack block that uses the compatibility layer. + * @param {*} block The block to use the compatibility layer for. + * @private + * @returns {IntermediateStackBlock} The parsed node. + */ + descendCompatLayerStack (block) { const inputs = {}; for (const name of Object.keys(block.inputs)) { if (!name.startsWith('SUBSTACK')) { - inputs[name] = this.descendInputOfBlock(block, name); + inputs[name] = this.descendInputOfBlock(block, name, true); } } @@ -1415,32 +1226,27 @@ class ScriptTreeGenerator { const blockInfo = this.getBlockInfo(block.opcode); const blockType = (blockInfo && blockInfo.info && blockInfo.info.blockType) || BlockType.COMMAND; - const substacks = {}; + const substacks = []; if (blockType === BlockType.CONDITIONAL || blockType === BlockType.LOOP) { - for (const inputName in block.inputs) { - if (!inputName.startsWith('SUBSTACK')) continue; - const branchNum = inputName === 'SUBSTACK' ? 1 : +inputName.substring('SUBSTACK'.length); - if (!isNaN(branchNum)) { - substacks[branchNum] = this.descendSubstack(block, inputName); - } + const branchCount = blockInfo.info.branchCount; + for (let i = 0; i < branchCount; i++) { + const inputName = i === 0 ? 'SUBSTACK' : `SUBSTACK${i + 1}`; + substacks.push(this.descendSubstack(block, inputName)); } } - return { - kind: 'compat', - id: block.id, + return new IntermediateStackBlock(StackOpcode.COMPATIBILITY_LAYER, { opcode: block.opcode, + id: block.id, blockType, inputs, fields, substacks - }; + }, true); } analyzeLoop () { - if (!this.script.isWarp || this.script.warpTimer) { - this.script.yields = true; - } + return !this.script.isWarp || this.script.warpTimer; } readTopBlockComment (commentId) { @@ -1474,22 +1280,9 @@ class ScriptTreeGenerator { } } - descendVisualReport (block) { - if (!this.thread.stackClick || block.next) { - return null; - } - try { - return { - kind: 'visualReport', - input: this.descendInput(block) - }; - } catch (e) { - return null; - } - } - /** - * @param {Block} hatBlock + * @param {*} hatBlock + * @returns {IntermediateStack} */ walkHat (hatBlock) { const nextBlock = hatBlock.next; @@ -1501,10 +1294,10 @@ class ScriptTreeGenerator { // interpreter parity, but the reuslt is ignored. const opcodeFunction = this.runtime.getOpcodeFunction(opcode); if (opcodeFunction) { - return [ - this.descendCompatLayer(hatBlock), - ...this.walkStack(nextBlock) - ]; + return new IntermediateStack([ + this.descendCompatLayerStack(hatBlock), + ...this.walkStack(nextBlock).blocks + ]); } return this.walkStack(nextBlock); } @@ -1513,14 +1306,13 @@ class ScriptTreeGenerator { // Edge-activated HAT this.script.yields = true; this.script.executableHat = true; - return [ - { - kind: 'hat.edge', + return new IntermediateStack([ + new IntermediateStackBlock(StackOpcode.HAT_EDGE, { id: hatBlock.id, - condition: this.descendCompatLayer(hatBlock) - }, - ...this.walkStack(nextBlock) - ]; + condition: this.descendCompatLayerInput(hatBlock).toType(InputType.BOOLEAN) + }), + ...this.walkStack(nextBlock).blocks + ]); } const opcodeFunction = this.runtime.getOpcodeFunction(opcode); @@ -1528,13 +1320,12 @@ class ScriptTreeGenerator { // Predicate-based HAT this.script.yields = true; this.script.executableHat = true; - return [ - { - kind: 'hat.predicate', - condition: this.descendCompatLayer(hatBlock) - }, - ...this.walkStack(nextBlock) - ]; + return new IntermediateStack([ + new IntermediateStackBlock(StackOpcode.HAT_PREDICATE, { + condition: this.descendCompatLayerInput(hatBlock).toType(InputType.BOOLEAN) + }), + ...this.walkStack(nextBlock).blocks + ]); } return this.walkStack(nextBlock); @@ -1601,7 +1392,7 @@ class IRGenerator { addProcedureDependencies (dependencies) { for (const procedureVariant of dependencies) { - if (Object.prototype.hasOwnProperty.call(this.procedures, procedureVariant)) { + if (this.procedures.hasOwnProperty(procedureVariant)) { continue; } if (this.compilingProcedures.has(procedureVariant)) { @@ -1687,14 +1478,8 @@ class IRGenerator { // Analyze scripts until no changes are made. while (this.analyzeScript(entry)); - const ir = new IntermediateRepresentation(); - ir.entry = entry; - ir.procedures = this.procedures; - return ir; + return new IntermediateRepresentation(entry, this.procedures); } } -module.exports = { - ScriptTreeGenerator, - IRGenerator -}; +module.exports = IRGenerator; diff --git a/src/compiler/iroptimizer.js b/src/compiler/iroptimizer.js new file mode 100644 index 00000000..4e4ce7a5 --- /dev/null +++ b/src/compiler/iroptimizer.js @@ -0,0 +1,703 @@ +// @ts-check + +const {IntermediateStack, IntermediateInput, IntermediateScript, IntermediateRepresentation, IntermediateStackBlock} = require('./intermediate'); +const {StackOpcode, InputOpcode, InputType} = require('./enums.js'); + +class TypeState { + constructor () { + /** @type {Object.}*/ + this.variables = {}; + /** @type {InputType | 0} */ + this.defaultType = 0; + } + + /** + * @returns {boolean} + */ + clear () { + let modified = false; + for (const varId in this.variables) { + if (this.variables[varId] !== InputType.ANY) { + modified = true; + break; + } + } + this.variables = {}; + this.defaultType = InputType.ANY; + return modified; + } + + + /** + * @returns {TypeState} + */ + clone () { + const clone = new TypeState(); + for (const varId in this.variables) { + clone.variables[varId] = this.variables[varId]; + } + clone.defaultType = this.defaultType; + return clone; + } + + /** + * @param {TypeState} other + * @param {(varId: string) => InputType | 0} stateMutator + * @returns {boolean} + * @private + */ + mutate (other, stateMutator) { + let modified = false; + for (const varId in other.variables) { + const newValue = stateMutator(varId); + if (newValue !== this.variables[varId]) { + this.variables[varId] = newValue; + modified = modified || true; + } + } + + for (const varId in this.variables) { + if (!other.variables[varId]) { + const newValue = stateMutator(varId); + if (newValue !== this.variables[varId]) { + this.variables[varId] = newValue; + modified = modified || true; + } + } + } + return modified; + } + + /** + * @param {TypeState} other + * @returns {boolean} + */ + or (other) { + return this.mutate(other, varId => { + const thisType = this.variables[varId] ?? this.defaultType; + const otherType = other.variables[varId] ?? other.defaultType; + return thisType | otherType; + }); + } + + /** + * @param {TypeState} other + * @returns {boolean} + */ + after (other) { + return this.mutate(other, varId => { + const otherType = other.variables[varId] ?? other.defaultType; + if (otherType !== 0) return otherType; + return this.variables[varId] ?? this.defaultType; + }); + } + + /** + * @param {*} variable A variable codegen object. + * @param {InputType} type The type to set this variable to + * @returns {boolean} + */ + setVariableType (variable, type) { + if (this.getVariableType(variable) === type) return false; + this.variables[variable.id] = type; + return true; + } + + /** + * + * @param {*} variable A variable codegen object. + * @returns {InputType} + */ + getVariableType (variable) { + return this.variables[variable.id] ?? (this.defaultType === 0 ? InputType.ANY : this.defaultType); + } +} + +class IROptimizer { + + /** + * @param {IntermediateRepresentation} ir + */ + constructor (ir) { + /** @type {IntermediateRepresentation} */ + this.ir = ir; + /** @type {boolean} Used for testing */ + this.ignoreYields = false; + } + + /** + * @param {IntermediateInput} inputBlock + * @param {TypeState} state + * @returns {InputType} + */ + getInputType (inputBlock, state) { + const inputs = inputBlock.inputs; + + switch (inputBlock.opcode) { + case InputOpcode.VAR_GET: + return state.getVariableType(inputs.variable); + + case InputOpcode.ADDON_CALL: + + + case InputOpcode.CAST_NUMBER: { + const innerType = inputs.target.type; + if (innerType & InputType.NUMBER) return innerType; + return InputType.NUMBER; + } case InputOpcode.CAST_NUMBER_OR_NAN: { + const innerType = inputs.target; + if (innerType & InputType.NUMBER_OR_NAN) return innerType; + return InputType.NUMBER_OR_NAN; + } + + case InputOpcode.OP_ADD: { + const leftType = inputs.left.type; + const rightType = inputs.right.type; + + let resultType = 0; + + function canBeNaN () { + // Infinity + (-Infinity) = NaN + if ((leftType & InputType.NUMBER_POS_INF) && (rightType & InputType.NUMBER_NEG_INF)) return true; + // (-Infinity) + Infinity = NaN + if ((leftType & InputType.NUMBER_NEG_INF) && (rightType & InputType.NUMBER_POS_INF)) return true; + } + if (canBeNaN()) resultType |= InputType.NUMBER_NAN; + + function canBeFractional () { + // For the plus operation to return a non-whole number one of it's + // inputs has to be a non-whole number + if (leftType & InputType.NUMBER_FRACT) return true; + if (rightType & InputType.NUMBER_FRACT) return true; + } + const canBeFract = canBeFractional(); + + function canBePos () { + if (leftType & InputType.NUMBER_POS) return true; // POS + ANY ~= POS + if (rightType & InputType.NUMBER_POS) return true; // ANY + POS ~= POS + } + if (canBePos()) { + resultType |= InputType.NUMBER_POS_INT | InputType.NUMBER_POS_INF; + if (canBeFract) resultType |= InputType.NUMBER_POS_FRACT; + } + + function canBeNeg () { + if (leftType & InputType.NUMBER_NEG) return true; // NEG + ANY ~= NEG + if (rightType & InputType.NUMBER_NEG) return true; // ANY + NEG ~= NEG + } + if (canBeNeg()) { + resultType |= InputType.NUMBER_NEG_INT | InputType.NUMBER_NEG_INF; + if (canBeFract) resultType |= InputType.NUMBER_NEG_FRACT; + } + + function canBeZero () { + // POS_REAL + NEG_REAL ~= 0 + if ((leftType & InputType.NUMBER_POS_REAL) && (rightType & InputType.NUMBER_NEG_REAL)) return true; + // NEG_REAL + POS_REAL ~= 0 + if ((leftType & InputType.NUMBER_NEG_REAL) && (rightType & InputType.NUMBER_POS_REAL)) return true; + // 0 + 0 = 0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_ZERO)) return true; + // 0 + -0 = 0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + // -0 + 0 = 0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_ZERO)) return true; + } + if (canBeZero()) resultType |= InputType.NUMBER_ZERO; + + function canBeNegZero () { + // -0 + -0 = -0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + } + if (canBeNegZero()) resultType |= InputType.NUMBER_NEG_ZERO; + + return resultType; + } + + case InputOpcode.OP_SUBTRACT: { + const leftType = inputs.left.type; + const rightType = inputs.right.type; + + let resultType = 0; + + function canBeNaN () { + // Infinity - Infinity = NaN + if ((leftType & InputType.NUMBER_POS_INF) && (rightType & InputType.NUMBER_POS_INF)) return true; + // (-Infinity) - (-Infinity) = NaN + if ((leftType & InputType.NUMBER_NEG_INF) && (rightType & InputType.NUMBER_NEG_INF)) return true; + } + if (canBeNaN()) resultType |= InputType.NUMBER_NAN; + + function canBeFractional () { + // For the subtract operation to return a non-whole number one of it's + // inputs has to be a non-whole number + if (leftType & InputType.NUMBER_FRACT) return true; + if (rightType & InputType.NUMBER_FRACT) return true; + } + const canBeFract = canBeFractional(); + + function canBePos () { + if (leftType & InputType.NUMBER_POS) return true; // POS - ANY ~= POS + if (rightType & InputType.NUMBER_NEG) return true; // ANY - NEG ~= POS + } + if (canBePos()) { + resultType |= InputType.NUMBER_POS_INT | InputType.NUMBER_POS_INF; + if (canBeFract) resultType |= InputType.NUMBER_POS_FRACT; + } + + function canBeNeg () { + if (leftType & InputType.NUMBER_NEG) return true; // NEG - ANY ~= NEG + if (rightType & InputType.NUMBER_POS) return true; // ANY - POS ~= NEG + } + if (canBeNeg()) { + resultType |= InputType.NUMBER_NEG_INT | InputType.NUMBER_NEG_INF; + if (canBeFract) resultType |= InputType.NUMBER_NEG_FRACT; + } + + function canBeZero () { + // POS_REAL - POS_REAL ~= 0 + if ((leftType & InputType.NUMBER_POS_REAL) && (rightType & InputType.NUMBER_POS_REAL)) return true; + // NEG_REAL - NEG_REAL ~= 0 + if ((leftType & InputType.NUMBER_NEG_REAL) && (rightType & InputType.NUMBER_NEG_REAL)) return true; + // 0 - 0 = 0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_ZERO)) return true; + // 0 - (-0) = 0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + // (-0) - (-0) = 0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + } + if (canBeZero()) resultType |= InputType.NUMBER_ZERO; + + function canBeNegZero () { + // (-0) - 0 = -0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_ZERO)) return true; + } + if (canBeNegZero()) resultType |= InputType.NUMBER_NEG_ZERO; + + return resultType; + } + + case InputOpcode.OP_MULTIPLY: { + const leftType = inputs.left.type; + const rightType = inputs.right.type; + + let resultType = 0; + + function canBeNaN () { + // (-)Infinity * 0 = NaN + if ((leftType & InputType.NUMBER_INF) && (rightType & InputType.NUMBER_ANY_ZERO)) return true; + // 0 * (-)Infinity = NaN + if ((leftType & InputType.NUMBER_ANY_ZERO) && (rightType & InputType.NUMBER_INF)) return true; + } + if (canBeNaN()) resultType |= InputType.NUMBER_NAN; + + function canBeFractional () { + // For the subtract operation to return a non-whole number one of it's + // inputs has to be a non-whole number + if (leftType & InputType.NUMBER_FRACT) return true; + if (rightType & InputType.NUMBER_FRACT) return true; + } + const canBeFract = canBeFractional(); + + function canBePos () { + // POS * POS = POS + if ((leftType & InputType.NUMBER_POS) && (rightType & InputType.NUMBER_POS)) return true; + // NEG * NEG = POS + if ((leftType & InputType.NUMBER_NEG) && (rightType & InputType.NUMBER_NEG)) return true; + } + if (canBePos()) { + resultType |= InputType.NUMBER_POS_INT | InputType.NUMBER_POS_INF; + if (canBeFract) resultType |= InputType.NUMBER_POS_FRACT; + } + + function canBeNeg () { + // POS * NEG = NEG + if ((leftType & InputType.NUMBER_POS) && (rightType & InputType.NUMBER_NEG)) return true; + // NEG * POS = NEG + if ((leftType & InputType.NUMBER_NEG) && (rightType & InputType.NUMBER_POS)) return true; + } + if (canBeNeg()) { + resultType |= InputType.NUMBER_NEG_INT | InputType.NUMBER_NEG_INF; + if (canBeFract) resultType |= InputType.NUMBER_NEG_FRACT; + } + + function canBeZero () { + // 0 * 0 = 0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_ZERO)) return true; + // -0 * -0 = 0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + // 0 * POS_REAL = 0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_POS_REAL)) return true; + // -0 * NEG_REAL = 0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_NEG_REAL)) return true; + // POS_REAL * 0 = 0 + if ((leftType & InputType.NUMBER_POS_REAL) && (rightType & InputType.NUMBER_ZERO)) return true; + // NEG_REAL * -0 = 0 + if ((leftType & InputType.NUMBER_NEG_REAL) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + // Rounding errors like 1e-323 * 0.1 = 0 + if ((leftType & InputType.NUMBER_FRACT) && (rightType & InputType.NUMBER_FRACT)) return true; + } + if (canBeZero()) resultType |= InputType.NUMBER_ZERO; + + function canBeNegZero () { + // 0 * -0 = 0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + // -0 * 0 = 0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_ZERO)) return true; + // -0 * POS_REAL = -0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_POS_REAL)) return true; + // 0 * NEG_REAL = -0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_NEG_REAL)) return true; + // POS_REAL * -0 = -0 + if ((leftType & InputType.NUMBER_POS_REAL) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + // NEG_REAL * 0 = -0 + if ((leftType & InputType.NUMBER_NEG_REAL) && (rightType & InputType.NUMBER_ZERO)) return true; + // Rounding errors like -1e-323 / 10 = -0 + if ((leftType & InputType.NUMBER_NEG_REAL) && (rightType & InputType.NUMBER_POS_REAL)) return true; + // Rounding errors like 1e-323 / -10 = -0 + if ((leftType & InputType.NUMBER_POS_REAL) && (rightType & InputType.NUMBER_NEG_REAL)) return true; + } + if (canBeNegZero()) resultType |= InputType.NUMBER_NEG_ZERO; + + return resultType; + } + + case InputOpcode.OP_DIVIDE: { + const leftType = inputs.left.type; + const rightType = inputs.right.type; + + let resultType = 0; + + function canBeNaN () { + // REAL / 0 = NaN + if ((leftType & InputType.NUMBER_REAL) && (rightType & InputType.NUMBER_ZERO)) return true; + // (-)Infinity / (-)Infinity = NaN + if ((leftType & InputType.NUMBER_INF) && (rightType & InputType.NUMBER_INF)) return true; + // (-)0 / NaN = NaN + if ((leftType & InputType.NUMBER_ANY_ZERO) && (rightType & InputType.NUMBER_NAN)) return true; + } + if (canBeNaN()) resultType |= InputType.NUMBER_NAN; + + function canBePos () { + // POS / POS = POS + if ((leftType & InputType.NUMBER_POS) && (rightType & InputType.NUMBER_POS)) return true; + // NEG / NEG = POS + if ((leftType & InputType.NUMBER_NEG) && (rightType & InputType.NUMBER_NEG)) return true; + } + if (canBePos()) resultType |= InputType.NUMBER_POS; + + function canBeNegInfinity () { + // -Infinity / 0 = -Infinity + if ((leftType & InputType.NUMBER_NEG_INF) && (rightType & InputType.NUMBER_ZERO)) return true; + // Infinity / -0 = -Infinity + if ((leftType & InputType.NUMBER_POS_INF) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + // NEG_REAL / NaN = -Infinity + if ((leftType & InputType.NUMBER_NEG_REAL) && (rightType & InputType.NUMBER_NAN)) return true; + // NEG_REAL / NUMBER_OR_NAN ~= -Infinity + if ((leftType & InputType.NUMBER_NEG_REAL) && (rightType & InputType.NUMBER_OR_NAN)) return true; + } + if (canBeNegInfinity()) resultType |= InputType.NUMBER_NEG_INF; + + function canBeInfinity () { + // Infinity / 0 = Infinity + if ((leftType & InputType.NUMBER_POS_INF) && (rightType & InputType.NUMBER_ZERO)) return true; + // -Infinity / -0 = Infinity + if ((leftType & InputType.NUMBER_NEG_INF) && (rightType & InputType.NUMBER_NEG_ZERO)) return true; + // POS_REAL / NUMBER_OR_NAN ~= Infinity + if ((leftType & InputType.NUMBER_POS_REAL) && (rightType & InputType.NUMBER_OR_NAN)) return true; + } + if (canBeInfinity()) resultType |= InputType.NUMBER_POS_INF; + + function canBeNeg () { + // POS / NEG = NEG + if ((leftType & InputType.NUMBER_POS) && (rightType & InputType.NUMBER_NEG)) return true; + // NEG / POS = NEG + if ((leftType & InputType.NUMBER_NEG) && (rightType & InputType.NUMBER_POS)) return true; + } + if (canBeNeg()) resultType |= InputType.NUMBER_NEG; + + function canBeZero () { + // 0 / POS = 0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_POS)) return true; + // -0 / NEG = 0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_NEG)) return true; + // Rounding errors like 1e-323 / 10 = 0 + if ((leftType & InputType.NUMBER_POS_REAL) && (rightType & InputType.NUMBER_POS_REAL)) return true; + // Rounding errors like -1e-323 / -10 = 0 + if ((leftType & InputType.NUMBER_NEG_REAL) && (rightType & InputType.NUMBER_NEG_REAL)) return true; + // NUMBER_POS / Infinity = 0 + if ((leftType & InputType.NUMBER_POS) && (rightType & InputType.NUMBER_POS_INF)) return true; + // NUMBER_NEG / -Infinity = 0 + if ((leftType & InputType.NUMBER_NEG) && (rightType & InputType.NUMBER_NEG_INF)) return true; + } + if (canBeZero()) resultType |= InputType.NUMBER_ZERO; + + function canBeNegZero () { + // -0 / POS = -0 + if ((leftType & InputType.NUMBER_NEG_ZERO) && (rightType & InputType.NUMBER_POS)) return true; + // 0 / NEG = -0 + if ((leftType & InputType.NUMBER_ZERO) && (rightType & InputType.NUMBER_NEG)) return true; + // Rounding errors like -1e-323 / 10 = -0 + if ((leftType & InputType.NUMBER_NEG_REAL) && (rightType & InputType.NUMBER_POS_REAL)) return true; + // Rounding errors like 1e-323 / -10 = -0 + if ((leftType & InputType.NUMBER_POS_REAL) && (rightType & InputType.NUMBER_NEG_REAL)) return true; + // NUMBER_POS / -Infinity = -0 + if ((leftType & InputType.NUMBER_POS) && (rightType & InputType.NUMBER_NEG_INF)) return true; + // NUMBER_NEG / Infinity = -0 + if ((leftType & InputType.NUMBER_NEG) && (rightType & InputType.NUMBER_POS_INF)) return true; + } + if (canBeNegZero()) resultType |= InputType.NUMBER_NEG_ZERO; + + return resultType; + } + } + return inputBlock.type; + } + + /** + * @param {IntermediateInput} inputBlock + * @param {TypeState} state + * @returns {boolean} + * @private + */ + analyzeInputBlock (inputBlock, state) { + const inputs = inputBlock.inputs; + + let modified = this.analyzeInputs(inputs, state); + const newType = this.getInputType(inputBlock, state); + + modified = modified || newType !== inputBlock.type; + inputBlock.type = newType; + + switch (inputBlock.opcode) { + case InputOpcode.ADDON_CALL: + modified = state.clear() || modified; + break; + case InputOpcode.PROCEDURE_CALL: + modified = this.analyzeInputs(inputs.inputs, state) || modified; + const script = this.ir.procedures[inputs.variant]; + + if (!script || !script.cachedAnalysisEndState) { + modified = state.clear() || modified; + } else { + modified = state.after(script.cachedAnalysisEndState) || modified; + } + break; + } + + return modified; + } + + /** + * @param {Object} inputs + * @param {TypeState} state + * @returns {boolean} modified + */ + analyzeInputs (inputs, state) { + let modified = false; + for (const inputName in inputs) { + const input = inputs[inputName]; + if (input instanceof IntermediateInput) { + modified = this.analyzeInputBlock(input, state) || modified; + } + } + return modified; + } + + /** + * @param {IntermediateStackBlock} stackBlock + * @param {TypeState} state + * @returns {boolean} + * @private + */ + analyzeStackBlock (stackBlock, state) { + const inputs = stackBlock.inputs; + let modified = false; + + if (stackBlock.ignoreState) { + state = state.clone(); + } + + modified = modified || this.analyzeInputs(inputs, state); + + switch (stackBlock.opcode) { + case StackOpcode.VAR_SET: + modified = state.setVariableType(inputs.variable, inputs.value.type) || modified; + break; + case StackOpcode.CONTROL_WHILE: + case StackOpcode.CONTROL_FOR: + case StackOpcode.CONTROL_REPEAT: + modified = this.analyzeLoopedStack(inputs.do, state, stackBlock) || modified; + break; + case StackOpcode.CONTROL_IF_ELSE: { + const trueState = state.clone(); + modified = this.analyzeStack(inputs.whenTrue, trueState) || modified; + modified = this.analyzeStack(inputs.whenFalse, state) || modified; + modified = state.or(trueState) || modified; + break; + } + case StackOpcode.PROCEDURE_CALL: { + modified = this.analyzeInputs(inputs.inputs, state) || modified; + const script = this.ir.procedures[inputs.variant]; + + if (!script || !script.cachedAnalysisEndState) { + modified = state.clear() || modified; + } else { + modified = state.after(script.cachedAnalysisEndState) || modified; + } + break; + } + case StackOpcode.COMPATIBILITY_LAYER: { + this.analyzeInputs(inputs.inputs, state); + for (const substack of inputs.substacks) { + const newState = state.clone(); + modified = this.analyzeStack(substack, newState) || modified; + modified = state.or(newState) || modified; + } + break; + } + } + + // if (stackBlock.ignoreState) return false; + + return modified; + } + + /** + * @param {IntermediateStack?} stack + * @param {TypeState} state + * @returns {boolean} + * @private + */ + analyzeStack (stack, state) { + if (!stack) return false; + let modified = false; + for (const stackBlock of stack.blocks) { + let stateChanged = this.analyzeStackBlock(stackBlock, state); + + if (!stackBlock.ignoreState) { + if (stackBlock.yields && !this.ignoreYields) stateChanged = state.clear() || stateChanged; + + if (stateChanged) { + if (stackBlock.exitState) stackBlock.exitState.or(state); + else stackBlock.exitState = state.clone(); + modified = true; + } + } + } + return modified; + } + + /** + * @param {IntermediateStack} stack + * @param {TypeState} state + * @param {IntermediateStackBlock} block + * @returns {boolean} + * @private + */ + analyzeLoopedStack (stack, state, block) { + if (block.yields && !this.ignoreYields) { + const modified = state.clear(); + block.entryState = state.clone(); + block.exitState = state.clone(); + return this.analyzeStack(stack, state) || modified; + } + let modified = false; + let keepLooping; + do { + const newState = state.clone(); + this.analyzeStack(stack, newState); + modified = keepLooping = state.or(newState); + } while (keepLooping); + block.entryState = state.clone(); + return modified; + } + + /** + * @param {IntermediateInput} input + * @param {TypeState} state + * @returns {IntermediateInput} + * @private + */ + optimizeInput (input, state) { + for (const inputKey in input.inputs) { + const inputInput = input.inputs[inputKey]; + if (inputInput instanceof IntermediateInput) { + input.inputs[inputKey] = this.optimizeInput(inputInput, state); + } + } + + switch (input.opcode) { + case InputOpcode.CAST_NUMBER: { + const targetType = input.inputs.target.type; + if ((targetType & InputType.NUMBER) === targetType) { + return input.inputs.target; + } + return input; + } case InputOpcode.CAST_NUMBER_OR_NAN: { + const targetType = input.inputs.target.type; + if ((targetType & InputType.NUMBER_OR_NAN) === targetType) { + return input.inputs.target; + } + return input; + } + } + + return input; + } + + /** + * @param {IntermediateStack?} stack + * @param {TypeState} state The state of the project before this stack is run. + * @private + */ + optimizeStack (stack, state) { + if (!stack) return; + for (const stackBlock of stack.blocks) { + if (stackBlock.entryState) state = stackBlock.entryState; + for (const inputKey in stackBlock.inputs) { + const input = stackBlock.inputs[inputKey]; + if (input instanceof IntermediateInput) { + stackBlock.inputs[inputKey] = this.optimizeInput(input, state); + } else if (input instanceof IntermediateStack) { + this.optimizeStack(input, state); + } + } + if (stackBlock.exitState) { + state = stackBlock.exitState; + } + } + } + + /** + * @param {IntermediateScript} script + * @param {Set} alreadyOptimized + * @private + */ + optimizeScript (script, alreadyOptimized) { + if (script.isProcedure) { + if (alreadyOptimized.has(script.procedureCode)) { + return; + } + alreadyOptimized.add(script.procedureCode); + } + + for (const procVariant of script.dependedProcedures) { + this.optimizeScript(this.ir.procedures[procVariant], alreadyOptimized); + } + + script.cachedAnalysisEndState = new TypeState(); + this.analyzeStack(script.stack, script.cachedAnalysisEndState); + + this.optimizeStack(script.stack, new TypeState()); + } + + optimize () { + this.optimizeScript(this.ir.entry, new Set()); + } +} + + +module.exports = { + IROptimizer, + TypeState +}; diff --git a/src/compiler/jsexecute.js b/src/compiler/jsexecute.js index b7bff895..a682f880 100644 --- a/src/compiler/jsexecute.js +++ b/src/compiler/jsexecute.js @@ -1,3 +1,4 @@ +// @ts-check /** * @fileoverview Runtime for scripts generated by jsgen */ @@ -12,6 +13,7 @@ const globalState = { Cast: require('../util/cast'), log: require('../util/log'), blockUtility: require('./compat-block-utility'), + /** @type{import("../engine/thread")?} */ thread: null }; @@ -119,9 +121,8 @@ const waitPromise = function*(promise) { returnValue = value; thread.status = 0; // STATUS_RUNNING }, error => { - globalState.log.warn('Promise rejected in compiled script:', error); - returnValue = '' + error; thread.status = 0; // STATUS_RUNNING + globalState.log.warn('Promise rejected in compiled script:', error); }); yield; @@ -163,7 +164,7 @@ const executeInCompatibilityLayer = function*(inputs, blockFunction, isWarp, use return returnValue; } - if (thread.status === 1 /* STATUS_PROMISE_WAIT */ || thread.status === 4 /* STATUS_DONE */) { + if (thread.status === 1 /* STATUS_PROMISE_WAIT */) { // Something external is forcing us to stop yield; // Make up a return value because whatever is forcing us to stop can't specify one @@ -190,12 +191,14 @@ const executeInCompatibilityLayer = function*(inputs, blockFunction, isWarp, use return returnValue; } - if (thread.status === 1 /* STATUS_PROMISE_WAIT */ || thread.status === 4 /* STATUS_DONE */) { + if (thread.status === 1 /* STATUS_PROMISE_WAIT */) { yield; return finish(''); } } + // todo: do we have to do anything extra if status is STATUS_DONE? + return finish(returnValue); }`; @@ -586,7 +589,7 @@ runtimeFunctions.yieldThenCallGenerator = `const yieldThenCallGenerator = functi /** * Step a compiled thread. - * @param {Thread} thread The thread to step. + * @param {import("../engine/thread")} thread The thread to step. */ const execute = thread => { globalState.thread = thread; diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index d3e7784d..9f060c59 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -1,13 +1,12 @@ +// @ts-check + const log = require('../util/log'); -const Cast = require('../util/cast'); const BlockType = require('../extension-support/block-type'); const VariablePool = require('./variable-pool'); const jsexecute = require('./jsexecute'); const environment = require('./environment'); - -// Imported for JSDoc types, not to actually use -// eslint-disable-next-line no-unused-vars -const {IntermediateScript, IntermediateRepresentation} = require('./intermediate'); +const {StackOpcode, InputOpcode, InputType} = require('./enums.js'); +const {IntermediateStackBlock, IntermediateInput, IntermediateStack, IntermediateScript, IntermediateRepresentation} = require('./intermediate'); /** * @fileoverview Convert intermediate representations to JavaScript functions. @@ -24,12 +23,6 @@ const sanitize = string => { return JSON.stringify(string).slice(1, -1); }; -const TYPE_NUMBER = 1; -const TYPE_STRING = 2; -const TYPE_BOOLEAN = 3; -const TYPE_UNKNOWN = 4; -const TYPE_NUMBER_NAN = 5; - // Pen-related constants const PEN_EXT = 'runtime.ext_pen'; const PEN_STATE = `${PEN_EXT}._getPenState(target)`; @@ -49,281 +42,20 @@ const functionNameVariablePool = new VariablePool('fun'); */ const generatorNameVariablePool = new VariablePool('gen'); -/** - * @typedef Input - * @property {() => string} asNumber - * @property {() => string} asNumberOrNaN - * @property {() => string} asString - * @property {() => string} asBoolean - * @property {() => string} asColor - * @property {() => string} asUnknown - * @property {() => string} asSafe - * @property {() => boolean} isAlwaysNumber - * @property {() => boolean} isAlwaysNumberOrNaN - * @property {() => boolean} isNeverNumber - */ - -/** - * @implements {Input} - */ -class TypedInput { - constructor (source, type) { - // for debugging - if (typeof type !== 'number') throw new Error('type is invalid'); - this.source = source; - this.type = type; - } - - asNumber () { - if (this.type === TYPE_NUMBER) return this.source; - if (this.type === TYPE_NUMBER_NAN) return `(${this.source} || 0)`; - return `(+${this.source} || 0)`; - } - - asNumberOrNaN () { - if (this.type === TYPE_NUMBER || this.type === TYPE_NUMBER_NAN) return this.source; - return `(+${this.source})`; - } - - asString () { - if (this.type === TYPE_STRING) return this.source; - return `("" + ${this.source})`; - } - - asBoolean () { - if (this.type === TYPE_BOOLEAN) return this.source; - return `toBoolean(${this.source})`; - } - - asColor () { - return this.asUnknown(); - } - - asUnknown () { - return this.source; - } - - asSafe () { - return this.asUnknown(); - } - - isAlwaysNumber () { - return this.type === TYPE_NUMBER; - } - - isAlwaysNumberOrNaN () { - return this.type === TYPE_NUMBER || this.type === TYPE_NUMBER_NAN; - } - - isNeverNumber () { - return false; - } -} - -/** - * @implements {Input} - */ -class ConstantInput { - constructor (constantValue, safe) { - this.constantValue = constantValue; - this.safe = safe; - } - - asNumber () { - // Compute at compilation time - const numberValue = +this.constantValue; - if (numberValue) { - // It's important that we use the number's stringified value and not the constant value - // Using the constant value allows numbers such as "010" to be interpreted as 8 (or SyntaxError in strict mode) instead of 10. - return numberValue.toString(); - } - // numberValue is one of 0, -0, or NaN - if (Object.is(numberValue, -0)) { - return '-0'; - } - return '0'; - } - - asNumberOrNaN () { - return this.asNumber(); - } - - asString () { - return `"${sanitize('' + this.constantValue)}"`; - } - - asBoolean () { - // Compute at compilation time - return Cast.toBoolean(this.constantValue).toString(); - } - - asColor () { - // Attempt to parse hex code at compilation time - if (/^#[0-9a-f]{6,8}$/i.test(this.constantValue)) { - const hex = this.constantValue.substr(1); - return Number.parseInt(hex, 16).toString(); - } - return this.asUnknown(); - } - - asUnknown () { - // Attempt to convert strings to numbers if it is unlikely to break things - if (typeof this.constantValue === 'number') { - // todo: handle NaN? - return this.constantValue; - } - const numberValue = +this.constantValue; - if (numberValue.toString() === this.constantValue) { - return this.constantValue; - } - return this.asString(); - } - - asSafe () { - if (this.safe) { - return this.asUnknown(); - } - return this.asString(); - } - - isAlwaysNumber () { - const value = +this.constantValue; - if (Number.isNaN(value)) { - return false; - } - // Empty strings evaluate to 0 but should not be considered a number. - if (value === 0) { - return this.constantValue.toString().trim() !== ''; +const isSafeInputForEqualsOptimization = (input, other) => { + // Only optimize constants + if (input.opcode !== InputOpcode.CONSTANT) return false; + // Only optimize when the constant can always be thought of as a number + if (input.isAlwaysType(InputType.NUMBER) || input.isAlwaysType(InputType.STRING_NUM)) { + if (other.isSometimesType(InputType.STRING_NAN) || other.isSometimesType(InputType.BOOLEAN_INTERPRETABLE)) { + // Never optimize 0 if the other input can be '' or a boolean. + // eg. if '< 0 = "" >' was optimized it would turn into `0 === +""`, + // which would be true even though Scratch would return false. + return (+input.inputs.value) !== 0; } return true; } - - isAlwaysNumberOrNaN () { - return this.isAlwaysNumber(); - } - - isNeverNumber () { - return Number.isNaN(+this.constantValue); - } -} - -/** - * @implements {Input} - */ -class VariableInput { - constructor (source) { - this.source = source; - this.type = TYPE_UNKNOWN; - /** - * The value this variable was most recently set to, if any. - * @type {Input} - * @private - */ - this._value = null; - } - - /** - * @param {Input} input The input this variable was most recently set to. - */ - setInput (input) { - if (input instanceof VariableInput) { - // When being set to another variable, extract the value it was set to. - // Otherwise, you may end up with infinite recursion in analysis methods when a variable is set to itself. - if (input._value) { - input = input._value; - } else { - this.type = TYPE_UNKNOWN; - this._value = null; - return; - } - } - this._value = input; - if (input instanceof TypedInput) { - this.type = input.type; - } else { - this.type = TYPE_UNKNOWN; - } - } - - asNumber () { - if (this.type === TYPE_NUMBER) return this.source; - if (this.type === TYPE_NUMBER_NAN) return `(${this.source} || 0)`; - return `(+${this.source} || 0)`; - } - - asNumberOrNaN () { - if (this.type === TYPE_NUMBER || this.type === TYPE_NUMBER_NAN) return this.source; - return `(+${this.source})`; - } - - asString () { - if (this.type === TYPE_STRING) return this.source; - return `("" + ${this.source})`; - } - - asBoolean () { - if (this.type === TYPE_BOOLEAN) return this.source; - return `toBoolean(${this.source})`; - } - - asColor () { - return this.asUnknown(); - } - - asUnknown () { - return this.source; - } - - asSafe () { - return this.asUnknown(); - } - - isAlwaysNumber () { - if (this._value) { - return this._value.isAlwaysNumber(); - } - return false; - } - - isAlwaysNumberOrNaN () { - if (this._value) { - return this._value.isAlwaysNumberOrNaN(); - } - return false; - } - - isNeverNumber () { - if (this._value) { - return this._value.isNeverNumber(); - } - return false; - } -} - -const getNamesOfCostumesAndSounds = runtime => { - const result = new Set(); - for (const target of runtime.targets) { - if (target.isOriginal) { - const sprite = target.sprite; - for (const costume of sprite.costumes) { - result.add(costume.name); - } - for (const sound of sprite.sounds) { - result.add(sound.name); - } - } - } - return result; -}; - -const isSafeConstantForEqualsOptimization = input => { - const numberValue = +input.constantValue; - // Do not optimize 0 - if (!numberValue) { - return false; - } - // Do not optimize numbers when the original form does not match - return numberValue.toString() === input.constantValue.toString(); + return false; }; /** @@ -350,7 +82,7 @@ class JSGenerator { /** * @param {IntermediateScript} script * @param {IntermediateRepresentation} ir - * @param {Target} target + * @param {import("../sprites/rendered-target")} target */ constructor (script, ir, target) { this.script = script; @@ -358,11 +90,6 @@ class JSGenerator { this.target = target; this.source = ''; - /** - * @type {Object.} - */ - this.variableInputs = {}; - this.isWarp = script.isWarp; this.isProcedure = script.isProcedure; this.warpTimer = script.warpTimer; @@ -375,12 +102,10 @@ class JSGenerator { /** * The current Frame. - * @type {Frame} + * @type {Frame?} */ this.currentFrame = null; - this.namesOfCostumesAndSounds = getNamesOfCostumesAndSounds(target.runtime); - this.localVariables = new VariablePool('a'); this._setupVariablesPool = new VariablePool('b'); this._setupVariables = {}; @@ -425,222 +150,230 @@ class JSGenerator { } /** - * @param {object} node Input node to compile. - * @returns {Input} Compiled input. + * @param {IntermediateInput} block Input node to compile. + * @returns {string} Compiled input. */ - descendInput (node) { - switch (node.kind) { - case 'addons.call': - return new TypedInput(`(${this.descendAddonCall(node)})`, TYPE_UNKNOWN); + descendInput (block) { + const node = block.inputs; + switch (block.opcode) { + case InputOpcode.NOP: + return `""`; - case 'args.boolean': - return new TypedInput(`toBoolean(p${node.index})`, TYPE_BOOLEAN); - case 'args.stringNumber': - return new TypedInput(`p${node.index}`, TYPE_UNKNOWN); + case InputOpcode.PROCEDURE_ARG_BOOLEAN: + return `toBoolean(p${node.index})`; + case InputOpcode.PROCEDURE_ARG_STRING_NUMBER: + return `p${node.index}`; - case 'compat': + case InputOpcode.ADDON_CALL: + return `(${this.descendAddonCall(node)})`; + + case InputOpcode.CAST_BOOLEAN: + return `toBoolean(${this.descendInput(node.target)})`; + case InputOpcode.CAST_NUMBER: + if (node.target.isAlwaysType(InputType.BOOLEAN_INTERPRETABLE)) { + return `(+${this.descendInput(node.target.toType(InputType.BOOLEAN))})`; + } + if (node.target.isAlwaysType(InputType.NUMBER_OR_NAN)) { + return `(${this.descendInput(node.target)} || 0)`; + } + return `(+${this.descendInput(node.target)} || 0)`; + case InputOpcode.CAST_NUMBER_OR_NAN: + return `(+${this.descendInput(node.target)})`; + case InputOpcode.CAST_NUMBER_INDEX: + return `(${this.descendInput(node.target.toType(InputType.NUMBER_OR_NAN))} | 0)`; + case InputOpcode.CAST_STRING: + return `("" + ${this.descendInput(node.target)})`; + + case InputOpcode.COMPATIBILITY_LAYER: // Compatibility layer inputs never use flags. - return new TypedInput(`(${this.generateCompatibilityLayerCall(node, false)})`, TYPE_UNKNOWN); + return `(${this.generateCompatibilityLayerCall(node, false)})`; - case 'constant': - return this.safeConstantInput(node.value); + case InputOpcode.CONSTANT: + if (block.isAlwaysType(InputType.NUMBER)) { + if (typeof node.value !== 'number') throw new Error(`JS: '${block.type}' type constant had ${typeof node.value} type value. Expected number.`); + if (Object.is(node.value, -0)) return '-0'; + return node.value.toString(); + } else if (block.isAlwaysType(InputType.BOOLEAN)) { + if (typeof node.value !== 'boolean') throw new Error(`JS: '${block.type}' type constant had ${typeof node.value} type value. Expected boolean.`); + return node.value.toString(); + } else if (block.isSometimesType(InputType.STRING)) { + return `"${sanitize(node.value.toString())}"`; + } throw new Error(`JS: Unknown constant input type '${block.type}'.`); - case 'counter.get': - return new TypedInput('runtime.ext_scratch3_control._counter', TYPE_NUMBER); + case InputOpcode.SENSING_KEY_DOWN: + return `runtime.ioDevices.keyboard.getKeyIsDown(${this.descendInput(node.key)})`; - case 'keyboard.pressed': - return new TypedInput(`runtime.ioDevices.keyboard.getKeyIsDown(${this.descendInput(node.key).asSafe()})`, TYPE_BOOLEAN); - - case 'list.contains': - return new TypedInput(`listContains(${this.referenceVariable(node.list)}, ${this.descendInput(node.item).asUnknown()})`, TYPE_BOOLEAN); - case 'list.contents': - return new TypedInput(`listContents(${this.referenceVariable(node.list)})`, TYPE_STRING); - case 'list.get': { - const index = this.descendInput(node.index); + case InputOpcode.LIST_CONTAINS: + return `listContains(${this.referenceVariable(node.list)}, ${this.descendInput(node.item)})`; + case InputOpcode.LIST_CONTENTS: + return `listContents(${this.referenceVariable(node.list)})`; + case InputOpcode.LIST_GET: { if (environment.supportsNullishCoalescing) { - if (index.isAlwaysNumberOrNaN()) { - return new TypedInput(`(${this.referenceVariable(node.list)}.value[(${index.asNumber()} | 0) - 1] ?? "")`, TYPE_UNKNOWN); + if (node.index.isAlwaysType(InputType.NUMBER_INTERPRETABLE | InputType.NUMBER_NAN)) { + return `(${this.referenceVariable(node.list)}.value[${this.descendInput(node.index.toType(InputType.NUMBER_INDEX))} - 1] ?? "")`; } - if (index instanceof ConstantInput && index.constantValue === 'last') { - return new TypedInput(`(${this.referenceVariable(node.list)}.value[${this.referenceVariable(node.list)}.value.length - 1] ?? "")`, TYPE_UNKNOWN); + if (node.index.isConstant('last')) { + return `(${this.referenceVariable(node.list)}.value[${this.referenceVariable(node.list)}.value.length - 1] ?? "")`; } } - return new TypedInput(`listGet(${this.referenceVariable(node.list)}.value, ${index.asUnknown()})`, TYPE_UNKNOWN); + return `listGet(${this.referenceVariable(node.list)}.value, ${this.descendInput(node.index)})`; } - case 'list.indexOf': - return new TypedInput(`listIndexOf(${this.referenceVariable(node.list)}, ${this.descendInput(node.item).asUnknown()})`, TYPE_NUMBER); - case 'list.length': - return new TypedInput(`${this.referenceVariable(node.list)}.value.length`, TYPE_NUMBER); + case InputOpcode.LIST_INDEX_OF: + return `listIndexOf(${this.referenceVariable(node.list)}, ${this.descendInput(node.item)})`; + case InputOpcode.LIST_LENGTH: + return `${this.referenceVariable(node.list)}.value.length`; - case 'looks.size': - return new TypedInput('Math.round(target.size)', TYPE_NUMBER); - case 'looks.backdropName': - return new TypedInput('stage.getCostumes()[stage.currentCostume].name', TYPE_STRING); - case 'looks.backdropNumber': - return new TypedInput('(stage.currentCostume + 1)', TYPE_NUMBER); - case 'looks.costumeName': - return new TypedInput('target.getCostumes()[target.currentCostume].name', TYPE_STRING); - case 'looks.costumeNumber': - return new TypedInput('(target.currentCostume + 1)', TYPE_NUMBER); + case InputOpcode.LOOKS_SIZE_GET: + return 'Math.round(target.size)'; + case InputOpcode.LOOKS_BACKDROP_NAME: + return 'stage.getCostumes()[stage.currentCostume].name'; + case InputOpcode.LOOKS_BACKDROP_NUMBER: + return '(stage.currentCostume + 1)'; + case InputOpcode.LOOKS_COSTUME_NAME: + return 'target.getCostumes()[target.currentCostume].name'; + case InputOpcode.LOOKS_COSTUME_NUMBER: + return '(target.currentCostume + 1)'; - case 'motion.direction': - return new TypedInput('target.direction', TYPE_NUMBER); - case 'motion.x': - return new TypedInput('limitPrecision(target.x)', TYPE_NUMBER); - case 'motion.y': - return new TypedInput('limitPrecision(target.y)', TYPE_NUMBER); + case InputOpcode.MOTION_DIRECTION_GET: + return 'target.direction'; + case InputOpcode.MOTION_X_GET: + return 'limitPrecision(target.x)'; + case InputOpcode.MOTION_Y_GET: + return 'limitPrecision(target.y)'; - case 'mouse.down': - return new TypedInput('runtime.ioDevices.mouse.getIsDown()', TYPE_BOOLEAN); - case 'mouse.x': - return new TypedInput('runtime.ioDevices.mouse.getScratchX()', TYPE_NUMBER); - case 'mouse.y': - return new TypedInput('runtime.ioDevices.mouse.getScratchY()', TYPE_NUMBER); + case InputOpcode.SENSING_MOUSE_DOWN: + return 'runtime.ioDevices.mouse.getIsDown()'; + case InputOpcode.SENSING_MOUSE_X: + return 'runtime.ioDevices.mouse.getScratchX()'; + case InputOpcode.SENSING_MOUSE_Y: + return 'runtime.ioDevices.mouse.getScratchY()'; - case 'noop': - return new TypedInput('""', TYPE_STRING); + case InputOpcode.OP_ABS: + return `Math.abs(${this.descendInput(node.value)})`; + case InputOpcode.OP_ACOS: + return `((Math.acos(${this.descendInput(node.value)}) * 180) / Math.PI)`; + case InputOpcode.OP_ADD: + return `(${this.descendInput(node.left)} + ${this.descendInput(node.right)})`; + case InputOpcode.OP_AND: + return `(${this.descendInput(node.left)} && ${this.descendInput(node.right)})`; + case InputOpcode.OP_ASIN: + return `((Math.asin(${this.descendInput(node.value)}) * 180) / Math.PI)`; + case InputOpcode.OP_ATAN: + return `((Math.atan(${this.descendInput(node.value)}) * 180) / Math.PI)`; + case InputOpcode.OP_CEILING: + return `Math.ceil(${this.descendInput(node.value)})`; + case InputOpcode.OP_CONTAINS: + return `(${this.descendInput(node.string)}.toLowerCase().indexOf(${this.descendInput(node.contains)}.toLowerCase()) !== -1)`; + case InputOpcode.OP_COS: + return `(Math.round(Math.cos((Math.PI * ${this.descendInput(node.value)}) / 180) * 1e10) / 1e10)`; + case InputOpcode.OP_DIVIDE: + return `(${this.descendInput(node.left)} / ${this.descendInput(node.right)})`; + case InputOpcode.OP_EQUALS: { + const left = node.left; + const right = node.right; - case 'op.abs': - return new TypedInput(`Math.abs(${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER); - case 'op.acos': - // Needs to be marked as NaN because Math.acos(1.0001) === NaN - return new TypedInput(`((Math.acos(${this.descendInput(node.value).asNumber()}) * 180) / Math.PI)`, TYPE_NUMBER_NAN); - case 'op.add': - // Needs to be marked as NaN because Infinity + -Infinity === NaN - return new TypedInput(`(${this.descendInput(node.left).asNumber()} + ${this.descendInput(node.right).asNumber()})`, TYPE_NUMBER_NAN); - case 'op.and': - return new TypedInput(`(${this.descendInput(node.left).asBoolean()} && ${this.descendInput(node.right).asBoolean()})`, TYPE_BOOLEAN); - case 'op.asin': - // Needs to be marked as NaN because Math.asin(1.0001) === NaN - return new TypedInput(`((Math.asin(${this.descendInput(node.value).asNumber()}) * 180) / Math.PI)`, TYPE_NUMBER_NAN); - case 'op.atan': - return new TypedInput(`((Math.atan(${this.descendInput(node.value).asNumber()}) * 180) / Math.PI)`, TYPE_NUMBER); - case 'op.ceiling': - return new TypedInput(`Math.ceil(${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER); - case 'op.contains': - return new TypedInput(`(${this.descendInput(node.string).asString()}.toLowerCase().indexOf(${this.descendInput(node.contains).asString()}.toLowerCase()) !== -1)`, TYPE_BOOLEAN); - case 'op.cos': - return new TypedInput(`(Math.round(Math.cos((Math.PI * ${this.descendInput(node.value).asNumber()}) / 180) * 1e10) / 1e10)`, TYPE_NUMBER_NAN); - case 'op.divide': - // Needs to be marked as NaN because 0 / 0 === NaN - return new TypedInput(`(${this.descendInput(node.left).asNumber()} / ${this.descendInput(node.right).asNumber()})`, TYPE_NUMBER_NAN); - case 'op.equals': { - const left = this.descendInput(node.left); - const right = this.descendInput(node.right); - // When both operands are known to never be numbers, only use string comparison to avoid all number parsing. - if (left.isNeverNumber() || right.isNeverNumber()) { - return new TypedInput(`(${left.asString()}.toLowerCase() === ${right.asString()}.toLowerCase())`, TYPE_BOOLEAN); - } - const leftAlwaysNumber = left.isAlwaysNumber(); - const rightAlwaysNumber = right.isAlwaysNumber(); // When both operands are known to be numbers, we can use === - if (leftAlwaysNumber && rightAlwaysNumber) { - return new TypedInput(`(${left.asNumber()} === ${right.asNumber()})`, TYPE_BOOLEAN); + if (left.isAlwaysType(InputType.NUMBER_INTERPRETABLE) && right.isAlwaysType(InputType.NUMBER_INTERPRETABLE)) { + return `(${this.descendInput(left.toType(InputType.NUMBER))} === ${this.descendInput(right.toType(InputType.NUMBER))})`; } // In certain conditions, we can use === when one of the operands is known to be a safe number. - if (leftAlwaysNumber && left instanceof ConstantInput && isSafeConstantForEqualsOptimization(left)) { - return new TypedInput(`(${left.asNumber()} === ${right.asNumber()})`, TYPE_BOOLEAN); + if (isSafeInputForEqualsOptimization(left, right) || isSafeInputForEqualsOptimization(right, left)) { + return `(${this.descendInput(left.toType(InputType.NUMBER))} === ${this.descendInput(right.toType(InputType.NUMBER))})`; } - if (rightAlwaysNumber && right instanceof ConstantInput && isSafeConstantForEqualsOptimization(right)) { - return new TypedInput(`(${left.asNumber()} === ${right.asNumber()})`, TYPE_BOOLEAN); + // When either operand is known to never be a number, only use string comparison to avoid all number parsing. + if (!left.isSometimesType(InputType.NUMBER_INTERPRETABLE) || !right.isSometimesType(InputType.NUMBER_INTERPRETABLE)) { + return `(${this.descendInput(left.toType(InputType.STRING))}.toLowerCase() === ${this.descendInput(right.toType(InputType.STRING))}.toLowerCase())`; } // No compile-time optimizations possible - use fallback method. - return new TypedInput(`compareEqual(${left.asUnknown()}, ${right.asUnknown()})`, TYPE_BOOLEAN); + return `compareEqual(${this.descendInput(left)}, ${this.descendInput(right)})`; } - case 'op.e^': - return new TypedInput(`Math.exp(${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER); - case 'op.floor': - return new TypedInput(`Math.floor(${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER); - case 'op.greater': { - const left = this.descendInput(node.left); - const right = this.descendInput(node.right); + case InputOpcode.OP_POW_E: + return `Math.exp(${this.descendInput(node.value)})`; + case InputOpcode.OP_FLOOR: + return `Math.floor(${this.descendInput(node.value)})`; + case InputOpcode.OP_GREATER: { + const left = node.left; + const right = node.right; // When the left operand is a number and the right operand is a number or NaN, we can use > - if (left.isAlwaysNumber() && right.isAlwaysNumberOrNaN()) { - return new TypedInput(`(${left.asNumber()} > ${right.asNumberOrNaN()})`, TYPE_BOOLEAN); + if (left.isAlwaysType(InputType.NUMBER_INTERPRETABLE) && right.isAlwaysType(InputType.NUMBER_INTERPRETABLE | InputType.NUMBER_NAN)) { + return `(${this.descendInput(left.toType(InputType.NUMBER))} > ${this.descendInput(right.toType(InputType.NUMBER_OR_NAN))})`; } // When the left operand is a number or NaN and the right operand is a number, we can negate <= - if (left.isAlwaysNumberOrNaN() && right.isAlwaysNumber()) { - return new TypedInput(`!(${left.asNumberOrNaN()} <= ${right.asNumber()})`, TYPE_BOOLEAN); + if (left.isAlwaysType(InputType.NUMBER_INTERPRETABLE | InputType.NUMBER_NAN) && right.isAlwaysType(InputType.NUMBER_INTERPRETABLE)) { + return `!(${this.descendInput(left.toType(InputType.NUMBER_OR_NAN))} <= ${this.descendInput(right.toType(InputType.NUMBER))})`; } // When either operand is known to never be a number, avoid all number parsing. - if (left.isNeverNumber() || right.isNeverNumber()) { - return new TypedInput(`(${left.asString()}.toLowerCase() > ${right.asString()}.toLowerCase())`, TYPE_BOOLEAN); + if (!left.isSometimesType(InputType.NUMBER_INTERPRETABLE) || !right.isSometimesType(InputType.NUMBER_INTERPRETABLE)) { + return `(${this.descendInput(left.toType(InputType.STRING))}.toLowerCase() > ${this.descendInput(right.toType(InputType.STRING))}.toLowerCase())`; } // No compile-time optimizations possible - use fallback method. - return new TypedInput(`compareGreaterThan(${left.asUnknown()}, ${right.asUnknown()})`, TYPE_BOOLEAN); + return `compareGreaterThan(${this.descendInput(left)}, ${this.descendInput(right)})`; } - case 'op.join': - return new TypedInput(`(${this.descendInput(node.left).asString()} + ${this.descendInput(node.right).asString()})`, TYPE_STRING); - case 'op.length': - return new TypedInput(`${this.descendInput(node.string).asString()}.length`, TYPE_NUMBER); - case 'op.less': { - const left = this.descendInput(node.left); - const right = this.descendInput(node.right); + case InputOpcode.OP_JOIN: + return `(${this.descendInput(node.left)} + ${this.descendInput(node.right)})`; + case InputOpcode.OP_LENGTH: + return `${this.descendInput(node.string)}.length`; + case InputOpcode.OP_LESS: { + const left = node.left; + const right = node.right; // When the left operand is a number or NaN and the right operand is a number, we can use < - if (left.isAlwaysNumberOrNaN() && right.isAlwaysNumber()) { - return new TypedInput(`(${left.asNumberOrNaN()} < ${right.asNumber()})`, TYPE_BOOLEAN); + if (left.isAlwaysType(InputType.NUMBER_INTERPRETABLE | InputType.NUMBER_NAN) && right.isAlwaysType(InputType.NUMBER_INTERPRETABLE)) { + return `(${this.descendInput(left.toType(InputType.NUMBER_OR_NAN))} < ${this.descendInput(right.toType(InputType.NUMBER))})`; } // When the left operand is a number and the right operand is a number or NaN, we can negate >= - if (left.isAlwaysNumber() && right.isAlwaysNumberOrNaN()) { - return new TypedInput(`!(${left.asNumber()} >= ${right.asNumberOrNaN()})`, TYPE_BOOLEAN); + if (left.isAlwaysType(InputType.NUMBER_INTERPRETABLE) && right.isAlwaysType(InputType.NUMBER_INTERPRETABLE | InputType.NUMBER_NAN)) { + return `!(${this.descendInput(left.toType(InputType.NUMBER))} >= ${this.descendInput(right.toType(InputType.NUMBER_OR_NAN))})`; } // When either operand is known to never be a number, avoid all number parsing. - if (left.isNeverNumber() || right.isNeverNumber()) { - return new TypedInput(`(${left.asString()}.toLowerCase() < ${right.asString()}.toLowerCase())`, TYPE_BOOLEAN); + if (!left.isSometimesType(InputType.NUMBER_INTERPRETABLE) || !right.isSometimesType(InputType.NUMBER_INTERPRETABLE)) { + return `(${this.descendInput(left.toType(InputType.STRING))}.toLowerCase() < ${this.descendInput(right.toType(InputType.STRING))}.toLowerCase())`; } // No compile-time optimizations possible - use fallback method. - return new TypedInput(`compareLessThan(${left.asUnknown()}, ${right.asUnknown()})`, TYPE_BOOLEAN); + return `compareLessThan(${this.descendInput(left)}, ${this.descendInput(right)})`; } - case 'op.letterOf': - return new TypedInput(`((${this.descendInput(node.string).asString()})[(${this.descendInput(node.letter).asNumber()} | 0) - 1] || "")`, TYPE_STRING); - case 'op.ln': - // Needs to be marked as NaN because Math.log(-1) == NaN - return new TypedInput(`Math.log(${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER_NAN); - case 'op.log': - // Needs to be marked as NaN because Math.log(-1) == NaN - return new TypedInput(`(Math.log(${this.descendInput(node.value).asNumber()}) / Math.LN10)`, TYPE_NUMBER_NAN); - case 'op.mod': + case InputOpcode.OP_LETTER_OF: + return `((${this.descendInput(node.string)})[${this.descendInput(node.letter)} - 1] || "")`; + case InputOpcode.OP_LOG_E: + return `Math.log(${this.descendInput(node.value)})`; + case InputOpcode.OP_LOG_10: + return `(Math.log(${this.descendInput(node.value)}) / Math.LN10)`; + case InputOpcode.OP_MOD: this.descendedIntoModulo = true; - // Needs to be marked as NaN because mod(0, 0) (and others) == NaN - return new TypedInput(`mod(${this.descendInput(node.left).asNumber()}, ${this.descendInput(node.right).asNumber()})`, TYPE_NUMBER_NAN); - case 'op.multiply': - // Needs to be marked as NaN because Infinity * 0 === NaN - return new TypedInput(`(${this.descendInput(node.left).asNumber()} * ${this.descendInput(node.right).asNumber()})`, TYPE_NUMBER_NAN); - case 'op.not': - return new TypedInput(`!${this.descendInput(node.operand).asBoolean()}`, TYPE_BOOLEAN); - case 'op.or': - return new TypedInput(`(${this.descendInput(node.left).asBoolean()} || ${this.descendInput(node.right).asBoolean()})`, TYPE_BOOLEAN); - case 'op.random': + return `mod(${this.descendInput(node.left)}, ${this.descendInput(node.right)})`; + case InputOpcode.OP_MULTIPLY: + return `(${this.descendInput(node.left)} * ${this.descendInput(node.right)})`; + case InputOpcode.OP_NOT: + return `!${this.descendInput(node.operand)}`; + case InputOpcode.OP_OR: + return `(${this.descendInput(node.left)} || ${this.descendInput(node.right)})`; + case InputOpcode.OP_RANDOM: if (node.useInts) { - // Both inputs are ints, so we know neither are NaN - return new TypedInput(`randomInt(${this.descendInput(node.low).asNumber()}, ${this.descendInput(node.high).asNumber()})`, TYPE_NUMBER); + return `randomInt(${this.descendInput(node.low)}, ${this.descendInput(node.high)})`; } if (node.useFloats) { - return new TypedInput(`randomFloat(${this.descendInput(node.low).asNumber()}, ${this.descendInput(node.high).asNumber()})`, TYPE_NUMBER_NAN); + return `randomFloat(${this.descendInput(node.low)}, ${this.descendInput(node.high)})`; } - return new TypedInput(`runtime.ext_scratch3_operators._random(${this.descendInput(node.low).asUnknown()}, ${this.descendInput(node.high).asUnknown()})`, TYPE_NUMBER_NAN); - case 'op.round': - return new TypedInput(`Math.round(${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER); - case 'op.sin': - return new TypedInput(`(Math.round(Math.sin((Math.PI * ${this.descendInput(node.value).asNumber()}) / 180) * 1e10) / 1e10)`, TYPE_NUMBER_NAN); - case 'op.sqrt': - // Needs to be marked as NaN because Math.sqrt(-1) === NaN - return new TypedInput(`Math.sqrt(${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER_NAN); - case 'op.subtract': - // Needs to be marked as NaN because Infinity - Infinity === NaN - return new TypedInput(`(${this.descendInput(node.left).asNumber()} - ${this.descendInput(node.right).asNumber()})`, TYPE_NUMBER_NAN); - case 'op.tan': - return new TypedInput(`tan(${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER_NAN); - case 'op.10^': - return new TypedInput(`(10 ** ${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER); + return `runtime.ext_scratch3_operators._random(${this.descendInput(node.low)}, ${this.descendInput(node.high)})`; + case InputOpcode.OP_ROUND: + return `Math.round(${this.descendInput(node.value)})`; + case InputOpcode.OP_SIN: + return `(Math.round(Math.sin((Math.PI * ${this.descendInput(node.value)}) / 180) * 1e10) / 1e10)`; + case InputOpcode.OP_SQRT: + return `Math.sqrt(${this.descendInput(node.value)})`; + case InputOpcode.OP_SUBTRACT: + return `(${this.descendInput(node.left)} - ${this.descendInput(node.right)})`; + case InputOpcode.OP_TAN: + return `tan(${this.descendInput(node.value)})`; + case InputOpcode.OP_POW_10: + return `(10 ** ${this.descendInput(node.value)})`; - case 'procedures.call': { + case InputOpcode.PROCEDURE_CALL: { const procedureCode = node.code; const procedureVariant = node.variant; const procedureData = this.ir.procedures[procedureVariant]; if (procedureData.stack === null) { // TODO still need to evaluate arguments for side effects - return new TypedInput('""', TYPE_STRING); + return '""'; } // Recursion makes this complicated because: @@ -650,7 +383,7 @@ class JSGenerator { const procedureReference = `thread.procedures["${sanitize(procedureVariant)}"]`; const args = []; for (const input of node.arguments) { - args.push(this.descendInput(input).asSafe()); + args.push(this.descendInput(input)); } const joinedArgs = args.join(','); @@ -658,109 +391,105 @@ class JSGenerator { const yieldForHat = this.isInHat; if (yieldForRecursion || yieldForHat) { const runtimeFunction = procedureData.yields ? 'yieldThenCallGenerator' : 'yieldThenCall'; - return new TypedInput(`(yield* ${runtimeFunction}(${procedureReference}, ${joinedArgs}))`, TYPE_UNKNOWN); + return `(yield* ${runtimeFunction}(${procedureReference}, ${joinedArgs}))`; } if (procedureData.yields) { - return new TypedInput(`(yield* ${procedureReference}(${joinedArgs}))`, TYPE_UNKNOWN); + return `(yield* ${procedureReference}(${joinedArgs}))`; } - return new TypedInput(`${procedureReference}(${joinedArgs})`, TYPE_UNKNOWN); + return `${procedureReference}(${joinedArgs})`; } - case 'sensing.answer': - return new TypedInput(`runtime.ext_scratch3_sensing._answer`, TYPE_STRING); - case 'sensing.colorTouchingColor': - return new TypedInput(`target.colorIsTouchingColor(colorToList(${this.descendInput(node.target).asColor()}), colorToList(${this.descendInput(node.mask).asColor()}))`, TYPE_BOOLEAN); - case 'sensing.date': - return new TypedInput(`(new Date().getDate())`, TYPE_NUMBER); - case 'sensing.dayofweek': - return new TypedInput(`(new Date().getDay() + 1)`, TYPE_NUMBER); - case 'sensing.daysSince2000': - return new TypedInput('daysSince2000()', TYPE_NUMBER); - case 'sensing.distance': + case InputOpcode.SENSING_ANSWER: + return `runtime.ext_scratch3_sensing._answer`; + case InputOpcode.SENSING_COLOR_TOUCHING_COLOR: + return `target.colorIsTouchingColor(colorToList(${this.descendInput(node.target)}), colorToList(${this.descendInput(node.mask)}))`; + case InputOpcode.SENSING_TIME_DATE: + return `(new Date().getDate())`; + case InputOpcode.SENSING_TIME_WEEKDAY: + return `(new Date().getDay() + 1)`; + case InputOpcode.SENSING_TIME_DAYS_SINCE_2000: + return 'daysSince2000()'; + case InputOpcode.SENSING_DISTANCE: // TODO: on stages, this can be computed at compile time - return new TypedInput(`distance(${this.descendInput(node.target).asString()})`, TYPE_NUMBER); - case 'sensing.hour': - return new TypedInput(`(new Date().getHours())`, TYPE_NUMBER); - case 'sensing.minute': - return new TypedInput(`(new Date().getMinutes())`, TYPE_NUMBER); - case 'sensing.month': - return new TypedInput(`(new Date().getMonth() + 1)`, TYPE_NUMBER); - case 'sensing.of': { - const object = this.descendInput(node.object).asString(); - const property = node.property; - if (node.object.kind === 'constant') { - const isStage = node.object.value === '_stage_'; - // Note that if target isn't a stage, we can't assume it exists - const objectReference = isStage ? 'stage' : this.evaluateOnce(`runtime.getSpriteTargetByName(${object})`); - if (property === 'volume') { - return new TypedInput(`(${objectReference} ? ${objectReference}.volume : 0)`, TYPE_NUMBER); - } - if (isStage) { - switch (property) { - case 'background #': - // fallthrough for scratch 1.0 compatibility - case 'backdrop #': - return new TypedInput(`(${objectReference}.currentCostume + 1)`, TYPE_NUMBER); - case 'backdrop name': - return new TypedInput(`${objectReference}.getCostumes()[${objectReference}.currentCostume].name`, TYPE_STRING); - } - } else { - switch (property) { - case 'x position': - return new TypedInput(`(${objectReference} ? ${objectReference}.x : 0)`, TYPE_NUMBER); - case 'y position': - return new TypedInput(`(${objectReference} ? ${objectReference}.y : 0)`, TYPE_NUMBER); - case 'direction': - return new TypedInput(`(${objectReference} ? ${objectReference}.direction : 0)`, TYPE_NUMBER); - case 'costume #': - return new TypedInput(`(${objectReference} ? ${objectReference}.currentCostume + 1 : 0)`, TYPE_NUMBER); - case 'costume name': - return new TypedInput(`(${objectReference} ? ${objectReference}.getCostumes()[${objectReference}.currentCostume].name : 0)`, TYPE_UNKNOWN); - case 'size': - return new TypedInput(`(${objectReference} ? ${objectReference}.size : 0)`, TYPE_NUMBER); - } - } - const variableReference = this.evaluateOnce(`${objectReference} && ${objectReference}.lookupVariableByNameAndType("${sanitize(property)}", "", true)`); - return new TypedInput(`(${variableReference} ? ${variableReference}.value : 0)`, TYPE_UNKNOWN); - } - return new TypedInput(`runtime.ext_scratch3_sensing.getAttributeOf({OBJECT: ${object}, PROPERTY: "${sanitize(property)}" })`, TYPE_UNKNOWN); - } - case 'sensing.second': - return new TypedInput(`(new Date().getSeconds())`, TYPE_NUMBER); - case 'sensing.touching': - return new TypedInput(`target.isTouchingObject(${this.descendInput(node.object).asUnknown()})`, TYPE_BOOLEAN); - case 'sensing.touchingColor': - return new TypedInput(`target.isTouchingColor(colorToList(${this.descendInput(node.color).asColor()}))`, TYPE_BOOLEAN); - case 'sensing.username': - return new TypedInput('runtime.ioDevices.userData.getUsername()', TYPE_STRING); - case 'sensing.year': - return new TypedInput(`(new Date().getFullYear())`, TYPE_NUMBER); + return `distance(${this.descendInput(node.target)})`; + case InputOpcode.SENSING_TIME_HOUR: + return `(new Date().getHours())`; + case InputOpcode.SENSING_TIME_MINUTE: + return `(new Date().getMinutes())`; + case InputOpcode.SENSING_TIME_MONTH: + return `(new Date().getMonth() + 1)`; + case InputOpcode.SENSING_OF: + return `runtime.ext_scratch3_sensing.getAttributeOf({OBJECT: ${this.descendInput(node.object)}, PROPERTY: "${sanitize(node.property)}" })`; + case InputOpcode.SENSING_OF_VOLUME: { + const targetRef = this.descendTargetReference(node.object); + return `(${targetRef} ? ${targetRef}.volume : 0)`; + } case InputOpcode.SENSING_OF_BACKDROP_NUMBER: + return `(stage.currentCostume + 1)`; + case InputOpcode.SENSING_OF_BACKDROP_NAME: + return `stage.getCostumes()[stage.currentCostume].name`; + case InputOpcode.SENSING_OF_POS_X: { + const targetRef = this.descendTargetReference(node.object); + return `(${targetRef} ? ${targetRef}.x : 0)`; + } case InputOpcode.SENSING_OF_POS_Y: { + const targetRef = this.descendTargetReference(node.object); + return `(${targetRef} ? ${targetRef}.y : 0)`; + } case InputOpcode.SENSING_OF_DIRECTION: { + const targetRef = this.descendTargetReference(node.object); + return `(${targetRef} ? ${targetRef}.direction : 0)`; + } case InputOpcode.SENSING_OF_COSTUME_NUMBER: { + const targetRef = this.descendTargetReference(node.object); + return `(${targetRef} ? ${targetRef}.currentCostume + 1 : 0)`; + } case InputOpcode.SENSING_OF_COSTUME_NAME: { + const targetRef = this.descendTargetReference(node.object); + return `(${targetRef} ? ${targetRef}.getCostumes()[${targetRef}.currentCostume].name : 0)`; + } case InputOpcode.SENSING_OF_SIZE: { + const targetRef = this.descendTargetReference(node.object); + return `(${targetRef} ? ${targetRef}.size : 0)`; + } case InputOpcode.SENSING_OF_VAR: { + const targetRef = this.descendTargetReference(node.object); + const varRef = this.evaluateOnce(`${targetRef} && ${targetRef}.lookupVariableByNameAndType("${sanitize(node.property)}", "", true)`); + return `(${varRef} ? ${varRef}.value : 0)`; + } case InputOpcode.SENSING_TIME_SECOND: + return `(new Date().getSeconds())`; + case InputOpcode.SENSING_TOUCHING_OBJECT: + return `target.isTouchingObject(${this.descendInput(node.object)})`; + case InputOpcode.SENSING_TOUCHING_COLOR: + return `target.isTouchingColor(colorToList(${this.descendInput(node.color)}))`; + case InputOpcode.SENSING_USERNAME: + return 'runtime.ioDevices.userData.getUsername()'; + case InputOpcode.SENSING_TIME_YEAR: + return `(new Date().getFullYear())`; - case 'timer.get': - return new TypedInput('runtime.ioDevices.clock.projectTimer()', TYPE_NUMBER); + case InputOpcode.SENSING_TIMER_GET: + return 'runtime.ioDevices.clock.projectTimer()'; - case 'tw.lastKeyPressed': - return new TypedInput('runtime.ioDevices.keyboard.getLastKeyPressed()', TYPE_STRING); + case InputOpcode.CONTROL_COUNTER: + return 'runtime.ext_scratch3_control._counter'; - case 'var.get': - return this.descendVariable(node.variable); + case InputOpcode.TW_KEY_LAST_PRESSED: + return 'runtime.ioDevices.keyboard.getLastKeyPressed()'; + + case InputOpcode.VAR_GET: + return `${this.referenceVariable(node.variable)}.value`; default: - log.warn(`JS: Unknown input: ${node.kind}`, node); - throw new Error(`JS: Unknown input: ${node.kind}`); + log.warn(`JS: Unknown input: ${block.opcode}`, node); + throw new Error(`JS: Unknown input: ${block.opcode}`); } } /** - * @param {*} node Stacked node to compile. + * @param {IntermediateStackBlock} block Stacked block to compile. */ - descendStackedBlock (node) { - switch (node.kind) { - case 'addons.call': + descendStackedBlock (block) { + const node = block.inputs; + switch (block.opcode) { + case StackOpcode.ADDON_CALL: { this.source += `${this.descendAddonCall(node)};\n`; break; + } - case 'compat': { + case StackOpcode.COMPATIBILITY_LAYER: { // If the last command in a loop returns a promise, immediately continue to the next iteration. // If you don't do this, the loop effectively yields twice per iteration and will run at half-speed. const isLastInLoop = this.isLastBlockInLoop(); @@ -773,9 +502,9 @@ class JSGenerator { this.source += `const ${branchVariable} = createBranchInfo(${blockType === BlockType.LOOP});\n`; this.source += `while (${branchVariable}.branch = +(${this.generateCompatibilityLayerCall(node, false, branchVariable)})) {\n`; this.source += `switch (${branchVariable}.branch) {\n`; - for (const index in node.substacks) { - this.source += `case ${+index}: {\n`; - this.descendStack(node.substacks[index], new Frame(false)); + for (let i = 0; i < node.substacks.length; i++) { + this.source += `case ${i + 1}: {\n`; + this.descendStack(node.substacks[i], new Frame(false)); this.source += `break;\n`; this.source += `}\n`; // close case } @@ -793,102 +522,12 @@ class JSGenerator { break; } - case 'control.createClone': - this.source += `runtime.ext_scratch3_control._createClone(${this.descendInput(node.target).asString()}, target);\n`; - break; - case 'control.deleteClone': - this.source += 'if (!target.isOriginal) {\n'; - this.source += ' runtime.disposeTarget(target);\n'; - this.source += ' runtime.stopForTarget(target);\n'; - this.retire(); - this.source += '}\n'; - break; - case 'control.for': { - this.resetVariableInputs(); - const index = this.localVariables.next(); - this.source += `var ${index} = 0; `; - this.source += `while (${index} < ${this.descendInput(node.count).asNumber()}) { `; - this.source += `${index}++; `; - this.source += `${this.referenceVariable(node.variable)}.value = ${index};\n`; - this.descendStack(node.do, new Frame(true)); - this.yieldLoop(); - this.source += '}\n'; - break; - } - case 'control.if': - this.source += `if (${this.descendInput(node.condition).asBoolean()}) {\n`; - this.descendStack(node.whenTrue, new Frame(false)); - // only add the else branch if it won't be empty - // this makes scripts have a bit less useless noise in them - if (node.whenFalse.length) { - this.source += `} else {\n`; - this.descendStack(node.whenFalse, new Frame(false)); - } - this.source += `}\n`; - break; - case 'control.repeat': { - const i = this.localVariables.next(); - this.source += `for (var ${i} = ${this.descendInput(node.times).asNumber()}; ${i} >= 0.5; ${i}--) {\n`; - this.descendStack(node.do, new Frame(true)); - this.yieldLoop(); - this.source += `}\n`; - break; - } - case 'control.stopAll': - this.source += 'runtime.stopAll();\n'; - this.retire(); - break; - case 'control.stopOthers': - this.source += 'runtime.stopForTarget(target, thread);\n'; - break; - case 'control.stopScript': - this.stopScript(); - break; - case 'control.wait': { - const duration = this.localVariables.next(); - this.source += `thread.timer = timer();\n`; - this.source += `var ${duration} = Math.max(0, 1000 * ${this.descendInput(node.seconds).asNumber()});\n`; - this.requestRedraw(); - // always yield at least once, even on 0 second durations - this.yieldNotWarp(); - this.source += `while (thread.timer.timeElapsed() < ${duration}) {\n`; - this.yieldStuckOrNotWarp(); - this.source += '}\n'; - this.source += 'thread.timer = null;\n'; - break; - } - case 'control.waitUntil': { - this.resetVariableInputs(); - this.source += `while (!${this.descendInput(node.condition).asBoolean()}) {\n`; - this.yieldStuckOrNotWarp(); - this.source += `}\n`; - break; - } - case 'control.while': - this.resetVariableInputs(); - this.source += `while (${this.descendInput(node.condition).asBoolean()}) {\n`; - this.descendStack(node.do, new Frame(true)); - if (node.warpTimer) { - this.yieldStuckOrNotWarp(); - } else { - this.yieldLoop(); - } - this.source += `}\n`; - break; - - case 'counter.clear': - this.source += 'runtime.ext_scratch3_control._counter = 0;\n'; - break; - case 'counter.increment': - this.source += 'runtime.ext_scratch3_control._counter++;\n'; - break; - - case 'hat.edge': + case StackOpcode.HAT_EDGE: this.isInHat = true; this.source += '{\n'; // For exact Scratch parity, evaluate the input before checking old edge state. // Can matter if the input is not instantly evaluated. - this.source += `const resolvedValue = ${this.descendInput(node.condition).asBoolean()};\n`; + this.source += `const resolvedValue = ${this.descendInput(node.condition)};\n`; this.source += `const id = "${sanitize(node.id)}";\n`; this.source += 'const hasOldEdgeValue = target.hasEdgeActivatedValue(id);\n'; this.source += `const oldEdgeValue = target.updateEdgeActivatedValue(id, resolvedValue);\n`; @@ -900,209 +539,290 @@ class JSGenerator { this.source += '}\n'; this.isInHat = false; break; - case 'hat.predicate': + + case StackOpcode.HAT_PREDICATE: this.isInHat = true; - this.source += `if (!${this.descendInput(node.condition).asBoolean()}) {\n`; + this.source += `if (!${this.descendInput(node.condition)}) {\n`; this.retire(); this.source += '}\n'; this.source += 'yield;\n'; this.isInHat = false; break; - case 'event.broadcast': - this.source += `startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: ${this.descendInput(node.broadcast).asString()} });\n`; - this.resetVariableInputs(); + case StackOpcode.CONTROL_CLONE_CREATE: + this.source += `runtime.ext_scratch3_control._createClone(${this.descendInput(node.target)}, target);\n`; break; - case 'event.broadcastAndWait': - this.source += `yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: ${this.descendInput(node.broadcast).asString()} }));\n`; + case StackOpcode.CONTROL_CLONE_DELETE: + this.source += 'if (!target.isOriginal) {\n'; + this.source += ' runtime.disposeTarget(target);\n'; + this.source += ' runtime.stopForTarget(target);\n'; + this.retire(); + this.source += '}\n'; + break; + case StackOpcode.CONTROL_FOR: + const index = this.localVariables.next(); + this.source += `var ${index} = 0; `; + this.source += `while (${index} < ${this.descendInput(node.count)}) { `; + this.source += `${index}++; `; + this.source += `${this.referenceVariable(node.variable)}.value = ${index};\n`; + this.descendStack(node.do, new Frame(true)); + this.yieldLoop(); + this.source += '}\n'; + break; + case StackOpcode.CONTROL_IF_ELSE: + this.source += `if (${this.descendInput(node.condition)}) {\n`; + this.descendStack(node.whenTrue, new Frame(false)); + // only add the else branch if it won't be empty + // this makes scripts have a bit less useless noise in them + if (node.whenFalse.blocks.length) { + this.source += `} else {\n`; + this.descendStack(node.whenFalse, new Frame(false)); + } + this.source += `}\n`; + break; + case StackOpcode.CONTROL_REPEAT: { + const i = this.localVariables.next(); + this.source += `for (var ${i} = ${this.descendInput(node.times)}; ${i} >= 0.5; ${i}--) {\n`; + this.descendStack(node.do, new Frame(true)); + this.yieldLoop(); + this.source += `}\n`; + break; + } + case StackOpcode.CONTROL_STOP_ALL: + this.source += 'runtime.stopAll();\n'; + this.retire(); + break; + case StackOpcode.CONTROL_STOP_OTHERS: + this.source += 'runtime.stopForTarget(target, thread);\n'; + break; + case StackOpcode.CONTROL_STOP_SCRIPT: + this.stopScript(); + break; + case StackOpcode.CONTROL_WAIT: { + const duration = this.localVariables.next(); + this.source += `thread.timer = timer();\n`; + this.source += `var ${duration} = Math.max(0, 1000 * ${this.descendInput(node.seconds)});\n`; + this.requestRedraw(); + // always yield at least once, even on 0 second durations + this.yieldNotWarp(); + this.source += `while (thread.timer.timeElapsed() < ${duration}) {\n`; + this.yieldStuckOrNotWarp(); + this.source += '}\n'; + this.source += 'thread.timer = null;\n'; + break; + } + case StackOpcode.CONTROL_WAIT_UNTIL: { + this.source += `while (!${this.descendInput(node.condition)}) {\n`; + this.yieldStuckOrNotWarp(); + this.source += `}\n`; + break; + } + case StackOpcode.CONTROL_WHILE: + this.source += `while (${this.descendInput(node.condition)}) {\n`; + this.descendStack(node.do, new Frame(true)); + if (node.warpTimer) { + this.yieldStuckOrNotWarp(); + } else { + this.yieldLoop(); + } + this.source += `}\n`; + break; + case StackOpcode.CONTROL_CLEAR_COUNTER: + this.source += 'runtime.ext_scratch3_control._counter = 0;\n'; + break; + case StackOpcode.CONTORL_INCR_COUNTER: + this.source += 'runtime.ext_scratch3_control._counter++;\n'; + break; + + case StackOpcode.EVENT_BROADCAST: + this.source += `startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: ${this.descendInput(node.broadcast)} });\n`; + break; + case StackOpcode.EVENT_BROADCAST_AND_WAIT: + this.source += `yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: ${this.descendInput(node.broadcast)} }));\n`; this.yielded(); break; - case 'list.add': { + case StackOpcode.LIST_ADD: { const list = this.referenceVariable(node.list); - this.source += `${list}.value.push(${this.descendInput(node.item).asSafe()});\n`; + this.source += `${list}.value.push(${this.descendInput(node.item)});\n`; this.source += `${list}._monitorUpToDate = false;\n`; break; } - case 'list.delete': { + case StackOpcode.LIST_DELETE: { const list = this.referenceVariable(node.list); - const index = this.descendInput(node.index); - if (index instanceof ConstantInput) { - if (index.constantValue === 'last') { - this.source += `${list}.value.pop();\n`; - this.source += `${list}._monitorUpToDate = false;\n`; - break; - } - if (+index.constantValue === 1) { - this.source += `${list}.value.shift();\n`; - this.source += `${list}._monitorUpToDate = false;\n`; - break; - } - // do not need a special case for all as that is handled in IR generation (list.deleteAll) - } - this.source += `listDelete(${list}, ${index.asUnknown()});\n`; - break; - } - case 'list.deleteAll': - this.source += `${this.referenceVariable(node.list)}.value = [];\n`; - break; - case 'list.hide': - this.source += `runtime.monitorBlocks.changeBlock({ id: "${sanitize(node.list.id)}", element: "checkbox", value: false }, runtime);\n`; - break; - case 'list.insert': { - const list = this.referenceVariable(node.list); - const index = this.descendInput(node.index); - const item = this.descendInput(node.item); - if (index instanceof ConstantInput && +index.constantValue === 1) { - this.source += `${list}.value.unshift(${item.asSafe()});\n`; + if (node.index.isConstant('last')) { + this.source += `${list}.value.pop();\n`; this.source += `${list}._monitorUpToDate = false;\n`; break; } - this.source += `listInsert(${list}, ${index.asUnknown()}, ${item.asSafe()});\n`; + if (node.index.isConstant(1)) { + this.source += `${list}.value.shift();\n`; + this.source += `${list}._monitorUpToDate = false;\n`; + break; + } + // do not need a special case for all as that is handled in IR generation (list.deleteAll) + this.source += `listDelete(${list}, ${this.descendInput(node.index)});\n`; break; } - case 'list.replace': - this.source += `listReplace(${this.referenceVariable(node.list)}, ${this.descendInput(node.index).asUnknown()}, ${this.descendInput(node.item).asSafe()});\n`; + case StackOpcode.LIST_DELETE_ALL: + this.source += `${this.referenceVariable(node.list)}.value = [];\n`; break; - case 'list.show': + case StackOpcode.LIST_HIDE: + this.source += `runtime.monitorBlocks.changeBlock({ id: "${sanitize(node.list.id)}", element: "checkbox", value: false }, runtime);\n`; + break; + case StackOpcode.LIST_INSERT: { + const list = this.referenceVariable(node.list); + const item = this.descendInput(node.item); + if (node.index.isConstant(1)) { + this.source += `${list}.value.unshift(${item});\n`; + this.source += `${list}._monitorUpToDate = false;\n`; + break; + } + this.source += `listInsert(${list}, ${this.descendInput(node.index)}, ${item});\n`; + break; + } + case StackOpcode.LIST_REPLACE: + this.source += `listReplace(${this.referenceVariable(node.list)}, ${this.descendInput(node.index)}, ${this.descendInput(node.item)});\n`; + break; + case StackOpcode.LIST_SHOW: this.source += `runtime.monitorBlocks.changeBlock({ id: "${sanitize(node.list.id)}", element: "checkbox", value: true }, runtime);\n`; break; - case 'looks.backwardLayers': + case StackOpcode.LOOKS_LAYER_BACKWARD: if (!this.target.isStage) { - this.source += `target.goBackwardLayers(${this.descendInput(node.layers).asNumber()});\n`; + this.source += `target.goBackwardLayers(${this.descendInput(node.layers)});\n`; } break; - case 'looks.clearEffects': + case StackOpcode.LOOKS_EFFECT_CLEAR: this.source += 'target.clearEffects();\n'; break; - case 'looks.changeEffect': - if (Object.prototype.hasOwnProperty.call(this.target.effects, node.effect)) { - this.source += `target.setEffect("${sanitize(node.effect)}", runtime.ext_scratch3_looks.clampEffect("${sanitize(node.effect)}", ${this.descendInput(node.value).asNumber()} + target.effects["${sanitize(node.effect)}"]));\n`; + case StackOpcode.LOOKS_EFFECT_CHANGE: + if (this.target.effects.hasOwnProperty(node.effect)) { + this.source += `target.setEffect("${sanitize(node.effect)}", runtime.ext_scratch3_looks.clampEffect("${sanitize(node.effect)}", ${this.descendInput(node.value)} + target.effects["${sanitize(node.effect)}"]));\n`; } break; - case 'looks.changeSize': - this.source += `target.setSize(target.size + ${this.descendInput(node.size).asNumber()});\n`; + case StackOpcode.LOOKS_SIZE_CHANGE: + this.source += `target.setSize(target.size + ${this.descendInput(node.size)});\n`; break; - case 'looks.forwardLayers': + case StackOpcode.LOOKS_LAYER_FORWARD: if (!this.target.isStage) { - this.source += `target.goForwardLayers(${this.descendInput(node.layers).asNumber()});\n`; + this.source += `target.goForwardLayers(${this.descendInput(node.layers)});\n`; } break; - case 'looks.goToBack': + case StackOpcode.LOOKS_LAYER_BACK: if (!this.target.isStage) { this.source += 'target.goToBack();\n'; } break; - case 'looks.goToFront': + case StackOpcode.LOOKS_LAYER_FRONT: if (!this.target.isStage) { this.source += 'target.goToFront();\n'; } break; - case 'looks.hide': + case StackOpcode.LOOKS_HIDE: this.source += 'target.setVisible(false);\n'; this.source += 'runtime.ext_scratch3_looks._renderBubble(target);\n'; break; - case 'looks.nextBackdrop': + case StackOpcode.LOOKS_BACKDROP_NEXT: this.source += 'runtime.ext_scratch3_looks._setBackdrop(stage, stage.currentCostume + 1, true);\n'; break; - case 'looks.nextCostume': + case StackOpcode.LOOKS_COSTUME_NEXT: this.source += 'target.setCostume(target.currentCostume + 1);\n'; break; - case 'looks.setEffect': - if (Object.prototype.hasOwnProperty.call(this.target.effects, node.effect)) { - this.source += `target.setEffect("${sanitize(node.effect)}", runtime.ext_scratch3_looks.clampEffect("${sanitize(node.effect)}", ${this.descendInput(node.value).asNumber()}));\n`; + case StackOpcode.LOOKS_EFFECT_SET: + if (this.target.effects.hasOwnProperty(node.effect)) { + this.source += `target.setEffect("${sanitize(node.effect)}", runtime.ext_scratch3_looks.clampEffect("${sanitize(node.effect)}", ${this.descendInput(node.value)}));\n`; } break; - case 'looks.setSize': - this.source += `target.setSize(${this.descendInput(node.size).asNumber()});\n`; + case StackOpcode.LOOKS_SIZE_SET: + this.source += `target.setSize(${this.descendInput(node.size)});\n`; break; - case 'looks.show': + case StackOpcode.LOOKS_SHOW: this.source += 'target.setVisible(true);\n'; this.source += 'runtime.ext_scratch3_looks._renderBubble(target);\n'; break; - case 'looks.switchBackdrop': - this.source += `runtime.ext_scratch3_looks._setBackdrop(stage, ${this.descendInput(node.backdrop).asSafe()});\n`; + case StackOpcode.LOOKS_BACKDROP_SET: + this.source += `runtime.ext_scratch3_looks._setBackdrop(stage, ${this.descendInput(node.backdrop)});\n`; break; - case 'looks.switchCostume': - this.source += `runtime.ext_scratch3_looks._setCostume(target, ${this.descendInput(node.costume).asSafe()});\n`; + case StackOpcode.LOOKS_COSTUME_SET: + this.source += `runtime.ext_scratch3_looks._setCostume(target, ${this.descendInput(node.costume)});\n`; break; - case 'motion.changeX': - this.source += `target.setXY(target.x + ${this.descendInput(node.dx).asNumber()}, target.y);\n`; + case StackOpcode.MOTION_X_CHANGE: + this.source += `target.setXY(target.x + ${this.descendInput(node.dx)}, target.y);\n`; break; - case 'motion.changeY': - this.source += `target.setXY(target.x, target.y + ${this.descendInput(node.dy).asNumber()});\n`; + case StackOpcode.MOTION_Y_CHANGE: + this.source += `target.setXY(target.x, target.y + ${this.descendInput(node.dy)});\n`; break; - case 'motion.ifOnEdgeBounce': + case StackOpcode.MOTION_IF_ON_EDGE_BOUNCE: this.source += `runtime.ext_scratch3_motion._ifOnEdgeBounce(target);\n`; break; - case 'motion.setDirection': - this.source += `target.setDirection(${this.descendInput(node.direction).asNumber()});\n`; + case StackOpcode.MOTION_DIRECTION_SET: + this.source += `target.setDirection(${this.descendInput(node.direction)});\n`; break; - case 'motion.setRotationStyle': + case StackOpcode.MOTION_ROTATION_STYLE_SET: this.source += `target.setRotationStyle("${sanitize(node.style)}");\n`; break; - case 'motion.setX': // fallthrough - case 'motion.setY': // fallthrough - case 'motion.setXY': { + case StackOpcode.MOTION_X_SET: // fallthrough + case StackOpcode.MOTION_Y_SET: // fallthrough + case StackOpcode.MOTION_XY_SET: { this.descendedIntoModulo = false; - const x = 'x' in node ? this.descendInput(node.x).asNumber() : 'target.x'; - const y = 'y' in node ? this.descendInput(node.y).asNumber() : 'target.y'; + const x = 'x' in node ? this.descendInput(node.x) : 'target.x'; + const y = 'y' in node ? this.descendInput(node.y) : 'target.y'; this.source += `target.setXY(${x}, ${y});\n`; if (this.descendedIntoModulo) { this.source += `if (target.interpolationData) target.interpolationData = null;\n`; } break; } - case 'motion.step': - this.source += `runtime.ext_scratch3_motion._moveSteps(${this.descendInput(node.steps).asNumber()}, target);\n`; + case StackOpcode.MOTION_STEP: + this.source += `runtime.ext_scratch3_motion._moveSteps(${this.descendInput(node.steps)}, target);\n`; break; - case 'noop': + case StackOpcode.NOP: break; - case 'pen.clear': + case StackOpcode.PEN_CLEAR: this.source += `${PEN_EXT}.clear();\n`; break; - case 'pen.down': + case StackOpcode.PEN_DOWN: this.source += `${PEN_EXT}._penDown(target);\n`; break; - case 'pen.changeParam': - this.source += `${PEN_EXT}._setOrChangeColorParam(${this.descendInput(node.param).asString()}, ${this.descendInput(node.value).asNumber()}, ${PEN_STATE}, true);\n`; + case StackOpcode.PEN_COLOR_PARAM_CHANGE: + this.source += `${PEN_EXT}._setOrChangeColorParam(${this.descendInput(node.param)}, ${this.descendInput(node.value)}, ${PEN_STATE}, true);\n`; break; - case 'pen.changeSize': - this.source += `${PEN_EXT}._changePenSizeBy(${this.descendInput(node.size).asNumber()}, target);\n`; + case StackOpcode.PEN_SIZE_CHANGE: + this.source += `${PEN_EXT}._changePenSizeBy(${this.descendInput(node.size)}, target);\n`; break; - case 'pen.legacyChangeHue': - this.source += `${PEN_EXT}._changePenHueBy(${this.descendInput(node.hue).asNumber()}, target);\n`; + case StackOpcode.PEN_COLOR_HUE_CHANGE_LEGACY: + this.source += `${PEN_EXT}._changePenHueBy(${this.descendInput(node.hue)}, target);\n`; break; - case 'pen.legacyChangeShade': - this.source += `${PEN_EXT}._changePenShadeBy(${this.descendInput(node.shade).asNumber()}, target);\n`; + case StackOpcode.PEN_COLOR_SHADE_CHANGE_LEGACY: + this.source += `${PEN_EXT}._changePenShadeBy(${this.descendInput(node.shade)}, target);\n`; break; - case 'pen.legacySetHue': - this.source += `${PEN_EXT}._setPenHueToNumber(${this.descendInput(node.hue).asNumber()}, target);\n`; + case StackOpcode.PEN_COLOR_HUE_SET_LEGACY: + this.source += `${PEN_EXT}._setPenHueToNumber(${this.descendInput(node.hue)}, target);\n`; break; - case 'pen.legacySetShade': - this.source += `${PEN_EXT}._setPenShadeToNumber(${this.descendInput(node.shade).asNumber()}, target);\n`; + case StackOpcode.PEN_COLOR_SHADE_SET_LEGACY: + this.source += `${PEN_EXT}._setPenShadeToNumber(${this.descendInput(node.shade)}, target);\n`; break; - case 'pen.setColor': - this.source += `${PEN_EXT}._setPenColorToColor(${this.descendInput(node.color).asColor()}, target);\n`; + case StackOpcode.PEN_COLOR_SET: + this.source += `${PEN_EXT}._setPenColorToColor(${this.descendInput(node.color)}, target);\n`; break; - case 'pen.setParam': - this.source += `${PEN_EXT}._setOrChangeColorParam(${this.descendInput(node.param).asString()}, ${this.descendInput(node.value).asNumber()}, ${PEN_STATE}, false);\n`; + case StackOpcode.PEN_COLOR_PARAM_SET: + this.source += `${PEN_EXT}._setOrChangeColorParam(${this.descendInput(node.param)}, ${this.descendInput(node.value)}, ${PEN_STATE}, false);\n`; break; - case 'pen.setSize': - this.source += `${PEN_EXT}._setPenSizeTo(${this.descendInput(node.size).asNumber()}, target);\n`; + case StackOpcode.PEN_SIZE_SET: + this.source += `${PEN_EXT}._setPenSizeTo(${this.descendInput(node.size)}, target);\n`; break; - case 'pen.stamp': + case StackOpcode.PEN_STAMP: this.source += `${PEN_EXT}._stamp(target);\n`; break; - case 'pen.up': + case StackOpcode.PEN_UP: this.source += `${PEN_EXT}._penUp(target);\n`; break; - case 'procedures.call': { + case StackOpcode.PROCEDURE_CALL: { const procedureCode = node.code; const procedureVariant = node.variant; const procedureData = this.ir.procedures[procedureVariant]; @@ -1110,121 +830,118 @@ class JSGenerator { // TODO still need to evaluate arguments break; } - const yieldForRecursion = !this.isWarp && procedureCode === this.script.procedureCode; if (yieldForRecursion) { + // Direct yields. this.yieldNotWarp(); } - if (procedureData.yields) { this.source += 'yield* '; + if (!this.script.yields) { + throw new Error('Script uses yielding procedure but is not marked as yielding.'); + } } this.source += `thread.procedures["${sanitize(procedureVariant)}"](`; const args = []; for (const input of node.arguments) { - args.push(this.descendInput(input).asSafe()); + args.push(this.descendInput(input)); } this.source += args.join(','); - this.source += ');\n'; - - this.resetVariableInputs(); + this.source += `);\n`; break; } - case 'procedures.return': - this.stopScriptAndReturn(this.descendInput(node.value).asSafe()); + case StackOpcode.PROCEDURE_RETURN: + this.stopScriptAndReturn(this.descendInput(node.value)); break; - case 'timer.reset': + case StackOpcode.SENSING_TIMER_RESET: this.source += 'runtime.ioDevices.clock.resetProjectTimer();\n'; break; - case 'tw.debugger': + case StackOpcode.DEBUGGER: this.source += 'debugger;\n'; break; - case 'var.hide': + case StackOpcode.VAR_HIDE: this.source += `runtime.monitorBlocks.changeBlock({ id: "${sanitize(node.variable.id)}", element: "checkbox", value: false }, runtime);\n`; break; - case 'var.set': { - const variable = this.descendVariable(node.variable); - const value = this.descendInput(node.value); - variable.setInput(value); - this.source += `${variable.source} = ${value.asSafe()};\n`; + case StackOpcode.VAR_SET: { + const varReference = this.referenceVariable(node.variable); + this.source += `${varReference}.value = ${this.descendInput(node.value)};\n`; if (node.variable.isCloud) { - this.source += `runtime.ioDevices.cloud.requestUpdateVariable("${sanitize(node.variable.name)}", ${variable.source});\n`; + this.source += `runtime.ioDevices.cloud.requestUpdateVariable("${sanitize(node.variable.name)}", ${varReference}.value);\n`; } break; } - case 'var.show': + case StackOpcode.VAR_SHOW: this.source += `runtime.monitorBlocks.changeBlock({ id: "${sanitize(node.variable.id)}", element: "checkbox", value: true }, runtime);\n`; break; - case 'visualReport': { + case StackOpcode.VISUAL_REPORT: { const value = this.localVariables.next(); - this.source += `const ${value} = ${this.descendInput(node.input).asUnknown()};`; + this.source += `const ${value} = ${this.descendInput(node.input)};`; // blocks like legacy no-ops can return a literal `undefined` this.source += `if (${value} !== undefined) runtime.visualReport("${sanitize(this.script.topBlockId)}", ${value});\n`; break; } default: - log.warn(`JS: Unknown stacked block: ${node.kind}`, node); - throw new Error(`JS: Unknown stacked block: ${node.kind}`); + log.warn(`JS: Unknown stacked block: ${block.opcode}`, node); + throw new Error(`JS: Unknown stacked block: ${block.opcode}`); } } + /** + * Compiles a reference to a target. + * @param {IntermediateInput} input The target reference. Must be a string. + * @returns {string} The compiled target reference + */ + descendTargetReference (input) { + if (!input.isAlwaysType(InputType.STRING)) { + throw new Error(`JS: Object references must be strings!`); + } + if (input.isConstant('_stage_')) return 'stage'; + return this.evaluateOnce(`runtime.getSpriteTargetByName(${this.descendInput(input)})`); + } + /** * Compile a Record of input objects into a safe JS string. - * @param {Record} inputs + * @param {Record} inputs * @returns {string} */ descendInputRecord (inputs) { let result = '{'; for (const name of Object.keys(inputs)) { const node = inputs[name]; - result += `"${sanitize(name)}":${this.descendInput(node).asSafe()},`; + result += `"${sanitize(name)}":${this.descendInput(node)},`; } result += '}'; return result; } - resetVariableInputs () { - this.variableInputs = {}; - } - - descendStack (nodes, frame) { + /** + * @param {IntermediateStack} stack + * @param {Frame} frame + */ + descendStack (stack, frame) { // Entering a stack -- all bets are off. // TODO: allow if/else to inherit values - this.resetVariableInputs(); this.pushFrame(frame); - for (let i = 0; i < nodes.length; i++) { - frame.isLastBlock = i === nodes.length - 1; - this.descendStackedBlock(nodes[i]); + for (let i = 0; i < stack.blocks.length; i++) { + frame.isLastBlock = i === stack.blocks.length - 1; + this.descendStackedBlock(stack.blocks[i]); } // Leaving a stack -- any assumptions made in the current stack do not apply outside of it // TODO: in if/else this might create an extra unused object - this.resetVariableInputs(); this.popFrame(); } - descendVariable (variable) { - if (Object.prototype.hasOwnProperty.call(this.variableInputs, variable.id)) { - return this.variableInputs[variable.id]; - } - const input = new VariableInput(`${this.referenceVariable(variable)}.value`); - this.variableInputs[variable.id] = input; - return input; - } - - referenceVariable (variable) { - if (variable.scope === 'target') { - return this.evaluateOnce(`target.variables["${sanitize(variable.id)}"]`); - } - return this.evaluateOnce(`stage.variables["${sanitize(variable.id)}"]`); - } - + /** + * @param {*} node + * @returns {string} + */ descendAddonCall (node) { const inputs = this.descendInputRecord(node.arguments); const blockFunction = `runtime.getAddonBlock("${sanitize(node.code)}").callback`; @@ -1232,8 +949,23 @@ class JSGenerator { return `yield* executeInCompatibilityLayer(${inputs}, ${blockFunction}, ${this.isWarp}, false, ${blockId})`; } + /** + * @param {*} variable + * @returns {string} + */ + referenceVariable (variable) { + if (variable.scope === 'target') { + return this.evaluateOnce(`target.variables["${sanitize(variable.id)}"]`); + } + return this.evaluateOnce(`stage.variables["${sanitize(variable.id)}"]`); + } + + /** + * @param {string} source + * @returns {string} + */ evaluateOnce (source) { - if (Object.prototype.hasOwnProperty.call(this._setupVariables, source)) { + if (this._setupVariables.hasOwnProperty(source)) { return this._setupVariables[source]; } const variable = this._setupVariablesPool.next(); @@ -1252,25 +984,6 @@ class JSGenerator { } } - stopScript () { - if (this.isProcedure) { - this.source += 'return "";\n'; - } else { - this.retire(); - } - } - - /** - * @param {string} valueJS JS code of value to return. - */ - stopScriptAndReturn (valueJS) { - if (this.isProcedure) { - this.source += `return ${valueJS};\n`; - } else { - this.retire(); - } - } - yieldLoop () { if (this.warpTimer) { this.yieldStuckOrNotWarp(); @@ -1306,7 +1019,6 @@ class JSGenerator { throw new Error('Script yielded but is not marked as yielding.'); } // Control may have been yielded to another script -- all bets are off. - this.resetVariableInputs(); } /** @@ -1316,14 +1028,9 @@ class JSGenerator { this.source += 'runtime.requestRedraw();\n'; } - safeConstantInput (value) { - const unsafe = typeof value === 'string' && this.namesOfCostumesAndSounds.has(value); - return new ConstantInput(value, !unsafe); - } - /** * Generate a call into the compatibility layer. - * @param {*} node The "compat" kind node to generate from. + * @param {*} node The node of the block to generate from. * @param {boolean} setFlags Whether flags should be set describing how this function was processed. * @param {string|null} [frameName] Name of the stack frame variable, if any * @returns {string} The JS of the call. @@ -1335,7 +1042,7 @@ class JSGenerator { for (const inputName of Object.keys(node.inputs)) { const input = node.inputs[inputName]; - const compiledInput = this.descendInput(input).asSafe(); + const compiledInput = this.descendInput(input); result += `"${sanitize(inputName)}":${compiledInput},`; } for (const fieldName of Object.keys(node.fields)) { @@ -1364,6 +1071,25 @@ class JSGenerator { return name; } + stopScript () { + if (this.isProcedure) { + this.source += 'return "";\n'; + } else { + this.retire(); + } + } + + /** + * @param {string} valueJS JS code of value to return. + */ + stopScriptAndReturn (valueJS) { + if (this.isProcedure) { + this.source += `return ${valueJS};\n`; + } else { + this.retire(); + } + } + /** * Generate the JS to pass into eval() based on the current state of the compiler. * @returns {string} JS to pass into eval() @@ -1431,26 +1157,6 @@ class JSGenerator { } } -// For extensions. -JSGenerator.unstable_exports = { - TYPE_NUMBER, - TYPE_STRING, - TYPE_BOOLEAN, - TYPE_UNKNOWN, - TYPE_NUMBER_NAN, - factoryNameVariablePool, - functionNameVariablePool, - generatorNameVariablePool, - VariablePool, - PEN_EXT, - PEN_STATE, - TypedInput, - ConstantInput, - VariableInput, - Frame, - sanitize -}; - // Test hook used by automated snapshot testing. JSGenerator.testingApparatus = null; diff --git a/src/compiler/variable-pool.js b/src/compiler/variable-pool.js index 11f50594..47f463e9 100644 --- a/src/compiler/variable-pool.js +++ b/src/compiler/variable-pool.js @@ -1,3 +1,5 @@ +// @ts-check + class VariablePool { /** * @param {string} prefix The prefix at the start of the variable name. diff --git a/test/snapshot/__snapshots__/order-library-reverse.sb3.tw-snapshot b/test/snapshot/__snapshots__/order-library-reverse.sb3.tw-snapshot index 91457816..96c7ced7 100644 --- a/test/snapshot/__snapshots__/order-library-reverse.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/order-library-reverse.sb3.tw-snapshot @@ -17,7 +17,7 @@ yield; } thread.timer = null; b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 1)) { +if ((b0.value === 1)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (1)",}, b2, false, false, "]4hbk*5ix]V00h|!x1oy", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 1 != " + ("" + b0.value)),}, b2, false, false, "H=x@7SpNJeX|!}8x5y4,", null); @@ -75,7 +75,7 @@ yield; } thread.timer = null; b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 2)) { +if ((b0.value === 2)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (2)",}, b2, false, false, "0i[-T:vYTt=bi47@byUE", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 2 != " + ("" + b0.value)),}, b2, false, false, "Coc1aZ;L9M-RyEt`syps", null); diff --git a/test/snapshot/__snapshots__/order-library.sb3.tw-snapshot b/test/snapshot/__snapshots__/order-library.sb3.tw-snapshot index 2d10b51e..5145078f 100644 --- a/test/snapshot/__snapshots__/order-library.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/order-library.sb3.tw-snapshot @@ -51,7 +51,7 @@ yield; } thread.timer = null; b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 1)) { +if ((b0.value === 1)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (1)",}, b2, false, false, "RSQ{nVCc)6E)(`KlnFCF", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 1 != " + ("" + b0.value)),}, b2, false, false, "8k^j~`c^|YO@hkFd?~2d", null); @@ -75,7 +75,7 @@ yield; } thread.timer = null; b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 2)) { +if ((b0.value === 2)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (2)",}, b2, false, false, "KP?op(=Vg2#;@]!,C#.~", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 2 != " + ("" + b0.value)),}, b2, false, false, "=]|}L~4uQXTNtwJKw_;R", null); diff --git a/test/snapshot/__snapshots__/tw-NaN.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-NaN.sb3.tw-snapshot index 106f6edd..417094fe 100644 --- a/test/snapshot/__snapshots__/tw-NaN.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-NaN.sb3.tw-snapshot @@ -9,61 +9,61 @@ yield* executeInCompatibilityLayer({"MESSAGE":"plan 20",}, b0, false, false, "v" if ((("" + (0 * Infinity)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "6", null); } -if (compareEqual((((0 * Infinity) || 0) * 1), 0)) { +if (((((0 * Infinity) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "#", null); } if ((("" + ((Math.acos(1.01) * 180) / Math.PI)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "9", null); } -if (compareEqual(((((Math.acos(1.01) * 180) / Math.PI) || 0) * 1), 0)) { +if ((((((Math.acos(1.01) * 180) / Math.PI) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "(", null); } if ((("" + ((Math.asin(1.01) * 180) / Math.PI)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "*", null); } -if (compareEqual(((((Math.asin(1.01) * 180) / Math.PI) || 0) * 1), 0)) { +if ((((((Math.asin(1.01) * 180) / Math.PI) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, ",", null); } if ((("" + (0 / 0)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, ".", null); } -if (compareEqual((((0 / 0) || 0) * 1), 0)) { +if (((((0 / 0) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, ":", null); } if ((("" + Math.sqrt(-1)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "?", null); } -if (compareEqual(((Math.sqrt(-1) || 0) * 1), 0)) { +if ((((Math.sqrt(-1) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "@", null); } if ((("" + mod(0, 0)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "]", null); } -if (compareEqual(((mod(0, 0) || 0) * 1), 0)) { +if ((((mod(0, 0) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "_", null); } if ((("" + Math.log(-1)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "{", null); } -if (compareEqual(((Math.log(-1) || 0) * 1), 0)) { +if ((((Math.log(-1) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "}", null); } if ((("" + (Math.log(-1) / Math.LN10)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "aa", null); } -if (compareEqual((((Math.log(-1) / Math.LN10) || 0) * 1), 0)) { +if (((((Math.log(-1) / Math.LN10) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ac", null); } -if (compareEqual((((Math.round(Math.sin((Math.PI * ((1 / 0) || 0)) / 180) * 1e10) / 1e10) || 0) * 1), 0)) { +if (((((Math.round(Math.sin((Math.PI * ((1 / 0) || 0)) / 180) * 1e10) / 1e10) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ae", null); } -if (compareEqual((((Math.round(Math.cos((Math.PI * ((1 / 0) || 0)) / 180) * 1e10) / 1e10) || 0) * 1), 0)) { +if (((((Math.round(Math.cos((Math.PI * ((1 / 0) || 0)) / 180) * 1e10) / 1e10) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ag", null); } -if (compareEqual(((tan(((1 / 0) || 0)) || 0) * 1), 0)) { +if ((((tan(((1 / 0) || 0)) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ai", null); } -if (compareEqual(((runtime.ext_scratch3_operators._random((-1 / 0), (1 / 0)) || 0) * 1), 0)) { +if ((((runtime.ext_scratch3_operators._random((-1 / 0), (1 / 0)) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ak", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "7", null); diff --git a/test/snapshot/__snapshots__/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot index c133a6cb..b7f47584 100644 --- a/test/snapshot/__snapshots__/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot @@ -11,7 +11,7 @@ target.setSize(96); b1.value = 0; while (!(100 === Math.round(target.size))) { b1.value = ((+b1.value || 0) + 1); -target.setSize(target.size + ((((100 - Math.round(target.size)) || 0) / 10) || 0)); +target.setSize(target.size + ((100 - Math.round(target.size)) / 10)); yield; } if (((+b1.value || 0) === 20)) { diff --git a/test/snapshot/__snapshots__/tw-color-input-returns-hex.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-color-input-returns-hex.sb3.tw-snapshot index bccad8ff..070b355d 100644 --- a/test/snapshot/__snapshots__/tw-color-input-returns-hex.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-color-input-returns-hex.sb3.tw-snapshot @@ -8,7 +8,7 @@ const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "c", null); b1.value = "#22388a"; -if ((("" + b1.value).toLowerCase() === "#22388a".toLowerCase())) { +if ((b1.value.toLowerCase() === "#22388a".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "f", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "e", null); diff --git a/test/snapshot/__snapshots__/tw-comparison-matrix-inline.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-comparison-matrix-inline.sb3.tw-snapshot index 66d03cf7..741ccca8 100644 --- a/test/snapshot/__snapshots__/tw-comparison-matrix-inline.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-comparison-matrix-inline.sb3.tw-snapshot @@ -123,13 +123,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 38: 0 should be = 🎉",}, b if (!(("" + ("0".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 39: 0 should be > 🎉",}, b0, false, false, "uq", null); } -if (!(("" + compareLessThan(0, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 40: 0 should be < ",}, b0, false, false, "us", null); } -if (!(("" + compareEqual(0, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0".toLowerCase() === "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 41: 0 should be = ",}, b0, false, false, "uu", null); } -if (!(("" + compareGreaterThan(0, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("0".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 42: 0 should be > ",}, b0, false, false, "uw", null); } if (!(("" + (0 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -249,13 +249,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 80: 0.0 should be = 🎉",}, if (!(("" + ("0.0".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 81: 0.0 should be > 🎉",}, b0, false, false, "vn", null); } -if (!(("" + compareLessThan("0.0", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0.0".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 82: 0.0 should be < ",}, b0, false, false, "vp", null); } -if (!(("" + compareEqual("0.0", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0.0".toLowerCase() === "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 83: 0.0 should be = ",}, b0, false, false, "vr", null); } -if (!(("" + compareGreaterThan("0.0", "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("0.0".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 84: 0.0 should be > ",}, b0, false, false, "vt", null); } if (!(("" + (1.23 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -324,7 +324,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 105: 1.23 should be > -1",}, if (!(("" + ("1.23".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 106: 1.23 should be < true",}, b0, false, false, "v#", null); } -if (!(("" + ("1.23".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 107: 1.23 should be = true",}, b0, false, false, "v(", null); } if (!(("" + ("1.23".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -333,7 +333,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 108: 1.23 should be > true", if (!(("" + ("1.23".toLowerCase() < "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 109: 1.23 should be < false",}, b0, false, false, "v,", null); } -if (!(("" + ("1.23".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 110: 1.23 should be = false",}, b0, false, false, "v.", null); } if (!(("" + ("1.23".toLowerCase() > "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -342,7 +342,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 111: 1.23 should be > false" if (!(("" + ("1.23".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 112: 1.23 should be < NaN",}, b0, false, false, "v=", null); } -if (!(("" + ("1.23".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 113: 1.23 should be = NaN",}, b0, false, false, "v@", null); } if (!(("" + ("1.23".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -360,7 +360,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 117: 1.23 should be > Infini if (!(("" + ("1.23".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 118: 1.23 should be < banana",}, b0, false, false, "wa", null); } -if (!(("" + ("1.23".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 119: 1.23 should be = banana",}, b0, false, false, "wc", null); } if (!(("" + ("1.23".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -369,19 +369,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 120: 1.23 should be > banana if (!(("" + ("1.23".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 121: 1.23 should be < 🎉",}, b0, false, false, "wg", null); } -if (!(("" + ("1.23".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 122: 1.23 should be = 🎉",}, b0, false, false, "wi", null); } if (!(("" + ("1.23".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 123: 1.23 should be > 🎉",}, b0, false, false, "wk", null); } -if (!(("" + compareLessThan(1.23, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("1.23".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 124: 1.23 should be < ",}, b0, false, false, "wm", null); } if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 125: 1.23 should be = ",}, b0, false, false, "wo", null); } -if (!(("" + compareGreaterThan(1.23, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("1.23".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 126: 1.23 should be > ",}, b0, false, false, "wq", null); } if (!(("" + (0.23 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -450,7 +450,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 147: .23 should be > -1",}, if (!(("" + (".23".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 148: .23 should be < true",}, b0, false, false, "w8", null); } -if (!(("" + (".23".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 149: .23 should be = true",}, b0, false, false, "w!", null); } if (!(("" + (".23".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -459,7 +459,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 150: .23 should be > true",} if (!(("" + (".23".toLowerCase() < "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 151: .23 should be < false",}, b0, false, false, "w)", null); } -if (!(("" + (".23".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 152: .23 should be = false",}, b0, false, false, "w+", null); } if (!(("" + (".23".toLowerCase() > "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -468,7 +468,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 153: .23 should be > false", if (!(("" + (".23".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 154: .23 should be < NaN",}, b0, false, false, "w/", null); } -if (!(("" + (".23".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 155: .23 should be = NaN",}, b0, false, false, "w;", null); } if (!(("" + (".23".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -486,7 +486,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 159: .23 should be > Infinit if (!(("" + (".23".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 160: .23 should be < banana",}, b0, false, false, "w|", null); } -if (!(("" + (".23".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 161: .23 should be = banana",}, b0, false, false, "w~", null); } if (!(("" + (".23".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -495,19 +495,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 162: .23 should be > banana" if (!(("" + (".23".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 163: .23 should be < 🎉",}, b0, false, false, "xd", null); } -if (!(("" + (".23".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 164: .23 should be = 🎉",}, b0, false, false, "xf", null); } if (!(("" + (".23".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 165: .23 should be > 🎉",}, b0, false, false, "xh", null); } -if (!(("" + compareLessThan(".23", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (".23".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 166: .23 should be < ",}, b0, false, false, "xj", null); } -if (!(("" + compareEqual(".23", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 167: .23 should be = ",}, b0, false, false, "xl", null); } -if (!(("" + compareGreaterThan(".23", "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + (".23".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 168: .23 should be > ",}, b0, false, false, "xn", null); } if (!(("" + (0.123 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -576,7 +576,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 189: 0.123 should be > -1",} if (!(("" + ("0.123".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 190: 0.123 should be < true",}, b0, false, false, "x5", null); } -if (!(("" + ("0.123".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 191: 0.123 should be = true",}, b0, false, false, "x7", null); } if (!(("" + ("0.123".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -585,7 +585,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 192: 0.123 should be > true" if (!(("" + ("0.123".toLowerCase() < "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 193: 0.123 should be < false",}, b0, false, false, "x#", null); } -if (!(("" + ("0.123".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 194: 0.123 should be = false",}, b0, false, false, "x(", null); } if (!(("" + ("0.123".toLowerCase() > "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -594,7 +594,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 195: 0.123 should be > false if (!(("" + ("0.123".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 196: 0.123 should be < NaN",}, b0, false, false, "x,", null); } -if (!(("" + ("0.123".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 197: 0.123 should be = NaN",}, b0, false, false, "x.", null); } if (!(("" + ("0.123".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -612,7 +612,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 201: 0.123 should be > Infin if (!(("" + ("0.123".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 202: 0.123 should be < banana",}, b0, false, false, "x_", null); } -if (!(("" + ("0.123".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 203: 0.123 should be = banana",}, b0, false, false, "x{", null); } if (!(("" + ("0.123".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -621,19 +621,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 204: 0.123 should be > banan if (!(("" + ("0.123".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 205: 0.123 should be < 🎉",}, b0, false, false, "ya", null); } -if (!(("" + ("0.123".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 206: 0.123 should be = 🎉",}, b0, false, false, "yc", null); } if (!(("" + ("0.123".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 207: 0.123 should be > 🎉",}, b0, false, false, "ye", null); } -if (!(("" + compareLessThan(0.123, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0.123".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 208: 0.123 should be < ",}, b0, false, false, "yg", null); } if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 209: 0.123 should be = ",}, b0, false, false, "yi", null); } -if (!(("" + compareGreaterThan(0.123, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("0.123".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 210: 0.123 should be > ",}, b0, false, false, "yk", null); } if (!(("" + (-0 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -753,13 +753,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 248: -0 should be = 🎉",}, if (!(("" + ("-0".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 249: -0 should be > 🎉",}, b0, false, false, "zb", null); } -if (!(("" + compareLessThan("-0", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("-0".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 250: -0 should be < ",}, b0, false, false, "zd", null); } -if (!(("" + compareEqual("-0", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("-0".toLowerCase() === "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 251: -0 should be = ",}, b0, false, false, "zf", null); } -if (!(("" + compareGreaterThan("-0", "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("-0".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 252: -0 should be > ",}, b0, false, false, "zh", null); } if (!(("" + (-1 < 0)).toLowerCase() === "true".toLowerCase())) { @@ -828,7 +828,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 273: -1 should be > -1",}, b if (!(("" + ("-1".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 274: -1 should be < true",}, b0, false, false, "zZ", null); } -if (!(("" + ("-1".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 275: -1 should be = true",}, b0, false, false, "z1", null); } if (!(("" + ("-1".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -837,7 +837,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 276: -1 should be > true",}, if (!(("" + ("-1".toLowerCase() < "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 277: -1 should be < false",}, b0, false, false, "z5", null); } -if (!(("" + ("-1".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 278: -1 should be = false",}, b0, false, false, "z7", null); } if (!(("" + ("-1".toLowerCase() > "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -846,7 +846,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 279: -1 should be > false",} if (!(("" + ("-1".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 280: -1 should be < NaN",}, b0, false, false, "z#", null); } -if (!(("" + ("-1".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 281: -1 should be = NaN",}, b0, false, false, "z(", null); } if (!(("" + ("-1".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -864,7 +864,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 285: -1 should be > Infinity if (!(("" + ("-1".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 286: -1 should be < banana",}, b0, false, false, "z=", null); } -if (!(("" + ("-1".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 287: -1 should be = banana",}, b0, false, false, "z@", null); } if (!(("" + ("-1".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -873,19 +873,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 288: -1 should be > banana", if (!(("" + ("-1".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 289: -1 should be < 🎉",}, b0, false, false, "z_", null); } -if (!(("" + ("-1".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 290: -1 should be = 🎉",}, b0, false, false, "z{", null); } if (!(("" + ("-1".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 291: -1 should be > 🎉",}, b0, false, false, "z}", null); } -if (!(("" + compareLessThan(-1, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("-1".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 292: -1 should be < ",}, b0, false, false, "Aa", null); } if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 293: -1 should be = ",}, b0, false, false, "Ac", null); } -if (!(("" + compareGreaterThan(-1, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("-1".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 294: -1 should be > ",}, b0, false, false, "Ae", null); } if (!(("" + ("true".toLowerCase() < "0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -909,7 +909,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 300: true should be > 0.0",} if (!(("" + ("true".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 301: true should be < 1.23",}, b0, false, false, "As", null); } -if (!(("" + ("true".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 302: true should be = 1.23",}, b0, false, false, "Au", null); } if (!(("" + ("true".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -918,7 +918,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 303: true should be > 1.23", if (!(("" + ("true".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 304: true should be < .23",}, b0, false, false, "Ay", null); } -if (!(("" + ("true".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 305: true should be = .23",}, b0, false, false, "AA", null); } if (!(("" + ("true".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -927,7 +927,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 306: true should be > .23",} if (!(("" + ("true".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 307: true should be < 0.123",}, b0, false, false, "AE", null); } -if (!(("" + ("true".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 308: true should be = 0.123",}, b0, false, false, "AG", null); } if (!(("" + ("true".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -945,7 +945,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 312: true should be > -0",}, if (!(("" + ("true".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 313: true should be < -1",}, b0, false, false, "AQ", null); } -if (!(("" + ("true".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 314: true should be = -1",}, b0, false, false, "AS", null); } if (!(("" + ("true".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -981,7 +981,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 324: true should be > NaN",} if (!(("" + ("true".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 325: true should be < Infinity",}, b0, false, false, "A)", null); } -if (!(("" + ("true".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 326: true should be = Infinity",}, b0, false, false, "A+", null); } if (!(("" + ("true".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1035,7 +1035,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 342: false should be > 0.0", if (!(("" + ("false".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 343: false should be < 1.23",}, b0, false, false, "Bp", null); } -if (!(("" + ("false".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 344: false should be = 1.23",}, b0, false, false, "Br", null); } if (!(("" + ("false".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1044,7 +1044,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 345: false should be > 1.23" if (!(("" + ("false".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 346: false should be < .23",}, b0, false, false, "Bv", null); } -if (!(("" + ("false".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 347: false should be = .23",}, b0, false, false, "Bx", null); } if (!(("" + ("false".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1053,7 +1053,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 348: false should be > .23", if (!(("" + ("false".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 349: false should be < 0.123",}, b0, false, false, "BB", null); } -if (!(("" + ("false".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 350: false should be = 0.123",}, b0, false, false, "BD", null); } if (!(("" + ("false".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1071,7 +1071,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 354: false should be > -0",} if (!(("" + ("false".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 355: false should be < -1",}, b0, false, false, "BN", null); } -if (!(("" + ("false".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 356: false should be = -1",}, b0, false, false, "BP", null); } if (!(("" + ("false".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1107,7 +1107,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 366: false should be > NaN", if (!(("" + ("false".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 367: false should be < Infinity",}, b0, false, false, "B#", null); } -if (!(("" + ("false".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 368: false should be = Infinity",}, b0, false, false, "B(", null); } if (!(("" + ("false".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1161,7 +1161,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 384: NaN should be > 0.0",}, if (!(("" + ("NaN".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 385: NaN should be < 1.23",}, b0, false, false, "Cm", null); } -if (!(("" + ("NaN".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 386: NaN should be = 1.23",}, b0, false, false, "Co", null); } if (!(("" + ("NaN".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1170,7 +1170,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 387: NaN should be > 1.23",} if (!(("" + ("NaN".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 388: NaN should be < .23",}, b0, false, false, "Cs", null); } -if (!(("" + ("NaN".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 389: NaN should be = .23",}, b0, false, false, "Cu", null); } if (!(("" + ("NaN".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1179,7 +1179,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 390: NaN should be > .23",}, if (!(("" + ("NaN".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 391: NaN should be < 0.123",}, b0, false, false, "Cy", null); } -if (!(("" + ("NaN".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 392: NaN should be = 0.123",}, b0, false, false, "CA", null); } if (!(("" + ("NaN".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1197,7 +1197,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 396: NaN should be > -0",}, if (!(("" + ("NaN".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 397: NaN should be < -1",}, b0, false, false, "CK", null); } -if (!(("" + ("NaN".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 398: NaN should be = -1",}, b0, false, false, "CM", null); } if (!(("" + ("NaN".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1233,7 +1233,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 408: NaN should be > NaN",}, if (!(("" + ("NaN".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 409: NaN should be < Infinity",}, b0, false, false, "C8", null); } -if (!(("" + ("NaN".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 410: NaN should be = Infinity",}, b0, false, false, "C!", null); } if (!(("" + ("NaN".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1332,7 +1332,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 441: Infinity should be > -1 if (!(("" + ("Infinity".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 442: Infinity should be < true",}, b0, false, false, "DN", null); } -if (!(("" + ("Infinity".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 443: Infinity should be = true",}, b0, false, false, "DP", null); } if (!(("" + ("Infinity".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1341,7 +1341,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 444: Infinity should be > tr if (!(("" + ("Infinity".toLowerCase() < "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 445: Infinity should be < false",}, b0, false, false, "DT", null); } -if (!(("" + ("Infinity".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 446: Infinity should be = false",}, b0, false, false, "DV", null); } if (!(("" + ("Infinity".toLowerCase() > "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1350,7 +1350,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 447: Infinity should be > fa if (!(("" + ("Infinity".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 448: Infinity should be < NaN",}, b0, false, false, "DZ", null); } -if (!(("" + ("Infinity".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 449: Infinity should be = NaN",}, b0, false, false, "D1", null); } if (!(("" + ("Infinity".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1368,7 +1368,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 453: Infinity should be > In if (!(("" + ("Infinity".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 454: Infinity should be < banana",}, b0, false, false, "D#", null); } -if (!(("" + ("Infinity".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 455: Infinity should be = banana",}, b0, false, false, "D(", null); } if (!(("" + ("Infinity".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1377,19 +1377,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 456: Infinity should be > ba if (!(("" + ("Infinity".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 457: Infinity should be < 🎉",}, b0, false, false, "D,", null); } -if (!(("" + ("Infinity".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 458: Infinity should be = 🎉",}, b0, false, false, "D.", null); } if (!(("" + ("Infinity".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 459: Infinity should be > 🎉",}, b0, false, false, "D:", null); } -if (!(("" + compareLessThan(Infinity, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("Infinity".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 460: Infinity should be < ",}, b0, false, false, "D=", null); } if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 461: Infinity should be = ",}, b0, false, false, "D@", null); } -if (!(("" + compareGreaterThan(Infinity, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("Infinity".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 462: Infinity should be > ",}, b0, false, false, "D]", null); } if (!(("" + ("banana".toLowerCase() < "0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1413,7 +1413,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 468: banana should be > 0.0" if (!(("" + ("banana".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 469: banana should be < 1.23",}, b0, false, false, "Eg", null); } -if (!(("" + ("banana".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 470: banana should be = 1.23",}, b0, false, false, "Ei", null); } if (!(("" + ("banana".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1422,7 +1422,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 471: banana should be > 1.23 if (!(("" + ("banana".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 472: banana should be < .23",}, b0, false, false, "Em", null); } -if (!(("" + ("banana".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 473: banana should be = .23",}, b0, false, false, "Eo", null); } if (!(("" + ("banana".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1431,7 +1431,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 474: banana should be > .23" if (!(("" + ("banana".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 475: banana should be < 0.123",}, b0, false, false, "Es", null); } -if (!(("" + ("banana".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 476: banana should be = 0.123",}, b0, false, false, "Eu", null); } if (!(("" + ("banana".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1449,7 +1449,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 480: banana should be > -0", if (!(("" + ("banana".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 481: banana should be < -1",}, b0, false, false, "EE", null); } -if (!(("" + ("banana".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 482: banana should be = -1",}, b0, false, false, "EG", null); } if (!(("" + ("banana".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1485,7 +1485,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 492: banana should be > NaN" if (!(("" + ("banana".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 493: banana should be < Infinity",}, b0, false, false, "E2", null); } -if (!(("" + ("banana".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 494: banana should be = Infinity",}, b0, false, false, "E4", null); } if (!(("" + ("banana".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1539,7 +1539,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 510: 🎉 should be > 0.0",} if (!(("" + ("🎉".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 511: 🎉 should be < 1.23",}, b0, false, false, "Fd", null); } -if (!(("" + ("🎉".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 512: 🎉 should be = 1.23",}, b0, false, false, "Ff", null); } if (!(("" + ("🎉".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1548,7 +1548,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 513: 🎉 should be > 1.23", if (!(("" + ("🎉".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 514: 🎉 should be < .23",}, b0, false, false, "Fj", null); } -if (!(("" + ("🎉".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 515: 🎉 should be = .23",}, b0, false, false, "Fl", null); } if (!(("" + ("🎉".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1557,7 +1557,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 516: 🎉 should be > .23",} if (!(("" + ("🎉".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 517: 🎉 should be < 0.123",}, b0, false, false, "Fp", null); } -if (!(("" + ("🎉".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 518: 🎉 should be = 0.123",}, b0, false, false, "Fr", null); } if (!(("" + ("🎉".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1575,7 +1575,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 522: 🎉 should be > -0",}, if (!(("" + ("🎉".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 523: 🎉 should be < -1",}, b0, false, false, "FB", null); } -if (!(("" + ("🎉".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 524: 🎉 should be = -1",}, b0, false, false, "FD", null); } if (!(("" + ("🎉".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1611,7 +1611,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 534: 🎉 should be > NaN",} if (!(("" + ("🎉".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 535: 🎉 should be < Infinity",}, b0, false, false, "FZ", null); } -if (!(("" + ("🎉".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 536: 🎉 should be = Infinity",}, b0, false, false, "F1", null); } if (!(("" + ("🎉".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1644,67 +1644,67 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 545: 🎉 should be = ",}, b if (!(("" + ("🎉".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 546: 🎉 should be > ",}, b0, false, false, "F:", null); } -if (!(("" + compareLessThan("", 0)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "0".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 547: should be < 0",}, b0, false, false, "F=", null); } -if (!(("" + compareEqual("", 0)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() === "0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 548: should be = 0",}, b0, false, false, "F@", null); } -if (!(("" + compareGreaterThan("", 0)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 549: should be > 0",}, b0, false, false, "F]", null); } -if (!(("" + compareLessThan("", "0.0")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "0.0".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 550: should be < 0.0",}, b0, false, false, "F_", null); } -if (!(("" + compareEqual("", "0.0")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() === "0.0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 551: should be = 0.0",}, b0, false, false, "F{", null); } -if (!(("" + compareGreaterThan("", "0.0")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "0.0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 552: should be > 0.0",}, b0, false, false, "F}", null); } -if (!(("" + compareLessThan("", 1.23)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 553: should be < 1.23",}, b0, false, false, "Ga", null); } if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 554: should be = 1.23",}, b0, false, false, "Gc", null); } -if (!(("" + compareGreaterThan("", 1.23)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 555: should be > 1.23",}, b0, false, false, "Ge", null); } -if (!(("" + compareLessThan("", ".23")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 556: should be < .23",}, b0, false, false, "Gg", null); } -if (!(("" + compareEqual("", ".23")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 557: should be = .23",}, b0, false, false, "Gi", null); } -if (!(("" + compareGreaterThan("", ".23")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 558: should be > .23",}, b0, false, false, "Gk", null); } -if (!(("" + compareLessThan("", 0.123)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 559: should be < 0.123",}, b0, false, false, "Gm", null); } if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 560: should be = 0.123",}, b0, false, false, "Go", null); } -if (!(("" + compareGreaterThan("", 0.123)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 561: should be > 0.123",}, b0, false, false, "Gq", null); } -if (!(("" + compareLessThan("", "-0")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "-0".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 562: should be < -0",}, b0, false, false, "Gs", null); } -if (!(("" + compareEqual("", "-0")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() === "-0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 563: should be = -0",}, b0, false, false, "Gu", null); } -if (!(("" + compareGreaterThan("", "-0")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "-0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 564: should be > -0",}, b0, false, false, "Gw", null); } -if (!(("" + compareLessThan("", -1)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 565: should be < -1",}, b0, false, false, "Gy", null); } if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 566: should be = -1",}, b0, false, false, "GA", null); } -if (!(("" + compareGreaterThan("", -1)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 567: should be > -1",}, b0, false, false, "GC", null); } if (!(("" + ("".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1734,13 +1734,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 575: should be = NaN",}, b0 if (!(("" + ("".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 576: should be > NaN",}, b0, false, false, "GU", null); } -if (!(("" + compareLessThan("", Infinity)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 577: should be < Infinity",}, b0, false, false, "GW", null); } if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 578: should be = Infinity",}, b0, false, false, "GY", null); } -if (!(("" + compareGreaterThan("", Infinity)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 579: should be > Infinity",}, b0, false, false, "G0", null); } if (!(("" + ("".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1761,13 +1761,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 584: should be = 🎉",}, b if (!(("" + ("".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 585: should be > 🎉",}, b0, false, false, "G%", null); } -if (!(("" + compareLessThan("", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 586: should be < ",}, b0, false, false, "G)", null); } -if (!(("" + compareEqual("", "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() === "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 587: should be = ",}, b0, false, false, "G+", null); } -if (!(("" + compareGreaterThan("", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 588: should be > ",}, b0, false, false, "G.", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "G-", null); diff --git a/test/snapshot/__snapshots__/tw-comparison-matrix-runtime.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-comparison-matrix-runtime.sb3.tw-snapshot index f97edfc1..69c59c95 100644 --- a/test/snapshot/__snapshots__/tw-comparison-matrix-runtime.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-comparison-matrix-runtime.sb3.tw-snapshot @@ -11,7 +11,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "aZ", n retire(); return; }; }) -// Sprite1 Wrun test +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["mfV;yS}9e:%h5UZ)QyiY"]; const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; @@ -29,15 +29,15 @@ b3.value = 0; for (var a1 = b2.value.length; a1 >= 0.5; a1--) { b3.value = ((+b3.value || 0) + 1); b0.value = ((+b0.value || 0) + 1); -if (!compareEqual(compareGreaterThan(listGet(b2.value, b1.value), (b2.value[((b3.value || 0) | 0) - 1] ?? "")), (b4.value[((b0.value || 0) | 0) - 1] ?? ""))) { -yield* executeInCompatibilityLayer({"MESSAGE":("fail " + (("" + listGet(b2.value, b1.value)) + (" should be > " + ("" + listGet(b2.value, b3.value))))),}, b5, true, false, "]", null); +if (!compareEqual(compareGreaterThan(listGet(b2.value, b1.value), (b2.value[(b3.value | 0) - 1] ?? "")), (b4.value[(b0.value | 0) - 1] ?? ""))) { +yield* executeInCompatibilityLayer({"MESSAGE":("fail " + (("" + listGet(b2.value, b1.value)) + (" should be > " + ("" + (b2.value[(b3.value | 0) - 1] ?? ""))))),}, b5, true, false, "]", null); } b0.value = ((+b0.value || 0) + 1); -if (!compareEqual(compareEqual(listGet(b2.value, b1.value), listGet(b2.value, b3.value)), (b4.value[((b0.value || 0) | 0) - 1] ?? ""))) { +if (!compareEqual(compareEqual(listGet(b2.value, b1.value), listGet(b2.value, b3.value)), (b4.value[(b0.value | 0) - 1] ?? ""))) { yield* executeInCompatibilityLayer({"MESSAGE":("fail " + (("" + listGet(b2.value, b1.value)) + (" should be = " + ("" + listGet(b2.value, b3.value))))),}, b5, true, false, "|", null); } b0.value = ((+b0.value || 0) + 1); -if (!compareEqual(compareLessThan(listGet(b2.value, b1.value), listGet(b2.value, b3.value)), (b4.value[((b0.value || 0) | 0) - 1] ?? ""))) { +if (!compareEqual(compareLessThan(listGet(b2.value, b1.value), listGet(b2.value, b3.value)), (b4.value[(b0.value | 0) - 1] ?? ""))) { yield* executeInCompatibilityLayer({"MESSAGE":("fail " + (("" + listGet(b2.value, b1.value)) + (" should be < " + ("" + listGet(b2.value, b3.value))))),}, b5, true, true, "ab", null); if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;} } @@ -46,7 +46,7 @@ if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;} return ""; }; }) -// Sprite1 Wsetup values +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["n^wm8jw#b24sggt.S^tD"]; return function funXYZ_setup_values () { @@ -105,7 +105,7 @@ b0.value.push((-1 / 0)); b0._monitorUpToDate = false; b0.value.push((0 / 0)); b0._monitorUpToDate = false; -b0.value.push(NaN); +b0.value.push("NaN"); b0._monitorUpToDate = false; b0.value.push("nan"); b0._monitorUpToDate = false; diff --git a/test/snapshot/__snapshots__/tw-custom-report-repeat.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-custom-report-repeat.sb3.tw-snapshot index 060537d6..1442da79 100644 --- a/test/snapshot/__snapshots__/tw-custom-report-repeat.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-custom-report-repeat.sb3.tw-snapshot @@ -19,7 +19,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "l", nu retire(); return; }; }) -// Sprite1 Zblock name +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_block_name () { return 40; diff --git a/test/snapshot/__snapshots__/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot index a63ba884..5f84b8b4 100644 --- a/test/snapshot/__snapshots__/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot @@ -12,10 +12,10 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "k", n if (compareLessThan(("something".toLowerCase() === "else".toLowerCase()), ("1" + ""))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "m", null); } -if (compareGreaterThan(("something".toLowerCase() === "something".toLowerCase()), (0 + 0))) { +if (((+("something".toLowerCase() === "something".toLowerCase())) > (0 + 0))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "n", null); } -if (compareLessThan(("something".toLowerCase() === "else".toLowerCase()), (1 + 0))) { +if (((+("something".toLowerCase() === "else".toLowerCase())) < (1 + 0))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "o", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "l", null); diff --git a/test/snapshot/__snapshots__/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot index 7c736d43..d52a998a 100644 --- a/test/snapshot/__snapshots__/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot @@ -9,13 +9,13 @@ const b2 = target.variables["zShM`!CD?d_|Z,]5X}N6"]; return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, "d", null); b1.value = 2; -if (((+b1.value || 0) === 2)) { +if ((b1.value === 2)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass variable",}, b0, false, false, "k", null); } b2.value = []; b2.value.push(3); b2._monitorUpToDate = false; -if (((+(b2.value[(1 | 0) - 1] ?? "") || 0) === 3)) { +if (((+(b2.value[1 - 1] ?? "") || 0) === 3)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass list",}, b0, false, false, "m", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "l", null); diff --git a/test/snapshot/__snapshots__/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot index 704116bf..27d2ced7 100644 --- a/test/snapshot/__snapshots__/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot @@ -21,7 +21,7 @@ retire(); return; retire(); return; }; }) -// Sprite1 Wno refresh +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("music_restForBeats"); const b1 = runtime.getOpcodeFunction("music_playDrumForBeats"); @@ -41,14 +41,14 @@ runtime.ext_scratch3_motion._moveSteps(0, target); return ""; }; }) -// Sprite1 Wruns below with no refresh +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function* genXYZ_runs_below_with_no_r () { yield* thread.procedures["Whas refresh"](); return ""; }; }) -// Sprite1 Whas refresh +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("music_restForBeats"); const b1 = runtime.getOpcodeFunction("music_playDrumForBeats"); diff --git a/test/snapshot/__snapshots__/tw-gh-201-stop-script-does-not-reevaluate-arguments.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-gh-201-stop-script-does-not-reevaluate-arguments.sb3.tw-snapshot index 07442f3c..1fc921d2 100644 --- a/test/snapshot/__snapshots__/tw-gh-201-stop-script-does-not-reevaluate-arguments.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-gh-201-stop-script-does-not-reevaluate-arguments.sb3.tw-snapshot @@ -11,14 +11,14 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "g", nu retire(); return; }; }) -// Sprite1 Zfoo %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_foo_ (p0) { return ""; return ""; }; }) -// Sprite1 Zno op +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_no_op () { return ""; diff --git a/test/snapshot/__snapshots__/tw-list-any.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-list-any.sb3.tw-snapshot index cb39a249..d6988304 100644 --- a/test/snapshot/__snapshots__/tw-list-any.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-list-any.sb3.tw-snapshot @@ -30,7 +30,7 @@ listDelete(b1, "any"); if ((b1.value.length === 2)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "C", null); } -if (compareEqual(listGet(b1.value, "*"), "")) { +if ((("" + listGet(b1.value, "*")).toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "F", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "E", null); diff --git a/test/snapshot/__snapshots__/tw-one-divide-negative-zero.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-one-divide-negative-zero.sb3.tw-snapshot index 2557ac32..d87b4b22 100644 --- a/test/snapshot/__snapshots__/tw-one-divide-negative-zero.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-one-divide-negative-zero.sb3.tw-snapshot @@ -6,7 +6,7 @@ const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "@|B*yJ0zKh!acN`7L-N5", null); -if ((((1 / -0) || 0) === -Infinity)) { +if (((1 / -0) === -Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "=enYDFG11Nj/0BL:y56w", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, ",Cpv8W0RH0RgNky[1xb:", null); diff --git a/test/snapshot/__snapshots__/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot index 58b908a7..d6e18a37 100644 --- a/test/snapshot/__snapshots__/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot @@ -11,7 +11,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "Q^(MKg retire(); return; }; }) -// Player ZSet Costume +// Player script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ_Set_Costume () { diff --git a/test/snapshot/__snapshots__/tw-procedure-arguments-with-same-name.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-arguments-with-same-name.sb3.tw-snapshot index f2456f2e..2555707c 100644 --- a/test/snapshot/__snapshots__/tw-procedure-arguments-with-same-name.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-arguments-with-same-name.sb3.tw-snapshot @@ -12,7 +12,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "$R-1lb retire(); return; }; }) -// Sprite1 Znumber or text %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ_number_or_text__ (p0,p1) { @@ -22,7 +22,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "HH`yR return ""; }; }) -// Sprite1 Zboolean %b %b +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ_boolean__ (p0,p1) { diff --git a/test/snapshot/__snapshots__/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot index 548eb4b3..4c466711 100644 --- a/test/snapshot/__snapshots__/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot @@ -9,14 +9,14 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "qf{MD}-f+l?U+)KA#Vnm", null); b1.value = ""; thread.procedures["Zdo something"](); -if (!compareEqual(b1.value, "")) { +if (!(b1.value.toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "Sgf_#7|GOpx!R]?Q3]$s", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, ",vD-ZG7f{]FoJ`,))JWh", null); retire(); return; }; }) -// Sprite1 Zdo something +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; return function funXYZ_do_something () { diff --git a/test/snapshot/__snapshots__/tw-procedure-return-non-existant.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-non-existant.sb3.tw-snapshot index 2aa9b207..4e435ae0 100644 --- a/test/snapshot/__snapshots__/tw-procedure-return-non-existant.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-return-non-existant.sb3.tw-snapshot @@ -39,14 +39,14 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "P", nu retire(); return; }; }) -// Sprite1 Zinvalid params - reporter +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_invalid_params___rep () { return 0; return ""; }; }) -// Sprite1 Zinvalid params - boolean +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_invalid_params___boo () { return 0; diff --git a/test/snapshot/__snapshots__/tw-procedure-return-non-existent.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-non-existent.sb3.tw-snapshot index 8a2837c6..cea7eccc 100644 --- a/test/snapshot/__snapshots__/tw-procedure-return-non-existent.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-return-non-existent.sb3.tw-snapshot @@ -9,7 +9,7 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "d", null); b1.value = "discard me"; b1.value = ""; -if (compareEqual(b1.value, "")) { +if ((("" + b1.value).toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non existent procedure returned empty string",}, b0, false, false, "h", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "g", null); diff --git a/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot index 6251bdac..e1176792 100644 --- a/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot @@ -10,17 +10,17 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 18",}, b0, false, false, "G", null); b1.value = 0; b2.value = (yield* thread.procedures["Znon warp recursion should yield %s"](8)); -if (((+b1.value || 0) === 4)) { +if ((b1.value === 4)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp recursion yields",}, b0, false, false, "ao", null); } b1.value = 0; b2.value = thread.procedures["Wwarp recursion should not yield %s"](8); -if (((+b1.value || 0) === 0)) { +if ((b1.value === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass warp recursion does not yield",}, b0, false, false, "ar", null); } b1.value = 0; b2.value = (yield* thread.procedures["Zfib %s"](7)); -if (((+b1.value || 0) === 20)) { +if ((b1.value === 20)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp fib yielded",}, b0, false, false, "au", null); } yield* thread.procedures["Zrecursing yields between each %s"]("initial"); @@ -30,7 +30,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "av", n retire(); return; }; }) -// Sprite1 Znon warp recursion should yield %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function* genXYZ_non_warp_recursion_s (p0) { if (compareGreaterThan(p0, 0)) { @@ -39,7 +39,7 @@ return (yield* yieldThenCallGenerator(thread.procedures["Znon warp recursion sho return ""; }; }) -// Sprite1 Wwarp recursion should not yield %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_warp_recursion_shoul (p0) { if (compareGreaterThan(p0, 0)) { @@ -48,7 +48,7 @@ return thread.procedures["Wwarp recursion should not yield %s"](((+p0 || 0) - 1) return ""; }; }) -// Sprite1 Zfib %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function* genXYZ_fib_ (p0) { if (compareLessThan(p0, 2)) { @@ -59,7 +59,7 @@ return ((+(yield* yieldThenCallGenerator(thread.procedures["Zfib %s"], ((+p0 || return ""; }; }) -// Sprite1 Zrecursing yields between each %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; @@ -68,7 +68,7 @@ return function* genXYZ_recursing_yields_bet (p0) { if ((("" + p0).toLowerCase() === "initial".toLowerCase())) { b0.value = 0; b1.value = ((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 1)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 1)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 2)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 2)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 3)) || 0) + (+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 3)) || 0)) || 0)) || 0)) || 0)) || 0)); -if (((+b0.value || 0) === 3)) { +if ((b0.value === 3)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recursing between calls yields final",}, b2, false, false, "aK", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":"fail recursing between calls yields final",}, b2, false, false, "aL", null); @@ -83,7 +83,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":("fail recursing between calls yie return ""; }; }) -// Sprite1 Zrecursing arguments eval order %s %s %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["4HH82mPlVMOONdl(Ot*7"]; const b1 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; @@ -94,25 +94,25 @@ if ((("" + p0).toLowerCase() === "initial".toLowerCase())) { b0.value = []; b1.value = 0; b2.value = (yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 1",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 2",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 3",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 4","","","")),(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 5","","","")),(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 6","","","")))),"","")),"",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 7","","","")))); -if ((("" + (b0.value[(1 | 0) - 1] ?? "")).toLowerCase() === "1/child 4".toLowerCase())) { +if ((("" + (b0.value[1 - 1] ?? "")).toLowerCase() === "1/child 4".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 1",}, b3, false, false, "aZ", null); } -if ((("" + (b0.value[(2 | 0) - 1] ?? "")).toLowerCase() === "1/child 5".toLowerCase())) { +if ((("" + (b0.value[2 - 1] ?? "")).toLowerCase() === "1/child 5".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 2",}, b3, false, false, "a#", null); } -if ((("" + (b0.value[(3 | 0) - 1] ?? "")).toLowerCase() === "2/child 6".toLowerCase())) { +if ((("" + (b0.value[3 - 1] ?? "")).toLowerCase() === "2/child 6".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 3",}, b3, false, false, "a(", null); } -if ((("" + (b0.value[(4 | 0) - 1] ?? "")).toLowerCase() === "2/child 3".toLowerCase())) { +if ((("" + (b0.value[4 - 1] ?? "")).toLowerCase() === "2/child 3".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 4",}, b3, false, false, "a*", null); } -if ((("" + (b0.value[(5 | 0) - 1] ?? "")).toLowerCase() === "3/child 2".toLowerCase())) { +if ((("" + (b0.value[5 - 1] ?? "")).toLowerCase() === "3/child 2".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 5",}, b3, false, false, "a,", null); } -if ((("" + (b0.value[(6 | 0) - 1] ?? "")).toLowerCase() === "3/child 7".toLowerCase())) { +if ((("" + (b0.value[6 - 1] ?? "")).toLowerCase() === "3/child 7".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 6",}, b3, false, false, "a.", null); } -if ((("" + (b0.value[(7 | 0) - 1] ?? "")).toLowerCase() === "4/child 1".toLowerCase())) { +if ((("" + (b0.value[7 - 1] ?? "")).toLowerCase() === "4/child 1".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 7",}, b3, false, false, "a:", null); } if ((b0.value.length === 7)) { diff --git a/test/snapshot/__snapshots__/tw-procedure-return-simple.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-simple.sb3.tw-snapshot index b0ae54eb..c37d030d 100644 --- a/test/snapshot/__snapshots__/tw-procedure-return-simple.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-return-simple.sb3.tw-snapshot @@ -20,21 +20,21 @@ if (((+thread.procedures["Wfactorial %s"](12) || 0) === 479001600)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass factorial 12",}, b0, false, false, "]", null); } b1.value = (yield* thread.procedures["Zno shadowing 1 %s %s"]("f","g")); -if (compareEqual(b1.value, "")) { +if ((("" + b1.value).toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass default return value",}, b0, false, false, "|", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "`", null); retire(); return; }; }) -// Sprite1 Zsimplest +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_simplest () { return "It works!"; return ""; }; }) -// Sprite1 Znesting 1 +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_nesting_1 () { thread.procedures["Znesting 2"](); @@ -42,14 +42,14 @@ return (("" + thread.procedures["Znesting 3 %s %s"](6,7)) + ("" + thread.procedu return ""; }; }) -// Sprite1 Wwarp fib %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_warp_fib_ (p0) { return thread.procedures["Wfib %s"](p0); return ""; }; }) -// Sprite1 Wfactorial %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_factorial_ (p0) { if (compareGreaterThan(p0, 1)) { @@ -59,7 +59,7 @@ return 1; return ""; }; }) -// Sprite1 Zno shadowing 1 %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("looks_say"); const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; @@ -78,21 +78,21 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass shadow check 3",}, b0, false return ""; }; }) -// Sprite1 Znesting 2 +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_nesting_2 () { return "discard nesting 2"; return ""; }; }) -// Sprite1 Znesting 3 %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_nesting_3__ (p0,p1) { return ((+p0 || 0) * (+p1 || 0)); return ""; }; }) -// Sprite1 Wfib %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_fib_ (p0) { if (compareLessThan(p0, 2)) { @@ -103,7 +103,7 @@ return ((+thread.procedures["Wfib %s"](((+p0 || 0) - 1)) || 0) + (+thread.proced return ""; }; }) -// Sprite1 Zno shadowing 2 %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_no_shadowing_2__ (p0,p1) { return "discard shadow 2"; diff --git a/test/snapshot/__snapshots__/tw-procedure-return-stops-scripts.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-stops-scripts.sb3.tw-snapshot index 29d85a58..4980c3d3 100644 --- a/test/snapshot/__snapshots__/tw-procedure-return-stops-scripts.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-return-stops-scripts.sb3.tw-snapshot @@ -19,15 +19,15 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "v", nu retire(); return; }; }) -// Sprite1 Wreturn stops the script immediately +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["PsAI*C{QHI3*4?O8p#TM"]; const b1 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ_return_stops_the_scr () { b0.value = 0; for (var a0 = 100; a0 >= 0.5; a0--) { -b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 25)) { +b0.value = (b0.value + 1); +if ((b0.value === 25)) { return "stopped!"; } } @@ -42,7 +42,7 @@ return function* genXYZ () { b0.value = 0; while (true) { b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 18)) { +if ((b0.value === 18)) { retire(); return; } yield; diff --git a/test/snapshot/__snapshots__/tw-procedure-return-warp.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-warp.sb3.tw-snapshot index fde5f016..cc9735df 100644 --- a/test/snapshot/__snapshots__/tw-procedure-return-warp.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-return-warp.sb3.tw-snapshot @@ -32,7 +32,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "N", nu retire(); return; }; }) -// Sprite1 Znon warp +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; const b1 = runtime.getOpcodeFunction("looks_say"); @@ -73,7 +73,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp 2",}, b1, false, fa return ""; }; }) -// Sprite1 Wverify runs warp %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; const b1 = runtime.getOpcodeFunction("looks_say"); @@ -95,7 +95,7 @@ return ("warp: " + ("" + p0)); return ""; }; }) -// Sprite1 Zverify runs non warp %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; const b1 = runtime.getOpcodeFunction("looks_say"); diff --git a/test/snapshot/__snapshots__/tw-safe-procedure-argument-casting.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-safe-procedure-argument-casting.sb3.tw-snapshot index 88f7ab0b..033cebf8 100644 --- a/test/snapshot/__snapshots__/tw-safe-procedure-argument-casting.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-safe-procedure-argument-casting.sb3.tw-snapshot @@ -18,7 +18,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "$R-1lb retire(); return; }; }) -// Sprite1 Zswitch %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_switch_ (p0) { runtime.ext_scratch3_looks._setCostume(target, p0); diff --git a/test/snapshot/__snapshots__/tw-sensing-of.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-sensing-of.sb3.tw-snapshot index ee917806..a370af3e 100644 --- a/test/snapshot/__snapshots__/tw-sensing-of.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-sensing-of.sb3.tw-snapshot @@ -36,7 +36,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass direction",}, b0, false, fal if (((b2 ? b2.currentCostume + 1 : 0) === 3)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass costume #",}, b0, false, false, "+zO[+f?c7F`ZGTbD.oqI", null); } -if ((("" + (b2 ? b2.getCostumes()[b2.currentCostume].name : 0)).toLowerCase() === "Costume name test".toLowerCase())) { +if (((b2 ? b2.getCostumes()[b2.currentCostume].name : 0).toLowerCase() === "Costume name test".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass costume name",}, b0, false, false, "Y6L|T0Pvwsct2gq+HGvv", null); } if (((b2 ? b2.size : 0) === 76.01)) { @@ -52,7 +52,7 @@ if (compareEqual((b4 ? b4.value : 0), 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non existent variable",}, b0, false, false, ")nnN?*l+E)dC(fT5(_@q", null); } b5.value = (("" + randomInt(1, 9)) + ("" + randomInt(1, 9))); -if (compareEqual(runtime.ext_scratch3_sensing.getAttributeOf({OBJECT: b5.value, PROPERTY: "backdrop #" }), 0)) { +if (compareEqual(runtime.ext_scratch3_sensing.getAttributeOf({OBJECT: ("" + b5.value), PROPERTY: "backdrop #" }), 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass NE backdrop #",}, b0, false, false, "UFr{fbR3@a.u_paq:r]F", null); } if (compareEqual(runtime.ext_scratch3_sensing.getAttributeOf({OBJECT: ("" + b5.value), PROPERTY: "backdrop name" }), 0)) { diff --git a/test/snapshot/__snapshots__/tw-tab-equals-zero.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-tab-equals-zero.sb3.tw-snapshot index 0169ba16..119556a4 100644 --- a/test/snapshot/__snapshots__/tw-tab-equals-zero.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-tab-equals-zero.sb3.tw-snapshot @@ -29,7 +29,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass \\t and other spaces = strin if (compareEqual(b0.value, (0 + 0))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass \\t in a variable = number 0",}, b1, false, false, "z", null); } -if (compareEqual("\t", (0 + 0))) { +if ((0 === (0 + 0))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass literal \\t = number 0",}, b1, false, false, "B", null); } if (compareEqual((" " + ("" + b0.value)), (0 + 0))) { diff --git a/test/snapshot/__snapshots__/tw-tangent.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-tangent.sb3.tw-snapshot index b2ccf9ce..17992397 100644 --- a/test/snapshot/__snapshots__/tw-tangent.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-tangent.sb3.tw-snapshot @@ -6,31 +6,31 @@ const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 15",}, b0, false, false, "p", null); -if (compareEqual(tan(0), 0)) { +if (((tan(0) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 0",}, b0, false, false, "O", null); } if (((tan(90) || 0) === Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 90",}, b0, false, false, "G", null); } -if (compareEqual(tan(180), 0)) { +if (((tan(180) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 180",}, b0, false, false, "I", null); } if (((tan(270) || 0) === -Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 270",}, b0, false, false, "K", null); } -if (compareEqual(tan(360), 0)) { +if (((tan(360) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 360",}, b0, false, false, "M", null); } if (((tan(450) || 0) === Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 450",}, b0, false, false, "Q", null); } -if (compareEqual(tan(540), 0)) { +if (((tan(540) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 540",}, b0, false, false, "S", null); } if (((tan(630) || 0) === -Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 630",}, b0, false, false, "U", null); } -if (compareEqual(tan(720), 0)) { +if (((tan(720) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 720",}, b0, false, false, "W", null); } if (((tan(810) || 0) === Infinity)) { @@ -39,13 +39,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass 810",}, b0, false, false, "Y if (((tan(-90) || 0) === -Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass -90",}, b0, false, false, "0", null); } -if (compareEqual(tan(-180), 0)) { +if (((tan(-180) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass -180",}, b0, false, false, "2", null); } if (((tan(-270) || 0) === Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass -270",}, b0, false, false, "4", null); } -if (compareEqual(tan(-360), 0)) { +if (((tan(-360) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass -360",}, b0, false, false, "6", null); } if (((tan(-450) || 0) === -Infinity)) { diff --git a/test/snapshot/__snapshots__/tw-unsafe-equals.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-unsafe-equals.sb3.tw-snapshot index 082b46ce..90a01cba 100644 --- a/test/snapshot/__snapshots__/tw-unsafe-equals.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-unsafe-equals.sb3.tw-snapshot @@ -15,23 +15,23 @@ if ((10 === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 2",}, b0, false, false, "@A5}x{mm-Gk?CVz3o0Yn", null); } b1.value = 10; -if (((+b1.value || 0) === 10)) { +if ((b1.value === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 3",}, b0, false, false, "Ul:BCck-}Fvdux~x#$${", null); } -if (compareEqual(b1.value, "010")) { +if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 4",}, b0, false, false, "8]2$7P)o#+#Lo]mFSBbx", null); } -if (compareEqual(b1.value, "0000000010")) { +if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 5",}, b0, false, false, "ZU^{OfKTg|+Au$$q0[]u", null); } for (var a0 = 1; a0 >= 0.5; a0--) { if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 6",}, b0, false, false, "HB+_IN}6=K[*ksxKXH0`", null); } -if (compareEqual(b1.value, "010")) { +if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 7",}, b0, false, false, ";73ODiwcp8IthYURTX;S", null); } -if (compareEqual(b1.value, "0000000010")) { +if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 8",}, b0, false, true, "${[MFmBL-D*1rbas9Q89", null); if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;} } @@ -41,20 +41,20 @@ b2.value = "010"; if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 9",}, b0, false, false, "#.`@SBj!g-c0:_q/tMZo", null); } -if (compareEqual(b2.value, "010")) { +if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 10",}, b0, false, false, "B`o?V6/q6g),/2w};a#y", null); } -if (compareEqual(b2.value, "0000000010")) { +if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 11",}, b0, false, false, "TJ:#TkYBys*!RYiKLXb)", null); } for (var a1 = 1; a1 >= 0.5; a1--) { if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 12",}, b0, false, false, ",Z,~10Qo~j;(+VL+I3q:", null); } -if (compareEqual(b2.value, "010")) { +if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 13",}, b0, false, false, "|Mqx([(26M%#ggW9)U0s", null); } -if (compareEqual(b2.value, "0000000010")) { +if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 14",}, b0, false, true, "YvtiKF231lU8p5Qd97RP", null); if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;} } @@ -69,16 +69,16 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "XzQt! if ((0 === 1)) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "rxZqw7cv8g;PDM4B%{`?", null); } -if (compareEqual(" ", 0)) { +if ((" ".toLowerCase() === "0".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "3G|)eVw1mQm;O~cRy`}0", null); } -if (compareEqual(0, " ")) { +if (("0".toLowerCase() === " ".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "sd5xXX*tsW/~A_Q!0;^w", null); } -if (compareEqual("", 0)) { +if (("".toLowerCase() === "0".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "7**baE=].WD9OoY1+IEu", null); } -if (compareEqual(0, "")) { +if (("0".toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "7!IB!=o/2H.Jqj-8Vwhz", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "df{tf!WhfRwXgQ?SN_dj", null); diff --git a/test/snapshot/__snapshots__/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot index bd4bdca8..d986e3e8 100644 --- a/test/snapshot/__snapshots__/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot @@ -11,14 +11,14 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "s", nu retire(); return; }; }) -// Sprite1 Wrun without screen refresh +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["F?*}X,`9XBpN_[piGRrz"]; const b1 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ_run_without_screen_r () { -b0.value = (((daysSince2000() * 86400) || 0) + 3); +b0.value = ((daysSince2000() * 86400) + 3); runtime.ioDevices.clock.resetProjectTimer(); -while (!((runtime.ioDevices.clock.projectTimer() > 0.1) || compareGreaterThan((daysSince2000() * 86400), b0.value))) { +while (!((runtime.ioDevices.clock.projectTimer() > 0.1) || ((daysSince2000() * 86400) > b0.value))) { if (isStuck()) yield; } if (compareLessThan((daysSince2000() * 86400), b0.value)) { diff --git a/test/snapshot/__snapshots__/tw-zombie-cube-escape-284516654.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-zombie-cube-escape-284516654.sb3.tw-snapshot index 2faaed41..826f1e00 100644 --- a/test/snapshot/__snapshots__/tw-zombie-cube-escape-284516654.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-zombie-cube-escape-284516654.sb3.tw-snapshot @@ -10,7 +10,7 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, ",a.euo_AgQTxR+D^x0M0", null); b1.value = 0; b2.value = ""; -while (!compareGreaterThan(b2.value, "")) { +while (!(b2.value.toLowerCase() > "".toLowerCase())) { startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "step" }); yield; } @@ -25,7 +25,7 @@ const b0 = stage.variables["7qur6!bGgvC9I(Nd5.HP"]; const b1 = stage.variables["sUOp@-6J4y0PqwiXit4!"]; return function* genXYZ () { b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 5)) { +if ((b0.value === 5)) { b1.value = "end"; } retire(); return; diff --git a/test/snapshot/__snapshots__/warp-timer/order-library-reverse.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/order-library-reverse.sb3.tw-snapshot index 91457816..96c7ced7 100644 --- a/test/snapshot/__snapshots__/warp-timer/order-library-reverse.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/order-library-reverse.sb3.tw-snapshot @@ -17,7 +17,7 @@ yield; } thread.timer = null; b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 1)) { +if ((b0.value === 1)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (1)",}, b2, false, false, "]4hbk*5ix]V00h|!x1oy", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 1 != " + ("" + b0.value)),}, b2, false, false, "H=x@7SpNJeX|!}8x5y4,", null); @@ -75,7 +75,7 @@ yield; } thread.timer = null; b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 2)) { +if ((b0.value === 2)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (2)",}, b2, false, false, "0i[-T:vYTt=bi47@byUE", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 2 != " + ("" + b0.value)),}, b2, false, false, "Coc1aZ;L9M-RyEt`syps", null); diff --git a/test/snapshot/__snapshots__/warp-timer/order-library.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/order-library.sb3.tw-snapshot index 2d10b51e..5145078f 100644 --- a/test/snapshot/__snapshots__/warp-timer/order-library.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/order-library.sb3.tw-snapshot @@ -51,7 +51,7 @@ yield; } thread.timer = null; b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 1)) { +if ((b0.value === 1)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (1)",}, b2, false, false, "RSQ{nVCc)6E)(`KlnFCF", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 1 != " + ("" + b0.value)),}, b2, false, false, "8k^j~`c^|YO@hkFd?~2d", null); @@ -75,7 +75,7 @@ yield; } thread.timer = null; b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 2)) { +if ((b0.value === 2)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (2)",}, b2, false, false, "KP?op(=Vg2#;@]!,C#.~", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 2 != " + ("" + b0.value)),}, b2, false, false, "=]|}L~4uQXTNtwJKw_;R", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-NaN.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-NaN.sb3.tw-snapshot index 106f6edd..417094fe 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-NaN.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-NaN.sb3.tw-snapshot @@ -9,61 +9,61 @@ yield* executeInCompatibilityLayer({"MESSAGE":"plan 20",}, b0, false, false, "v" if ((("" + (0 * Infinity)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "6", null); } -if (compareEqual((((0 * Infinity) || 0) * 1), 0)) { +if (((((0 * Infinity) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "#", null); } if ((("" + ((Math.acos(1.01) * 180) / Math.PI)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "9", null); } -if (compareEqual(((((Math.acos(1.01) * 180) / Math.PI) || 0) * 1), 0)) { +if ((((((Math.acos(1.01) * 180) / Math.PI) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "(", null); } if ((("" + ((Math.asin(1.01) * 180) / Math.PI)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "*", null); } -if (compareEqual(((((Math.asin(1.01) * 180) / Math.PI) || 0) * 1), 0)) { +if ((((((Math.asin(1.01) * 180) / Math.PI) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, ",", null); } if ((("" + (0 / 0)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, ".", null); } -if (compareEqual((((0 / 0) || 0) * 1), 0)) { +if (((((0 / 0) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, ":", null); } if ((("" + Math.sqrt(-1)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "?", null); } -if (compareEqual(((Math.sqrt(-1) || 0) * 1), 0)) { +if ((((Math.sqrt(-1) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "@", null); } if ((("" + mod(0, 0)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "]", null); } -if (compareEqual(((mod(0, 0) || 0) * 1), 0)) { +if ((((mod(0, 0) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "_", null); } if ((("" + Math.log(-1)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "{", null); } -if (compareEqual(((Math.log(-1) || 0) * 1), 0)) { +if ((((Math.log(-1) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "}", null); } if ((("" + (Math.log(-1) / Math.LN10)).toLowerCase() === "NaN".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "aa", null); } -if (compareEqual((((Math.log(-1) / Math.LN10) || 0) * 1), 0)) { +if (((((Math.log(-1) / Math.LN10) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ac", null); } -if (compareEqual((((Math.round(Math.sin((Math.PI * ((1 / 0) || 0)) / 180) * 1e10) / 1e10) || 0) * 1), 0)) { +if (((((Math.round(Math.sin((Math.PI * ((1 / 0) || 0)) / 180) * 1e10) / 1e10) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ae", null); } -if (compareEqual((((Math.round(Math.cos((Math.PI * ((1 / 0) || 0)) / 180) * 1e10) / 1e10) || 0) * 1), 0)) { +if (((((Math.round(Math.cos((Math.PI * ((1 / 0) || 0)) / 180) * 1e10) / 1e10) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ag", null); } -if (compareEqual(((tan(((1 / 0) || 0)) || 0) * 1), 0)) { +if ((((tan(((1 / 0) || 0)) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ai", null); } -if (compareEqual(((runtime.ext_scratch3_operators._random((-1 / 0), (1 / 0)) || 0) * 1), 0)) { +if ((((runtime.ext_scratch3_operators._random((-1 / 0), (1 / 0)) || 0) * 1) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "ak", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "7", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot index c133a6cb..b7f47584 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot @@ -11,7 +11,7 @@ target.setSize(96); b1.value = 0; while (!(100 === Math.round(target.size))) { b1.value = ((+b1.value || 0) + 1); -target.setSize(target.size + ((((100 - Math.round(target.size)) || 0) / 10) || 0)); +target.setSize(target.size + ((100 - Math.round(target.size)) / 10)); yield; } if (((+b1.value || 0) === 20)) { diff --git a/test/snapshot/__snapshots__/warp-timer/tw-color-input-returns-hex.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-color-input-returns-hex.sb3.tw-snapshot index bccad8ff..070b355d 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-color-input-returns-hex.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-color-input-returns-hex.sb3.tw-snapshot @@ -8,7 +8,7 @@ const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "c", null); b1.value = "#22388a"; -if ((("" + b1.value).toLowerCase() === "#22388a".toLowerCase())) { +if ((b1.value.toLowerCase() === "#22388a".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "f", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "e", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-inline.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-inline.sb3.tw-snapshot index 66d03cf7..741ccca8 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-inline.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-inline.sb3.tw-snapshot @@ -123,13 +123,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 38: 0 should be = 🎉",}, b if (!(("" + ("0".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 39: 0 should be > 🎉",}, b0, false, false, "uq", null); } -if (!(("" + compareLessThan(0, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 40: 0 should be < ",}, b0, false, false, "us", null); } -if (!(("" + compareEqual(0, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0".toLowerCase() === "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 41: 0 should be = ",}, b0, false, false, "uu", null); } -if (!(("" + compareGreaterThan(0, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("0".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 42: 0 should be > ",}, b0, false, false, "uw", null); } if (!(("" + (0 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -249,13 +249,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 80: 0.0 should be = 🎉",}, if (!(("" + ("0.0".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 81: 0.0 should be > 🎉",}, b0, false, false, "vn", null); } -if (!(("" + compareLessThan("0.0", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0.0".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 82: 0.0 should be < ",}, b0, false, false, "vp", null); } -if (!(("" + compareEqual("0.0", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0.0".toLowerCase() === "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 83: 0.0 should be = ",}, b0, false, false, "vr", null); } -if (!(("" + compareGreaterThan("0.0", "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("0.0".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 84: 0.0 should be > ",}, b0, false, false, "vt", null); } if (!(("" + (1.23 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -324,7 +324,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 105: 1.23 should be > -1",}, if (!(("" + ("1.23".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 106: 1.23 should be < true",}, b0, false, false, "v#", null); } -if (!(("" + ("1.23".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 107: 1.23 should be = true",}, b0, false, false, "v(", null); } if (!(("" + ("1.23".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -333,7 +333,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 108: 1.23 should be > true", if (!(("" + ("1.23".toLowerCase() < "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 109: 1.23 should be < false",}, b0, false, false, "v,", null); } -if (!(("" + ("1.23".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 110: 1.23 should be = false",}, b0, false, false, "v.", null); } if (!(("" + ("1.23".toLowerCase() > "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -342,7 +342,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 111: 1.23 should be > false" if (!(("" + ("1.23".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 112: 1.23 should be < NaN",}, b0, false, false, "v=", null); } -if (!(("" + ("1.23".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 113: 1.23 should be = NaN",}, b0, false, false, "v@", null); } if (!(("" + ("1.23".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -360,7 +360,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 117: 1.23 should be > Infini if (!(("" + ("1.23".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 118: 1.23 should be < banana",}, b0, false, false, "wa", null); } -if (!(("" + ("1.23".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 119: 1.23 should be = banana",}, b0, false, false, "wc", null); } if (!(("" + ("1.23".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -369,19 +369,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 120: 1.23 should be > banana if (!(("" + ("1.23".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 121: 1.23 should be < 🎉",}, b0, false, false, "wg", null); } -if (!(("" + ("1.23".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 122: 1.23 should be = 🎉",}, b0, false, false, "wi", null); } if (!(("" + ("1.23".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 123: 1.23 should be > 🎉",}, b0, false, false, "wk", null); } -if (!(("" + compareLessThan(1.23, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("1.23".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 124: 1.23 should be < ",}, b0, false, false, "wm", null); } if (!(("" + (1.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 125: 1.23 should be = ",}, b0, false, false, "wo", null); } -if (!(("" + compareGreaterThan(1.23, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("1.23".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 126: 1.23 should be > ",}, b0, false, false, "wq", null); } if (!(("" + (0.23 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -450,7 +450,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 147: .23 should be > -1",}, if (!(("" + (".23".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 148: .23 should be < true",}, b0, false, false, "w8", null); } -if (!(("" + (".23".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 149: .23 should be = true",}, b0, false, false, "w!", null); } if (!(("" + (".23".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -459,7 +459,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 150: .23 should be > true",} if (!(("" + (".23".toLowerCase() < "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 151: .23 should be < false",}, b0, false, false, "w)", null); } -if (!(("" + (".23".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 152: .23 should be = false",}, b0, false, false, "w+", null); } if (!(("" + (".23".toLowerCase() > "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -468,7 +468,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 153: .23 should be > false", if (!(("" + (".23".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 154: .23 should be < NaN",}, b0, false, false, "w/", null); } -if (!(("" + (".23".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 155: .23 should be = NaN",}, b0, false, false, "w;", null); } if (!(("" + (".23".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -486,7 +486,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 159: .23 should be > Infinit if (!(("" + (".23".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 160: .23 should be < banana",}, b0, false, false, "w|", null); } -if (!(("" + (".23".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 161: .23 should be = banana",}, b0, false, false, "w~", null); } if (!(("" + (".23".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -495,19 +495,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 162: .23 should be > banana" if (!(("" + (".23".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 163: .23 should be < 🎉",}, b0, false, false, "xd", null); } -if (!(("" + (".23".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 164: .23 should be = 🎉",}, b0, false, false, "xf", null); } if (!(("" + (".23".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 165: .23 should be > 🎉",}, b0, false, false, "xh", null); } -if (!(("" + compareLessThan(".23", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (".23".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 166: .23 should be < ",}, b0, false, false, "xj", null); } -if (!(("" + compareEqual(".23", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.23 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 167: .23 should be = ",}, b0, false, false, "xl", null); } -if (!(("" + compareGreaterThan(".23", "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + (".23".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 168: .23 should be > ",}, b0, false, false, "xn", null); } if (!(("" + (0.123 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -576,7 +576,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 189: 0.123 should be > -1",} if (!(("" + ("0.123".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 190: 0.123 should be < true",}, b0, false, false, "x5", null); } -if (!(("" + ("0.123".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 191: 0.123 should be = true",}, b0, false, false, "x7", null); } if (!(("" + ("0.123".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -585,7 +585,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 192: 0.123 should be > true" if (!(("" + ("0.123".toLowerCase() < "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 193: 0.123 should be < false",}, b0, false, false, "x#", null); } -if (!(("" + ("0.123".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 194: 0.123 should be = false",}, b0, false, false, "x(", null); } if (!(("" + ("0.123".toLowerCase() > "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -594,7 +594,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 195: 0.123 should be > false if (!(("" + ("0.123".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 196: 0.123 should be < NaN",}, b0, false, false, "x,", null); } -if (!(("" + ("0.123".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 197: 0.123 should be = NaN",}, b0, false, false, "x.", null); } if (!(("" + ("0.123".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -612,7 +612,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 201: 0.123 should be > Infin if (!(("" + ("0.123".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 202: 0.123 should be < banana",}, b0, false, false, "x_", null); } -if (!(("" + ("0.123".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 203: 0.123 should be = banana",}, b0, false, false, "x{", null); } if (!(("" + ("0.123".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -621,19 +621,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 204: 0.123 should be > banan if (!(("" + ("0.123".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 205: 0.123 should be < 🎉",}, b0, false, false, "ya", null); } -if (!(("" + ("0.123".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 206: 0.123 should be = 🎉",}, b0, false, false, "yc", null); } if (!(("" + ("0.123".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 207: 0.123 should be > 🎉",}, b0, false, false, "ye", null); } -if (!(("" + compareLessThan(0.123, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("0.123".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 208: 0.123 should be < ",}, b0, false, false, "yg", null); } if (!(("" + (0.123 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 209: 0.123 should be = ",}, b0, false, false, "yi", null); } -if (!(("" + compareGreaterThan(0.123, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("0.123".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 210: 0.123 should be > ",}, b0, false, false, "yk", null); } if (!(("" + (-0 < 0)).toLowerCase() === "false".toLowerCase())) { @@ -753,13 +753,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 248: -0 should be = 🎉",}, if (!(("" + ("-0".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 249: -0 should be > 🎉",}, b0, false, false, "zb", null); } -if (!(("" + compareLessThan("-0", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("-0".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 250: -0 should be < ",}, b0, false, false, "zd", null); } -if (!(("" + compareEqual("-0", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("-0".toLowerCase() === "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 251: -0 should be = ",}, b0, false, false, "zf", null); } -if (!(("" + compareGreaterThan("-0", "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("-0".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 252: -0 should be > ",}, b0, false, false, "zh", null); } if (!(("" + (-1 < 0)).toLowerCase() === "true".toLowerCase())) { @@ -828,7 +828,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 273: -1 should be > -1",}, b if (!(("" + ("-1".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 274: -1 should be < true",}, b0, false, false, "zZ", null); } -if (!(("" + ("-1".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 275: -1 should be = true",}, b0, false, false, "z1", null); } if (!(("" + ("-1".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -837,7 +837,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 276: -1 should be > true",}, if (!(("" + ("-1".toLowerCase() < "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 277: -1 should be < false",}, b0, false, false, "z5", null); } -if (!(("" + ("-1".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 278: -1 should be = false",}, b0, false, false, "z7", null); } if (!(("" + ("-1".toLowerCase() > "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -846,7 +846,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 279: -1 should be > false",} if (!(("" + ("-1".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 280: -1 should be < NaN",}, b0, false, false, "z#", null); } -if (!(("" + ("-1".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 281: -1 should be = NaN",}, b0, false, false, "z(", null); } if (!(("" + ("-1".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -864,7 +864,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 285: -1 should be > Infinity if (!(("" + ("-1".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 286: -1 should be < banana",}, b0, false, false, "z=", null); } -if (!(("" + ("-1".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 287: -1 should be = banana",}, b0, false, false, "z@", null); } if (!(("" + ("-1".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -873,19 +873,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 288: -1 should be > banana", if (!(("" + ("-1".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 289: -1 should be < 🎉",}, b0, false, false, "z_", null); } -if (!(("" + ("-1".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 290: -1 should be = 🎉",}, b0, false, false, "z{", null); } if (!(("" + ("-1".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 291: -1 should be > 🎉",}, b0, false, false, "z}", null); } -if (!(("" + compareLessThan(-1, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("-1".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 292: -1 should be < ",}, b0, false, false, "Aa", null); } if (!(("" + (-1 === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 293: -1 should be = ",}, b0, false, false, "Ac", null); } -if (!(("" + compareGreaterThan(-1, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("-1".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 294: -1 should be > ",}, b0, false, false, "Ae", null); } if (!(("" + ("true".toLowerCase() < "0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -909,7 +909,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 300: true should be > 0.0",} if (!(("" + ("true".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 301: true should be < 1.23",}, b0, false, false, "As", null); } -if (!(("" + ("true".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 302: true should be = 1.23",}, b0, false, false, "Au", null); } if (!(("" + ("true".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -918,7 +918,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 303: true should be > 1.23", if (!(("" + ("true".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 304: true should be < .23",}, b0, false, false, "Ay", null); } -if (!(("" + ("true".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 305: true should be = .23",}, b0, false, false, "AA", null); } if (!(("" + ("true".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -927,7 +927,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 306: true should be > .23",} if (!(("" + ("true".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 307: true should be < 0.123",}, b0, false, false, "AE", null); } -if (!(("" + ("true".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 308: true should be = 0.123",}, b0, false, false, "AG", null); } if (!(("" + ("true".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -945,7 +945,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 312: true should be > -0",}, if (!(("" + ("true".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 313: true should be < -1",}, b0, false, false, "AQ", null); } -if (!(("" + ("true".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 314: true should be = -1",}, b0, false, false, "AS", null); } if (!(("" + ("true".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -981,7 +981,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 324: true should be > NaN",} if (!(("" + ("true".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 325: true should be < Infinity",}, b0, false, false, "A)", null); } -if (!(("" + ("true".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (1 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 326: true should be = Infinity",}, b0, false, false, "A+", null); } if (!(("" + ("true".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1035,7 +1035,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 342: false should be > 0.0", if (!(("" + ("false".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 343: false should be < 1.23",}, b0, false, false, "Bp", null); } -if (!(("" + ("false".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 344: false should be = 1.23",}, b0, false, false, "Br", null); } if (!(("" + ("false".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1044,7 +1044,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 345: false should be > 1.23" if (!(("" + ("false".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 346: false should be < .23",}, b0, false, false, "Bv", null); } -if (!(("" + ("false".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 347: false should be = .23",}, b0, false, false, "Bx", null); } if (!(("" + ("false".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1053,7 +1053,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 348: false should be > .23", if (!(("" + ("false".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 349: false should be < 0.123",}, b0, false, false, "BB", null); } -if (!(("" + ("false".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 350: false should be = 0.123",}, b0, false, false, "BD", null); } if (!(("" + ("false".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1071,7 +1071,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 354: false should be > -0",} if (!(("" + ("false".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 355: false should be < -1",}, b0, false, false, "BN", null); } -if (!(("" + ("false".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 356: false should be = -1",}, b0, false, false, "BP", null); } if (!(("" + ("false".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1107,7 +1107,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 366: false should be > NaN", if (!(("" + ("false".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 367: false should be < Infinity",}, b0, false, false, "B#", null); } -if (!(("" + ("false".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 368: false should be = Infinity",}, b0, false, false, "B(", null); } if (!(("" + ("false".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1161,7 +1161,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 384: NaN should be > 0.0",}, if (!(("" + ("NaN".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 385: NaN should be < 1.23",}, b0, false, false, "Cm", null); } -if (!(("" + ("NaN".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 386: NaN should be = 1.23",}, b0, false, false, "Co", null); } if (!(("" + ("NaN".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1170,7 +1170,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 387: NaN should be > 1.23",} if (!(("" + ("NaN".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 388: NaN should be < .23",}, b0, false, false, "Cs", null); } -if (!(("" + ("NaN".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 389: NaN should be = .23",}, b0, false, false, "Cu", null); } if (!(("" + ("NaN".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1179,7 +1179,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 390: NaN should be > .23",}, if (!(("" + ("NaN".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 391: NaN should be < 0.123",}, b0, false, false, "Cy", null); } -if (!(("" + ("NaN".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 392: NaN should be = 0.123",}, b0, false, false, "CA", null); } if (!(("" + ("NaN".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1197,7 +1197,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 396: NaN should be > -0",}, if (!(("" + ("NaN".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 397: NaN should be < -1",}, b0, false, false, "CK", null); } -if (!(("" + ("NaN".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 398: NaN should be = -1",}, b0, false, false, "CM", null); } if (!(("" + ("NaN".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1233,7 +1233,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 408: NaN should be > NaN",}, if (!(("" + ("NaN".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 409: NaN should be < Infinity",}, b0, false, false, "C8", null); } -if (!(("" + ("NaN".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 410: NaN should be = Infinity",}, b0, false, false, "C!", null); } if (!(("" + ("NaN".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1332,7 +1332,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 441: Infinity should be > -1 if (!(("" + ("Infinity".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 442: Infinity should be < true",}, b0, false, false, "DN", null); } -if (!(("" + ("Infinity".toLowerCase() === "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 443: Infinity should be = true",}, b0, false, false, "DP", null); } if (!(("" + ("Infinity".toLowerCase() > "true".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1341,7 +1341,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 444: Infinity should be > tr if (!(("" + ("Infinity".toLowerCase() < "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 445: Infinity should be < false",}, b0, false, false, "DT", null); } -if (!(("" + ("Infinity".toLowerCase() === "false".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 446: Infinity should be = false",}, b0, false, false, "DV", null); } if (!(("" + ("Infinity".toLowerCase() > "false".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1350,7 +1350,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 447: Infinity should be > fa if (!(("" + ("Infinity".toLowerCase() < "NaN".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 448: Infinity should be < NaN",}, b0, false, false, "DZ", null); } -if (!(("" + ("Infinity".toLowerCase() === "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 449: Infinity should be = NaN",}, b0, false, false, "D1", null); } if (!(("" + ("Infinity".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1368,7 +1368,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 453: Infinity should be > In if (!(("" + ("Infinity".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 454: Infinity should be < banana",}, b0, false, false, "D#", null); } -if (!(("" + ("Infinity".toLowerCase() === "banana".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 455: Infinity should be = banana",}, b0, false, false, "D(", null); } if (!(("" + ("Infinity".toLowerCase() > "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1377,19 +1377,19 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 456: Infinity should be > ba if (!(("" + ("Infinity".toLowerCase() < "🎉".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 457: Infinity should be < 🎉",}, b0, false, false, "D,", null); } -if (!(("" + ("Infinity".toLowerCase() === "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 458: Infinity should be = 🎉",}, b0, false, false, "D.", null); } if (!(("" + ("Infinity".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 459: Infinity should be > 🎉",}, b0, false, false, "D:", null); } -if (!(("" + compareLessThan(Infinity, "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("Infinity".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 460: Infinity should be < ",}, b0, false, false, "D=", null); } if (!(("" + (Infinity === 0)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 461: Infinity should be = ",}, b0, false, false, "D@", null); } -if (!(("" + compareGreaterThan(Infinity, "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("Infinity".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 462: Infinity should be > ",}, b0, false, false, "D]", null); } if (!(("" + ("banana".toLowerCase() < "0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1413,7 +1413,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 468: banana should be > 0.0" if (!(("" + ("banana".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 469: banana should be < 1.23",}, b0, false, false, "Eg", null); } -if (!(("" + ("banana".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 470: banana should be = 1.23",}, b0, false, false, "Ei", null); } if (!(("" + ("banana".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1422,7 +1422,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 471: banana should be > 1.23 if (!(("" + ("banana".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 472: banana should be < .23",}, b0, false, false, "Em", null); } -if (!(("" + ("banana".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 473: banana should be = .23",}, b0, false, false, "Eo", null); } if (!(("" + ("banana".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1431,7 +1431,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 474: banana should be > .23" if (!(("" + ("banana".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 475: banana should be < 0.123",}, b0, false, false, "Es", null); } -if (!(("" + ("banana".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 476: banana should be = 0.123",}, b0, false, false, "Eu", null); } if (!(("" + ("banana".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1449,7 +1449,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 480: banana should be > -0", if (!(("" + ("banana".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 481: banana should be < -1",}, b0, false, false, "EE", null); } -if (!(("" + ("banana".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 482: banana should be = -1",}, b0, false, false, "EG", null); } if (!(("" + ("banana".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1485,7 +1485,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 492: banana should be > NaN" if (!(("" + ("banana".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 493: banana should be < Infinity",}, b0, false, false, "E2", null); } -if (!(("" + ("banana".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 494: banana should be = Infinity",}, b0, false, false, "E4", null); } if (!(("" + ("banana".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { @@ -1539,7 +1539,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 510: 🎉 should be > 0.0",} if (!(("" + ("🎉".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 511: 🎉 should be < 1.23",}, b0, false, false, "Fd", null); } -if (!(("" + ("🎉".toLowerCase() === "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 512: 🎉 should be = 1.23",}, b0, false, false, "Ff", null); } if (!(("" + ("🎉".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1548,7 +1548,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 513: 🎉 should be > 1.23", if (!(("" + ("🎉".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 514: 🎉 should be < .23",}, b0, false, false, "Fj", null); } -if (!(("" + ("🎉".toLowerCase() === ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 515: 🎉 should be = .23",}, b0, false, false, "Fl", null); } if (!(("" + ("🎉".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1557,7 +1557,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 516: 🎉 should be > .23",} if (!(("" + ("🎉".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 517: 🎉 should be < 0.123",}, b0, false, false, "Fp", null); } -if (!(("" + ("🎉".toLowerCase() === "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 518: 🎉 should be = 0.123",}, b0, false, false, "Fr", null); } if (!(("" + ("🎉".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1575,7 +1575,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 522: 🎉 should be > -0",}, if (!(("" + ("🎉".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 523: 🎉 should be < -1",}, b0, false, false, "FB", null); } -if (!(("" + ("🎉".toLowerCase() === "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 524: 🎉 should be = -1",}, b0, false, false, "FD", null); } if (!(("" + ("🎉".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1611,7 +1611,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 534: 🎉 should be > NaN",} if (!(("" + ("🎉".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 535: 🎉 should be < Infinity",}, b0, false, false, "FZ", null); } -if (!(("" + ("🎉".toLowerCase() === "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 536: 🎉 should be = Infinity",}, b0, false, false, "F1", null); } if (!(("" + ("🎉".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1644,67 +1644,67 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 545: 🎉 should be = ",}, b if (!(("" + ("🎉".toLowerCase() > "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 546: 🎉 should be > ",}, b0, false, false, "F:", null); } -if (!(("" + compareLessThan("", 0)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "0".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 547: should be < 0",}, b0, false, false, "F=", null); } -if (!(("" + compareEqual("", 0)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() === "0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 548: should be = 0",}, b0, false, false, "F@", null); } -if (!(("" + compareGreaterThan("", 0)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 549: should be > 0",}, b0, false, false, "F]", null); } -if (!(("" + compareLessThan("", "0.0")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "0.0".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 550: should be < 0.0",}, b0, false, false, "F_", null); } -if (!(("" + compareEqual("", "0.0")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() === "0.0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 551: should be = 0.0",}, b0, false, false, "F{", null); } -if (!(("" + compareGreaterThan("", "0.0")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "0.0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 552: should be > 0.0",}, b0, false, false, "F}", null); } -if (!(("" + compareLessThan("", 1.23)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "1.23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 553: should be < 1.23",}, b0, false, false, "Ga", null); } if (!(("" + (0 === 1.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 554: should be = 1.23",}, b0, false, false, "Gc", null); } -if (!(("" + compareGreaterThan("", 1.23)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "1.23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 555: should be > 1.23",}, b0, false, false, "Ge", null); } -if (!(("" + compareLessThan("", ".23")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < ".23".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 556: should be < .23",}, b0, false, false, "Gg", null); } -if (!(("" + compareEqual("", ".23")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + (0 === 0.23)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 557: should be = .23",}, b0, false, false, "Gi", null); } -if (!(("" + compareGreaterThan("", ".23")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > ".23".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 558: should be > .23",}, b0, false, false, "Gk", null); } -if (!(("" + compareLessThan("", 0.123)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "0.123".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 559: should be < 0.123",}, b0, false, false, "Gm", null); } if (!(("" + (0 === 0.123)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 560: should be = 0.123",}, b0, false, false, "Go", null); } -if (!(("" + compareGreaterThan("", 0.123)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "0.123".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 561: should be > 0.123",}, b0, false, false, "Gq", null); } -if (!(("" + compareLessThan("", "-0")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "-0".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 562: should be < -0",}, b0, false, false, "Gs", null); } -if (!(("" + compareEqual("", "-0")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() === "-0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 563: should be = -0",}, b0, false, false, "Gu", null); } -if (!(("" + compareGreaterThan("", "-0")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "-0".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 564: should be > -0",}, b0, false, false, "Gw", null); } -if (!(("" + compareLessThan("", -1)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "-1".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 565: should be < -1",}, b0, false, false, "Gy", null); } if (!(("" + (0 === -1)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 566: should be = -1",}, b0, false, false, "GA", null); } -if (!(("" + compareGreaterThan("", -1)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "-1".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 567: should be > -1",}, b0, false, false, "GC", null); } if (!(("" + ("".toLowerCase() < "true".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1734,13 +1734,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 575: should be = NaN",}, b0 if (!(("" + ("".toLowerCase() > "NaN".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 576: should be > NaN",}, b0, false, false, "GU", null); } -if (!(("" + compareLessThan("", Infinity)).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "Infinity".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 577: should be < Infinity",}, b0, false, false, "GW", null); } if (!(("" + (0 === Infinity)).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 578: should be = Infinity",}, b0, false, false, "GY", null); } -if (!(("" + compareGreaterThan("", Infinity)).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "Infinity".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 579: should be > Infinity",}, b0, false, false, "G0", null); } if (!(("" + ("".toLowerCase() < "banana".toLowerCase())).toLowerCase() === "true".toLowerCase())) { @@ -1761,13 +1761,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail 584: should be = 🎉",}, b if (!(("" + ("".toLowerCase() > "🎉".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 585: should be > 🎉",}, b0, false, false, "G%", null); } -if (!(("" + compareLessThan("", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() < "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 586: should be < ",}, b0, false, false, "G)", null); } -if (!(("" + compareEqual("", "")).toLowerCase() === "true".toLowerCase())) { +if (!(("" + ("".toLowerCase() === "".toLowerCase())).toLowerCase() === "true".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 587: should be = ",}, b0, false, false, "G+", null); } -if (!(("" + compareGreaterThan("", "")).toLowerCase() === "false".toLowerCase())) { +if (!(("" + ("".toLowerCase() > "".toLowerCase())).toLowerCase() === "false".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail 588: should be > ",}, b0, false, false, "G.", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "G-", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-runtime.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-runtime.sb3.tw-snapshot index 0808a3a2..40c61fa7 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-runtime.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-runtime.sb3.tw-snapshot @@ -11,7 +11,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "aZ", n retire(); return; }; }) -// Sprite1 Wrun test +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["mfV;yS}9e:%h5UZ)QyiY"]; const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; @@ -29,15 +29,15 @@ b3.value = 0; for (var a1 = b2.value.length; a1 >= 0.5; a1--) { b3.value = ((+b3.value || 0) + 1); b0.value = ((+b0.value || 0) + 1); -if (!compareEqual(compareGreaterThan(listGet(b2.value, b1.value), (b2.value[((b3.value || 0) | 0) - 1] ?? "")), (b4.value[((b0.value || 0) | 0) - 1] ?? ""))) { -yield* executeInCompatibilityLayer({"MESSAGE":("fail " + (("" + listGet(b2.value, b1.value)) + (" should be > " + ("" + listGet(b2.value, b3.value))))),}, b5, true, false, "]", null); +if (!compareEqual(compareGreaterThan(listGet(b2.value, b1.value), (b2.value[(b3.value | 0) - 1] ?? "")), (b4.value[(b0.value | 0) - 1] ?? ""))) { +yield* executeInCompatibilityLayer({"MESSAGE":("fail " + (("" + listGet(b2.value, b1.value)) + (" should be > " + ("" + (b2.value[(b3.value | 0) - 1] ?? ""))))),}, b5, true, false, "]", null); } b0.value = ((+b0.value || 0) + 1); -if (!compareEqual(compareEqual(listGet(b2.value, b1.value), listGet(b2.value, b3.value)), (b4.value[((b0.value || 0) | 0) - 1] ?? ""))) { +if (!compareEqual(compareEqual(listGet(b2.value, b1.value), listGet(b2.value, b3.value)), (b4.value[(b0.value | 0) - 1] ?? ""))) { yield* executeInCompatibilityLayer({"MESSAGE":("fail " + (("" + listGet(b2.value, b1.value)) + (" should be = " + ("" + listGet(b2.value, b3.value))))),}, b5, true, false, "|", null); } b0.value = ((+b0.value || 0) + 1); -if (!compareEqual(compareLessThan(listGet(b2.value, b1.value), listGet(b2.value, b3.value)), (b4.value[((b0.value || 0) | 0) - 1] ?? ""))) { +if (!compareEqual(compareLessThan(listGet(b2.value, b1.value), listGet(b2.value, b3.value)), (b4.value[(b0.value | 0) - 1] ?? ""))) { yield* executeInCompatibilityLayer({"MESSAGE":("fail " + (("" + listGet(b2.value, b1.value)) + (" should be < " + ("" + listGet(b2.value, b3.value))))),}, b5, true, true, "ab", null); if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;} } @@ -48,7 +48,7 @@ if (isStuck()) yield; return ""; }; }) -// Sprite1 Wsetup values +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["n^wm8jw#b24sggt.S^tD"]; return function funXYZ_setup_values () { @@ -107,7 +107,7 @@ b0.value.push((-1 / 0)); b0._monitorUpToDate = false; b0.value.push((0 / 0)); b0._monitorUpToDate = false; -b0.value.push(NaN); +b0.value.push("NaN"); b0._monitorUpToDate = false; b0.value.push("nan"); b0._monitorUpToDate = false; diff --git a/test/snapshot/__snapshots__/warp-timer/tw-custom-report-repeat.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-custom-report-repeat.sb3.tw-snapshot index 060537d6..1442da79 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-custom-report-repeat.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-custom-report-repeat.sb3.tw-snapshot @@ -19,7 +19,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "l", nu retire(); return; }; }) -// Sprite1 Zblock name +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_block_name () { return 40; diff --git a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot index a63ba884..5f84b8b4 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot @@ -12,10 +12,10 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "k", n if (compareLessThan(("something".toLowerCase() === "else".toLowerCase()), ("1" + ""))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "m", null); } -if (compareGreaterThan(("something".toLowerCase() === "something".toLowerCase()), (0 + 0))) { +if (((+("something".toLowerCase() === "something".toLowerCase())) > (0 + 0))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "n", null); } -if (compareLessThan(("something".toLowerCase() === "else".toLowerCase()), (1 + 0))) { +if (((+("something".toLowerCase() === "else".toLowerCase())) < (1 + 0))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "o", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "l", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot index 7c736d43..d52a998a 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot @@ -9,13 +9,13 @@ const b2 = target.variables["zShM`!CD?d_|Z,]5X}N6"]; return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, "d", null); b1.value = 2; -if (((+b1.value || 0) === 2)) { +if ((b1.value === 2)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass variable",}, b0, false, false, "k", null); } b2.value = []; b2.value.push(3); b2._monitorUpToDate = false; -if (((+(b2.value[(1 | 0) - 1] ?? "") || 0) === 3)) { +if (((+(b2.value[1 - 1] ?? "") || 0) === 3)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass list",}, b0, false, false, "m", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "l", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot index a3d93933..7a3bed7c 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot @@ -21,7 +21,7 @@ retire(); return; retire(); return; }; }) -// Sprite1 Wno refresh +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("music_restForBeats"); const b1 = runtime.getOpcodeFunction("music_playDrumForBeats"); @@ -42,14 +42,14 @@ if (isStuck()) yield; return ""; }; }) -// Sprite1 Wruns below with no refresh +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function* genXYZ_runs_below_with_no_r () { yield* thread.procedures["Whas refresh"](); return ""; }; }) -// Sprite1 Whas refresh +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("music_restForBeats"); const b1 = runtime.getOpcodeFunction("music_playDrumForBeats"); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-gh-201-stop-script-does-not-reevaluate-arguments.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-gh-201-stop-script-does-not-reevaluate-arguments.sb3.tw-snapshot index 07442f3c..1fc921d2 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-gh-201-stop-script-does-not-reevaluate-arguments.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-gh-201-stop-script-does-not-reevaluate-arguments.sb3.tw-snapshot @@ -11,14 +11,14 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "g", nu retire(); return; }; }) -// Sprite1 Zfoo %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_foo_ (p0) { return ""; return ""; }; }) -// Sprite1 Zno op +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_no_op () { return ""; diff --git a/test/snapshot/__snapshots__/warp-timer/tw-list-any.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-list-any.sb3.tw-snapshot index cb39a249..d6988304 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-list-any.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-list-any.sb3.tw-snapshot @@ -30,7 +30,7 @@ listDelete(b1, "any"); if ((b1.value.length === 2)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "C", null); } -if (compareEqual(listGet(b1.value, "*"), "")) { +if ((("" + listGet(b1.value, "*")).toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "F", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "E", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-one-divide-negative-zero.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-one-divide-negative-zero.sb3.tw-snapshot index 2557ac32..d87b4b22 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-one-divide-negative-zero.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-one-divide-negative-zero.sb3.tw-snapshot @@ -6,7 +6,7 @@ const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "@|B*yJ0zKh!acN`7L-N5", null); -if ((((1 / -0) || 0) === -Infinity)) { +if (((1 / -0) === -Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "=enYDFG11Nj/0BL:y56w", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, ",Cpv8W0RH0RgNky[1xb:", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot index 58b908a7..d6e18a37 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot @@ -11,7 +11,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "Q^(MKg retire(); return; }; }) -// Player ZSet Costume +// Player script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ_Set_Costume () { diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-arguments-with-same-name.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-arguments-with-same-name.sb3.tw-snapshot index f2456f2e..2555707c 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-arguments-with-same-name.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-arguments-with-same-name.sb3.tw-snapshot @@ -12,7 +12,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "$R-1lb retire(); return; }; }) -// Sprite1 Znumber or text %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ_number_or_text__ (p0,p1) { @@ -22,7 +22,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "HH`yR return ""; }; }) -// Sprite1 Zboolean %b %b +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ_boolean__ (p0,p1) { diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot index 548eb4b3..4c466711 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot @@ -9,14 +9,14 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "qf{MD}-f+l?U+)KA#Vnm", null); b1.value = ""; thread.procedures["Zdo something"](); -if (!compareEqual(b1.value, "")) { +if (!(b1.value.toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "Sgf_#7|GOpx!R]?Q3]$s", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, ",vD-ZG7f{]FoJ`,))JWh", null); retire(); return; }; }) -// Sprite1 Zdo something +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; return function funXYZ_do_something () { diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existant.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existant.sb3.tw-snapshot index 2aa9b207..4e435ae0 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existant.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existant.sb3.tw-snapshot @@ -39,14 +39,14 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "P", nu retire(); return; }; }) -// Sprite1 Zinvalid params - reporter +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_invalid_params___rep () { return 0; return ""; }; }) -// Sprite1 Zinvalid params - boolean +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_invalid_params___boo () { return 0; diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existent.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existent.sb3.tw-snapshot index 8a2837c6..cea7eccc 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existent.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existent.sb3.tw-snapshot @@ -9,7 +9,7 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "d", null); b1.value = "discard me"; b1.value = ""; -if (compareEqual(b1.value, "")) { +if ((("" + b1.value).toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non existent procedure returned empty string",}, b0, false, false, "h", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "g", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-recursion.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-recursion.sb3.tw-snapshot index 6251bdac..e1176792 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-recursion.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-recursion.sb3.tw-snapshot @@ -10,17 +10,17 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 18",}, b0, false, false, "G", null); b1.value = 0; b2.value = (yield* thread.procedures["Znon warp recursion should yield %s"](8)); -if (((+b1.value || 0) === 4)) { +if ((b1.value === 4)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp recursion yields",}, b0, false, false, "ao", null); } b1.value = 0; b2.value = thread.procedures["Wwarp recursion should not yield %s"](8); -if (((+b1.value || 0) === 0)) { +if ((b1.value === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass warp recursion does not yield",}, b0, false, false, "ar", null); } b1.value = 0; b2.value = (yield* thread.procedures["Zfib %s"](7)); -if (((+b1.value || 0) === 20)) { +if ((b1.value === 20)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp fib yielded",}, b0, false, false, "au", null); } yield* thread.procedures["Zrecursing yields between each %s"]("initial"); @@ -30,7 +30,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "av", n retire(); return; }; }) -// Sprite1 Znon warp recursion should yield %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function* genXYZ_non_warp_recursion_s (p0) { if (compareGreaterThan(p0, 0)) { @@ -39,7 +39,7 @@ return (yield* yieldThenCallGenerator(thread.procedures["Znon warp recursion sho return ""; }; }) -// Sprite1 Wwarp recursion should not yield %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_warp_recursion_shoul (p0) { if (compareGreaterThan(p0, 0)) { @@ -48,7 +48,7 @@ return thread.procedures["Wwarp recursion should not yield %s"](((+p0 || 0) - 1) return ""; }; }) -// Sprite1 Zfib %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function* genXYZ_fib_ (p0) { if (compareLessThan(p0, 2)) { @@ -59,7 +59,7 @@ return ((+(yield* yieldThenCallGenerator(thread.procedures["Zfib %s"], ((+p0 || return ""; }; }) -// Sprite1 Zrecursing yields between each %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; @@ -68,7 +68,7 @@ return function* genXYZ_recursing_yields_bet (p0) { if ((("" + p0).toLowerCase() === "initial".toLowerCase())) { b0.value = 0; b1.value = ((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 1)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 1)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 2)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 2)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 3)) || 0) + (+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 3)) || 0)) || 0)) || 0)) || 0)) || 0)); -if (((+b0.value || 0) === 3)) { +if ((b0.value === 3)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recursing between calls yields final",}, b2, false, false, "aK", null); } else { yield* executeInCompatibilityLayer({"MESSAGE":"fail recursing between calls yields final",}, b2, false, false, "aL", null); @@ -83,7 +83,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":("fail recursing between calls yie return ""; }; }) -// Sprite1 Zrecursing arguments eval order %s %s %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["4HH82mPlVMOONdl(Ot*7"]; const b1 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; @@ -94,25 +94,25 @@ if ((("" + p0).toLowerCase() === "initial".toLowerCase())) { b0.value = []; b1.value = 0; b2.value = (yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 1",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 2",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 3",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 4","","","")),(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 5","","","")),(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 6","","","")))),"","")),"",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 7","","","")))); -if ((("" + (b0.value[(1 | 0) - 1] ?? "")).toLowerCase() === "1/child 4".toLowerCase())) { +if ((("" + (b0.value[1 - 1] ?? "")).toLowerCase() === "1/child 4".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 1",}, b3, false, false, "aZ", null); } -if ((("" + (b0.value[(2 | 0) - 1] ?? "")).toLowerCase() === "1/child 5".toLowerCase())) { +if ((("" + (b0.value[2 - 1] ?? "")).toLowerCase() === "1/child 5".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 2",}, b3, false, false, "a#", null); } -if ((("" + (b0.value[(3 | 0) - 1] ?? "")).toLowerCase() === "2/child 6".toLowerCase())) { +if ((("" + (b0.value[3 - 1] ?? "")).toLowerCase() === "2/child 6".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 3",}, b3, false, false, "a(", null); } -if ((("" + (b0.value[(4 | 0) - 1] ?? "")).toLowerCase() === "2/child 3".toLowerCase())) { +if ((("" + (b0.value[4 - 1] ?? "")).toLowerCase() === "2/child 3".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 4",}, b3, false, false, "a*", null); } -if ((("" + (b0.value[(5 | 0) - 1] ?? "")).toLowerCase() === "3/child 2".toLowerCase())) { +if ((("" + (b0.value[5 - 1] ?? "")).toLowerCase() === "3/child 2".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 5",}, b3, false, false, "a,", null); } -if ((("" + (b0.value[(6 | 0) - 1] ?? "")).toLowerCase() === "3/child 7".toLowerCase())) { +if ((("" + (b0.value[6 - 1] ?? "")).toLowerCase() === "3/child 7".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 6",}, b3, false, false, "a.", null); } -if ((("" + (b0.value[(7 | 0) - 1] ?? "")).toLowerCase() === "4/child 1".toLowerCase())) { +if ((("" + (b0.value[7 - 1] ?? "")).toLowerCase() === "4/child 1".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 7",}, b3, false, false, "a:", null); } if ((b0.value.length === 7)) { diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-simple.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-simple.sb3.tw-snapshot index b0ae54eb..c37d030d 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-simple.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-simple.sb3.tw-snapshot @@ -20,21 +20,21 @@ if (((+thread.procedures["Wfactorial %s"](12) || 0) === 479001600)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass factorial 12",}, b0, false, false, "]", null); } b1.value = (yield* thread.procedures["Zno shadowing 1 %s %s"]("f","g")); -if (compareEqual(b1.value, "")) { +if ((("" + b1.value).toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass default return value",}, b0, false, false, "|", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "`", null); retire(); return; }; }) -// Sprite1 Zsimplest +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_simplest () { return "It works!"; return ""; }; }) -// Sprite1 Znesting 1 +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_nesting_1 () { thread.procedures["Znesting 2"](); @@ -42,14 +42,14 @@ return (("" + thread.procedures["Znesting 3 %s %s"](6,7)) + ("" + thread.procedu return ""; }; }) -// Sprite1 Wwarp fib %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_warp_fib_ (p0) { return thread.procedures["Wfib %s"](p0); return ""; }; }) -// Sprite1 Wfactorial %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_factorial_ (p0) { if (compareGreaterThan(p0, 1)) { @@ -59,7 +59,7 @@ return 1; return ""; }; }) -// Sprite1 Zno shadowing 1 %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = runtime.getOpcodeFunction("looks_say"); const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; @@ -78,21 +78,21 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass shadow check 3",}, b0, false return ""; }; }) -// Sprite1 Znesting 2 +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_nesting_2 () { return "discard nesting 2"; return ""; }; }) -// Sprite1 Znesting 3 %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_nesting_3__ (p0,p1) { return ((+p0 || 0) * (+p1 || 0)); return ""; }; }) -// Sprite1 Wfib %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_fib_ (p0) { if (compareLessThan(p0, 2)) { @@ -103,7 +103,7 @@ return ((+thread.procedures["Wfib %s"](((+p0 || 0) - 1)) || 0) + (+thread.proced return ""; }; }) -// Sprite1 Zno shadowing 2 %s %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_no_shadowing_2__ (p0,p1) { return "discard shadow 2"; diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-stops-scripts.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-stops-scripts.sb3.tw-snapshot index c0355f7a..7ea58c0c 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-stops-scripts.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-stops-scripts.sb3.tw-snapshot @@ -19,7 +19,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "v", nu retire(); return; }; }) -// Sprite1 Wreturn stops the script immediately +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["PsAI*C{QHI3*4?O8p#TM"]; const b1 = runtime.getOpcodeFunction("looks_say"); @@ -27,7 +27,7 @@ return function* genXYZ_return_stops_the_scr () { b0.value = 0; for (var a0 = 100; a0 >= 0.5; a0--) { b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 25)) { +if ((b0.value === 25)) { return "stopped!"; } if (isStuck()) yield; @@ -43,7 +43,7 @@ return function* genXYZ () { b0.value = 0; while (true) { b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 18)) { +if ((b0.value === 18)) { retire(); return; } yield; diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-warp.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-warp.sb3.tw-snapshot index d7afc882..edf65b39 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-warp.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-warp.sb3.tw-snapshot @@ -32,7 +32,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "N", nu retire(); return; }; }) -// Sprite1 Znon warp +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; const b1 = runtime.getOpcodeFunction("looks_say"); @@ -73,7 +73,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp 2",}, b1, false, fa return ""; }; }) -// Sprite1 Wverify runs warp %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; const b1 = runtime.getOpcodeFunction("looks_say"); @@ -96,7 +96,7 @@ return ("warp: " + ("" + p0)); return ""; }; }) -// Sprite1 Zverify runs non warp %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"]; const b1 = runtime.getOpcodeFunction("looks_say"); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-safe-procedure-argument-casting.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-safe-procedure-argument-casting.sb3.tw-snapshot index 88f7ab0b..033cebf8 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-safe-procedure-argument-casting.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-safe-procedure-argument-casting.sb3.tw-snapshot @@ -18,7 +18,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "$R-1lb retire(); return; }; }) -// Sprite1 Zswitch %s +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); return function funXYZ_switch_ (p0) { runtime.ext_scratch3_looks._setCostume(target, p0); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-sensing-of.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-sensing-of.sb3.tw-snapshot index ee917806..a370af3e 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-sensing-of.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-sensing-of.sb3.tw-snapshot @@ -36,7 +36,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass direction",}, b0, false, fal if (((b2 ? b2.currentCostume + 1 : 0) === 3)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass costume #",}, b0, false, false, "+zO[+f?c7F`ZGTbD.oqI", null); } -if ((("" + (b2 ? b2.getCostumes()[b2.currentCostume].name : 0)).toLowerCase() === "Costume name test".toLowerCase())) { +if (((b2 ? b2.getCostumes()[b2.currentCostume].name : 0).toLowerCase() === "Costume name test".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"pass costume name",}, b0, false, false, "Y6L|T0Pvwsct2gq+HGvv", null); } if (((b2 ? b2.size : 0) === 76.01)) { @@ -52,7 +52,7 @@ if (compareEqual((b4 ? b4.value : 0), 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non existent variable",}, b0, false, false, ")nnN?*l+E)dC(fT5(_@q", null); } b5.value = (("" + randomInt(1, 9)) + ("" + randomInt(1, 9))); -if (compareEqual(runtime.ext_scratch3_sensing.getAttributeOf({OBJECT: b5.value, PROPERTY: "backdrop #" }), 0)) { +if (compareEqual(runtime.ext_scratch3_sensing.getAttributeOf({OBJECT: ("" + b5.value), PROPERTY: "backdrop #" }), 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass NE backdrop #",}, b0, false, false, "UFr{fbR3@a.u_paq:r]F", null); } if (compareEqual(runtime.ext_scratch3_sensing.getAttributeOf({OBJECT: ("" + b5.value), PROPERTY: "backdrop name" }), 0)) { diff --git a/test/snapshot/__snapshots__/warp-timer/tw-tab-equals-zero.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-tab-equals-zero.sb3.tw-snapshot index 0169ba16..119556a4 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-tab-equals-zero.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-tab-equals-zero.sb3.tw-snapshot @@ -29,7 +29,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass \\t and other spaces = strin if (compareEqual(b0.value, (0 + 0))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass \\t in a variable = number 0",}, b1, false, false, "z", null); } -if (compareEqual("\t", (0 + 0))) { +if ((0 === (0 + 0))) { yield* executeInCompatibilityLayer({"MESSAGE":"pass literal \\t = number 0",}, b1, false, false, "B", null); } if (compareEqual((" " + ("" + b0.value)), (0 + 0))) { diff --git a/test/snapshot/__snapshots__/warp-timer/tw-tangent.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-tangent.sb3.tw-snapshot index b2ccf9ce..17992397 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-tangent.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-tangent.sb3.tw-snapshot @@ -6,31 +6,31 @@ const b0 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 15",}, b0, false, false, "p", null); -if (compareEqual(tan(0), 0)) { +if (((tan(0) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 0",}, b0, false, false, "O", null); } if (((tan(90) || 0) === Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 90",}, b0, false, false, "G", null); } -if (compareEqual(tan(180), 0)) { +if (((tan(180) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 180",}, b0, false, false, "I", null); } if (((tan(270) || 0) === -Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 270",}, b0, false, false, "K", null); } -if (compareEqual(tan(360), 0)) { +if (((tan(360) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 360",}, b0, false, false, "M", null); } if (((tan(450) || 0) === Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 450",}, b0, false, false, "Q", null); } -if (compareEqual(tan(540), 0)) { +if (((tan(540) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 540",}, b0, false, false, "S", null); } if (((tan(630) || 0) === -Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 630",}, b0, false, false, "U", null); } -if (compareEqual(tan(720), 0)) { +if (((tan(720) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 720",}, b0, false, false, "W", null); } if (((tan(810) || 0) === Infinity)) { @@ -39,13 +39,13 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass 810",}, b0, false, false, "Y if (((tan(-90) || 0) === -Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass -90",}, b0, false, false, "0", null); } -if (compareEqual(tan(-180), 0)) { +if (((tan(-180) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass -180",}, b0, false, false, "2", null); } if (((tan(-270) || 0) === Infinity)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass -270",}, b0, false, false, "4", null); } -if (compareEqual(tan(-360), 0)) { +if (((tan(-360) || 0) === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass -360",}, b0, false, false, "6", null); } if (((tan(-450) || 0) === -Infinity)) { diff --git a/test/snapshot/__snapshots__/warp-timer/tw-unsafe-equals.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-unsafe-equals.sb3.tw-snapshot index 082b46ce..90a01cba 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-unsafe-equals.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-unsafe-equals.sb3.tw-snapshot @@ -15,23 +15,23 @@ if ((10 === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 2",}, b0, false, false, "@A5}x{mm-Gk?CVz3o0Yn", null); } b1.value = 10; -if (((+b1.value || 0) === 10)) { +if ((b1.value === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 3",}, b0, false, false, "Ul:BCck-}Fvdux~x#$${", null); } -if (compareEqual(b1.value, "010")) { +if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 4",}, b0, false, false, "8]2$7P)o#+#Lo]mFSBbx", null); } -if (compareEqual(b1.value, "0000000010")) { +if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 5",}, b0, false, false, "ZU^{OfKTg|+Au$$q0[]u", null); } for (var a0 = 1; a0 >= 0.5; a0--) { if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 6",}, b0, false, false, "HB+_IN}6=K[*ksxKXH0`", null); } -if (compareEqual(b1.value, "010")) { +if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 7",}, b0, false, false, ";73ODiwcp8IthYURTX;S", null); } -if (compareEqual(b1.value, "0000000010")) { +if (((+b1.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 8",}, b0, false, true, "${[MFmBL-D*1rbas9Q89", null); if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;} } @@ -41,20 +41,20 @@ b2.value = "010"; if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 9",}, b0, false, false, "#.`@SBj!g-c0:_q/tMZo", null); } -if (compareEqual(b2.value, "010")) { +if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 10",}, b0, false, false, "B`o?V6/q6g),/2w};a#y", null); } -if (compareEqual(b2.value, "0000000010")) { +if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 11",}, b0, false, false, "TJ:#TkYBys*!RYiKLXb)", null); } for (var a1 = 1; a1 >= 0.5; a1--) { if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 12",}, b0, false, false, ",Z,~10Qo~j;(+VL+I3q:", null); } -if (compareEqual(b2.value, "010")) { +if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 13",}, b0, false, false, "|Mqx([(26M%#ggW9)U0s", null); } -if (compareEqual(b2.value, "0000000010")) { +if (((+b2.value || 0) === 10)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass 14",}, b0, false, true, "YvtiKF231lU8p5Qd97RP", null); if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;} } @@ -69,16 +69,16 @@ yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "XzQt! if ((0 === 1)) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "rxZqw7cv8g;PDM4B%{`?", null); } -if (compareEqual(" ", 0)) { +if ((" ".toLowerCase() === "0".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "3G|)eVw1mQm;O~cRy`}0", null); } -if (compareEqual(0, " ")) { +if (("0".toLowerCase() === " ".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "sd5xXX*tsW/~A_Q!0;^w", null); } -if (compareEqual("", 0)) { +if (("".toLowerCase() === "0".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "7**baE=].WD9OoY1+IEu", null); } -if (compareEqual(0, "")) { +if (("0".toLowerCase() === "".toLowerCase())) { yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, "7!IB!=o/2H.Jqj-8Vwhz", null); } yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "df{tf!WhfRwXgQ?SN_dj", null); diff --git a/test/snapshot/__snapshots__/warp-timer/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot index bd4bdca8..d986e3e8 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot @@ -11,14 +11,14 @@ yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "s", nu retire(); return; }; }) -// Sprite1 Wrun without screen refresh +// Sprite1 script (function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); const b0 = stage.variables["F?*}X,`9XBpN_[piGRrz"]; const b1 = runtime.getOpcodeFunction("looks_say"); return function* genXYZ_run_without_screen_r () { -b0.value = (((daysSince2000() * 86400) || 0) + 3); +b0.value = ((daysSince2000() * 86400) + 3); runtime.ioDevices.clock.resetProjectTimer(); -while (!((runtime.ioDevices.clock.projectTimer() > 0.1) || compareGreaterThan((daysSince2000() * 86400), b0.value))) { +while (!((runtime.ioDevices.clock.projectTimer() > 0.1) || ((daysSince2000() * 86400) > b0.value))) { if (isStuck()) yield; } if (compareLessThan((daysSince2000() * 86400), b0.value)) { diff --git a/test/snapshot/__snapshots__/warp-timer/tw-zombie-cube-escape-284516654.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-zombie-cube-escape-284516654.sb3.tw-snapshot index 2faaed41..826f1e00 100644 --- a/test/snapshot/__snapshots__/warp-timer/tw-zombie-cube-escape-284516654.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/warp-timer/tw-zombie-cube-escape-284516654.sb3.tw-snapshot @@ -10,7 +10,7 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, ",a.euo_AgQTxR+D^x0M0", null); b1.value = 0; b2.value = ""; -while (!compareGreaterThan(b2.value, "")) { +while (!(b2.value.toLowerCase() > "".toLowerCase())) { startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "step" }); yield; } @@ -25,7 +25,7 @@ const b0 = stage.variables["7qur6!bGgvC9I(Nd5.HP"]; const b1 = stage.variables["sUOp@-6J4y0PqwiXit4!"]; return function* genXYZ () { b0.value = ((+b0.value || 0) + 1); -if (((b0.value || 0) === 5)) { +if ((b0.value === 5)) { b1.value = "end"; } retire(); return;