Skip to content

Commit 9054d11

Browse files
committed
Merge "Optimize counter blocks in compiler"
1 parent a94785a commit 9054d11

File tree

4 files changed

+35
-197
lines changed

4 files changed

+35
-197
lines changed

src/compiler/enums.js

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
const InputType = {
2222
/** The value Infinity */
2323
NUMBER_POS_INF: 0x001,
24-
/** Any natrual number */
24+
/** Any natural number */
2525
NUMBER_POS_INT: 0x002,
2626
/** Any positive fractional number, excluding integers. */
2727
NUMBER_POS_FRACT: 0x004,
@@ -65,41 +65,13 @@ const InputType = {
6565
/** Any number, excluding NaN. Equal to NUMBER_REAL | NUMBER_INF */
6666
NUMBER: 0x0FF,
6767
/** Any number, including NaN. Equal to NUMBER | NUMBER_NAN */
68-
<<<<<<< HEAD
69-
NUMBER_OR_NAN: 0x03F,
70-
/** Anything that can be interperated as a number. Equal to NUMBER | STRING_NUM | BOOLEAN */
71-
NUMBER_INTERPRETABLE: 0x15F,
72-
73-
=======
7468
NUMBER_OR_NAN: 0x1FF,
7569
/** Anything that can be interperated as a number. Equal to NUMBER | STRING_NUM | BOOLEAN */
7670
NUMBER_INTERPRETABLE: 0x12FF,
77-
<<<<<<< HEAD
78-
79-
>>>>>>> 2087b0e2 (Teach the compiler what an integer is)
80-
=======
8171

82-
>>>>>>> a620ae88 (Merge "Fix compiler's hat implementation")
8372
/** Any string which as a non-NaN neumeric interpretation, excluding ''. */
8473
STRING_NUM: 0x200,
8574
/** Any string which has no non-NaN neumeric interpretation, including ''. */
86-
<<<<<<< HEAD
87-
STRING_NAN: 0x080,
88-
/** One of the strings 'true' or 'false'. */
89-
STRING_BOOLEAN: 0x200,
90-
91-
/** Any string. Equal to STRING_NUM | STRING_NAN */
92-
STRING: 0x2C0,
93-
94-
/** Any boolean. */
95-
BOOLEAN: 0x100,
96-
/** Any input that can be interperated as a boolean. Equal to BOOLEAN | STRING_BOOLEAN */
97-
BOOLEAN_INTERPRETABLE: 0x300,
98-
99-
100-
/** Any type. Equal to NUMBER_OR_NAN | STRING | BOOLEAN */
101-
ANY: 0x3FF
102-
=======
10375
STRING_NAN: 0x400,
10476
/** Either of the strings 'true' or 'false'. */
10577
STRING_BOOLEAN: 0x800,
@@ -114,7 +86,6 @@ const InputType = {
11486

11587
/** Any type. Equal to NUMBER_OR_NAN | STRING | BOOLEAN */
11688
ANY: 0x1FFF
117-
>>>>>>> 2087b0e2 (Teach the compiler what an integer is)
11889
};
11990

12091
/**
@@ -144,6 +115,8 @@ const StackOpcode = {
144115
CONTROL_STOP_SCRIPT: "control.stopScript",
145116
CONTROL_WAIT: "control.wait",
146117
CONTROL_WAIT_UNTIL: "control.waitUntil",
118+
CONTROL_CLEAR_COUNTER: "control.counterClear",
119+
CONTORL_INCR_COUNTER: "control.counterIncr",
147120

148121
LIST_ADD: "list.add",
149122
LIST_INSERT: "list.instert",
@@ -310,6 +283,8 @@ const InputOpcode = {
310283
PROCEDURE_ARG_STRING_NUMBER: "args.stringNumber",
311284
PROCEDURE_ARG_BOOLEAN: "args.boolean",
312285

286+
CONTROL_COUNTER: "control.counter",
287+
313288
TW_KEY_LAST_PRESSED: "tw.lastKeyPressed"
314289
};
315290

src/compiler/irgen.js

Lines changed: 15 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,9 @@ class ScriptTreeGenerator {
544544
// This menu is special compared to other menus -- it actually has an opcode function.
545545
return this.createConstantInput(block.fields.SOUND_MENU.value);
546546

547+
case 'control_get_counter':
548+
return new IntermediateInput(InputOpcode.CONTROL_COUNTER, InputType.NUMBER_POS_INT | InputType.NUMBER_ZERO);
549+
547550
case 'tw_getLastKeyPressed':
548551
return new IntermediateInput(InputOpcode.TW_KEY_LAST_PRESSED, InputType.STRING);
549552

@@ -666,6 +669,10 @@ class ScriptTreeGenerator {
666669
// We should consider analyzing this like we do for control_repeat_until
667670
warpTimer: false
668671
}, this.analyzeLoop());
672+
case 'control_clear_counter':
673+
return new IntermediateStackBlock(StackOpcode.CONTROL_CLEAR_COUNTER);
674+
case 'control_incr_counter':
675+
return new IntermediateStackBlock(StackOpcode.CONTORL_INCR_COUNTER);
669676

670677
case 'data_addtolist':
671678
return new IntermediateStackBlock(StackOpcode.LIST_ADD, {
@@ -1102,7 +1109,7 @@ class ScriptTreeGenerator {
11021109
const variable = block.fields[fieldName];
11031110
const id = variable.id;
11041111

1105-
if (Object.prototype.hasOwnProperty.call(this.variableCache, id)) {
1112+
if (this.variableCache.hasOwnProperty(id)) {
11061113
return this.variableCache[id];
11071114
}
11081115

@@ -1123,20 +1130,20 @@ class ScriptTreeGenerator {
11231130
const stage = this.stage;
11241131

11251132
// Look for by ID in target...
1126-
if (Object.prototype.hasOwnProperty.call(target.variables, id)) {
1133+
if (target.variables.hasOwnProperty(id)) {
11271134
return createVariableData('target', target.variables[id]);
11281135
}
11291136

11301137
// Look for by ID in stage...
11311138
if (!target.isStage) {
1132-
if (stage && Object.prototype.hasOwnProperty.call(stage.variables, id)) {
1139+
if (stage && stage.variables.hasOwnProperty(id)) {
11331140
return createVariableData('stage', stage.variables[id]);
11341141
}
11351142
}
11361143

11371144
// Look for by name and type in target...
11381145
for (const varId in target.variables) {
1139-
if (Object.prototype.hasOwnProperty.call(target.variables, varId)) {
1146+
if (target.variables.hasOwnProperty(varId)) {
11401147
const currVar = target.variables[varId];
11411148
if (currVar.name === name && currVar.type === type) {
11421149
return createVariableData('target', currVar);
@@ -1147,7 +1154,7 @@ class ScriptTreeGenerator {
11471154
// Look for by name and type in stage...
11481155
if (!target.isStage && stage) {
11491156
for (const varId in stage.variables) {
1150-
if (Object.prototype.hasOwnProperty.call(stage.variables, varId)) {
1157+
if (stage.variables.hasOwnProperty(varId)) {
11511158
const currVar = stage.variables[varId];
11521159
if (currVar.name === name && currVar.type === type) {
11531160
return createVariableData('stage', currVar);
@@ -1165,7 +1172,7 @@ class ScriptTreeGenerator {
11651172
// This is necessary because the script cache is shared between clones.
11661173
// sprite.clones has all instances of this sprite including the original and all clones
11671174
for (const clone of target.sprite.clones) {
1168-
if (!Object.prototype.hasOwnProperty.call(clone.variables, id)) {
1175+
if (!clone.variables.hasOwnProperty(id)) {
11691176
clone.variables[id] = new Variable(id, name, type, false);
11701177
}
11711178
}
@@ -1174,97 +1181,6 @@ class ScriptTreeGenerator {
11741181
return createVariableData('target', newVariable);
11751182
}
11761183

1177-
descendProcedure (block) {
1178-
const procedureCode = block.mutation.proccode;
1179-
const paramNamesIdsAndDefaults = this.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode);
1180-
if (paramNamesIdsAndDefaults === null) {
1181-
return {
1182-
kind: 'noop'
1183-
};
1184-
}
1185-
1186-
const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
1187-
1188-
const addonBlock = this.runtime.getAddonBlock(procedureCode);
1189-
if (addonBlock) {
1190-
this.script.yields = true;
1191-
const args = {};
1192-
for (let i = 0; i < paramIds.length; i++) {
1193-
let value;
1194-
if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) {
1195-
value = this.descendInputOfBlock(block, paramIds[i]);
1196-
} else {
1197-
value = {
1198-
kind: 'constant',
1199-
value: paramDefaults[i]
1200-
};
1201-
}
1202-
args[paramNames[i]] = value;
1203-
}
1204-
return {
1205-
kind: 'addons.call',
1206-
code: procedureCode,
1207-
arguments: args,
1208-
blockId: block.id
1209-
};
1210-
}
1211-
1212-
const definitionId = this.blocks.getProcedureDefinition(procedureCode);
1213-
const definitionBlock = this.blocks.getBlock(definitionId);
1214-
if (!definitionBlock) {
1215-
return {
1216-
kind: 'noop'
1217-
};
1218-
}
1219-
const innerDefinition = this.blocks.getBlock(definitionBlock.inputs.custom_block.block);
1220-
1221-
let isWarp = this.script.isWarp;
1222-
if (!isWarp) {
1223-
if (innerDefinition && innerDefinition.mutation) {
1224-
const warp = innerDefinition.mutation.warp;
1225-
if (typeof warp === 'boolean') {
1226-
isWarp = warp;
1227-
} else if (typeof warp === 'string') {
1228-
isWarp = JSON.parse(warp);
1229-
}
1230-
}
1231-
}
1232-
1233-
const variant = generateProcedureVariant(procedureCode, isWarp);
1234-
1235-
if (!this.script.dependedProcedures.includes(variant)) {
1236-
this.script.dependedProcedures.push(variant);
1237-
}
1238-
1239-
// Non-warp direct recursion yields.
1240-
if (!this.script.isWarp) {
1241-
if (procedureCode === this.script.procedureCode) {
1242-
this.script.yields = true;
1243-
}
1244-
}
1245-
1246-
const args = [];
1247-
for (let i = 0; i < paramIds.length; i++) {
1248-
let value;
1249-
if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) {
1250-
value = this.descendInputOfBlock(block, paramIds[i]);
1251-
} else {
1252-
value = {
1253-
kind: 'constant',
1254-
value: paramDefaults[i]
1255-
};
1256-
}
1257-
args.push(value);
1258-
}
1259-
1260-
return {
1261-
kind: 'procedures.call',
1262-
code: procedureCode,
1263-
variant,
1264-
arguments: args
1265-
};
1266-
}
1267-
12681184
/**
12691185
* Descend into an input block that uses the compatibility layer.
12701186
* @param {*} block The block to use the compatibility layer for.
@@ -1273,10 +1189,10 @@ class ScriptTreeGenerator {
12731189
*/
12741190
descendCompatLayerInput(block) {
12751191
const inputs = {};
1192+
const fields = {};
12761193
for (const name of Object.keys(block.inputs)) {
12771194
inputs[name] = this.descendInputOfBlock(block, name, true);
12781195
}
1279-
const fields = {};
12801196
for (const name of Object.keys(block.fields)) {
12811197
fields[name] = block.fields[name].value;
12821198
}
@@ -1363,72 +1279,6 @@ class ScriptTreeGenerator {
13631279
}
13641280
}
13651281

1366-
descendVisualReport (block) {
1367-
if (!this.thread.stackClick || block.next) {
1368-
return null;
1369-
}
1370-
try {
1371-
return {
1372-
kind: 'visualReport',
1373-
input: this.descendInput(block)
1374-
};
1375-
} catch (e) {
1376-
return null;
1377-
}
1378-
}
1379-
1380-
/**
1381-
* @param {Block} hatBlock
1382-
*/
1383-
walkHat (hatBlock) {
1384-
const nextBlock = hatBlock.next;
1385-
const opcode = hatBlock.opcode;
1386-
const hatInfo = this.runtime._hats[opcode];
1387-
1388-
if (this.thread.stackClick) {
1389-
// We still need to treat the hat as a normal block (so executableHat should be false) for
1390-
// interpreter parity, but the reuslt is ignored.
1391-
const opcodeFunction = this.runtime.getOpcodeFunction(opcode);
1392-
if (opcodeFunction) {
1393-
return [
1394-
this.descendCompatLayer(hatBlock),
1395-
...this.walkStack(nextBlock)
1396-
];
1397-
}
1398-
return this.walkStack(nextBlock);
1399-
}
1400-
1401-
if (hatInfo.edgeActivated) {
1402-
// Edge-activated HAT
1403-
this.script.yields = true;
1404-
this.script.executableHat = true;
1405-
return [
1406-
{
1407-
kind: 'hat.edge',
1408-
id: hatBlock.id,
1409-
condition: this.descendCompatLayer(hatBlock)
1410-
},
1411-
...this.walkStack(nextBlock)
1412-
];
1413-
}
1414-
1415-
const opcodeFunction = this.runtime.getOpcodeFunction(opcode);
1416-
if (opcodeFunction) {
1417-
// Predicate-based HAT
1418-
this.script.yields = true;
1419-
this.script.executableHat = true;
1420-
return [
1421-
{
1422-
kind: 'hat.predicate',
1423-
condition: this.descendCompatLayer(hatBlock)
1424-
},
1425-
...this.walkStack(nextBlock)
1426-
];
1427-
}
1428-
1429-
return this.walkStack(nextBlock);
1430-
}
1431-
14321282
/**
14331283
* @param {*} hatBlock
14341284
* @returns {IntermediateStack}
@@ -1541,7 +1391,7 @@ class IRGenerator {
15411391

15421392
addProcedureDependencies(dependencies) {
15431393
for (const procedureVariant of dependencies) {
1544-
if (Object.prototype.hasOwnProperty.call(this.procedures, procedureVariant)) {
1394+
if (this.procedures.hasOwnProperty(procedureVariant)) {
15451395
continue;
15461396
}
15471397
if (this.compilingProcedures.has(procedureVariant)) {

src/compiler/jsgen.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,9 @@ class JSGenerator {
461461
case InputOpcode.SENSING_TIMER_GET:
462462
return 'runtime.ioDevices.clock.projectTimer()';
463463

464+
case InputOpcode.CONTROL_COUNTER:
465+
return 'runtime.ext_scratch3_control._counter';
466+
464467
case InputOpcode.TW_KEY_LAST_PRESSED:
465468
return 'runtime.ioDevices.keyboard.getLastKeyPressed()';
466469

@@ -622,6 +625,12 @@ class JSGenerator {
622625
}
623626
this.source += `}\n`;
624627
break;
628+
case StackOpcode.CONTROL_CLEAR_COUNTER:
629+
this.source += 'runtime.ext_scratch3_control._counter = 0;\n';
630+
break;
631+
case StackOpcode.CONTORL_INCR_COUNTER:
632+
this.source += 'runtime.ext_scratch3_control._counter++;\n';
633+
break;
625634

626635
case StackOpcode.EVENT_BROADCAST:
627636
this.source += `startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: ${this.descendInput(node.broadcast)} });\n`;

test/integration/execute.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,12 @@ fs.readdirSync(executeDir)
8181
t.fail(reason);
8282
},
8383
plan (count) {
84-
didPlan = true;
85-
t.plan(Number(count));
84+
if (didPlan) {
85+
t.fail("Must plan exactly once.");
86+
} else {
87+
didPlan = true;
88+
t.plan(Number(count));
89+
}
8690
},
8791
end () {
8892
didEnd = true;

0 commit comments

Comments
 (0)