From 2cd5b0c437a1913d075e2bec68db5e2a0fef4553 Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 6 Feb 2024 19:05:47 -0500 Subject: [PATCH 01/46] Update README --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e39bf07e5bb..dd73f68002e 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ -## TurboWarp/scratch-vm +Modified TurboWarp VM with more features. -Modified Scratch VM with a JIT compiler and more features. - -This is a drop-in replacement for LLK/scratch-vm. +This is a drop-in replacement for `LLK/scratch-vm`. ## Setup -See https://github.com/TurboWarp/scratch-gui/wiki/Getting-Started to setup the complete TurboWarp environment. +See [getting started](https://docs.turbowarp.org/development/getting-started) to setup the complete NitroBolt environment. -If you just want to play with the VM then it's the same process as upstream scratch-vm. +If you just want to play with the VM then it's the same process as upstream `scratch-vm`. ## Extension authors @@ -16,9 +14,9 @@ If you only use the standard reporter, boolean, and command block types, everyth ## Compiler Overview -For a high-level overview of how the compiler works, see https://docs.turbowarp.org/how +For a high-level overview of how the compiler works, see [how](https://docs.turbowarp.org/how). -For more technical information, read the code in src/compiler. +For more technical information, read the code in `src/compiler`. ## Public API From dd6e89f80d1143b41baa209fe37fef68c43a880e Mon Sep 17 00:00:00 2001 From: Cubester Date: Fri, 9 Feb 2024 18:57:07 -0500 Subject: [PATCH 02/46] Update to attribute new dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 15829fb6fae..49948c55c0c 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "0.1.0-prerelease.20230527085947", + "scratch-blocks": "git+https://github.com/NitroBolt/scratch-blocks.git#develop-builds", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", From cb7d493f172c1dabe9f499d10b629c90c9bc5f31 Mon Sep 17 00:00:00 2001 From: Cubester Date: Fri, 9 Feb 2024 19:04:36 -0500 Subject: [PATCH 03/46] Fix quick mistake --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 49948c55c0c..04d0a3d0e38 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "git+https://github.com/NitroBolt/scratch-blocks.git#develop-builds", + "scratch-blocks": "git+https://github.com/Nitro-Bolt/scratch-blocks.git#develop-builds", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", From ace8b1dfa6d32f7cc23fc62f2f34031c83924d9d Mon Sep 17 00:00:00 2001 From: Cubester Date: Fri, 9 Feb 2024 19:11:35 -0500 Subject: [PATCH 04/46] Update `package-lock.json` as well --- package-lock.json | 48 +++++------------------------------------------ 1 file changed, 5 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index 467eaf24d06..41b115b0745 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "0.1.0-prerelease.20230527085947", + "scratch-blocks": "git+https://github.com/Nitro-Bolt/scratch-blocks.git#develop-builds", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", @@ -8176,12 +8176,6 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, - "node_modules/google-closure-library": { - "version": "20190301.0.0", - "resolved": "https://registry.npmjs.org/google-closure-library/-/google-closure-library-20190301.0.0.tgz", - "integrity": "sha512-mpeszbnXpRhXZ0sPqUxBgUmk0RtmzrJRy3KFygp0Ih9JuRUjQTCLhwYQeIlK2vB2lShhY/KUo9E1Z1gvxDFxOQ==", - "dev": true - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -13304,45 +13298,13 @@ } }, "node_modules/scratch-blocks": { - "version": "0.1.0-prerelease.20230527085947", - "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20230527085947.tgz", - "integrity": "sha512-wvh7+9X5FeuPM0tfhvqu3TFpNCmx52IcMR1Ysxm0cR/s1dvnwHEAhRZM/6xlx1WQpVT7aD7hU04j7qYaRO0YWw==", + "version": "0.1.0", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#6cfb72633c3c2b4df2df3382baebd2ae1d380aa2", "dev": true, + "license": "GPL-3.0", "dependencies": { "exports-loader": "0.7.0", - "google-closure-library": "20190301.0.0", - "imports-loader": "0.8.0", - "scratch-l10n": "3.15.20230527032201" - } - }, - "node_modules/scratch-blocks/node_modules/@transifex/api": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@transifex/api/-/api-4.2.5.tgz", - "integrity": "sha512-br8ubTnKohECW3dPrR3h1casERTn3EvaV+gpHkWDZf3rlurkksAvEh/i9U+fIUzBF6ooYI0SG1JjRzuKkeaSZg==", - "dev": true, - "dependencies": { - "core-js": "^3.22.4" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/scratch-blocks/node_modules/scratch-l10n": { - "version": "3.15.20230527032201", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.15.20230527032201.tgz", - "integrity": "sha512-nUqHPQrG9Eal1M+AY/2gJlje7O5W9yZ9yaLsWWScgYPYGFm8rxFnVk8pcEmZCzghz+folsfuN0zS0bPKDopr/Q==", - "dev": true, - "dependencies": { - "@babel/cli": "^7.1.2", - "@babel/core": "^7.1.2", - "@transifex/api": "4.2.5", - "babel-plugin-react-intl": "^3.0.1", - "download": "^8.0.0", - "transifex": "1.6.6" - }, - "bin": { - "build-i18n-src": "scripts/build-i18n-src.js", - "tx-push-src": "scripts/tx-push-src.js" + "imports-loader": "0.8.0" } }, "node_modules/scratch-l10n": { From e13f4a28ac22350cdc4797c2d2eb36763f96b79f Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 13 Feb 2024 09:20:13 -0500 Subject: [PATCH 05/46] Revert "Fix quick mistake" This reverts commit cb7d493f172c1dabe9f499d10b629c90c9bc5f31. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 04d0a3d0e38..49948c55c0c 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "git+https://github.com/Nitro-Bolt/scratch-blocks.git#develop-builds", + "scratch-blocks": "git+https://github.com/NitroBolt/scratch-blocks.git#develop-builds", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", From 5c3265eb252dacd129ce42e787f61774f96b93ed Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 13 Feb 2024 09:21:23 -0500 Subject: [PATCH 06/46] Revert "Update to attribute new dependencies" This reverts commit dd6e89f80d1143b41baa209fe37fef68c43a880e. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 49948c55c0c..15829fb6fae 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "git+https://github.com/NitroBolt/scratch-blocks.git#develop-builds", + "scratch-blocks": "0.1.0-prerelease.20230527085947", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", From a915cf9637e7492f928a8a59dd6e527982d03d1a Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 13 Feb 2024 09:21:49 -0500 Subject: [PATCH 07/46] Revert "Update `package-lock.json` as well" This reverts commit ace8b1dfa6d32f7cc23fc62f2f34031c83924d9d. --- package-lock.json | 48 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41b115b0745..467eaf24d06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "git+https://github.com/Nitro-Bolt/scratch-blocks.git#develop-builds", + "scratch-blocks": "0.1.0-prerelease.20230527085947", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", @@ -8176,6 +8176,12 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, + "node_modules/google-closure-library": { + "version": "20190301.0.0", + "resolved": "https://registry.npmjs.org/google-closure-library/-/google-closure-library-20190301.0.0.tgz", + "integrity": "sha512-mpeszbnXpRhXZ0sPqUxBgUmk0RtmzrJRy3KFygp0Ih9JuRUjQTCLhwYQeIlK2vB2lShhY/KUo9E1Z1gvxDFxOQ==", + "dev": true + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -13298,13 +13304,45 @@ } }, "node_modules/scratch-blocks": { - "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#6cfb72633c3c2b4df2df3382baebd2ae1d380aa2", + "version": "0.1.0-prerelease.20230527085947", + "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20230527085947.tgz", + "integrity": "sha512-wvh7+9X5FeuPM0tfhvqu3TFpNCmx52IcMR1Ysxm0cR/s1dvnwHEAhRZM/6xlx1WQpVT7aD7hU04j7qYaRO0YWw==", "dev": true, - "license": "GPL-3.0", "dependencies": { "exports-loader": "0.7.0", - "imports-loader": "0.8.0" + "google-closure-library": "20190301.0.0", + "imports-loader": "0.8.0", + "scratch-l10n": "3.15.20230527032201" + } + }, + "node_modules/scratch-blocks/node_modules/@transifex/api": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@transifex/api/-/api-4.2.5.tgz", + "integrity": "sha512-br8ubTnKohECW3dPrR3h1casERTn3EvaV+gpHkWDZf3rlurkksAvEh/i9U+fIUzBF6ooYI0SG1JjRzuKkeaSZg==", + "dev": true, + "dependencies": { + "core-js": "^3.22.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/scratch-blocks/node_modules/scratch-l10n": { + "version": "3.15.20230527032201", + "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.15.20230527032201.tgz", + "integrity": "sha512-nUqHPQrG9Eal1M+AY/2gJlje7O5W9yZ9yaLsWWScgYPYGFm8rxFnVk8pcEmZCzghz+folsfuN0zS0bPKDopr/Q==", + "dev": true, + "dependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@transifex/api": "4.2.5", + "babel-plugin-react-intl": "^3.0.1", + "download": "^8.0.0", + "transifex": "1.6.6" + }, + "bin": { + "build-i18n-src": "scripts/build-i18n-src.js", + "tx-push-src": "scripts/tx-push-src.js" } }, "node_modules/scratch-l10n": { From e7baa0e9fdbedf05600f4b4d7b4f3476c4e1794a Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 13 Feb 2024 10:23:03 -0500 Subject: [PATCH 08/46] Readd Dependencies --- package-lock.json | 48 +++++------------------------------------------ package.json | 2 +- 2 files changed, 6 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index 467eaf24d06..4c684942f42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "0.1.0-prerelease.20230527085947", + "scratch-blocks": "git+https://github.com/Nitro-Bolt/scratch-blocks.git#develop-builds", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", @@ -8176,12 +8176,6 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, - "node_modules/google-closure-library": { - "version": "20190301.0.0", - "resolved": "https://registry.npmjs.org/google-closure-library/-/google-closure-library-20190301.0.0.tgz", - "integrity": "sha512-mpeszbnXpRhXZ0sPqUxBgUmk0RtmzrJRy3KFygp0Ih9JuRUjQTCLhwYQeIlK2vB2lShhY/KUo9E1Z1gvxDFxOQ==", - "dev": true - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -13304,45 +13298,13 @@ } }, "node_modules/scratch-blocks": { - "version": "0.1.0-prerelease.20230527085947", - "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.20230527085947.tgz", - "integrity": "sha512-wvh7+9X5FeuPM0tfhvqu3TFpNCmx52IcMR1Ysxm0cR/s1dvnwHEAhRZM/6xlx1WQpVT7aD7hU04j7qYaRO0YWw==", + "version": "0.1.0", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#40e2e6c08fc43121f804ab6786a8d8e577ab7897", "dev": true, + "license": "GPL-3.0", "dependencies": { "exports-loader": "0.7.0", - "google-closure-library": "20190301.0.0", - "imports-loader": "0.8.0", - "scratch-l10n": "3.15.20230527032201" - } - }, - "node_modules/scratch-blocks/node_modules/@transifex/api": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@transifex/api/-/api-4.2.5.tgz", - "integrity": "sha512-br8ubTnKohECW3dPrR3h1casERTn3EvaV+gpHkWDZf3rlurkksAvEh/i9U+fIUzBF6ooYI0SG1JjRzuKkeaSZg==", - "dev": true, - "dependencies": { - "core-js": "^3.22.4" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/scratch-blocks/node_modules/scratch-l10n": { - "version": "3.15.20230527032201", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.15.20230527032201.tgz", - "integrity": "sha512-nUqHPQrG9Eal1M+AY/2gJlje7O5W9yZ9yaLsWWScgYPYGFm8rxFnVk8pcEmZCzghz+folsfuN0zS0bPKDopr/Q==", - "dev": true, - "dependencies": { - "@babel/cli": "^7.1.2", - "@babel/core": "^7.1.2", - "@transifex/api": "4.2.5", - "babel-plugin-react-intl": "^3.0.1", - "download": "^8.0.0", - "transifex": "1.6.6" - }, - "bin": { - "build-i18n-src": "scripts/build-i18n-src.js", - "tx-push-src": "scripts/tx-push-src.js" + "imports-loader": "0.8.0" } }, "node_modules/scratch-l10n": { diff --git a/package.json b/package.json index 15829fb6fae..04d0a3d0e38 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "0.1.0-prerelease.20230527085947", + "scratch-blocks": "git+https://github.com/Nitro-Bolt/scratch-blocks.git#develop-builds", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", From 2a2ddf2064765b5fe0d157c4e90ed5c42d2daabd Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 13 Feb 2024 11:23:07 -0500 Subject: [PATCH 09/46] Add comments category (scratch-vm) --- src/blocks/scratch3_comments.js | 47 +++++++++++++++++++++++++++++++++ src/compiler/irgen.js | 33 +++++++++++++++++++++++ src/compiler/jsgen.js | 19 +++++++++++++ src/engine/runtime.js | 3 ++- src/serialization/sb3.js | 3 ++- 5 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/blocks/scratch3_comments.js diff --git a/src/blocks/scratch3_comments.js b/src/blocks/scratch3_comments.js new file mode 100644 index 00000000000..e6120895f9c --- /dev/null +++ b/src/blocks/scratch3_comments.js @@ -0,0 +1,47 @@ +const Cast = require('../util/cast'); + +class Scratch3CommentsBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + } + + /** + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. + */ + getPrimitives () { + return { + comments_hat: this.hat, + comments_command: this.command, + comments_loop: this.loop, + comments_reporter: this.reporter, + comments_boolean: this.boolean + }; + } + + hat (args) { + // No Operation; + } + + command (args) { + // No Operation; + } + + loop (args, util) { + util.startBranch(1, false); + } + + reporter (args) { + return args.VALUE; + } + + boolean (args) { + return Cast.toBoolean(args.VALUE); + } +} + +module.exports = Scratch3CommentsBlocks; diff --git a/src/compiler/irgen.js b/src/compiler/irgen.js index c3679fb41a7..0de885d24bb 100644 --- a/src/compiler/irgen.js +++ b/src/compiler/irgen.js @@ -647,6 +647,20 @@ class ScriptTreeGenerator { value: block.fields.SOUND_MENU.value }; + case 'comments_reporter': + return { + kind: 'comments.reporter', + value: this.descendInputOfBlock(block, 'VALUE'), + comment: this.descendInputOfBlock(block, 'COMMENT'), + }; + + case 'comments_boolean': + return { + kind: 'comments.boolean', + value: this.descendInputOfBlock(block, 'VALUE'), + comment: this.descendInputOfBlock(block, 'COMMENT'), + }; + case 'tw_getLastKeyPressed': return { kind: 'tw.lastKeyPressed' @@ -1147,6 +1161,25 @@ class ScriptTreeGenerator { kind: 'timer.reset' }; + case 'comments_hat': + return { + kind: 'comments.hat', + comment: this.descendInputOfBlock(block, 'COMMENT') + }; + + case 'comments_command': + return { + kind: 'comments.command', + comment: this.descendInputOfBlock(block, 'COMMENT') + }; + + case 'comments_loop': + return { + kind: 'comments.loop', + comment: this.descendInputOfBlock(block, 'COMMENT'), + do: this.descendSubstack(block, 'SUBSTACK') + }; + default: { const opcodeFunction = this.runtime.getOpcodeFunction(block.opcode); if (opcodeFunction) { diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index ff07f3f0d57..a7dbb4efd39 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -745,6 +745,12 @@ class JSGenerator { case 'var.get': return this.descendVariable(node.variable); + case 'comments.reporter': + return new TypedInput(`${this.descendInput(node.value).asString()}`, TYPE_STRING) + + case 'comments.boolean': + return new TypedInput(`${this.descendInput(node.value).asBoolean()}`, TYPE_BOOLEAN) + default: log.warn(`JS: Unknown input: ${node.kind}`, node); throw new Error(`JS: Unknown input: ${node.kind}`); @@ -1159,6 +1165,19 @@ class JSGenerator { this.source += `runtime.monitorBlocks.changeBlock({ id: "${sanitize(node.variable.id)}", element: "checkbox", value: true }, runtime);\n`; break; + case 'comments.hat': + this.source += `\n`; + break; + + case 'comments.command': + this.source += `\n`; + break; + + case 'comments.loop': + this.source += `\n`; + this.descendStack(node.do, new Frame(true)); + break; + case 'visualReport': { const value = this.localVariables.next(); this.source += `const ${value} = ${this.descendInput(node.input).asUnknown()};`; diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 6fc794268f7..17a388e0254 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -44,7 +44,8 @@ const defaultBlockPackages = { scratch3_sound: require('../blocks/scratch3_sound'), scratch3_sensing: require('../blocks/scratch3_sensing'), scratch3_data: require('../blocks/scratch3_data'), - scratch3_procedures: require('../blocks/scratch3_procedures') + scratch3_procedures: require('../blocks/scratch3_procedures'), + scratch3_comments: require('../blocks/scratch3_comments') }; const interpolate = require('./tw-interpolate'); diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index b760e05bb08..eb00729c9b8 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -54,7 +54,8 @@ const CORE_EXTENSIONS = [ 'operator', 'procedures', 'sensing', - 'sound' + 'sound', + 'comments' ]; // Constants referring to 'primitive' blocks that are usually shadows, From bfd41f227b786735371785057e40cc2edc0a7487 Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 13 Feb 2024 12:43:47 -0500 Subject: [PATCH 10/46] Update `scratch-blocks` --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 4c684942f42..7ab1ab3128d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13299,7 +13299,7 @@ }, "node_modules/scratch-blocks": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#40e2e6c08fc43121f804ab6786a8d8e577ab7897", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#5388237baef32456418e4b9629bf74f59d820456", "dev": true, "license": "GPL-3.0", "dependencies": { From fd1b5b40963d61222751dfe20fa2b596c437e9a5 Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 2 Apr 2024 16:58:19 -0400 Subject: [PATCH 11/46] Update Dependencies --- package-lock.json | 5 ++--- package.json | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index b747217c307..52871267dad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "git+https://github.com/Nitro-Bolt/scratch-blocks.git#develop-builds", + "scratch-blocks": "github:Nitro-Bolt/scratch-blocks#develop-builds", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", @@ -13307,9 +13307,8 @@ }, "node_modules/scratch-blocks": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#5388237baef32456418e4b9629bf74f59d820456", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#850f557e3a9cb94bbeec62078f277ed8417da839", "dev": true, - "license": "GPL-3.0", "dependencies": { "exports-loader": "0.7.0", "imports-loader": "0.8.0" diff --git a/package.json b/package.json index 352363ef294..bfc208fb89b 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "lodash.defaultsdeep": "4.6.1", "pngjs": "3.3.3", "scratch-audio": "0.1.0-prerelease.20231221012053", - "scratch-blocks": "git+https://github.com/Nitro-Bolt/scratch-blocks.git#develop-builds", + "scratch-blocks": "github:Nitro-Bolt/scratch-blocks#develop-builds", "scratch-l10n": "3.16.20231222031921", "scratch-render": "0.1.0-prerelease.20231220210403", "scratch-render-fonts": "1.0.0-prerelease.20231017225105", From f64f5d3ab4114c7c2400fa7ab34c0422bdc5c15a Mon Sep 17 00:00:00 2001 From: Cubester Date: Wed, 3 Apr 2024 17:46:18 -0400 Subject: [PATCH 12/46] Update Dependencies --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 52871267dad..3f9b60541b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13307,7 +13307,7 @@ }, "node_modules/scratch-blocks": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#850f557e3a9cb94bbeec62078f277ed8417da839", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#f0fb4fec02e09a2bdd1349992a19d3cdb7a66749", "dev": true, "dependencies": { "exports-loader": "0.7.0", From 3229b7a9c6e97b5f699c4d4c4ab9ce2e67b6703d Mon Sep 17 00:00:00 2001 From: Cubester Date: Mon, 15 Apr 2024 18:31:30 -0400 Subject: [PATCH 13/46] Add Dependabot --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..8cfebe16ea2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + allow: + - dependency-name: "scratch-blocks" From 2ec233a4cd2b77f958a3a18da0d96ef172c3b8e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:33:16 +0000 Subject: [PATCH 14/46] Bump scratch-blocks from `f0fb4fe` to `a1441f1` Bumps [scratch-blocks](https://github.com/Nitro-Bolt/scratch-blocks) from `f0fb4fe` to `a1441f1`. - [Commits](https://github.com/Nitro-Bolt/scratch-blocks/compare/f0fb4fec02e09a2bdd1349992a19d3cdb7a66749...a1441f16aa851d5559588ee9bea92bbd62cd0249) --- updated-dependencies: - dependency-name: scratch-blocks dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 3f9b60541b6..41937c38235 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13307,8 +13307,10 @@ }, "node_modules/scratch-blocks": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#f0fb4fec02e09a2bdd1349992a19d3cdb7a66749", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#a1441f16aa851d5559588ee9bea92bbd62cd0249", + "integrity": "sha512-Hp9ZM/bP2i7WjMteKXAlAtiHAcGjGSYNr6+uIcvfjBsf0vpmgLIylUAjXRovMD3NEXcm1ZAii6ntpYyxejz7IQ==", "dev": true, + "license": "GPL-3.0", "dependencies": { "exports-loader": "0.7.0", "imports-loader": "0.8.0" From a4c9f9b134bac04ea182fa4cfa0a74233a725ec9 Mon Sep 17 00:00:00 2001 From: Cubester <78769806+CubesterYT@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:38:50 -0400 Subject: [PATCH 15/46] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bfc208fb89b..8d3d44bd997 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "htmlparser2": "3.10.0", "immutable": "3.8.2", "jszip": "^3.1.5", - "scratch-parser": "github:TurboWarp/scratch-parser#master", + "scratch-parser": "github:Nitro-Bolt/scratch-parser#master", "scratch-sb1-converter": "0.2.7", "scratch-translate-extension-languages": "0.0.20191118205314", "text-encoding": "0.7.0", From 5bd5283e6701a7b8d76f453b36e4d9a537ce235f Mon Sep 17 00:00:00 2001 From: Cubester <78769806+CubesterYT@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:39:49 -0400 Subject: [PATCH 16/46] Update Dependabot --- .github/dependabot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8cfebe16ea2..9ac9d8ccf4c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,3 +6,4 @@ updates: interval: "daily" allow: - dependency-name: "scratch-blocks" + - dependency-name: "scratch-parser" From 90058ffc79208d0b17e445be8c5d533163c0a776 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:00:34 +0000 Subject: [PATCH 17/46] Bump scratch-parser from `663ea98` to `e2ab58d` Bumps [scratch-parser](https://github.com/Nitro-Bolt/scratch-parser) from `663ea98` to `e2ab58d`. - [Commits](https://github.com/Nitro-Bolt/scratch-parser/compare/663ea983c66649419ff31312dced694d0a5bea77...e2ab58d88307cd1ac0d2d40068f8197dd42d3b9a) --- updated-dependencies: - dependency-name: scratch-parser dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41937c38235..70f001bc99f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "htmlparser2": "3.10.0", "immutable": "3.8.2", "jszip": "^3.1.5", - "scratch-parser": "github:TurboWarp/scratch-parser#master", + "scratch-parser": "github:Nitro-Bolt/scratch-parser#master", "scratch-sb1-converter": "0.2.7", "scratch-translate-extension-languages": "0.0.20191118205314", "text-encoding": "0.7.0", @@ -13336,7 +13336,8 @@ }, "node_modules/scratch-parser": { "version": "0.0.0-development", - "resolved": "git+ssh://git@github.com/TurboWarp/scratch-parser.git#663ea983c66649419ff31312dced694d0a5bea77", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-parser.git#e2ab58d88307cd1ac0d2d40068f8197dd42d3b9a", + "integrity": "sha512-ug3yQEppb9GvJ5fvft7VQC974X73F9n9+ree7i7vgKrrHWTWNTZOD6NgaYY257o24185+FB03wkVxG6CEgo0/Q==", "license": "MPL-2.0", "dependencies": { "@turbowarp/json": "^0.1.1", From 593ff16b43a8739c3ca3e6a9d06ff3ab555ad7ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:24:05 +0000 Subject: [PATCH 18/46] Bump scratch-blocks from `a1441f1` to `29de013` Bumps [scratch-blocks](https://github.com/Nitro-Bolt/scratch-blocks) from `a1441f1` to `29de013`. - [Commits](https://github.com/Nitro-Bolt/scratch-blocks/compare/a1441f16aa851d5559588ee9bea92bbd62cd0249...29de013fa21daacad41c80e074a1838c69b3ab67) --- updated-dependencies: - dependency-name: scratch-blocks dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 70f001bc99f..0dc0a49c100 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13307,8 +13307,8 @@ }, "node_modules/scratch-blocks": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#a1441f16aa851d5559588ee9bea92bbd62cd0249", - "integrity": "sha512-Hp9ZM/bP2i7WjMteKXAlAtiHAcGjGSYNr6+uIcvfjBsf0vpmgLIylUAjXRovMD3NEXcm1ZAii6ntpYyxejz7IQ==", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#29de013fa21daacad41c80e074a1838c69b3ab67", + "integrity": "sha512-JtAn4QNFr5TzZT+ztfJxbgBlgrugF9a55vGikUu1VSUiNajEL7Vdc9xgJSSSxtVvTqJXOSIzB6LNWY9INBfiGw==", "dev": true, "license": "GPL-3.0", "dependencies": { From ed4db15c218968d6e4b5105afdf30dbe17cfb899 Mon Sep 17 00:00:00 2001 From: Cubester Date: Sat, 11 May 2024 19:42:54 -0400 Subject: [PATCH 19/46] Update Platform --- src/engine/tw-platform.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/tw-platform.js b/src/engine/tw-platform.js index ee6a2e2d627..823a32cb237 100644 --- a/src/engine/tw-platform.js +++ b/src/engine/tw-platform.js @@ -2,6 +2,6 @@ // This can be accessed externally on `vm.runtime.platform` module.exports = { - name: 'TurboWarp', - url: 'https://turbowarp.org/' + name: 'NitroBolt', + url: 'https://github.com/Nitro-Bolt/' }; From 60b01380d2cb87da8c49311bc0328e97626dc3ef Mon Sep 17 00:00:00 2001 From: Cubester Date: Wed, 15 May 2024 18:08:33 -0400 Subject: [PATCH 20/46] Add `Scratch.extensions.isNitroBolt` --- src/extension-support/tw-unsandboxed-extension-runner.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extension-support/tw-unsandboxed-extension-runner.js b/src/extension-support/tw-unsandboxed-extension-runner.js index 45312e76ac4..fec5ba81891 100644 --- a/src/extension-support/tw-unsandboxed-extension-runner.js +++ b/src/extension-support/tw-unsandboxed-extension-runner.js @@ -35,6 +35,7 @@ const setupUnsandboxedExtensionAPI = vm => new Promise(resolve => { const Scratch = Object.assign({}, global.Scratch || {}, ScratchCommon); Scratch.extensions = { unsandboxed: true, + isNitroBolt: true, register }; Scratch.vm = vm; From b3c39f506a6eea7fd5918056c488dfb60b775d58 Mon Sep 17 00:00:00 2001 From: Cubester Date: Sun, 19 May 2024 16:03:03 -0400 Subject: [PATCH 21/46] Add Extension Manager Modal (scratch-vm) --- src/engine/runtime.js | 24 +++++++++++++++++++++- src/extension-support/extension-manager.js | 21 +++++++++++++++++++ src/virtual-machine.js | 1 + 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 2a97976caca..b3c2db1c287 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -1092,7 +1092,29 @@ class Runtime extends EventEmitter { } /** - * Reregister the primitives for an extension + * Remove the primitives of an extension + * @param {ExtensionMetadata} extensionId - Id of the target extension + * @private + */ + _removeExtensionPrimitive(extensionId) { + const extensionIndex = this._blockInfo.findIndex(extension => extension.id === extensionId); + const info = this._blockInfo[extensionIndex]; + this._blockInfo.splice(extensionIndex, 1); + this.emit(Runtime.EXTENSION_REMOVED); + // Clean up blocks + for (const target of this.targets) { + for (const blockId in target.blocks._blocks) { + const {opcode} = target.blocks.getBlock(blockId); + if (info.blocks.find(block => block.json?.type === opcode)) { + target.blocks.deleteBlock(blockId, true); + } + } + } + this.emit(Runtime.BLOCKS_NEED_UPDATE); + } + + /** + * Register the primitives for an extension * @param {ExtensionMetadata} extensionInfo - new info (results of running getInfo) for an extension * @private */ diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index a092af35482..de9004ebe7a 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -259,6 +259,27 @@ class ExtensionManager { }).catch(error => this._failedLoadingExtensionScript(error)); } + /** + * Unload an extension by URL or internal extension ID + * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension + * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure + */ + removeExtension(extensionURL) { + if (!this.isExtensionLoaded(extensionURL)) { + const message = `Rejecting attempt to remove an unloaded extension with ID ${extensionURL}`; + log.warn(message); + return; + } + const serviceName = this._loadedExtensions.get(extensionURL); + delete dispatch.services[serviceName]; + delete this.runtime[`ext_${extensionURL}`]; + this._loadedExtensions.delete(extensionURL); + const workerId = +serviceName.split('.')[1]; + delete this.workerURLs[workerId]; + dispatch.call('runtime', '_removeExtensionPrimitive', extensionURL); + this.refreshBlocks(); + } + /** * Wait until all async extensions have loaded * @returns {Promise} resolved when all async extensions have loaded diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 46d26c05a90..5e8c4cf7d7f 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -51,6 +51,7 @@ const createRuntimeService = runtime => { const service = {}; service._refreshExtensionPrimitives = runtime._refreshExtensionPrimitives.bind(runtime); service._registerExtensionPrimitives = runtime._registerExtensionPrimitives.bind(runtime); + service._removeExtensionPrimitive = runtime._removeExtensionPrimitive.bind(runtime); return service; }; From 2825a27ab5d4998e7154906151334b137895a95e Mon Sep 17 00:00:00 2001 From: Cubester Date: Sun, 19 May 2024 16:16:53 -0400 Subject: [PATCH 22/46] Quite down, lint --- src/engine/runtime.js | 2 +- src/extension-support/extension-manager.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index b3c2db1c287..017d5608211 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -1096,7 +1096,7 @@ class Runtime extends EventEmitter { * @param {ExtensionMetadata} extensionId - Id of the target extension * @private */ - _removeExtensionPrimitive(extensionId) { + _removeExtensionPrimitive (extensionId) { const extensionIndex = this._blockInfo.findIndex(extension => extension.id === extensionId); const info = this._blockInfo[extensionIndex]; this._blockInfo.splice(extensionIndex, 1); diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index de9004ebe7a..a7dc96040ef 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -264,7 +264,7 @@ class ExtensionManager { * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure */ - removeExtension(extensionURL) { + removeExtension (extensionURL) { if (!this.isExtensionLoaded(extensionURL)) { const message = `Rejecting attempt to remove an unloaded extension with ID ${extensionURL}`; log.warn(message); From 2777239692254903b3d36e7e733b7b391439044c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 10:51:56 +0000 Subject: [PATCH 23/46] Bump scratch-parser from `e2ab58d` to `51b5a05` Bumps [scratch-parser](https://github.com/Nitro-Bolt/scratch-parser) from `e2ab58d` to `51b5a05`. - [Commits](https://github.com/Nitro-Bolt/scratch-parser/compare/e2ab58d88307cd1ac0d2d40068f8197dd42d3b9a...51b5a0565f5d7ab391198597565b5e7229b0f056) --- updated-dependencies: - dependency-name: scratch-parser dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 564417cc505..42ff5e0832a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10234,6 +10234,49 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -13343,8 +13386,8 @@ }, "node_modules/scratch-parser": { "version": "0.0.0-development", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-parser.git#e2ab58d88307cd1ac0d2d40068f8197dd42d3b9a", - "integrity": "sha512-ug3yQEppb9GvJ5fvft7VQC974X73F9n9+ree7i7vgKrrHWTWNTZOD6NgaYY257o24185+FB03wkVxG6CEgo0/Q==", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-parser.git#51b5a0565f5d7ab391198597565b5e7229b0f056", + "integrity": "sha512-fXT7JY+37+t/HR6lORZ+fsk8Z6s/tmuwGO5RjmDyIuseViGDBo+zYoPl0LrpNm1+Y0Yko9wnoJ288YEuHWiiEg==", "license": "MPL-2.0", "dependencies": { "@turbowarp/json": "^0.1.1", From 9fe30ef238f06384e3a8bacf9c6ca7e7bf13f1ef Mon Sep 17 00:00:00 2001 From: Cubester Date: Sat, 22 Jun 2024 05:00:09 -0400 Subject: [PATCH 24/46] =?UTF-8?q?Add=20reordering=20support=20to=20Extensi?= =?UTF-8?q?on=20Manager=20=F0=9F=8E=89=20(scratch-vm)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/engine/runtime.js | 17 +++++++++++++++++ src/extension-support/extension-manager.js | 20 ++++++++++++++++++++ src/virtual-machine.js | 1 + 3 files changed, 38 insertions(+) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 7245fe17110..e982bbf1d60 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -1091,6 +1091,23 @@ class Runtime extends EventEmitter { this.emit(Runtime.EXTENSION_ADDED, categoryInfo); } + /** + * Reorder the primitives of an extension + * @param {ExtensionMetadata} extensionIndex - Index of the target extension + * @param {ExtensionMetadata} reorderIndex - Reorder Index of the target extension + * @private + */ + _reorderExtensionPrimitive (extensionIndex, reorderIndex) { + if (reorderIndex >= this._blockInfo.length) { + const padding = reorderIndex - this._blockInfo + 1; + while (padding--) { + this._blockInfo.push(undefined); + } + } + this._blockInfo.splice(reorderIndex, 0, this._blockInfo.splice(extensionIndex, 1)[0]); + this.emit(Runtime.EXTENSION_REORDERED); + } + /** * Remove the primitives of an extension * @param {ExtensionMetadata} extensionId - Id of the target extension diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index a7dc96040ef..6254c919102 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -259,6 +259,26 @@ class ExtensionManager { }).catch(error => this._failedLoadingExtensionScript(error)); } + /** + * Reorder an extension by using current index and reorder to index + * @param {string} extensionIndex - the index of the extension to reorder + * @param {string} reorderIndex - the index to reorder the extension to + * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure + */ + reorderExtension (extensionIndex, reorderIndex) { + let extensions = Array.from(this._loadedExtensions); + if (reorderIndex >= extensions.length) { + const padding = reorderIndex - extensions + 1; + while (padding--) { + extensions.push(undefined); + } + } + extensions.splice(reorderIndex, 0, extensions.splice(extensionIndex, 1)[0]); + this._loadedExtensions = new Map(extensions.map((extension) => [extension[0], extension[1]])); + dispatch.call('runtime', '_reorderExtensionPrimitive', extensionIndex, reorderIndex); + this.refreshBlocks(); + } + /** * Unload an extension by URL or internal extension ID * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 5e934f11b98..b3b6b7752e9 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -51,6 +51,7 @@ const createRuntimeService = runtime => { const service = {}; service._refreshExtensionPrimitives = runtime._refreshExtensionPrimitives.bind(runtime); service._registerExtensionPrimitives = runtime._registerExtensionPrimitives.bind(runtime); + service._reorderExtensionPrimitive = runtime._reorderExtensionPrimitive.bind(runtime); service._removeExtensionPrimitive = runtime._removeExtensionPrimitive.bind(runtime); return service; }; From 70bf5de07169b2c949c3c8df07ef4bb70513a3cf Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 9 Jul 2024 22:26:10 -0400 Subject: [PATCH 25/46] Add the new Block and Argument Type (JSON) (scratch-vm) --- src/blocks/scratch3_comments.js | 12 +- src/blocks/scratch3_json.js | 152 +++++++++++++++++++++++++ src/blocks/scratch3_procedures.js | 20 +++- src/compiler/compat-blocks.js | 24 +++- src/compiler/irgen.js | 37 +++++- src/compiler/jsexecute.js | 38 +++++++ src/compiler/jsgen.js | 44 ++++++- src/engine/runtime.js | 22 +++- src/engine/scratch-blocks-constants.js | 11 +- src/extension-support/argument-type.js | 10 ++ src/extension-support/block-type.js | 10 ++ src/serialization/sb3.js | 4 +- src/util/cast.js | 41 +++++++ src/util/string-util.js | 5 +- src/util/xml-escape.js | 5 +- 15 files changed, 418 insertions(+), 17 deletions(-) create mode 100644 src/blocks/scratch3_json.js diff --git a/src/blocks/scratch3_comments.js b/src/blocks/scratch3_comments.js index e6120895f9c..20508be8f51 100644 --- a/src/blocks/scratch3_comments.js +++ b/src/blocks/scratch3_comments.js @@ -19,7 +19,9 @@ class Scratch3CommentsBlocks { comments_command: this.command, comments_loop: this.loop, comments_reporter: this.reporter, - comments_boolean: this.boolean + comments_boolean: this.boolean, + comments_object: this.object, + comments_array: this.array }; } @@ -42,6 +44,14 @@ class Scratch3CommentsBlocks { boolean (args) { return Cast.toBoolean(args.VALUE); } + + object (args) { + return Cast.toObject(args.VALUE); + } + + array (args) { + return Cast.toArray(args.VALUE); + } } module.exports = Scratch3CommentsBlocks; diff --git a/src/blocks/scratch3_json.js b/src/blocks/scratch3_json.js new file mode 100644 index 00000000000..3aabca2e815 --- /dev/null +++ b/src/blocks/scratch3_json.js @@ -0,0 +1,152 @@ +const Cast = require('../util/cast'); + +class Scratch3JSONBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + } + + /** + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. + */ + getPrimitives () { + return { + json_new_object: this.newObject, + json_to_object: this.toObject_, + json_to_string: this.toString_, + json_keys: this.keys, + json_values: this.values, + json_value_of_key: this.valueOfKey, + json_set_key: this.setKey, + json_delete_key: this.deleteKey, + json_join_object: this.joinObject, + json_has_key: this.hasKey, + json_new_array: this.newArray, + json_to_array: this.toArray_, + json_value_of_index: this.valueOfIndex, + json_index_of_value: this.indexOfValue, + json_add_item: this.addItem, + json_replace_index: this.replaceIndex, + json_delete_index: this.deleteIndex, + json_delete_all_occurrences: this.deleteAllOccurrences, + json_join_array: this.joinArray, + json_has_item: this.hasItem + }; + } + + newObject () { + return new Object(); + } + + toObject_ (args) { + return Cast.toObject(args.STR); + } + + toString_ (args) { + return Cast.toString(args.OBJ); + } + + keys (args) { + args.OBJ = Cast.toObject(args.OBJ); + return Object.keys(args.OBJ); + } + + values (args) { + args.OBJ = Cast.toObject(args.OBJ); + return Object.values(args.OBJ); + } + + valueOfKey (args) { + args.OBJ = Cast.toObject(args.OBJ); + return args.OBJ[args.KEY] ?? ""; + } + + setKey (args) { + args.OBJ = Cast.toObject(args.OBJ); + args.OBJ[args.KEY] = args.VALUE; + return args.OBJ; + } + + deleteKey (args) { + args.OBJ = Cast.toObject(args.OBJ); + delete args.OBJ[args.KEY]; + return args.OBJ; + } + + joinObject (args) { + args.OBJ1 = Cast.toObject(args.OBJ1); + args.OBJ2 = Cast.toObject(args.OBJ2); + return {...args.OBJ1, ...args.OBJ2}; + } + + hasKey (args) { + args.OBJ = Cast.toObject(args.OBJ); + return args.OBJ.hasOwnProperty(args.KEY); + } + + newArray () { + return new Array(); + } + + toArray_ (args) { + return Cast.toArray(args.STR); + } + + valueOfIndex (args) { + args.ARR = Cast.toArray(args.ARR); + return args.ARR[args.INDEX] ?? ""; + } + + indexOfValue (args) { + args.ARR = Cast.toArray(args.ARR); + return args.ARR.indexOf(args.VALUE) !== -1 ? args.ARR.indexOf(args.VALUE) : ""; + } + + addItem (args) { + args.ARR = Cast.toArray(args.ARR); + args.ARR.push(args.ITEM); + return args.ARR; + } + + replaceIndex (args) { + args.ARR = Cast.toArray(args.ARR); + if (args.INDEX >= 0 && args.INDEX < args.ARR.length) { + args.ARR[args.INDEX] = args.ITEM; + return args.ARR; + } else { + return new Array(); + } + } + + deleteIndex (args) { + args.ARR = Cast.toArray(args.ARR); + if (args.INDEX >= 0 && args.INDEX < args.ARR.length) { + args.ARR.splice(args.INDEX, 1); + return args.ARR; + } else { + return new Array(); + } + } + + deleteAllOccurrences (args) { + args.ARR = Cast.toArray(args.ARR); + return args.ARR.filter((item) => item !== args.ITEM); + } + + joinArray (args) { + args.ARR1 = Cast.toArray(args.ARR1); + args.ARR2 = Cast.toArray(args.ARR2); + return [...args.ARR1, ...args.ARR2]; + } + + hasItem (args) { + args.ARR = Cast.toArray(args.ARR); + return args.ARR.includes(args.ITEM); + } +} + +module.exports = Scratch3JSONBlocks; diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index 679fdf128e8..f6d88ff8b2d 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -17,7 +17,9 @@ class Scratch3ProcedureBlocks { procedures_call: this.call, procedures_return: this.return, argument_reporter_string_number: this.argumentReporterStringNumber, - argument_reporter_boolean: this.argumentReporterBoolean + argument_reporter_boolean: this.argumentReporterBoolean, + argument_reporter_object: this.argumentReporterObject, + argument_reporter_array: this.argumentReporterArray }; } @@ -131,6 +133,22 @@ class Scratch3ProcedureBlocks { } return value; } + + argumentReporterObject (args, util) { + const value = util.getParam(args.VALUE); + if (value === null) { + return 0; + } + return value; + } + + argumentReporterArray (args, util) { + const value = util.getParam(args.VALUE); + if (value === null) { + return 0; + } + return value; + } } module.exports = Scratch3ProcedureBlocks; diff --git a/src/compiler/compat-blocks.js b/src/compiler/compat-blocks.js index 1c9d8f5ae9a..298af369d19 100644 --- a/src/compiler/compat-blocks.js +++ b/src/compiler/compat-blocks.js @@ -39,7 +39,29 @@ const inputs = [ 'sensing_loud', 'sensing_loudness', 'sensing_userid', - 'sound_volume' + 'sound_volume', + 'json_new_object', + 'json_to_object', + 'json_to_string', + 'json_keys', + 'json_values', + 'json_value_of_key', + 'json_set_key', + 'json_delete_key', + 'json_join_object', + 'json_has_key', + 'json_new_array', + 'json_to_array', + 'json_value_of_index', + 'json_index_of_value', + 'json_add_item', + 'json_replace_index', + 'json_delete_index', + 'json_delete_all_occurrences', + 'json_join_array', + 'json_has_item', + 'comments_object', + 'comments_array' ]; module.exports = { diff --git a/src/compiler/irgen.js b/src/compiler/irgen.js index 10053282dba..2d5239fdddd 100644 --- a/src/compiler/irgen.js +++ b/src/compiler/irgen.js @@ -227,6 +227,36 @@ class ScriptTreeGenerator { index: index }; } + case 'argument_reporter_object': { + // see argument_reporter_string_number above + const name = block.fields.VALUE.value; + const index = this.script.arguments.lastIndexOf(name); + if (index === -1) { + return { + kind: 'constant', + value: 0 + }; + } + return { + kind: 'args.object', + index: index + }; + } + case 'argument_reporter_array': { + // see argument_reporter_string_number above + const name = block.fields.VALUE.value; + const index = this.script.arguments.lastIndexOf(name); + if (index === -1) { + return { + kind: 'constant', + value: 0 + }; + } + return { + kind: 'args.array', + index: index + }; + } case 'control_get_counter': return { @@ -678,7 +708,12 @@ class ScriptTreeGenerator { const blockInfo = this.getBlockInfo(block.opcode); if (blockInfo) { const type = blockInfo.info.blockType; - if (type === BlockType.REPORTER || type === BlockType.BOOLEAN) { + if ( + type === BlockType.REPORTER + || type === BlockType.BOOLEAN + || type === BlockType.OBJECT + || type === BlockType.ARRAY + ) { return this.descendCompatLayer(block); } } diff --git a/src/compiler/jsexecute.js b/src/compiler/jsexecute.js index b7bff8956ad..fb239600d62 100644 --- a/src/compiler/jsexecute.js +++ b/src/compiler/jsexecute.js @@ -237,6 +237,44 @@ runtimeFunctions.toBoolean = `const toBoolean = value => { return !!value; }`; +/** + * Scratch cast to object. + * Similar to Cast.toObject() + * @param {*} value The value to cast + * @returns {object} The value cast to a object + */ +runtimeFunctions.toObject = `const toObject = value => { + if (typeof value === 'object' && !Array.isArray(value)) { + return value; + } else if (typeof value === 'number') { + return new Object(); + } + try { + return JSON.parse(value); + } catch { + return new Object(); + } +}`; + +/** + * Scratch cast to array. + * Similar to Cast.toArray() + * @param {*} value The value to cast + * @returns {array} The value cast to a array + */ +runtimeFunctions.toArray = `const toArray = value => { + if (Array.isArray(value)) { + return value; + } else if (typeof value === 'number' || typeof value === 'object') { + return new Array(); + } + try { + return JSON.parse(value); + } catch { + return new Array(); + } +}`; + /** * If a number is very close to a whole number, round to that whole number. * @param {number} value Value to round diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index abb0df7d091..81459acee55 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -27,8 +27,10 @@ const sanitize = string => { const TYPE_NUMBER = 1; const TYPE_STRING = 2; const TYPE_BOOLEAN = 3; -const TYPE_UNKNOWN = 4; -const TYPE_NUMBER_NAN = 5; +const TYPE_OBJECT = 4; +const TYPE_ARRAY = 5; +const TYPE_UNKNOWN = 6; +const TYPE_NUMBER_NAN = 7; // Pen-related constants const PEN_EXT = 'runtime.ext_pen'; @@ -55,6 +57,8 @@ const generatorNameVariablePool = new VariablePool('gen'); * @property {() => string} asNumberOrNaN * @property {() => string} asString * @property {() => string} asBoolean + * @property {() => string} asObject + * @property {() => string} asArray * @property {() => string} asColor * @property {() => string} asUnknown * @property {() => string} asSafe @@ -95,6 +99,16 @@ class TypedInput { return `toBoolean(${this.source})`; } + asObject () { + if (this.type === TYPE_OBJECT) return this.source; + return `toObject(${this.source})`; + } + + asArray () { + if (this.type === TYPE_ARRAY) return this.source; + return `toArray(${this.source})`; + } + asColor () { return this.asUnknown(); } @@ -157,6 +171,16 @@ class ConstantInput { return Cast.toBoolean(this.constantValue).toString(); } + asObject () { + // Compute at compilation time + return Cast.toObject(this.constantValue); + } + + asArray () { + // Compute at compilation time + return Cast.toArray(this.constantValue); + } + asColor () { // Attempt to parse hex code at compilation time if (/^#[0-9a-f]{6,8}$/i.test(this.constantValue)) { @@ -266,6 +290,16 @@ class VariableInput { return `toBoolean(${this.source})`; } + asObject () { + if (this.type === TYPE_OBJECT) return this.source; + return `toObject(${this.source})`; + } + + asArray () { + if (this.type === TYPE_ARRAY) return this.source; + return `toArray(${this.source})`; + } + asColor () { return this.asUnknown(); } @@ -435,6 +469,10 @@ class JSGenerator { case 'args.boolean': return new TypedInput(`toBoolean(p${node.index})`, TYPE_BOOLEAN); + case 'args.object': + return new TypedInput(`toObject(p${node.index})`, TYPE_OBJECT); + case 'args.array': + return new TypedInput(`toArray(p${node.index})`, TYPE_ARRAY); case 'args.stringNumber': return new TypedInput(`p${node.index}`, TYPE_UNKNOWN); @@ -1455,6 +1493,8 @@ JSGenerator.unstable_exports = { TYPE_NUMBER, TYPE_STRING, TYPE_BOOLEAN, + TYPE_OBJECT, + TYPE_ARRAY, TYPE_UNKNOWN, TYPE_NUMBER_NAN, factoryNameVariablePool, diff --git a/src/engine/runtime.js b/src/engine/runtime.js index e982bbf1d60..00c50515382 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -45,16 +45,18 @@ const defaultBlockPackages = { scratch3_sound: require('../blocks/scratch3_sound'), scratch3_sensing: require('../blocks/scratch3_sensing'), scratch3_data: require('../blocks/scratch3_data'), + scratch3_json: require('../blocks/scratch3_json'), scratch3_procedures: require('../blocks/scratch3_procedures'), scratch3_comments: require('../blocks/scratch3_comments') }; const interpolate = require('./tw-interpolate'); const FrameLoop = require('./tw-frame-loop'); +const Cast = require('../util/cast.js'); const defaultExtensionColors = ['#0FBD8C', '#0DA57A', '#0B8E69']; -const COMMENT_CONFIG_MAGIC = ' // _twconfig_'; +const COMMENT_CONFIG_MAGIC = ' // _nbconfig_'; /** * Information used for converting Scratch argument types into scratch-blocks data. @@ -96,6 +98,12 @@ const ArgumentTypeMap = (() => { map[ArgumentType.BOOLEAN] = { check: 'Boolean' }; + map[ArgumentType.OBJECT] = { + check: 'Object' + }; + map[ArgumentType.ARRAY] = { + check: 'Array' + }; map[ArgumentType.MATRIX] = { shadow: { type: 'matrix', @@ -1467,6 +1475,14 @@ class Runtime extends EventEmitter { blockJSON.nextStatement = null; // null = available connection; undefined = terminal } break; + case BlockType.OBJECT: + blockJSON.output = 'Object'; + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_OBJECT; + break; + case BlockType.ARRAY: + blockJSON.output = 'Array'; + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; + break; } const blockText = Array.isArray(blockInfo.text) ? blockInfo.text : [blockInfo.text]; @@ -2949,7 +2965,7 @@ class Runtime extends EventEmitter { storeProjectOptions () { const options = this.generateDifferingProjectOptions(); // TODO: translate - const text = `Configuration for https://turbowarp.org/\nYou can move, resize, and minimize this comment, but don't edit it by hand. This comment can be deleted to remove the stored settings.\n${ExtendedJSON.stringify(options)}${COMMENT_CONFIG_MAGIC}`; + const text = `Configuration for https://github.com/Nitro-Bolt/\nYou can move, resize, and minimize this comment, but don't edit it by hand. This comment can be deleted to remove the stored settings.\n${ExtendedJSON.stringify(options)}${COMMENT_CONFIG_MAGIC}`; const existingComment = this.findProjectOptionsComment(); if (existingComment) { existingComment.text = text; @@ -3116,7 +3132,7 @@ class Runtime extends EventEmitter { * @param {string} value Value to show associated with the block. */ visualReport (blockId, value) { - this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); + this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: Cast.toString(value)}); } /** diff --git a/src/engine/scratch-blocks-constants.js b/src/engine/scratch-blocks-constants.js index ee558549705..1f6ba894711 100644 --- a/src/engine/scratch-blocks-constants.js +++ b/src/engine/scratch-blocks-constants.js @@ -12,16 +12,21 @@ const ScratchBlocksConstants = { OUTPUT_SHAPE_HEXAGONAL: 1, /** - * ENUM for output shape: rounded (numbers). + * ENUM for output shape: rounded (strings and numbers). * @const */ OUTPUT_SHAPE_ROUND: 2, /** - * ENUM for output shape: squared (any/all values; strings). + * ENUM for output shape: squared (array). * @const */ - OUTPUT_SHAPE_SQUARE: 3 + OUTPUT_SHAPE_SQUARE: 3, + + /** + * ENUM for output shape: object (object). + */ + OUTPUT_SHAPE_OBJECT: 4 }; module.exports = ScratchBlocksConstants; diff --git a/src/extension-support/argument-type.js b/src/extension-support/argument-type.js index 2e8c13fff84..75aa4342c7a 100644 --- a/src/extension-support/argument-type.js +++ b/src/extension-support/argument-type.js @@ -28,6 +28,16 @@ const ArgumentType = { */ STRING: 'string', + /** + * Object value with object shaped placeholder. + */ + OBJECT: 'Object', + + /** + * Array value with array shaped placeholder. + */ + ARRAY: 'Array', + /** * String value with matrix field */ diff --git a/src/extension-support/block-type.js b/src/extension-support/block-type.js index ecaf70754dc..954df9f4c62 100644 --- a/src/extension-support/block-type.js +++ b/src/extension-support/block-type.js @@ -51,6 +51,16 @@ const BlockType = { */ REPORTER: 'reporter', + /** + * Object Shaped Block that returns pure Objects + */ + OBJECT: 'Object', + + /** + * Array Shaped Block that returns pure Arrays. + */ + ARRAY: 'Array', + /** * Arbitrary scratch-blocks XML. */ diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 6b13150b0c1..36351982415 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -48,6 +48,7 @@ const CORE_EXTENSIONS = [ 'colour', 'control', 'data', + 'json', 'event', 'looks', 'math', @@ -489,7 +490,8 @@ const serializeSound = function (sound) { const isVariableValueSafeForJSON = value => ( typeof value === 'number' || typeof value === 'string' || - typeof value === 'boolean' + typeof value === 'boolean' || + typeof value === 'object' ); const makeSafeForJSON = value => { if (Array.isArray(value)) { diff --git a/src/util/cast.js b/src/util/cast.js index d78717abc66..300a080ba08 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -92,9 +92,50 @@ class Cast { * @return {string} The Scratch-casted string value. */ static toString (value) { + if (typeof value === 'undefined'|| typeof value === 'null') { + return String(); + } else if (typeof value === 'object') { + return JSON.stringify(value); + } return String(value); } + /** + * Scratch cast to object. + * @param {*} value Value to cast to object. + * @return {object} The Scratch-casted object value. + */ + static toObject (value) { + if (typeof value === 'object' && !Array.isArray(value)) { + return value; + } else if (typeof value === 'number') { + return new Object(); + } + try { + return JSON.parse(value); + } catch { + return new Object(); + } + } + + /** + * Scratch cast to array. + * @param {*} value Value to cast to array. + * @return {array} The Scratch-casted array value. + */ + static toArray (value) { + if (Array.isArray(value)) { + return value; + } else if (typeof value === 'number' || typeof value === 'object') { + return new Array(); + } + try { + return JSON.parse(value); + } catch { + return new Array(); + } + } + /** * Cast any Scratch argument to an RGB color array to be used for the renderer. * @param {*} value Value to convert to RGB color array. diff --git a/src/util/string-util.js b/src/util/string-util.js index 6fc99530f96..aa8f5ca2011 100644 --- a/src/util/string-util.js +++ b/src/util/string-util.js @@ -1,3 +1,4 @@ +const Cast = require('../util/cast.js'); const log = require('./log'); class StringUtil { @@ -70,10 +71,10 @@ class StringUtil { */ static replaceUnsafeChars (unsafe) { if (typeof unsafe !== 'string') { - if (Array.isArray(unsafe)) { + if (typeof unsafe === 'object') { // This happens when we have hacked blocks from 2.0 // See #1030 - unsafe = String(unsafe); + unsafe = Cast.toString(unsafe); } else { log.error('Unexpected input recieved in replaceUnsafeChars'); return unsafe; diff --git a/src/util/xml-escape.js b/src/util/xml-escape.js index eebad772f55..2636f8bf5af 100644 --- a/src/util/xml-escape.js +++ b/src/util/xml-escape.js @@ -1,3 +1,4 @@ +const Cast = require('../util/cast.js'); const log = require('./log'); /** @@ -10,10 +11,10 @@ const log = require('./log'); */ const xmlEscape = function (unsafe) { if (typeof unsafe !== 'string') { - if (Array.isArray(unsafe)) { + if (typeof unsafe === 'object') { // This happens when we have hacked blocks from 2.0 // See #1030 - unsafe = String(unsafe); + unsafe = Cast.toString(unsafe); } else { log.error('Unexpected input recieved in replaceUnsafeChars'); return unsafe; From 496a68ef990223ca538117efd6ef6926f3b3834c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:50:42 +0000 Subject: [PATCH 26/46] Bump scratch-blocks from `29de013` to `7e31f46` Bumps [scratch-blocks](https://github.com/Nitro-Bolt/scratch-blocks) from `29de013` to `7e31f46`. - [Commits](https://github.com/Nitro-Bolt/scratch-blocks/compare/29de013fa21daacad41c80e074a1838c69b3ab67...7e31f46f9444016d212975fca7439fa8627ab854) --- updated-dependencies: - dependency-name: scratch-blocks dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42ff5e0832a..009e8f48b91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13357,8 +13357,8 @@ }, "node_modules/scratch-blocks": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#29de013fa21daacad41c80e074a1838c69b3ab67", - "integrity": "sha512-JtAn4QNFr5TzZT+ztfJxbgBlgrugF9a55vGikUu1VSUiNajEL7Vdc9xgJSSSxtVvTqJXOSIzB6LNWY9INBfiGw==", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#7e31f46f9444016d212975fca7439fa8627ab854", + "integrity": "sha512-oHa6k8QUNqf6/ymVovYuHkv7nIeGyfMxmP+6GrvvKKwK5M3+MOjpiZeS4qTrVdDL9hOAwYa04zV6cNjPjhbUDA==", "dev": true, "license": "GPL-3.0", "dependencies": { From 1ab25f8eb3f80024e3763154581663635e8dd4f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:50:54 +0000 Subject: [PATCH 27/46] Bump scratch-parser from `51b5a05` to `3b5dc5d` Bumps [scratch-parser](https://github.com/Nitro-Bolt/scratch-parser) from `51b5a05` to `3b5dc5d`. - [Commits](https://github.com/Nitro-Bolt/scratch-parser/compare/51b5a0565f5d7ab391198597565b5e7229b0f056...3b5dc5d7874683d1b5c191f76cb10ffca40c5a6f) --- updated-dependencies: - dependency-name: scratch-parser dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42ff5e0832a..20bdf78efc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13386,8 +13386,8 @@ }, "node_modules/scratch-parser": { "version": "0.0.0-development", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-parser.git#51b5a0565f5d7ab391198597565b5e7229b0f056", - "integrity": "sha512-fXT7JY+37+t/HR6lORZ+fsk8Z6s/tmuwGO5RjmDyIuseViGDBo+zYoPl0LrpNm1+Y0Yko9wnoJ288YEuHWiiEg==", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-parser.git#3b5dc5d7874683d1b5c191f76cb10ffca40c5a6f", + "integrity": "sha512-Avsk5C2H367agJx4cC09luz5HJ7fIp3fAsagUVWIr/6E+e2IMAbuzo633ocjFshUCNZoDsDh625bCvcJq2JGMQ==", "license": "MPL-2.0", "dependencies": { "@turbowarp/json": "^0.1.1", From cebc5bd6d0faee25e6153a8f86b975f7300e13a0 Mon Sep 17 00:00:00 2001 From: Cubester Date: Wed, 10 Jul 2024 22:18:17 -0400 Subject: [PATCH 28/46] Changes and Bug Fixes (scratch-vm) --- src/blocks/scratch3_data.js | 2 +- src/blocks/scratch3_json.js | 8 ++++---- src/compiler/compat-blocks.js | 4 ++-- src/compiler/jsexecute.js | 11 ++++++++++- src/util/cast.js | 4 +++- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/blocks/scratch3_data.js b/src/blocks/scratch3_data.js index 1a2e5b86eea..b80c5041c79 100644 --- a/src/blocks/scratch3_data.js +++ b/src/blocks/scratch3_data.js @@ -119,7 +119,7 @@ class Scratch3DataBlocks { if (allSingleLetters) { return list.value.join(''); } - return list.value.join(' '); + return list.value.map(item => Cast.toString(item)).join(' '); } diff --git a/src/blocks/scratch3_json.js b/src/blocks/scratch3_json.js index 3aabca2e815..8c578e97c2d 100644 --- a/src/blocks/scratch3_json.js +++ b/src/blocks/scratch3_json.js @@ -23,7 +23,7 @@ class Scratch3JSONBlocks { json_value_of_key: this.valueOfKey, json_set_key: this.setKey, json_delete_key: this.deleteKey, - json_join_object: this.joinObject, + json_merge_object: this.mergeObject, json_has_key: this.hasKey, json_new_array: this.newArray, json_to_array: this.toArray_, @@ -33,7 +33,7 @@ class Scratch3JSONBlocks { json_replace_index: this.replaceIndex, json_delete_index: this.deleteIndex, json_delete_all_occurrences: this.deleteAllOccurrences, - json_join_array: this.joinArray, + json_merge_array: this.mergeArray, json_has_item: this.hasItem }; } @@ -77,7 +77,7 @@ class Scratch3JSONBlocks { return args.OBJ; } - joinObject (args) { + mergeObject (args) { args.OBJ1 = Cast.toObject(args.OBJ1); args.OBJ2 = Cast.toObject(args.OBJ2); return {...args.OBJ1, ...args.OBJ2}; @@ -137,7 +137,7 @@ class Scratch3JSONBlocks { return args.ARR.filter((item) => item !== args.ITEM); } - joinArray (args) { + mergeArray (args) { args.ARR1 = Cast.toArray(args.ARR1); args.ARR2 = Cast.toArray(args.ARR2); return [...args.ARR1, ...args.ARR2]; diff --git a/src/compiler/compat-blocks.js b/src/compiler/compat-blocks.js index 298af369d19..c6d5a7f1917 100644 --- a/src/compiler/compat-blocks.js +++ b/src/compiler/compat-blocks.js @@ -48,7 +48,7 @@ const inputs = [ 'json_value_of_key', 'json_set_key', 'json_delete_key', - 'json_join_object', + 'json_merge_object', 'json_has_key', 'json_new_array', 'json_to_array', @@ -58,7 +58,7 @@ const inputs = [ 'json_replace_index', 'json_delete_index', 'json_delete_all_occurrences', - 'json_join_array', + 'json_merge_array', 'json_has_item', 'comments_object', 'comments_array' diff --git a/src/compiler/jsexecute.js b/src/compiler/jsexecute.js index fb239600d62..9751edb371d 100644 --- a/src/compiler/jsexecute.js +++ b/src/compiler/jsexecute.js @@ -246,6 +246,8 @@ runtimeFunctions.toBoolean = `const toBoolean = value => { runtimeFunctions.toObject = `const toObject = value => { if (typeof value === 'object' && !Array.isArray(value)) { return value; + } else if (Array.isArray(value) || Array.isArray(this.toArray(value))) { + return Object.fromEntries(value.map((item, index) => [index, item])); } else if (typeof value === 'number') { return new Object(); } @@ -564,7 +566,14 @@ runtimeFunctions.listContents = `const listContents = list => { // this is an intentional break from what scratch 3 does to address our automatic string -> number conversions // it fixes more than it breaks if ((listItem + '').length !== 1) { - return list.value.join(' '); + return list.value.map(value => { + if (typeof value === 'undefined' || typeof value === 'null') { + return String(); + } else if (typeof value === 'object') { + return JSON.stringify(value); + } + return String(value); + }).join(' '); } } return list.value.join(''); diff --git a/src/util/cast.js b/src/util/cast.js index 300a080ba08..96027c31de2 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -92,7 +92,7 @@ class Cast { * @return {string} The Scratch-casted string value. */ static toString (value) { - if (typeof value === 'undefined'|| typeof value === 'null') { + if (typeof value === 'undefined' || typeof value === 'null') { return String(); } else if (typeof value === 'object') { return JSON.stringify(value); @@ -108,6 +108,8 @@ class Cast { static toObject (value) { if (typeof value === 'object' && !Array.isArray(value)) { return value; + } else if (Array.isArray(value) || Array.isArray(this.toArray(value))) { + return Object.fromEntries(value.map((item, index) => [index, item])); } else if (typeof value === 'number') { return new Object(); } From 738fa49c8c3dcc85510e7cf9062a459b4d1aaff1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:28:25 +0000 Subject: [PATCH 29/46] Bump scratch-blocks from `7e31f46` to `c581278` Bumps [scratch-blocks](https://github.com/Nitro-Bolt/scratch-blocks) from `7e31f46` to `c581278`. - [Commits](https://github.com/Nitro-Bolt/scratch-blocks/compare/7e31f46f9444016d212975fca7439fa8627ab854...c581278726d9e09243ca428e74cc75e565f3f411) --- updated-dependencies: - dependency-name: scratch-blocks dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1f78296dae7..b7b708a11c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13357,8 +13357,8 @@ }, "node_modules/scratch-blocks": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#7e31f46f9444016d212975fca7439fa8627ab854", - "integrity": "sha512-oHa6k8QUNqf6/ymVovYuHkv7nIeGyfMxmP+6GrvvKKwK5M3+MOjpiZeS4qTrVdDL9hOAwYa04zV6cNjPjhbUDA==", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#c581278726d9e09243ca428e74cc75e565f3f411", + "integrity": "sha512-W+OBw3JizOkpi6HQatanMT4MMnWqFgOHvMhLVNNHoG+OBdFu9SiczleQvzRbgh1KzeQ7UWP+xwWSsK96a2pmMA==", "dev": true, "license": "GPL-3.0", "dependencies": { From ad8e51aa01e6d906b6d9d7f7dbb02d9ada535447 Mon Sep 17 00:00:00 2001 From: Cubester Date: Sat, 13 Jul 2024 20:38:28 -0400 Subject: [PATCH 30/46] =?UTF-8?q?Compiler=20Support=20for=20the=20JSON=20c?= =?UTF-8?q?ategory=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/blocks/scratch3_json.js | 14 ++++ src/compiler/compat-blocks.js | 24 +------ src/compiler/irgen.js | 129 +++++++++++++++++++++++++++++++++- src/compiler/jsexecute.js | 17 ++++- src/compiler/jsgen.js | 74 ++++++++++++++++--- src/util/cast.js | 2 - 6 files changed, 221 insertions(+), 39 deletions(-) diff --git a/src/blocks/scratch3_json.js b/src/blocks/scratch3_json.js index 8c578e97c2d..db4c54c4e4b 100644 --- a/src/blocks/scratch3_json.js +++ b/src/blocks/scratch3_json.js @@ -43,10 +43,12 @@ class Scratch3JSONBlocks { } toObject_ (args) { + args.STR = Cast.toString(args.STR); return Cast.toObject(args.STR); } toString_ (args) { + args.OBJ = Cast.toObject(args.OBJ); return Cast.toString(args.OBJ); } @@ -62,17 +64,20 @@ class Scratch3JSONBlocks { valueOfKey (args) { args.OBJ = Cast.toObject(args.OBJ); + args.KEY = Cast.toString(args.KEY); return args.OBJ[args.KEY] ?? ""; } setKey (args) { args.OBJ = Cast.toObject(args.OBJ); + args.KEY = Cast.toString(args.KEY); args.OBJ[args.KEY] = args.VALUE; return args.OBJ; } deleteKey (args) { args.OBJ = Cast.toObject(args.OBJ); + args.KEY = Cast.toString(args.KEY); delete args.OBJ[args.KEY]; return args.OBJ; } @@ -85,6 +90,7 @@ class Scratch3JSONBlocks { hasKey (args) { args.OBJ = Cast.toObject(args.OBJ); + args.KEY = Cast.toString(args.KEY); return args.OBJ.hasOwnProperty(args.KEY); } @@ -93,27 +99,32 @@ class Scratch3JSONBlocks { } toArray_ (args) { + args.STR = Cast.toString(args.STR); return Cast.toArray(args.STR); } valueOfIndex (args) { args.ARR = Cast.toArray(args.ARR); + args.INDEX = Cast.toNumber(args.INDEX); return args.ARR[args.INDEX] ?? ""; } indexOfValue (args) { args.ARR = Cast.toArray(args.ARR); + args.VALUE = Cast.toString(args.VALUE); return args.ARR.indexOf(args.VALUE) !== -1 ? args.ARR.indexOf(args.VALUE) : ""; } addItem (args) { args.ARR = Cast.toArray(args.ARR); + args.ITEM = Cast.toString(args.ITEM); args.ARR.push(args.ITEM); return args.ARR; } replaceIndex (args) { args.ARR = Cast.toArray(args.ARR); + args.INDEX = Cast.toNumber(args.INDEX); if (args.INDEX >= 0 && args.INDEX < args.ARR.length) { args.ARR[args.INDEX] = args.ITEM; return args.ARR; @@ -124,6 +135,7 @@ class Scratch3JSONBlocks { deleteIndex (args) { args.ARR = Cast.toArray(args.ARR); + args.INDEX = Cast.toNumber(args.INDEX); if (args.INDEX >= 0 && args.INDEX < args.ARR.length) { args.ARR.splice(args.INDEX, 1); return args.ARR; @@ -134,6 +146,7 @@ class Scratch3JSONBlocks { deleteAllOccurrences (args) { args.ARR = Cast.toArray(args.ARR); + args.ITEM = Cast.toString(args.ITEM); return args.ARR.filter((item) => item !== args.ITEM); } @@ -145,6 +158,7 @@ class Scratch3JSONBlocks { hasItem (args) { args.ARR = Cast.toArray(args.ARR); + args.ITEM = Cast.toString(args.ITEM); return args.ARR.includes(args.ITEM); } } diff --git a/src/compiler/compat-blocks.js b/src/compiler/compat-blocks.js index c6d5a7f1917..1c9d8f5ae9a 100644 --- a/src/compiler/compat-blocks.js +++ b/src/compiler/compat-blocks.js @@ -39,29 +39,7 @@ const inputs = [ 'sensing_loud', 'sensing_loudness', 'sensing_userid', - 'sound_volume', - 'json_new_object', - 'json_to_object', - 'json_to_string', - 'json_keys', - 'json_values', - 'json_value_of_key', - 'json_set_key', - 'json_delete_key', - 'json_merge_object', - 'json_has_key', - 'json_new_array', - 'json_to_array', - 'json_value_of_index', - 'json_index_of_value', - 'json_add_item', - 'json_replace_index', - 'json_delete_index', - 'json_delete_all_occurrences', - 'json_merge_array', - 'json_has_item', - 'comments_object', - 'comments_array' + 'sound_volume' ]; module.exports = { diff --git a/src/compiler/irgen.js b/src/compiler/irgen.js index 2d5239fdddd..f50e81e261d 100644 --- a/src/compiler/irgen.js +++ b/src/compiler/irgen.js @@ -297,6 +297,120 @@ class ScriptTreeGenerator { list: this.descendVariable(block, 'LIST', LIST_TYPE) }; + case 'json_new_object': + return { + kind: 'json.newObject' + }; + case 'json_to_object': + return { + kind: 'json.toObject_', + string: this.descendInputOfBlock(block, 'STR') + }; + case 'json_to_string': + return { + kind: 'json.toString_', + object: this.descendInputOfBlock(block, 'OBJ') + }; + case 'json_keys': + return { + kind: 'json.keys', + object: this.descendInputOfBlock(block, 'OBJ') + }; + case 'json_values': + return { + kind: 'json.values', + object: this.descendInputOfBlock(block, 'OBJ') + }; + case 'json_value_of_key': + return { + kind: 'json.valueOfKey', + key: this.descendInputOfBlock(block, 'KEY'), + object: this.descendInputOfBlock(block, 'OBJ') + }; + case 'json_set_key': + return { + kind: 'json.setKey', + key: this.descendInputOfBlock(block, 'KEY'), + object: this.descendInputOfBlock(block, 'OBJ'), + value: this.descendInputOfBlock(block, 'VALUE') + }; + case 'json_delete_key': + return { + kind: 'json.deleteKey', + key: this.descendInputOfBlock(block, 'KEY'), + object: this.descendInputOfBlock(block, 'OBJ') + }; + case 'json_merge_object': + return { + kind: 'json.mergeObject', + object1: this.descendInputOfBlock(block, 'OBJ1'), + object2: this.descendInputOfBlock(block, 'OBJ2') + }; + case 'json_has_key': + return { + kind: 'json.hasKey', + object: this.descendInputOfBlock(block, 'OBJ'), + key: this.descendInputOfBlock(block, 'KEY') + }; + case 'json_new_array': + return { + kind: 'json.newArray' + }; + case 'json_to_array': + return { + kind: 'json.toArray_', + string: this.descendInputOfBlock(block, 'STR') + }; + case 'json_value_of_index': + return { + kind: 'json.valueOfIndex', + index: this.descendInputOfBlock(block, 'INDEX'), + array: this.descendInputOfBlock(block, 'ARR') + }; + case 'json_index_of_value': + return { + kind: 'json.indexOfValue', + value: this.descendInputOfBlock(block, 'VALUE'), + array: this.descendInputOfBlock(block, 'ARR') + }; + case 'json_add_item': + return { + kind: 'json.addItem', + item: this.descendInputOfBlock(block, 'ITEM'), + array: this.descendInputOfBlock(block, 'ARR') + }; + case 'json_replace_index': + return { + kind: 'json.replaceIndex', + index: this.descendInputOfBlock(block, 'INDEX'), + array: this.descendInputOfBlock(block, 'ARR'), + item: this.descendInputOfBlock(block, 'ITEM') + }; + case 'json_delete_index': + return { + kind: 'json.deleteIndex', + index: this.descendInputOfBlock(block, 'INDEX'), + array: this.descendInputOfBlock(block, 'ARR') + }; + case 'json_delete_all_occurrences': + return { + kind: 'json.deleteAllOccurrences', + item: this.descendInputOfBlock(block, 'ITEM'), + array: this.descendInputOfBlock(block, 'ARR') + }; + case 'json_merge_array': + return { + kind: 'json.mergeArray', + array1: this.descendInputOfBlock(block, 'ARR1'), + array2: this.descendInputOfBlock(block, 'ARR2') + }; + case 'json_has_item': + return { + kind: 'json.hasItem', + array: this.descendInputOfBlock(block, 'ARR'), + item: this.descendInputOfBlock(block, 'ITEM') + }; + case 'event_broadcast_menu': { const broadcastOption = block.fields.BROADCAST_OPTION; const broadcastVariable = this.target.lookupBroadcastMsg(broadcastOption.id, broadcastOption.value); @@ -684,13 +798,24 @@ class ScriptTreeGenerator { value: this.descendInputOfBlock(block, 'VALUE'), comment: this.descendInputOfBlock(block, 'COMMENT'), }; - case 'comments_boolean': return { kind: 'comments.boolean', value: this.descendInputOfBlock(block, 'VALUE'), comment: this.descendInputOfBlock(block, 'COMMENT'), }; + case 'comments_object': + return { + kind: 'comments.object', + value: this.descendInputOfBlock(block, 'VALUE'), + comment: this.descendInputOfBlock(block, 'COMMENT'), + }; + case 'comments_array': + return { + kind: 'comments.array', + value: this.descendInputOfBlock(block, 'VALUE'), + comment: this.descendInputOfBlock(block, 'COMMENT'), + }; case 'tw_getLastKeyPressed': return { @@ -1202,13 +1327,11 @@ class ScriptTreeGenerator { kind: 'comments.hat', comment: this.descendInputOfBlock(block, 'COMMENT') }; - case 'comments_command': return { kind: 'comments.command', comment: this.descendInputOfBlock(block, 'COMMENT') }; - case 'comments_loop': return { kind: 'comments.loop', diff --git a/src/compiler/jsexecute.js b/src/compiler/jsexecute.js index 9751edb371d..d0876e8d421 100644 --- a/src/compiler/jsexecute.js +++ b/src/compiler/jsexecute.js @@ -218,6 +218,21 @@ runtimeFunctions.retire = `const retire = () => { thread.target.runtime.sequencer.retireThread(thread); }`; +/** + * Scratch cast to string. + * Similar to Cast.toString() + * @param {*} value The value to cast + * @returns {string} The value cast to a string + */ +runtimeFunctions.toString = `const toString = value => { + if (typeof value === 'undefined' || typeof value === 'null') { + return String(); + } else if (typeof value === 'object') { + return JSON.stringify(value); + } + return String(value); +}`; + /** * Scratch cast to boolean. * Similar to Cast.toBoolean() @@ -246,8 +261,6 @@ runtimeFunctions.toBoolean = `const toBoolean = value => { runtimeFunctions.toObject = `const toObject = value => { if (typeof value === 'object' && !Array.isArray(value)) { return value; - } else if (Array.isArray(value) || Array.isArray(this.toArray(value))) { - return Object.fromEntries(value.map((item, index) => [index, item])); } else if (typeof value === 'number') { return new Object(); } diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index 81459acee55..eeed1bb4565 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -24,6 +24,20 @@ const sanitize = string => { return JSON.stringify(string).slice(1, -1); }; +const stringify = (object, type) => { + if (typeof object === 'number') { + switch (type) { + case 'object': + object = '{}'; + break; + case 'array': + object = '[]'; + break; + } + } + return typeof object === 'string' ? object : JSON.stringify(object); +}; + const TYPE_NUMBER = 1; const TYPE_STRING = 2; const TYPE_BOOLEAN = 3; @@ -91,7 +105,7 @@ class TypedInput { asString () { if (this.type === TYPE_STRING) return this.source; - return `("" + ${this.source})`; + return `toString(${this.source})`; } asBoolean () { @@ -173,12 +187,12 @@ class ConstantInput { asObject () { // Compute at compilation time - return Cast.toObject(this.constantValue); + return `${stringify(this.constantValue, 'object')}`; } asArray () { // Compute at compilation time - return Cast.toArray(this.constantValue); + return `${stringify(this.constantValue, 'array')}`; } asColor () { @@ -282,7 +296,7 @@ class VariableInput { asString () { if (this.type === TYPE_STRING) return this.source; - return `("" + ${this.source})`; + return `toString(${this.source})`; } asBoolean () { @@ -510,6 +524,47 @@ class JSGenerator { case 'list.length': return new TypedInput(`${this.referenceVariable(node.list)}.value.length`, TYPE_NUMBER); + case 'json.newObject': + return new TypedInput('new Object()', TYPE_OBJECT); + case 'json.toObject_': + return new TypedInput(`${this.descendInput(node.string).asObject()}`, TYPE_OBJECT); + case 'json.toString_': + return new TypedInput(`toString(${this.descendInput(node.object).asObject()})`, TYPE_STRING); + case 'json.keys': + return new TypedInput(`Object.keys(${this.descendInput(node.object).asObject()})`, TYPE_ARRAY); + case 'json.values': + return new TypedInput(`Object.values(${this.descendInput(node.object).asObject()})`, TYPE_ARRAY); + case 'json.valueOfKey': + return new TypedInput(`${this.descendInput(node.object).asObject()}[${this.descendInput(node.key).asString()}] ?? ""`, TYPE_STRING); + case 'json.setKey': + return new TypedInput(`(object = ${this.descendInput(node.object).asObject()}, object[${this.descendInput(node.key).asString()}] = ${this.descendInput(node.value).asString()}, object)`, TYPE_OBJECT); + case 'json.deleteKey': + return new TypedInput(`(object = ${this.descendInput(node.object).asObject()}, delete object[${this.descendInput(node.key).asString()}], object)`, TYPE_OBJECT); + case 'json.mergeObject': + return new TypedInput(`{...${this.descendInput(node.object1).asObject()}, ...${this.descendInput(node.object2).asObject()}}`, TYPE_OBJECT); + case 'json.hasKey': + return new TypedInput(`${this.descendInput(node.object).asObject()}.hasOwnProperty(${this.descendInput(node.key).asString()})`, TYPE_BOOLEAN); + case 'json.newArray': + return new TypedInput('new Array()', TYPE_ARRAY); + case 'json.toArray_': + return new TypedInput(`${this.descendInput(node.string).asArray()}`, TYPE_ARRAY); + case 'json.valueOfIndex': + return new TypedInput(`${this.descendInput(node.array).asArray()}[${this.descendInput(node.index).asNumber()}] ?? ""`, TYPE_STRING); + case 'json.indexOfValue': + return new TypedInput(`${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asString()}) !== -1 ? ${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asString()}) : ""`, TYPE_STRING); + case 'json.addItem': + return new TypedInput(`(array = ${this.descendInput(node.array).asArray()}, array.push(${this.descendInput(node.item).asString()}), array)`, TYPE_ARRAY); + case 'json.replaceIndex': + return new TypedInput(`(${this.descendInput(node.index).asNumber()} >= 0 && ${this.descendInput(node.index).asNumber()} < ${this.descendInput(node.array).asArray()}.length ? (array = ${this.descendInput(node.array).asArray()}, array[${this.descendInput(node.index).asNumber()}] = ${this.descendInput(node.item).asString()}, array) : new Array())`, TYPE_ARRAY); + case 'json.deleteIndex': + return new TypedInput(`(${this.descendInput(node.index).asNumber()} >= 0 && ${this.descendInput(node.index).asNumber()} < ${this.descendInput(node.array).asArray()}.length ? (array = ${this.descendInput(node.array).asArray()}, array.splice(${this.descendInput(node.index).asNumber()}, 1), array) : new Array())`, TYPE_ARRAY); + case 'json.deleteAllOccurrences': + return new TypedInput(`${this.descendInput(node.array).asArray()}.filter((item) => item !== ${this.descendInput(node.item).asString()})`, TYPE_ARRAY); + case 'json.mergeArray': + return new TypedInput(`[...${this.descendInput(node.array1).asArray()}, ...${this.descendInput(node.array2).asArray()}]`, TYPE_ARRAY); + case 'json.hasItem': + return new TypedInput(`${this.descendInput(node.array).asArray()}.includes(${this.descendInput(node.item).asString()})`, TYPE_BOOLEAN); + case 'looks.size': return new TypedInput('Math.round(target.size)', TYPE_NUMBER); case 'looks.backdropName': @@ -784,10 +839,13 @@ class JSGenerator { return this.descendVariable(node.variable); case 'comments.reporter': - return new TypedInput(`${this.descendInput(node.value).asString()}`, TYPE_STRING) - + return new TypedInput(`${this.descendInput(node.value).asString()}`, TYPE_STRING); case 'comments.boolean': - return new TypedInput(`${this.descendInput(node.value).asBoolean()}`, TYPE_BOOLEAN) + return new TypedInput(`${this.descendInput(node.value).asBoolean()}`, TYPE_BOOLEAN); + case 'comments.object': + return new TypedInput(`${this.descendInput(node.value).asObject()}`, TYPE_OBJECT); + case 'comments.array': + return new TypedInput(`${this.descendInput(node.value).asArray()}`, TYPE_ARRAY); default: log.warn(`JS: Unknown input: ${node.kind}`, node); @@ -1206,11 +1264,9 @@ class JSGenerator { case 'comments.hat': this.source += `\n`; break; - case 'comments.command': this.source += `\n`; break; - case 'comments.loop': this.source += `\n`; this.descendStack(node.do, new Frame(true)); diff --git a/src/util/cast.js b/src/util/cast.js index 96027c31de2..2cbff227dea 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -108,8 +108,6 @@ class Cast { static toObject (value) { if (typeof value === 'object' && !Array.isArray(value)) { return value; - } else if (Array.isArray(value) || Array.isArray(this.toArray(value))) { - return Object.fromEntries(value.map((item, index) => [index, item])); } else if (typeof value === 'number') { return new Object(); } From 7e52fc0088c8d3890a5114348dc106cebe1fe073 Mon Sep 17 00:00:00 2001 From: Cubester <78769806+CubesterYT@users.noreply.github.com> Date: Sun, 14 Jul 2024 12:33:34 -0400 Subject: [PATCH 31/46] Add parenthesis to some outputs JS gets messed up in equations --- src/compiler/jsgen.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index eeed1bb4565..0e2332b6a07 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -535,7 +535,7 @@ class JSGenerator { case 'json.values': return new TypedInput(`Object.values(${this.descendInput(node.object).asObject()})`, TYPE_ARRAY); case 'json.valueOfKey': - return new TypedInput(`${this.descendInput(node.object).asObject()}[${this.descendInput(node.key).asString()}] ?? ""`, TYPE_STRING); + return new TypedInput(`(${this.descendInput(node.object).asObject()}[${this.descendInput(node.key).asString()}] ?? "")`, TYPE_STRING); case 'json.setKey': return new TypedInput(`(object = ${this.descendInput(node.object).asObject()}, object[${this.descendInput(node.key).asString()}] = ${this.descendInput(node.value).asString()}, object)`, TYPE_OBJECT); case 'json.deleteKey': @@ -549,9 +549,9 @@ class JSGenerator { case 'json.toArray_': return new TypedInput(`${this.descendInput(node.string).asArray()}`, TYPE_ARRAY); case 'json.valueOfIndex': - return new TypedInput(`${this.descendInput(node.array).asArray()}[${this.descendInput(node.index).asNumber()}] ?? ""`, TYPE_STRING); + return new TypedInput(`(${this.descendInput(node.array).asArray()}[${this.descendInput(node.index).asNumber()}] ?? "")`, TYPE_STRING); case 'json.indexOfValue': - return new TypedInput(`${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asString()}) !== -1 ? ${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asString()}) : ""`, TYPE_STRING); + return new TypedInput(`(${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asString()}) !== -1 ? ${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asString()}) : "")`, TYPE_STRING); case 'json.addItem': return new TypedInput(`(array = ${this.descendInput(node.array).asArray()}, array.push(${this.descendInput(node.item).asString()}), array)`, TYPE_ARRAY); case 'json.replaceIndex': From 237f54c992ea231db7a8602c23e3962d7e381252 Mon Sep 17 00:00:00 2001 From: Cubester Date: Mon, 16 Sep 2024 17:54:21 -0400 Subject: [PATCH 32/46] Also update this --- src/compiler/irgen.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/irgen.js b/src/compiler/irgen.js index e489e5ea493..d1cd9e06b81 100644 --- a/src/compiler/irgen.js +++ b/src/compiler/irgen.js @@ -238,7 +238,7 @@ class ScriptTreeGenerator { }; } return { - kind: 'args.object', + kind: 'procedures.argument', index: index }; } @@ -253,7 +253,7 @@ class ScriptTreeGenerator { }; } return { - kind: 'args.array', + kind: 'procedures.argument', index: index }; } From 2fcaf7c7322f38c04f4775aa6355e249699c4c3a Mon Sep 17 00:00:00 2001 From: Cubester Date: Mon, 16 Sep 2024 19:13:41 -0400 Subject: [PATCH 33/46] Fix JSON block issues with variables and allow any value to be used in objects and arrays --- src/blocks/scratch3_json.js | 3 --- src/compiler/jsgen.js | 14 +++++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/blocks/scratch3_json.js b/src/blocks/scratch3_json.js index db4c54c4e4b..32df19f6c47 100644 --- a/src/blocks/scratch3_json.js +++ b/src/blocks/scratch3_json.js @@ -111,13 +111,11 @@ class Scratch3JSONBlocks { indexOfValue (args) { args.ARR = Cast.toArray(args.ARR); - args.VALUE = Cast.toString(args.VALUE); return args.ARR.indexOf(args.VALUE) !== -1 ? args.ARR.indexOf(args.VALUE) : ""; } addItem (args) { args.ARR = Cast.toArray(args.ARR); - args.ITEM = Cast.toString(args.ITEM); args.ARR.push(args.ITEM); return args.ARR; } @@ -158,7 +156,6 @@ class Scratch3JSONBlocks { hasItem (args) { args.ARR = Cast.toArray(args.ARR); - args.ITEM = Cast.toString(args.ITEM); return args.ARR.includes(args.ITEM); } } diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index 7d10d694216..b00e5910721 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -528,9 +528,9 @@ class JSGenerator { case 'json.valueOfKey': return new TypedInput(`(${this.descendInput(node.object).asObject()}[${this.descendInput(node.key).asString()}] ?? "")`, TYPE_STRING); case 'json.setKey': - return new TypedInput(`(object = ${this.descendInput(node.object).asObject()}, object[${this.descendInput(node.key).asString()}] = ${this.descendInput(node.value).asString()}, object)`, TYPE_OBJECT); + return new TypedInput(`(object = {...${this.descendInput(node.object).asObject()}}, object[${this.descendInput(node.key).asString()}] = ${this.descendInput(node.value).asUnknown()}, object)`, TYPE_OBJECT); case 'json.deleteKey': - return new TypedInput(`(object = ${this.descendInput(node.object).asObject()}, delete object[${this.descendInput(node.key).asString()}], object)`, TYPE_OBJECT); + return new TypedInput(`(object = {...${this.descendInput(node.object).asObject()}}, delete object[${this.descendInput(node.key).asString()}], object)`, TYPE_OBJECT); case 'json.mergeObject': return new TypedInput(`{...${this.descendInput(node.object1).asObject()}, ...${this.descendInput(node.object2).asObject()}}`, TYPE_OBJECT); case 'json.hasKey': @@ -542,19 +542,19 @@ class JSGenerator { case 'json.valueOfIndex': return new TypedInput(`(${this.descendInput(node.array).asArray()}[${this.descendInput(node.index).asNumber()}] ?? "")`, TYPE_STRING); case 'json.indexOfValue': - return new TypedInput(`(${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asString()}) !== -1 ? ${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asString()}) : "")`, TYPE_STRING); + return new TypedInput(`(${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asUnknown()}) !== -1 ? ${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asUnknown()}) : "")`, TYPE_NUMBER); case 'json.addItem': - return new TypedInput(`(array = ${this.descendInput(node.array).asArray()}, array.push(${this.descendInput(node.item).asString()}), array)`, TYPE_ARRAY); + return new TypedInput(`(array = [...${this.descendInput(node.array).asArray()}], array.push(${this.descendInput(node.item).asUnknown()}), array)`, TYPE_ARRAY); case 'json.replaceIndex': - return new TypedInput(`(${this.descendInput(node.index).asNumber()} >= 0 && ${this.descendInput(node.index).asNumber()} < ${this.descendInput(node.array).asArray()}.length ? (array = ${this.descendInput(node.array).asArray()}, array[${this.descendInput(node.index).asNumber()}] = ${this.descendInput(node.item).asString()}, array) : new Array())`, TYPE_ARRAY); + return new TypedInput(`(${this.descendInput(node.index).asNumber()} >= 0 && ${this.descendInput(node.index).asNumber()} < ${this.descendInput(node.array).asArray()}.length ? (array = [...${this.descendInput(node.array).asArray()}], array[${this.descendInput(node.index).asNumber()}] = ${this.descendInput(node.item).asUnknown()}, array) : new Array())`, TYPE_ARRAY); case 'json.deleteIndex': - return new TypedInput(`(${this.descendInput(node.index).asNumber()} >= 0 && ${this.descendInput(node.index).asNumber()} < ${this.descendInput(node.array).asArray()}.length ? (array = ${this.descendInput(node.array).asArray()}, array.splice(${this.descendInput(node.index).asNumber()}, 1), array) : new Array())`, TYPE_ARRAY); + return new TypedInput(`(${this.descendInput(node.index).asNumber()} >= 0 && ${this.descendInput(node.index).asNumber()} < ${this.descendInput(node.array).asArray()}.length ? (array = [...${this.descendInput(node.array).asArray()}], array.splice(${this.descendInput(node.index).asNumber()}, 1), array) : new Array())`, TYPE_ARRAY); case 'json.deleteAllOccurrences': return new TypedInput(`${this.descendInput(node.array).asArray()}.filter((item) => item !== ${this.descendInput(node.item).asString()})`, TYPE_ARRAY); case 'json.mergeArray': return new TypedInput(`[...${this.descendInput(node.array1).asArray()}, ...${this.descendInput(node.array2).asArray()}]`, TYPE_ARRAY); case 'json.hasItem': - return new TypedInput(`${this.descendInput(node.array).asArray()}.includes(${this.descendInput(node.item).asString()})`, TYPE_BOOLEAN); + return new TypedInput(`${this.descendInput(node.array).asArray()}.includes(${this.descendInput(node.item).asUnknown()})`, TYPE_BOOLEAN); case 'looks.size': return new TypedInput('Math.round(target.size)', TYPE_NUMBER); From abbfae02bf61e65997414c0f21fa29b8fd19222d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:24:36 +0000 Subject: [PATCH 34/46] Bump scratch-parser from `3b5dc5d` to `25b8b36` Bumps [scratch-parser](https://github.com/Nitro-Bolt/scratch-parser) from `3b5dc5d` to `25b8b36`. - [Commits](https://github.com/Nitro-Bolt/scratch-parser/compare/3b5dc5d7874683d1b5c191f76cb10ffca40c5a6f...25b8b36c99a4a7641b48dfc9a43bca8fd66590f7) --- updated-dependencies: - dependency-name: scratch-parser dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b7b708a11c0..6569bf2fb99 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13386,8 +13386,8 @@ }, "node_modules/scratch-parser": { "version": "0.0.0-development", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-parser.git#3b5dc5d7874683d1b5c191f76cb10ffca40c5a6f", - "integrity": "sha512-Avsk5C2H367agJx4cC09luz5HJ7fIp3fAsagUVWIr/6E+e2IMAbuzo633ocjFshUCNZoDsDh625bCvcJq2JGMQ==", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-parser.git#25b8b36c99a4a7641b48dfc9a43bca8fd66590f7", + "integrity": "sha512-nbcTRdsSJxWCJ6/lX2NTI5MTZpl5Ui6xCG4ODA5u/6y7yrn4XZGsXSCMHiG7yCedhkWkpYbM3Lmwx/zVsPcrcQ==", "license": "MPL-2.0", "dependencies": { "@turbowarp/json": "^0.1.1", From 0ff54887f8c1758ae0bb8168603cde6bd3eeb8c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 11:00:38 +0000 Subject: [PATCH 35/46] Bump scratch-blocks from `c581278` to `460d154` Bumps [scratch-blocks](https://github.com/Nitro-Bolt/scratch-blocks) from `c581278` to `460d154`. - [Commits](https://github.com/Nitro-Bolt/scratch-blocks/compare/c581278726d9e09243ca428e74cc75e565f3f411...460d15463635516b967318a622b4810a7b2efc53) --- updated-dependencies: - dependency-name: scratch-blocks dependency-version: 460d15463635516b967318a622b4810a7b2efc53 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f6ae9f494a0..36abecdd050 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13546,8 +13546,8 @@ }, "node_modules/scratch-blocks": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#c581278726d9e09243ca428e74cc75e565f3f411", - "integrity": "sha512-W+OBw3JizOkpi6HQatanMT4MMnWqFgOHvMhLVNNHoG+OBdFu9SiczleQvzRbgh1KzeQ7UWP+xwWSsK96a2pmMA==", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#460d15463635516b967318a622b4810a7b2efc53", + "integrity": "sha512-d8pLSVt1qV3Nq0P2D1th2B/lVmm1hmQqwUE6SBnn1VtJRzhge6SKAtcB9BQQqxyGB//aCEHUesdOAtGnFoDYLQ==", "dev": true, "license": "GPL-3.0", "dependencies": { From 636e4cb58b7f4373b686a722e076591273a9a4e3 Mon Sep 17 00:00:00 2001 From: Cubester Date: Tue, 29 Apr 2025 19:36:49 -0400 Subject: [PATCH 36/46] Final JSON fixes --- src/blocks/scratch3_looks.js | 6 +++--- src/compiler/jsgen.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index e84e0f83893..c0dc67c6aac 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -256,7 +256,7 @@ class Scratch3LooksBlocks { } // Limit the length of the string. - text = String(text).substr(0, Scratch3LooksBlocks.SAY_BUBBLE_LIMIT); + text = Cast.toString(text).substr(0, Scratch3LooksBlocks.SAY_BUBBLE_LIMIT); return text; } @@ -395,7 +395,7 @@ class Scratch3LooksBlocks { target.setCostume(optZeroIndex ? requestedCostume : requestedCostume - 1); } else { // Strings should be treated as costume names, where possible - const costumeIndex = target.getCostumeIndexByName(requestedCostume.toString()); + const costumeIndex = target.getCostumeIndexByName(Cast.toString(requestedCostume)); if (costumeIndex !== -1) { target.setCostume(costumeIndex); @@ -429,7 +429,7 @@ class Scratch3LooksBlocks { stage.setCostume(optZeroIndex ? requestedBackdrop : requestedBackdrop - 1); } else { // Strings should be treated as backdrop names where possible - const costumeIndex = stage.getCostumeIndexByName(requestedBackdrop.toString()); + const costumeIndex = stage.getCostumeIndexByName(Cast.toString(requestedBackdrop)); if (costumeIndex !== -1) { stage.setCostume(costumeIndex); diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index c4f3448af93..348f60bcce8 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -177,7 +177,7 @@ class ConstantInput { } asString () { - return `"${sanitize('' + this.constantValue)}"`; + return `"${sanitize('' + this.constantValue)}"`; // Should this be updated to use "Cast.toString"? } asBoolean () { From 588a9fbd2865dd7c03aa5f73aa2d410cd3436b7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 10:33:15 +0000 Subject: [PATCH 37/46] Bump scratch-parser from `25b8b36` to `e71bc6f` Bumps [scratch-parser](https://github.com/Nitro-Bolt/scratch-parser) from `25b8b36` to `e71bc6f`. - [Commits](https://github.com/Nitro-Bolt/scratch-parser/compare/25b8b36c99a4a7641b48dfc9a43bca8fd66590f7...e71bc6facf0510919dd7c80c44ed7bd9b659ce83) --- updated-dependencies: - dependency-name: scratch-parser dependency-version: e71bc6facf0510919dd7c80c44ed7bd9b659ce83 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 36abecdd050..8d23229d480 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13575,8 +13575,8 @@ }, "node_modules/scratch-parser": { "version": "0.0.0-development", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-parser.git#25b8b36c99a4a7641b48dfc9a43bca8fd66590f7", - "integrity": "sha512-nbcTRdsSJxWCJ6/lX2NTI5MTZpl5Ui6xCG4ODA5u/6y7yrn4XZGsXSCMHiG7yCedhkWkpYbM3Lmwx/zVsPcrcQ==", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-parser.git#e71bc6facf0510919dd7c80c44ed7bd9b659ce83", + "integrity": "sha512-/+JVRxpb9xtppxw90zsLCpyxKIZ+uKnHoEyZ4X/3qHm8Kvf80oSawSYjF/EqJ3dgXEIishMRk143URSgfQoR4A==", "license": "MPL-2.0", "dependencies": { "@turbowarp/json": "^0.1.1", From 0c8c7053f08298822bedefb6562e3b66ed8fcbb0 Mon Sep 17 00:00:00 2001 From: Cubester Date: Wed, 30 Apr 2025 23:06:48 -0400 Subject: [PATCH 38/46] First round of rebranding (scratch-vm) --- src/blocks/scratch3_procedures.js | 2 +- src/compiler/irgen.js | 2 +- src/extensions/tw/index.js | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index f6d88ff8b2d..35828be032b 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -124,7 +124,7 @@ class Scratch3ProcedureBlocks { if (util.target.runtime.compilerOptions.enabled && lowercaseValue === 'is compiled?') { return true; } - if (lowercaseValue === 'is turbowarp?') { + if (lowercaseValue === 'is nitrobolt?') { return true; } // When the parameter is not found in the most recent procedure diff --git a/src/compiler/irgen.js b/src/compiler/irgen.js index a4d3278ac71..4c3137939a9 100644 --- a/src/compiler/irgen.js +++ b/src/compiler/irgen.js @@ -206,7 +206,7 @@ class ScriptTreeGenerator { const name = block.fields.VALUE.value; const index = this.script.arguments.lastIndexOf(name); if (index === -1) { - if (name.toLowerCase() === 'is compiled?' || name.toLowerCase() === 'is turbowarp?') { + if (name.toLowerCase() === 'is compiled?' || name.toLowerCase() === 'is nitrobolt?') { return { kind: 'constant', value: true diff --git a/src/extensions/tw/index.js b/src/extensions/tw/index.js index 9485ff006cf..c2e495e8b62 100644 --- a/src/extensions/tw/index.js +++ b/src/extensions/tw/index.js @@ -7,7 +7,7 @@ const Cast = require('../../util/cast'); const iconURI = `data:image/svg+xml;base64,${btoa('')}`; /** - * Class for TurboWarp blocks + * Class for NitroBolt blocks * @constructor */ class TurboWarpBlocks { @@ -25,13 +25,13 @@ class TurboWarpBlocks { getInfo () { return { id: 'tw', - name: 'TurboWarp', - color1: '#ff4c4c', - color2: '#e64444', - color3: '#c73a3a', + name: 'NitroBolt', + color1: '#ff5726', + color2: '#f34b1a', + color3: '#e63e0d', docsURI: 'https://docs.turbowarp.org/blocks', - menuIconURI: iconURI, - blockIconURI: iconURI, + // menuIconURI: iconURI, + // blockIconURI: iconURI, blocks: [ { opcode: 'getLastKeyPressed', From 0651e62eb4951a2a21e0618e7734567d6e69dd49 Mon Sep 17 00:00:00 2001 From: Miyo Sho <135030944+yuri-kiss@users.noreply.github.com> Date: Thu, 1 May 2025 01:49:34 -0400 Subject: [PATCH 39/46] nitpick branding fix --- src/extensions/tw/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extensions/tw/index.js b/src/extensions/tw/index.js index c2e495e8b62..a259a9c185c 100644 --- a/src/extensions/tw/index.js +++ b/src/extensions/tw/index.js @@ -10,7 +10,7 @@ const iconURI = `data:image/svg+xml;base64,${btoa(' Date: Sun, 4 May 2025 18:07:14 -0400 Subject: [PATCH 41/46] Fix object / array support (cuz cube f***ed it up) --- src/compiler/jsexecute.js | 35 ++++++++++++++++++++--------------- src/compiler/jsgen.js | 34 ++++++++++++++++++++-------------- src/util/cast.js | 25 ++++++++++++++----------- 3 files changed, 54 insertions(+), 40 deletions(-) diff --git a/src/compiler/jsexecute.js b/src/compiler/jsexecute.js index d0876e8d421..b1db9c70ff5 100644 --- a/src/compiler/jsexecute.js +++ b/src/compiler/jsexecute.js @@ -2,6 +2,8 @@ * @fileoverview Runtime for scripts generated by jsgen */ +const { supportsNullishCoalescing: canNullCoalsh } = require('./environment'); + /* eslint-disable no-unused-vars */ /* eslint-disable prefer-template */ /* eslint-disable valid-jsdoc */ @@ -18,6 +20,9 @@ const globalState = { let baseRuntime = ''; const runtimeFunctions = {}; +/** Null coalshing function */ +baseRuntime += `const nullCoalsh = (n, v) => ((n === null || n === (void 0)) ? v : n);`; + /** * Determine whether the current tick is likely stuck. * This implements similar functionality to the warp timer found in Scratch. @@ -260,15 +265,13 @@ runtimeFunctions.toBoolean = `const toBoolean = value => { */ runtimeFunctions.toObject = `const toObject = value => { if (typeof value === 'object' && !Array.isArray(value)) { - return value; - } else if (typeof value === 'number') { - return new Object(); - } - try { - return JSON.parse(value); - } catch { - return new Object(); + return ${canNullCoalsh ? 'value ?? (new Object())' : 'nullCoalsh(value, new Object())'}; + } else if (typeof value === 'string') { + try { + return toObject(JSON.parse(value)); + } catch {} } + return new Object(); }`; /** @@ -280,14 +283,16 @@ runtimeFunctions.toObject = `const toObject = value => { runtimeFunctions.toArray = `const toArray = value => { if (Array.isArray(value)) { return value; - } else if (typeof value === 'number' || typeof value === 'object') { - return new Array(); - } - try { - return JSON.parse(value); - } catch { - return new Array(); + } else if (typeof value === 'string') { + try { + return toArray(JSON.parse(value)); + } catch {} + } else { + try { + return Array.from(value); + } catch {} } + return new Array(); }`; /** diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index bccdac80dc0..d51916f8a75 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -25,17 +25,19 @@ const sanitize = string => { }; const stringify = (object, type) => { - if (typeof object === 'number') { - switch (type) { - case 'object': - object = '{}'; - break; - case 'array': - object = '[]'; - break; - } - } - return typeof object === 'string' ? object : JSON.stringify(object); + if (typeof object !== 'object' && typeof object !== 'string') { + if (type === 'object') return '{}'; + if (type === 'array') return '[]'; + throw new Error('Unexpected stringify type.'); + } + if (typeof object === 'object') { + if (type === 'array' && !Array.isArray(object)) return '[]'; + if (type === 'object' && Array.isArray(object)) return '{}'; + } + console.log(object, type); + return ((typeof object === 'string') ? object : JSON.stringify(object ?? ( + type === 'object' ? (new Object()) : (new Array()) + ))); }; const TYPE_NUMBER = 1; @@ -158,6 +160,7 @@ class ConstantInput { } asNumber () { + if (typeof this.constantValue === 'object') return '0'; // Compute at compilation time const numberValue = +this.constantValue; if (numberValue) { @@ -187,17 +190,20 @@ class ConstantInput { asObject () { // Compute at compilation time - return `${stringify(this.constantValue, 'object')}`; + return `${stringify(Cast.toObject(this.constantValue), 'object')}`; } asArray () { // Compute at compilation time - return `${stringify(this.constantValue, 'array')}`; + return `${stringify(Cast.toArray(this.constantValue), 'array')}`; } asColor () { // Attempt to parse hex code at compilation time - if (/^#[0-9a-f]{6,8}$/i.test(this.constantValue)) { + if ( + (typeof this.constantValue === 'string') && + /^#[0-9a-f]{6,8}$/i.test(this.constantValue) + ) { const hex = this.constantValue.substr(1); return Number.parseInt(hex, 16).toString(); } diff --git a/src/util/cast.js b/src/util/cast.js index 2cbff227dea..9f3e0eb3931 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -103,16 +103,20 @@ class Cast { /** * Scratch cast to object. * @param {*} value Value to cast to object. + * @param {?boolean} nullSafe Is null allowed. * @return {object} The Scratch-casted object value. */ - static toObject (value) { - if (typeof value === 'object' && !Array.isArray(value)) { + static toObject (value, nullSafe) { + if (typeof value === 'object') { + if (value === null) { + if (nullSafe) return null; + return new Object(); + } + if (Array.isArray(value)) return new Object(); return value; - } else if (typeof value === 'number') { - return new Object(); } try { - return JSON.parse(value); + return this.toObject(JSON.parse(value)); } catch { return new Object(); } @@ -124,13 +128,12 @@ class Cast { * @return {array} The Scratch-casted array value. */ static toArray (value) { - if (Array.isArray(value)) { - return value; - } else if (typeof value === 'number' || typeof value === 'object') { - return new Array(); - } + if (Array.isArray(value)) return value; try { - return JSON.parse(value); + if (typeof value === 'string') { + return this.toArray(JSON.parse(value)); + } + return Array.from(value); } catch { return new Array(); } From 25d5e4806a36d54a3be383cd01e6d4fb84ab08da Mon Sep 17 00:00:00 2001 From: Miyo Sho <135030944+yuri-kiss@users.noreply.github.com> Date: Sun, 4 May 2025 18:43:07 -0400 Subject: [PATCH 42/46] Fix cubesters implementation of some blocks --- src/blocks/scratch3_json.js | 4 ++-- src/compiler/jsexecute.js | 17 ++++++++++++++++- src/compiler/jsgen.js | 11 +++++------ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/blocks/scratch3_json.js b/src/blocks/scratch3_json.js index 32df19f6c47..786f78a1708 100644 --- a/src/blocks/scratch3_json.js +++ b/src/blocks/scratch3_json.js @@ -85,7 +85,7 @@ class Scratch3JSONBlocks { mergeObject (args) { args.OBJ1 = Cast.toObject(args.OBJ1); args.OBJ2 = Cast.toObject(args.OBJ2); - return {...args.OBJ1, ...args.OBJ2}; + return Object.fromEntries(Object.entries(args.OBJ1).concat(Object.entries(args.OBJ2))); } hasKey (args) { @@ -151,7 +151,7 @@ class Scratch3JSONBlocks { mergeArray (args) { args.ARR1 = Cast.toArray(args.ARR1); args.ARR2 = Cast.toArray(args.ARR2); - return [...args.ARR1, ...args.ARR2]; + return args.ARR1.concat(args.ARR2); } hasItem (args) { diff --git a/src/compiler/jsexecute.js b/src/compiler/jsexecute.js index b1db9c70ff5..9a7aa1b81e7 100644 --- a/src/compiler/jsexecute.js +++ b/src/compiler/jsexecute.js @@ -20,9 +20,24 @@ const globalState = { let baseRuntime = ''; const runtimeFunctions = {}; -/** Null coalshing function */ +/** + * Null coalescing just incase it's not supported by the browser. + * @param {any} n + * @param {any} v + * @returns {!any} + */ baseRuntime += `const nullCoalsh = (n, v) => ((n === null || n === (void 0)) ? v : n);`; +/** + * Merges 2 objects. (use this to get around spread overflow) + * @param {object} a + * @param {object} b + * @returns {object} + */ +runtimeFunctions.mergeObjects = `const mergeObjects = (a, b) => { + return Object.fromEntries(Object.entries(a).concat(Object.entries(b))); +};`; + /** * Determine whether the current tick is likely stuck. * This implements similar functionality to the warp timer found in Scratch. diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index d51916f8a75..86e37cab0b6 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -34,7 +34,6 @@ const stringify = (object, type) => { if (type === 'array' && !Array.isArray(object)) return '[]'; if (type === 'object' && Array.isArray(object)) return '{}'; } - console.log(object, type); return ((typeof object === 'string') ? object : JSON.stringify(object ?? ( type === 'object' ? (new Object()) : (new Array()) ))); @@ -536,11 +535,11 @@ class JSGenerator { case 'json.valueOfKey': return new TypedInput(`(${this.descendInput(node.object).asObject()}[${this.descendInput(node.key).asString()}] ?? "")`, TYPE_STRING); case 'json.setKey': - return new TypedInput(`(object = {...${this.descendInput(node.object).asObject()}}, object[${this.descendInput(node.key).asString()}] = ${this.descendInput(node.value).asUnknown()}, object)`, TYPE_OBJECT); + return new TypedInput(`(object = Object.assign({}, ${this.descendInput(node.object).asObject()}), object[${this.descendInput(node.key).asString()}] = ${this.descendInput(node.value).asUnknown()}, object)`, TYPE_OBJECT); case 'json.deleteKey': - return new TypedInput(`(object = {...${this.descendInput(node.object).asObject()}}, delete object[${this.descendInput(node.key).asString()}], object)`, TYPE_OBJECT); + return new TypedInput(`(object = Object.assign({}, ${this.descendInput(node.object).asObject()}), delete object[${this.descendInput(node.key).asString()}], object)`, TYPE_OBJECT); case 'json.mergeObject': - return new TypedInput(`{...${this.descendInput(node.object1).asObject()}, ...${this.descendInput(node.object2).asObject()}}`, TYPE_OBJECT); + return new TypedInput(`mergeObjects(${this.descendInput(node.object1).asObject()}, ${this.descendInput(node.object2).asObject()})`, TYPE_OBJECT); case 'json.hasKey': return new TypedInput(`${this.descendInput(node.object).asObject()}.hasOwnProperty(${this.descendInput(node.key).asString()})`, TYPE_BOOLEAN); case 'json.newArray': @@ -552,7 +551,7 @@ class JSGenerator { case 'json.indexOfValue': return new TypedInput(`(${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asUnknown()}) !== -1 ? ${this.descendInput(node.array).asArray()}.indexOf(${this.descendInput(node.value).asUnknown()}) : "")`, TYPE_NUMBER); case 'json.addItem': - return new TypedInput(`(array = [...${this.descendInput(node.array).asArray()}], array.push(${this.descendInput(node.item).asUnknown()}), array)`, TYPE_ARRAY); + return new TypedInput(`(array = ${this.descendInput(node.array).asArray()}.slice(0), array.push(${this.descendInput(node.item).asUnknown()}), array)`, TYPE_ARRAY); case 'json.replaceIndex': return new TypedInput(`(${this.descendInput(node.index).asNumber()} >= 0 && ${this.descendInput(node.index).asNumber()} < ${this.descendInput(node.array).asArray()}.length ? (array = [...${this.descendInput(node.array).asArray()}], array[${this.descendInput(node.index).asNumber()}] = ${this.descendInput(node.item).asUnknown()}, array) : new Array())`, TYPE_ARRAY); case 'json.deleteIndex': @@ -560,7 +559,7 @@ class JSGenerator { case 'json.deleteAllOccurrences': return new TypedInput(`${this.descendInput(node.array).asArray()}.filter((item) => item !== ${this.descendInput(node.item).asString()})`, TYPE_ARRAY); case 'json.mergeArray': - return new TypedInput(`[...${this.descendInput(node.array1).asArray()}, ...${this.descendInput(node.array2).asArray()}]`, TYPE_ARRAY); + return new TypedInput(`${this.descendInput(node.array1).asArray()}.concat(${this.descendInput(node.array2).asArray()})`, TYPE_ARRAY); case 'json.hasItem': return new TypedInput(`${this.descendInput(node.array).asArray()}.includes(${this.descendInput(node.item).asUnknown()})`, TYPE_BOOLEAN); From fafa481175e9648a09fe8c47cb4c2dc6111bd67f Mon Sep 17 00:00:00 2001 From: Miyo Sho <135030944+yuri-kiss@users.noreply.github.com> Date: Sun, 4 May 2025 18:46:39 -0400 Subject: [PATCH 43/46] Fix cyclic objects in Cast.toString --- src/util/cast.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/util/cast.js b/src/util/cast.js index 9f3e0eb3931..bff89b3f4f7 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -95,7 +95,9 @@ class Cast { if (typeof value === 'undefined' || typeof value === 'null') { return String(); } else if (typeof value === 'object') { - return JSON.stringify(value); + try { + return JSON.stringify(value); + } catch {} // Fallthrough } return String(value); } From 14cc8f09d44540f83fe0ca5d9d96963b2f74c07e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 10:48:41 +0000 Subject: [PATCH 44/46] Bump scratch-blocks from `460d154` to `cf86bf1` Bumps [scratch-blocks](https://github.com/Nitro-Bolt/scratch-blocks) from `460d154` to `cf86bf1`. - [Commits](https://github.com/Nitro-Bolt/scratch-blocks/compare/460d15463635516b967318a622b4810a7b2efc53...cf86bf1a09adaec740d3ba41e45630a587383a36) --- updated-dependencies: - dependency-name: scratch-blocks dependency-version: cf86bf1a09adaec740d3ba41e45630a587383a36 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8d23229d480..7939d9d07b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13546,8 +13546,8 @@ }, "node_modules/scratch-blocks": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#460d15463635516b967318a622b4810a7b2efc53", - "integrity": "sha512-d8pLSVt1qV3Nq0P2D1th2B/lVmm1hmQqwUE6SBnn1VtJRzhge6SKAtcB9BQQqxyGB//aCEHUesdOAtGnFoDYLQ==", + "resolved": "git+ssh://git@github.com/Nitro-Bolt/scratch-blocks.git#cf86bf1a09adaec740d3ba41e45630a587383a36", + "integrity": "sha512-YAXGXyM3S/IHAnia2nSl0iEX1u8/QTBBI7G+582w7Hfk4KAs6IO0w0B6qJwowb41Mwi0+T7EZOkBmvY1Qwvppw==", "dev": true, "license": "GPL-3.0", "dependencies": { From 6c1860ac48ff352340eb55e3484d86bb648e7ba7 Mon Sep 17 00:00:00 2001 From: yuri-kiss <135030944+yuri-kiss@users.noreply.github.com> Date: Thu, 8 May 2025 15:22:37 -0400 Subject: [PATCH 45/46] Implement checkbox on the vm side (1/2) --- src/blocks/scratch3_operators.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/blocks/scratch3_operators.js b/src/blocks/scratch3_operators.js index a2a5ab4bd2b..4a86e4738b9 100644 --- a/src/blocks/scratch3_operators.js +++ b/src/blocks/scratch3_operators.js @@ -33,10 +33,15 @@ class Scratch3OperatorsBlocks { operator_contains: this.contains, operator_mod: this.mod, operator_round: this.round, - operator_mathop: this.mathop + operator_mathop: this.mathop, + checkbox: this.checkbox }; } + checkbox () { + return true; + } + add (args) { return Cast.toNumber(args.NUM1) + Cast.toNumber(args.NUM2); } From 23c7270b905b0975fe00d7f6b6ad54ce34a7b621 Mon Sep 17 00:00:00 2001 From: yuri-kiss <135030944+yuri-kiss@users.noreply.github.com> Date: Thu, 8 May 2025 15:23:57 -0400 Subject: [PATCH 46/46] Implement checkbox on the vm side (2/2) --- src/compiler/irgen.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/compiler/irgen.js b/src/compiler/irgen.js index 4c3137939a9..c2724f4685d 100644 --- a/src/compiler/irgen.js +++ b/src/compiler/irgen.js @@ -177,6 +177,11 @@ class ScriptTreeGenerator { kind: 'constant', value: block.fields.TEXT.value }; + case 'checkbox': + return { + kind: 'constant', + value: true + }; case 'argument_reporter_string_number': { const name = block.fields.VALUE.value;