Merge remote-tracking branch 'refs/remotes/LLK/develop' into optimise/reduceGetBlockCalls

This commit is contained in:
griffpatch 2017-03-22 07:56:04 +00:00
commit a5ce0f60cf
8 changed files with 218 additions and 9 deletions

View file

@ -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();

View file

@ -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"

View file

@ -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++) {

View file

@ -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();

View file

@ -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

Binary file not shown.

View 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);
});

View file

@ -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();
}); });