mlogjs 0.4.7 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1304,38 +1304,67 @@ const SwitchStatement = (c, scope, node) => {
1304
1304
  const endLine = new AddressResolver(endAdress).bindBreak(innerScope);
1305
1305
  const caseJumps = [];
1306
1306
  let defaultJump;
1307
+ // if there is a case that is guaranteed to run
1308
+ let constantCase = false;
1309
+ let onlyConstantTests = true;
1307
1310
  for (const scase of node.cases) {
1308
1311
  const [, bodyInst] = c.handleMany(innerScope, scase.consequent);
1309
1312
  const bodyAdress = new LiteralValue(null);
1310
1313
  const bodyLine = new AddressResolver(bodyAdress);
1314
+ const canFallInto = !endsWithoutFalltrough(inst, endLine);
1315
+ let includeJump = !constantCase;
1316
+ const includeBody = !constantCase || canFallInto;
1311
1317
  if (!scase.test) {
1312
- [defaultJump] = c.handle(innerScope, scase, () => [
1313
- null,
1314
- [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1315
- ])[1];
1316
- inst.push(bodyLine, ...bodyInst);
1318
+ if (includeJump) {
1319
+ [defaultJump] = c.handle(innerScope, scase, () => [
1320
+ null,
1321
+ [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1322
+ ])[1];
1323
+ }
1324
+ if (includeBody)
1325
+ inst.push(bodyLine, ...bodyInst);
1317
1326
  continue;
1318
1327
  }
1319
1328
  const [test, testInst] = c.handleEval(innerScope, scase.test);
1329
+ if (onlyConstantTests)
1330
+ onlyConstantTests = test.mutability === EMutability.constant;
1331
+ let jump;
1320
1332
  // check if it can be evaluated at compile time
1321
- const [comp] = ref["=="](innerScope, test);
1322
- // if the constant expression resolves to false
1323
- // the whole case gets omitted
1324
- // otherwise it returns the instructions for
1325
- // the body of this case
1333
+ // the scope copy prevents unecessary increases in the temp counter
1334
+ const [comp] = ref["=="](innerScope.copy(), test);
1326
1335
  if (comp instanceof LiteralValue) {
1327
- if (comp.data)
1328
- return [null, bodyInst];
1329
- else
1330
- continue;
1336
+ // whether other cases can falltrough and reach this one
1337
+ const noFallInto = !canFallInto;
1338
+ if (!comp.data) {
1339
+ // this case is not accessible in any way
1340
+ if (noFallInto)
1341
+ continue;
1342
+ includeJump = false;
1343
+ }
1344
+ else {
1345
+ // this case is only accessible via the comparison jump
1346
+ // and all the other cases will never run
1347
+ if (noFallInto &&
1348
+ onlyConstantTests &&
1349
+ endsWithoutFalltrough(bodyInst, endLine)) {
1350
+ return [null, [...bodyInst, endLine]];
1351
+ }
1352
+ jump = c.handle(innerScope, scase, () => [
1353
+ null,
1354
+ [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1355
+ ])[1];
1356
+ constantCase = true;
1357
+ }
1331
1358
  }
1332
1359
  // makes sourcemapping for the jump more specific
1333
- const [, jump] = c.handle(innerScope, scase, () => [
1360
+ jump ??= c.handle(innerScope, scase, () => [
1334
1361
  null,
1335
1362
  [new JumpInstruction(bodyAdress, EJumpKind.StrictEqual, ref, test)],
1336
- ]);
1337
- caseJumps.push(...testInst, ...jump);
1338
- inst.push(bodyLine, ...bodyInst);
1363
+ ])[1];
1364
+ if (includeJump)
1365
+ caseJumps.push(...testInst, ...jump);
1366
+ if (includeBody)
1367
+ inst.push(bodyLine, ...bodyInst);
1339
1368
  }
1340
1369
  // ensures that the processor exits
1341
1370
  // the switch if no cases match
@@ -1350,12 +1379,34 @@ const SwitchStatement = (c, scope, node) => {
1350
1379
  [
1351
1380
  ...refInst,
1352
1381
  ...caseJumps,
1353
- ...(caseJumps.length > 0 ? [defaultJump] : []),
1382
+ ...(caseJumps.length > 0 && !constantCase ? [defaultJump] : []),
1354
1383
  ...inst,
1355
1384
  endLine,
1356
1385
  ],
1357
1386
  ];
1358
- };
1387
+ };
1388
+ function endsWithoutFalltrough(inst, endLine) {
1389
+ if (inst.length === 0)
1390
+ return true;
1391
+ for (let i = inst.length - 1; i >= 0; i--) {
1392
+ const instruction = inst[i];
1393
+ // this means that there is an instruction trying to reference
1394
+ // the final function `set @counter` instruction
1395
+ // of course this generates an unecessary
1396
+ // `set @counter` in cases where a control flow
1397
+ // structure contains a fallback return statement
1398
+ // TODO: fix this with static analysis
1399
+ if (instruction instanceof AddressResolver)
1400
+ return false;
1401
+ if (instruction.hidden)
1402
+ continue;
1403
+ return (instruction instanceof SetCounterInstruction ||
1404
+ instruction.intent === EInstIntent.return ||
1405
+ (instruction instanceof BreakInstruction &&
1406
+ endLine.bonds.includes(instruction.address)));
1407
+ }
1408
+ return false;
1409
+ }
1359
1410
 
1360
1411
  const WhileStatement = (c, scope, node) => {
1361
1412
  const lines = [];