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.cjs CHANGED
@@ -1325,38 +1325,67 @@ const SwitchStatement = (c, scope, node) => {
1325
1325
  const endLine = new AddressResolver(endAdress).bindBreak(innerScope);
1326
1326
  const caseJumps = [];
1327
1327
  let defaultJump;
1328
+ // if there is a case that is guaranteed to run
1329
+ let constantCase = false;
1330
+ let onlyConstantTests = true;
1328
1331
  for (const scase of node.cases) {
1329
1332
  const [, bodyInst] = c.handleMany(innerScope, scase.consequent);
1330
1333
  const bodyAdress = new LiteralValue(null);
1331
1334
  const bodyLine = new AddressResolver(bodyAdress);
1335
+ const canFallInto = !endsWithoutFalltrough(inst, endLine);
1336
+ let includeJump = !constantCase;
1337
+ const includeBody = !constantCase || canFallInto;
1332
1338
  if (!scase.test) {
1333
- [defaultJump] = c.handle(innerScope, scase, () => [
1334
- null,
1335
- [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1336
- ])[1];
1337
- inst.push(bodyLine, ...bodyInst);
1339
+ if (includeJump) {
1340
+ [defaultJump] = c.handle(innerScope, scase, () => [
1341
+ null,
1342
+ [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1343
+ ])[1];
1344
+ }
1345
+ if (includeBody)
1346
+ inst.push(bodyLine, ...bodyInst);
1338
1347
  continue;
1339
1348
  }
1340
1349
  const [test, testInst] = c.handleEval(innerScope, scase.test);
1350
+ if (onlyConstantTests)
1351
+ onlyConstantTests = test.mutability === EMutability.constant;
1352
+ let jump;
1341
1353
  // check if it can be evaluated at compile time
1342
- const [comp] = ref["=="](innerScope, test);
1343
- // if the constant expression resolves to false
1344
- // the whole case gets omitted
1345
- // otherwise it returns the instructions for
1346
- // the body of this case
1354
+ // the scope copy prevents unecessary increases in the temp counter
1355
+ const [comp] = ref["=="](innerScope.copy(), test);
1347
1356
  if (comp instanceof LiteralValue) {
1348
- if (comp.data)
1349
- return [null, bodyInst];
1350
- else
1351
- continue;
1357
+ // whether other cases can falltrough and reach this one
1358
+ const noFallInto = !canFallInto;
1359
+ if (!comp.data) {
1360
+ // this case is not accessible in any way
1361
+ if (noFallInto)
1362
+ continue;
1363
+ includeJump = false;
1364
+ }
1365
+ else {
1366
+ // this case is only accessible via the comparison jump
1367
+ // and all the other cases will never run
1368
+ if (noFallInto &&
1369
+ onlyConstantTests &&
1370
+ endsWithoutFalltrough(bodyInst, endLine)) {
1371
+ return [null, [...bodyInst, endLine]];
1372
+ }
1373
+ jump = c.handle(innerScope, scase, () => [
1374
+ null,
1375
+ [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1376
+ ])[1];
1377
+ constantCase = true;
1378
+ }
1352
1379
  }
1353
1380
  // makes sourcemapping for the jump more specific
1354
- const [, jump] = c.handle(innerScope, scase, () => [
1381
+ jump ??= c.handle(innerScope, scase, () => [
1355
1382
  null,
1356
1383
  [new JumpInstruction(bodyAdress, EJumpKind.StrictEqual, ref, test)],
1357
- ]);
1358
- caseJumps.push(...testInst, ...jump);
1359
- inst.push(bodyLine, ...bodyInst);
1384
+ ])[1];
1385
+ if (includeJump)
1386
+ caseJumps.push(...testInst, ...jump);
1387
+ if (includeBody)
1388
+ inst.push(bodyLine, ...bodyInst);
1360
1389
  }
1361
1390
  // ensures that the processor exits
1362
1391
  // the switch if no cases match
@@ -1371,12 +1400,34 @@ const SwitchStatement = (c, scope, node) => {
1371
1400
  [
1372
1401
  ...refInst,
1373
1402
  ...caseJumps,
1374
- ...(caseJumps.length > 0 ? [defaultJump] : []),
1403
+ ...(caseJumps.length > 0 && !constantCase ? [defaultJump] : []),
1375
1404
  ...inst,
1376
1405
  endLine,
1377
1406
  ],
1378
1407
  ];
1379
- };
1408
+ };
1409
+ function endsWithoutFalltrough(inst, endLine) {
1410
+ if (inst.length === 0)
1411
+ return true;
1412
+ for (let i = inst.length - 1; i >= 0; i--) {
1413
+ const instruction = inst[i];
1414
+ // this means that there is an instruction trying to reference
1415
+ // the final function `set @counter` instruction
1416
+ // of course this generates an unecessary
1417
+ // `set @counter` in cases where a control flow
1418
+ // structure contains a fallback return statement
1419
+ // TODO: fix this with static analysis
1420
+ if (instruction instanceof AddressResolver)
1421
+ return false;
1422
+ if (instruction.hidden)
1423
+ continue;
1424
+ return (instruction instanceof SetCounterInstruction ||
1425
+ instruction.intent === EInstIntent.return ||
1426
+ (instruction instanceof BreakInstruction &&
1427
+ endLine.bonds.includes(instruction.address)));
1428
+ }
1429
+ return false;
1430
+ }
1380
1431
 
1381
1432
  const WhileStatement = (c, scope, node) => {
1382
1433
  const lines = [];