mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 22:42:27 -05:00
Merge remote-tracking branch 'refs/remotes/LLK/develop' into optimise/reduceGetBlockCalls
This commit is contained in:
commit
a5ce0f60cf
8 changed files with 218 additions and 9 deletions
|
@ -31,7 +31,7 @@ npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
## Playground
|
## Playground
|
||||||
To run the Playground, make sure the dev server's running and go to [http://localhost:8073/](http://localhost:8073/) - you will be directed to the playground, which demonstrates various tools and internal state.
|
To run the Playground, make sure the dev server's running and go to [http://localhost:8073/playground/](http://localhost:8073/playground/) - you will be directed to the playground, which demonstrates various tools and internal state.
|
||||||
|
|
||||||
![VM Playground Screenshot](https://i.imgur.com/nOCNqEc.gif)
|
![VM Playground Screenshot](https://i.imgur.com/nOCNqEc.gif)
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
## How to include in a Node.js App
|
## How to include in a Node.js App
|
||||||
For an extended setup example, check out the /playground directory, which includes a fully running VM instance.
|
For an extended setup example, check out the /src/playground directory, which includes a fully running VM instance.
|
||||||
```js
|
```js
|
||||||
var VirtualMachine = require('scratch-vm');
|
var VirtualMachine = require('scratch-vm');
|
||||||
var vm = new VirtualMachine();
|
var vm = new VirtualMachine();
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
"scratch-render": "latest",
|
"scratch-render": "latest",
|
||||||
"script-loader": "0.7.0",
|
"script-loader": "0.7.0",
|
||||||
"stats.js": "0.17.0",
|
"stats.js": "0.17.0",
|
||||||
"tap": "10.1.1",
|
"tap": "10.1.2",
|
||||||
"travis-after-all": "1.4.4",
|
"travis-after-all": "1.4.4",
|
||||||
"webpack": "2.2.1",
|
"webpack": "2.2.1",
|
||||||
"webpack-dev-server": "2.3.0"
|
"webpack-dev-server": "2.3.0"
|
||||||
|
|
|
@ -414,7 +414,7 @@ Runtime.prototype.allScriptsDo = function (f, optTarget) {
|
||||||
if (optTarget) {
|
if (optTarget) {
|
||||||
targets = [optTarget];
|
targets = [optTarget];
|
||||||
}
|
}
|
||||||
for (var t = 0; t < targets.length; t++) {
|
for (var t = targets.length - 1; t >= 0; t--) {
|
||||||
var target = targets[t];
|
var target = targets[t];
|
||||||
var scripts = target.blocks.getScripts();
|
var scripts = target.blocks.getScripts();
|
||||||
for (var j = 0; j < scripts.length; j++) {
|
for (var j = 0; j < scripts.length; j++) {
|
||||||
|
|
|
@ -582,7 +582,7 @@ RenderedTarget.prototype.goBackLayers = function (nLayers) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move behind some other rendered target.
|
* Move behind some other rendered target.
|
||||||
* @param {!Clone} other Other rendered target to move behind.
|
* @param {!RenderedTarget} other Other rendered target to move behind.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype.goBehindOther = function (other) {
|
RenderedTarget.prototype.goBehindOther = function (other) {
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
|
@ -637,11 +637,11 @@ RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) {
|
||||||
/**
|
/**
|
||||||
* Make a clone, copying any run-time properties.
|
* Make a clone, copying any run-time properties.
|
||||||
* If we've hit the global clone limit, returns null.
|
* If we've hit the global clone limit, returns null.
|
||||||
* @return {!RenderedTarget} New clone.
|
* @return {RenderedTarget} New clone.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype.makeClone = function () {
|
RenderedTarget.prototype.makeClone = function () {
|
||||||
if (!this.runtime.clonesAvailable() || this.isStage) {
|
if (!this.runtime.clonesAvailable() || this.isStage) {
|
||||||
return; // Hit max clone limit, or this is the stage.
|
return null; // Hit max clone limit, or this is the stage.
|
||||||
}
|
}
|
||||||
this.runtime.changeCloneCounter(1);
|
this.runtime.changeCloneCounter(1);
|
||||||
var newClone = this.sprite.createClone();
|
var newClone = this.sprite.createClone();
|
||||||
|
|
|
@ -46,7 +46,7 @@ var Sprite = function (blocks, runtime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a clone of this sprite.
|
* Create a clone of this sprite.
|
||||||
* @returns {!Clone} Newly created clone.
|
* @returns {!RenderedTarget} Newly created clone.
|
||||||
*/
|
*/
|
||||||
Sprite.prototype.createClone = function () {
|
Sprite.prototype.createClone = function () {
|
||||||
var newClone = new RenderedTarget(this, this.runtime);
|
var newClone = new RenderedTarget(this, this.runtime);
|
||||||
|
|
BIN
test/fixtures/hat-execution-order.sb2
vendored
Normal file
BIN
test/fixtures/hat-execution-order.sb2
vendored
Normal file
Binary file not shown.
39
test/integration/hat-execution-order.js
Normal file
39
test/integration/hat-execution-order.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
var path = require('path');
|
||||||
|
var test = require('tap').test;
|
||||||
|
var extract = require('../fixtures/extract');
|
||||||
|
var VirtualMachine = require('../../src/index');
|
||||||
|
|
||||||
|
var projectUri = path.resolve(__dirname, '../fixtures/hat-execution-order.sb2');
|
||||||
|
var project = extract(projectUri);
|
||||||
|
|
||||||
|
test('complex', function (t) {
|
||||||
|
var vm = new VirtualMachine();
|
||||||
|
|
||||||
|
// Evaluate playground data and exit
|
||||||
|
vm.on('playgroundData', function (e) {
|
||||||
|
var threads = JSON.parse(e.threads);
|
||||||
|
t.ok(threads.length === 0);
|
||||||
|
|
||||||
|
var results = vm.runtime.targets[0].lists.results.contents;
|
||||||
|
t.deepEqual(results, ['3', '2', '1', 'stage']);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
process.nextTick(process.exit);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start VM, load project, and run
|
||||||
|
t.doesNotThrow(function () {
|
||||||
|
vm.start();
|
||||||
|
vm.clear();
|
||||||
|
vm.setCompatibilityMode(false);
|
||||||
|
vm.setTurboMode(false);
|
||||||
|
vm.loadProject(project);
|
||||||
|
vm.greenFlag();
|
||||||
|
});
|
||||||
|
|
||||||
|
// After two seconds, get playground data and stop
|
||||||
|
setTimeout(function () {
|
||||||
|
vm.getPlaygroundData();
|
||||||
|
vm.stopAll();
|
||||||
|
}, 2000);
|
||||||
|
});
|
|
@ -1,8 +1,178 @@
|
||||||
var test = require('tap').test;
|
var test = require('tap').test;
|
||||||
var Sequencer = require('../../src/engine/sequencer');
|
var Sequencer = require('../../src/engine/sequencer');
|
||||||
|
var Runtime = require('../../src/engine/runtime');
|
||||||
|
var Thread = require('../../src/engine/thread');
|
||||||
|
var RenderedTarget = require('../../src/sprites/rendered-target');
|
||||||
|
var Sprite = require('../../src/sprites/sprite');
|
||||||
|
|
||||||
test('spec', function (t) {
|
test('spec', function (t) {
|
||||||
t.type(Sequencer, 'function');
|
t.type(Sequencer, 'function');
|
||||||
// @todo
|
|
||||||
|
var r = new Runtime();
|
||||||
|
var s = new Sequencer(r);
|
||||||
|
|
||||||
|
t.type(s, 'object');
|
||||||
|
t.ok(s instanceof Sequencer);
|
||||||
|
|
||||||
|
t.type(s.stepThreads, 'function');
|
||||||
|
t.type(s.stepThread, 'function');
|
||||||
|
t.type(s.stepToBranch, 'function');
|
||||||
|
t.type(s.stepToProcedure, 'function');
|
||||||
|
t.type(s.retireThread, 'function');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
var randomString = function () {
|
||||||
|
var top = Math.random().toString(36);
|
||||||
|
return top.substring(7);
|
||||||
|
};
|
||||||
|
|
||||||
|
var generateBlock = function (id) {
|
||||||
|
var block = {fields: Object,
|
||||||
|
id: id,
|
||||||
|
inputs: {},
|
||||||
|
STEPS: Object,
|
||||||
|
block: 'fakeBlock',
|
||||||
|
name: 'fakeName',
|
||||||
|
next: null,
|
||||||
|
opcode: 'procedures_defnoreturn',
|
||||||
|
mutation: {proccode: 'fakeCode'},
|
||||||
|
parent: null,
|
||||||
|
shadow: false,
|
||||||
|
topLevel: true,
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
return block;
|
||||||
|
};
|
||||||
|
|
||||||
|
var generateBlockInput = function (id, next, inp) {
|
||||||
|
var block = {fields: Object,
|
||||||
|
id: id,
|
||||||
|
inputs: {SUBSTACK: {block: inp, name: 'SUBSTACK'}},
|
||||||
|
STEPS: Object,
|
||||||
|
block: 'fakeBlock',
|
||||||
|
name: 'fakeName',
|
||||||
|
next: next,
|
||||||
|
opcode: 'procedures_defnoreturn',
|
||||||
|
mutation: {proccode: 'fakeCode'},
|
||||||
|
parent: null,
|
||||||
|
shadow: false,
|
||||||
|
topLevel: true,
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
return block;
|
||||||
|
};
|
||||||
|
|
||||||
|
var generateThread = function (runtime) {
|
||||||
|
var s = new Sprite();
|
||||||
|
var rt = new RenderedTarget(s, runtime);
|
||||||
|
var th = new Thread(randomString());
|
||||||
|
|
||||||
|
var next = randomString();
|
||||||
|
var inp = randomString();
|
||||||
|
var name = th.topBlock;
|
||||||
|
|
||||||
|
rt.blocks.createBlock(generateBlockInput(name, next, inp));
|
||||||
|
th.pushStack(name);
|
||||||
|
rt.blocks.createBlock(generateBlock(inp));
|
||||||
|
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
name = next;
|
||||||
|
next = randomString();
|
||||||
|
inp = randomString();
|
||||||
|
|
||||||
|
rt.blocks.createBlock(generateBlockInput(name, next, inp));
|
||||||
|
th.pushStack(name);
|
||||||
|
rt.blocks.createBlock(generateBlock(inp));
|
||||||
|
}
|
||||||
|
rt.blocks.createBlock(generateBlock(next));
|
||||||
|
th.pushStack(next);
|
||||||
|
th.target = rt;
|
||||||
|
|
||||||
|
runtime.threads.push(th);
|
||||||
|
|
||||||
|
return th;
|
||||||
|
};
|
||||||
|
|
||||||
|
test('stepThread', function (t) {
|
||||||
|
var r = new Runtime();
|
||||||
|
var s = new Sequencer(r);
|
||||||
|
var th = generateThread(r);
|
||||||
|
t.notEquals(th.status, Thread.STATUS_DONE);
|
||||||
|
s.stepThread(th);
|
||||||
|
t.strictEquals(th.status, Thread.STATUS_DONE);
|
||||||
|
th = generateThread(r);
|
||||||
|
th.status = Thread.STATUS_YIELD;
|
||||||
|
s.stepThread(th);
|
||||||
|
t.notEquals(th.status, Thread.STATUS_DONE);
|
||||||
|
th.status = Thread.STATUS_PROMISE_WAIT;
|
||||||
|
s.stepThread(th);
|
||||||
|
t.notEquals(th.status, Thread.STATUS_DONE);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stepToBranch', function (t) {
|
||||||
|
var r = new Runtime();
|
||||||
|
var s = new Sequencer(r);
|
||||||
|
var th = generateThread(r);
|
||||||
|
s.stepToBranch(th, 2, false);
|
||||||
|
t.strictEquals(th.peekStack(), null);
|
||||||
|
th.popStack();
|
||||||
|
s.stepToBranch(th, 1, false);
|
||||||
|
t.strictEquals(th.peekStack(), null);
|
||||||
|
th.popStack();
|
||||||
|
th.popStack();
|
||||||
|
s.stepToBranch(th, 1, false);
|
||||||
|
t.notEquals(th.peekStack(), null);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('retireThread', function (t) {
|
||||||
|
var r = new Runtime();
|
||||||
|
var s = new Sequencer(r);
|
||||||
|
var th = generateThread(r);
|
||||||
|
t.strictEquals(th.stack.length, 12);
|
||||||
|
s.retireThread(th);
|
||||||
|
t.strictEquals(th.stack.length, 0);
|
||||||
|
t.strictEquals(th.status, Thread.STATUS_DONE);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stepToProcedure', function (t) {
|
||||||
|
var r = new Runtime();
|
||||||
|
var s = new Sequencer(r);
|
||||||
|
var th = generateThread(r);
|
||||||
|
var expectedBlock = th.peekStack();
|
||||||
|
s.stepToProcedure(th, '');
|
||||||
|
t.strictEquals(th.peekStack(), expectedBlock);
|
||||||
|
s.stepToProcedure(th, 'faceCode');
|
||||||
|
t.strictEquals(th.peekStack(), expectedBlock);
|
||||||
|
s.stepToProcedure(th, 'faceCode');
|
||||||
|
th.target.blocks.getBlock(th.stack[th.stack.length - 4]).mutation.proccode = 'othercode';
|
||||||
|
expectedBlock = th.stack[th.stack.length - 4];
|
||||||
|
s.stepToProcedure(th, 'othercode');
|
||||||
|
t.strictEquals(th.peekStack(), expectedBlock);
|
||||||
|
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stepThreads', function (t) {
|
||||||
|
var r = new Runtime();
|
||||||
|
r.currentStepTime = Infinity;
|
||||||
|
var s = new Sequencer(r);
|
||||||
|
t.strictEquals(s.stepThreads().length, 0);
|
||||||
|
generateThread(r);
|
||||||
|
t.strictEquals(r.threads.length, 1);
|
||||||
|
t.strictEquals(s.stepThreads().length, 0);
|
||||||
|
r.threads[0].status = Thread.STATUS_RUNNING;
|
||||||
|
t.strictEquals(s.stepThreads().length, 1);
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue