diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index 86aacd059..1bf782693 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -38,6 +38,7 @@ class Scratch3ProcedureBlocks { const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults; + util.initParams(); for (let i = 0; i < paramIds.length; i++) { if (args.hasOwnProperty(paramIds[i])) { util.pushParam(paramNames[i], args[paramIds[i]]); diff --git a/src/engine/block-utility.js b/src/engine/block-utility.js index 5aa79f727..b957342ae 100644 --- a/src/engine/block-utility.js +++ b/src/engine/block-utility.js @@ -170,6 +170,13 @@ class BlockUtility { return this.thread.target.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode); } + /** + * Initialize procedure parameters in the thread before pushing parameters. + */ + initParams () { + this.thread.initParams(); + } + /** * Store a procedure parameter value by its name. * @param {string} paramName The procedure's parameter name. diff --git a/src/engine/thread.js b/src/engine/thread.js index 47777a97a..e381dd9bf 100644 --- a/src/engine/thread.js +++ b/src/engine/thread.js @@ -321,6 +321,16 @@ class Thread { this.justReported = typeof value === 'undefined' ? null : value; } + /** + * Initialize procedure parameters on this stack frame. + */ + initParams () { + const stackFrame = this.peekStackFrame(); + if (stackFrame.params === null) { + stackFrame.params = {}; + } + } + /** * Add a parameter to the stack frame. * Use when calling a procedure with parameter values. @@ -329,9 +339,6 @@ class Thread { */ pushParam (paramName, value) { const stackFrame = this.peekStackFrame(); - if (stackFrame.params === null) { - stackFrame.params = {}; - } stackFrame.params[paramName] = value; } diff --git a/test/fixtures/execute/procedures-nested-missing-no-param.sb2 b/test/fixtures/execute/procedures-nested-missing-no-param.sb2 new file mode 100644 index 000000000..af6916e52 Binary files /dev/null and b/test/fixtures/execute/procedures-nested-missing-no-param.sb2 differ diff --git a/test/unit/engine_thread.js b/test/unit/engine_thread.js index 5b2174e8d..12ed08357 100644 --- a/test/unit/engine_thread.js +++ b/test/unit/engine_thread.js @@ -5,7 +5,7 @@ const Sprite = require('../../src/sprites/sprite'); test('spec', t => { t.type(Thread, 'function'); - + const th = new Thread('arbitraryString'); t.type(th, 'object'); t.ok(th instanceof Thread); @@ -17,20 +17,21 @@ test('spec', t => { t.type(th.peekStackFrame, 'function'); t.type(th.peekParentStackFrame, 'function'); t.type(th.pushReportedValue, 'function'); + t.type(th.initParams, 'function'); t.type(th.pushParam, 'function'); t.type(th.peekStack, 'function'); t.type(th.getParam, 'function'); t.type(th.atStackTop, 'function'); t.type(th.goToNextBlock, 'function'); t.type(th.isRecursiveCall, 'function'); - + t.end(); }); test('pushStack', t => { const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); - + t.end(); }); @@ -39,7 +40,7 @@ test('popStack', t => { th.pushStack('arbitraryString'); t.strictEquals(th.popStack(), 'arbitraryString'); t.strictEquals(th.popStack(), undefined); - + t.end(); }); @@ -50,7 +51,7 @@ test('atStackTop', t => { t.strictEquals(th.atStackTop(), false); th.popStack(); t.strictEquals(th.atStackTop(), true); - + t.end(); }); @@ -59,7 +60,7 @@ test('reuseStackForNextBlock', t => { th.pushStack('arbitraryString'); th.reuseStackForNextBlock('secondString'); t.strictEquals(th.popStack(), 'secondString'); - + t.end(); }); @@ -69,7 +70,7 @@ test('peekStackFrame', t => { t.strictEquals(th.peekStackFrame().warpMode, false); th.popStack(); t.strictEquals(th.peekStackFrame(), null); - + t.end(); }); @@ -80,7 +81,7 @@ test('peekParentStackFrame', t => { t.strictEquals(th.peekParentStackFrame(), null); th.pushStack('secondString'); t.strictEquals(th.peekParentStackFrame().warpMode, true); - + t.end(); }); @@ -100,13 +101,14 @@ test('peekStack', t => { t.strictEquals(th.peekStack(), 'arbitraryString'); th.popStack(); t.strictEquals(th.peekStack(), null); - + t.end(); }); test('PushGetParam', t => { const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); + th.initParams(); th.pushParam('testParam', 'testValue'); t.strictEquals(th.peekStackFrame().params.testParam, 'testValue'); t.strictEquals(th.getParam('testParam'), 'testValue'); @@ -149,12 +151,12 @@ test('goToNextBlock', t => { x: 0, y: 0 }; - + rt.blocks.createBlock(block1); rt.blocks.createBlock(block2); rt.blocks.createBlock(block2); th.target = rt; - + t.strictEquals(th.peekStack(), null); th.pushStack('secondString'); t.strictEquals(th.peekStack(), 'secondString'); @@ -167,7 +169,7 @@ test('goToNextBlock', t => { t.strictEquals(th.peekStack(), 'secondString'); th.goToNextBlock(); t.strictEquals(th.peekStack(), null); - + t.end(); }); @@ -204,11 +206,11 @@ test('stopThisScript', t => { x: 0, y: 0 }; - + rt.blocks.createBlock(block1); rt.blocks.createBlock(block2); th.target = rt; - + th.stopThisScript(); t.strictEquals(th.peekStack(), null); th.pushStack('arbitraryString'); @@ -219,7 +221,7 @@ test('stopThisScript', t => { th.pushStack('secondString'); th.stopThisScript(); t.strictEquals(th.peekStack(), 'secondString'); - + t.end(); }); @@ -256,11 +258,11 @@ test('isRecursiveCall', t => { x: 0, y: 0 }; - + rt.blocks.createBlock(block1); rt.blocks.createBlock(block2); th.target = rt; - + t.strictEquals(th.isRecursiveCall('fakeCode'), false); th.pushStack('secondString'); t.strictEquals(th.isRecursiveCall('fakeCode'), false); @@ -274,6 +276,6 @@ test('isRecursiveCall', t => { t.strictEquals(th.isRecursiveCall('fakeCode'), false); th.popStack(); t.strictEquals(th.isRecursiveCall('fakeCode'), false); - + t.end(); });