forked from scratchfoundation/scratch-vm
-
-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix rejected promise behavior in interpreter and compiler
Before this command: - Interpreter would popStack() and continue. Inside a loop this was like JS continue, outside it was like return - Compiler would return literal undefined and keep going. After this commit: Both return the error as a string and then continue as normal. This has several benefits: - Users can actually see the error messages - Logical execution flow - Consistent behavior - Blocks aren't even supposed to return rejected promises anyways - No vanilla blocks return rejected promises so no compatibility concerns Supersedes #207
- Loading branch information
1 parent
31c9eba
commit 0593628
Showing
5 changed files
with
135 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
const {test} = require('tap'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const VM = require('../../src/virtual-machine'); | ||
const Scratch = require('../../src/extension-support/tw-extension-api-common'); | ||
|
||
const commandFixture = fs.readFileSync(path.join(__dirname, '../fixtures/tw-rejected-promise-command.sb3')); | ||
const reporterFixture = fs.readFileSync(path.join(__dirname, '../fixtures/tw-rejected-promise-reporter.sb3')); | ||
|
||
class TestExtension { | ||
getInfo () { | ||
return { | ||
id: 'test123', | ||
name: 'test123', | ||
blocks: [ | ||
{ | ||
blockType: Scratch.BlockType.COMMAND, | ||
opcode: 'command', | ||
text: 'return rejected promise' | ||
}, | ||
{ | ||
blockType: Scratch.BlockType.REPORTER, | ||
opcode: 'reporter', | ||
text: 'return rejected promise' | ||
} | ||
] | ||
}; | ||
} | ||
command () { | ||
return Promise.reject(new Error('Test error 1')); | ||
} | ||
reporter () { | ||
return Promise.reject(new Error('Test error 2')); | ||
} | ||
} | ||
|
||
for (const enableCompiler of [true, false]) { | ||
test(`COMMAND returns rejected promise - ${enableCompiler ? 'compiler' : 'interpreter'}`, t => { | ||
const vm = new VM(); | ||
vm.extensionManager.addBuiltinExtension('test123', TestExtension); | ||
|
||
vm.setCompilerOptions({ | ||
enabled: enableCompiler | ||
}); | ||
t.equal(vm.runtime.compilerOptions.enabled, enableCompiler); | ||
|
||
vm.loadProject(commandFixture).then(async () => { | ||
vm.greenFlag(); | ||
|
||
for (let i = 0; i < 12; i++) { | ||
vm.runtime._step(); | ||
|
||
// wait for promise rejection to be handled | ||
await Promise.resolve(); | ||
} | ||
|
||
const stage = vm.runtime.getTargetForStage(); | ||
t.equal(stage.lookupVariableByNameAndType('before', '').value, 10); | ||
t.equal(stage.lookupVariableByNameAndType('after', '').value, 10); | ||
t.end(); | ||
}); | ||
}); | ||
|
||
test(`REPORTER returns rejected promise - ${enableCompiler ? 'compiler' : 'interpreter'}`, t => { | ||
const vm = new VM(); | ||
vm.extensionManager.addBuiltinExtension('test123', TestExtension); | ||
|
||
vm.setCompilerOptions({ | ||
enabled: enableCompiler | ||
}); | ||
t.equal(vm.runtime.compilerOptions.enabled, enableCompiler); | ||
|
||
vm.loadProject(reporterFixture).then(async () => { | ||
vm.greenFlag(); | ||
|
||
for (let i = 0; i < 12; i++) { | ||
vm.runtime._step(); | ||
|
||
// wait for promise rejection to be handled | ||
await Promise.resolve(); | ||
} | ||
|
||
const stage = vm.runtime.getTargetForStage(); | ||
t.equal(stage.lookupVariableByNameAndType('before', '').value, 10); | ||
t.equal(stage.lookupVariableByNameAndType('after', '').value, 10); | ||
t.same(stage.lookupVariableByNameAndType('values', 'list').value, [ | ||
'Error: Test error 2', | ||
'Error: Test error 2', | ||
'Error: Test error 2', | ||
'Error: Test error 2', | ||
'Error: Test error 2', | ||
'Error: Test error 2', | ||
'Error: Test error 2', | ||
'Error: Test error 2', | ||
'Error: Test error 2', | ||
'Error: Test error 2', | ||
'Error: Test error 2' | ||
]); | ||
t.end(); | ||
}); | ||
}); | ||
} |
0593628
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will also add that rejected promises in sandboxed extensions already behaved like this for a very long time in both compiler and interpreter.
0593628
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and s/command/commit/