Skip to content

Commit e1ad53e

Browse files
authored
Add compatibility layer for extensions relying on old compiler (#264)
The new compiler broke all the extensions doing compiler patches. We promised not to help them when those break but it seems enough people use those extensions that we sort of got forced into finding some solution, so this makes these changes: - The exports for the new compiler are now in `these_broke_before_and_will_break_again` - `i_will_not_ask_for_help_when_these_break` now returns some stubs that pretend to be the old compiler. It also emits an event so -gui can show a warning. - Focus is on compatibility & safety, not performance. There will be unnecessary casts and scripts marked as yielding. - Extensions that meet these assumptions should work without changes: - Extensions do not try to combine this compatibility layer with any APIs provided by the new compiler. - Extensions treat IR nodes received from descendSubstack and similar as opaque objects because they will be the new intermediate objects instead of { kind: "..." } - Extensions need to implement the JS generators for all AST node kinds they use. Can not rely on the default JS generator because those generators are expecting a different format than { kind: "..." } Lack of tests is intentional since this will be removed at some point when we have a proper API we can tell people to use
1 parent 98df18a commit e1ad53e

File tree

5 files changed

+399
-3
lines changed

5 files changed

+399
-3
lines changed

src/compiler/enums.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const StackOpcode = {
103103
DEBUGGER: 'tw.debugger',
104104
VISUAL_REPORT: 'visualReport',
105105
COMPATIBILITY_LAYER: 'compat',
106+
OLD_COMPILER_COMPATIBILITY_LAYER: 'oldCompiler',
106107

107108
HAT_EDGE: 'hat.edge',
108109
HAT_PREDICATE: 'hat.predicate',
@@ -201,6 +202,7 @@ const InputOpcode = {
201202
CAST_COLOR: 'cast.toColor',
202203

203204
COMPATIBILITY_LAYER: 'compat',
205+
OLD_COMPILER_COMPATIBILITY_LAYER: 'oldCompiler',
204206

205207
LOOKS_BACKDROP_NUMBER: 'looks.backdropNumber',
206208
LOOKS_BACKDROP_NAME: 'looks.backdropName',

src/compiler/irgen.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
IntermediateScript,
1515
IntermediateRepresentation
1616
} = require('./intermediate');
17+
const oldCompilerCompatiblity = require('./old-compiler-compatibility.js');
1718

1819
/**
1920
* @fileoverview Generate intermediate representations from Scratch blocks.
@@ -96,6 +97,12 @@ class ScriptTreeGenerator {
9697
}
9798
}
9899
}
100+
101+
this.oldCompilerStub = (
102+
oldCompilerCompatiblity.enabled ?
103+
new oldCompilerCompatiblity.ScriptTreeGeneratorStub(this) :
104+
null
105+
);
99106
}
100107

101108
setProcedureVariant (procedureVariant) {
@@ -199,6 +206,13 @@ class ScriptTreeGenerator {
199206
* @returns {IntermediateInput} Compiled input node for this input.
200207
*/
201208
descendInput (block, preserveStrings = false) {
209+
if (this.oldCompilerStub) {
210+
const oldCompilerResult = this.oldCompilerStub.descendInputFromNewCompiler(block);
211+
if (oldCompilerResult) {
212+
return oldCompilerResult;
213+
}
214+
}
215+
202216
switch (block.opcode) {
203217
case 'colour_picker':
204218
return this.createConstantInput(block.fields.COLOUR.value, true);
@@ -593,6 +607,13 @@ class ScriptTreeGenerator {
593607
* @returns {IntermediateStackBlock} Compiled node for this block.
594608
*/
595609
descendStackedBlock (block) {
610+
if (this.oldCompilerStub) {
611+
const oldCompilerResult = this.oldCompilerStub.descendStackedBlockFromNewCompiler(block);
612+
if (oldCompilerResult) {
613+
return oldCompilerResult;
614+
}
615+
}
616+
596617
switch (block.opcode) {
597618
case 'control_all_at_once':
598619
// In Scratch 3, this block behaves like "if 1 = 1"

src/compiler/jsgen.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const VariablePool = require('./variable-pool');
66
const jsexecute = require('./jsexecute');
77
const environment = require('./environment');
88
const {StackOpcode, InputOpcode, InputType} = require('./enums.js');
9+
const oldCompilerCompatibility = require('./old-compiler-compatibility.js');
910

1011
// These imports are used by jsdoc comments but eslint doesn't know that
1112
/* eslint-disable no-unused-vars */
@@ -124,6 +125,8 @@ class JSGenerator {
124125
this.isInHat = false;
125126

126127
this.debug = this.target.runtime.debug;
128+
129+
this.oldCompilerStub = new oldCompilerCompatibility.JSGeneratorStub(this);
127130
}
128131

129132
/**
@@ -198,6 +201,9 @@ class JSGenerator {
198201
// Compatibility layer inputs never use flags.
199202
return `(${this.generateCompatibilityLayerCall(node, false)})`;
200203

204+
case InputOpcode.OLD_COMPILER_COMPATIBILITY_LAYER:
205+
return this.oldCompilerStub.descendInputFromNewCompiler(block);
206+
201207
case InputOpcode.CONSTANT:
202208
if (block.isAlwaysType(InputType.NUMBER)) {
203209
if (typeof node.value !== 'number') throw new Error(`JS: '${block.type}' type constant had ${typeof node.value} type value. Expected number.`);
@@ -540,6 +546,9 @@ class JSGenerator {
540546
break;
541547
}
542548

549+
case InputOpcode.OLD_COMPILER_COMPATIBILITY_LAYER:
550+
return this.oldCompilerStub.descendStackedBlockFromNewCompiler(block);
551+
543552
case StackOpcode.HAT_EDGE:
544553
this.isInHat = true;
545554
this.source += '{\n';

0 commit comments

Comments
 (0)