@markw65/monkeyc-optimizer 1.1.2 → 1.1.3

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/README.md CHANGED
@@ -587,3 +587,28 @@ Bug Fixes
587
587
  - Analyze constants with casts to help with constant propagation
588
588
  - Ignore widening casts (eg a cast that is given a `Number` and converts it to `Number or String`)
589
589
  - More accurate deletion of unused constants. Sometimes a constant that was unused after the optimization phase ended, was still considered used because of references that were eventually deleted.
590
+
591
+ ### 1.1.3
592
+
593
+ - Tweaks and fixes
594
+
595
+ - Update to [@markw65/prettier-plugin-monkeyc@1.0.42](https://github.com/markw65/prettier-plugin-monkeyc#1042)
596
+ - Fixed an issue that cause inlining in return context to be too conservative
597
+ - Update inliner to keep a stack of locations, so that error messages can show exactly where an error occurred, even in the presence of inlining.
598
+ - Update diagnostic api to optionally include a uri to more detailing information.
599
+
600
+ - Type Analysis
601
+
602
+ - Track type info through branch conditions, so that in `if (x != null) { A } else { B }`, the type checker knows that x is not null in A, and it is null in B.
603
+ - Added checkers for return types, call arguments, assignments and variable declarations.
604
+ - Automatically infer Array and Dictionary types
605
+ - Track equivalencies, and use them for various optimizations.
606
+ - Add support for "strong" and "weak" type checking.
607
+ - Add type analysis to getProgramAnalysis.
608
+
609
+ - Optimizations
610
+ - Eliminate self-assignments (eg `x = x;`, but also `x = a; y = a; ... y = x;`).
611
+ - Eliminate dead stores.
612
+ - Replace more expensive accesses by less expensive ones.
613
+ - Delete empty else blocks.
614
+ - Delete if statements with empty body and no else.
package/build/api.cjs CHANGED
@@ -1,4 +1,4 @@
1
- 0 && (module.exports = {checkCompilerVersion,collectNamespaces,diagnostic,findUsingForNode,formatAst,getApiFunctionInfo,getApiMapping,getSuperClasses,hasProperty,isLocal,isLookupCandidate,isStateNode,markInvokeClassMethod,parseSdkVersion,sameLookupResult,traverseAst,variableDeclarationName,visitReferences,visit_resources,visitorNode});
1
+ 0 && (module.exports = {checkCompilerVersion,collectNamespaces,diagnostic,findUsingForNode,formatAst,getApiFunctionInfo,getApiMapping,getSuperClasses,hasProperty,isLocal,isLookupCandidate,isStateNode,lookupNext,markInvokeClassMethod,parseSdkVersion,sameLookupResult,traverseAst,variableDeclarationName,visitReferences,visit_resources,visitorNode});
2
2
  /******/ (() => { // webpackBootstrap
3
3
  /******/ "use strict";
4
4
  /******/ var __webpack_modules__ = ({
@@ -113,7 +113,7 @@ function isMCTreeNode(node) {
113
113
  * - if post returns false, the node it was called on is
114
114
  * removed.
115
115
  */
116
- function traverseAst(node, pre, post) {
116
+ function traverseAst(node, pre, post, parent) {
117
117
  const nodes = pre && pre(node);
118
118
  if (nodes === false)
119
119
  return;
@@ -128,7 +128,7 @@ function traverseAst(node, pre, post) {
128
128
  const values = value;
129
129
  const deletions = values.reduce((state, obj, i) => {
130
130
  if (isMCTreeNode(obj)) {
131
- const repl = traverseAst(obj, pre, post);
131
+ const repl = traverseAst(obj, pre, post, node);
132
132
  if (repl === false) {
133
133
  if (!state)
134
134
  state = {};
@@ -147,7 +147,7 @@ function traverseAst(node, pre, post) {
147
147
  }
148
148
  }
149
149
  else if (isMCTreeNode(value)) {
150
- let repl = traverseAst(value, pre, post);
150
+ let repl = traverseAst(value, pre, post, node);
151
151
  if (repl === false) {
152
152
  delete node[key];
153
153
  }
@@ -167,7 +167,7 @@ function traverseAst(node, pre, post) {
167
167
  }
168
168
  }
169
169
  }
170
- return post && post(node);
170
+ return post && post(node, parent);
171
171
  }
172
172
  function isStatement(node) {
173
173
  return hasProperty(mctreeTypeInfo[node.type], "stmt");
@@ -212,6 +212,9 @@ function withLoc(node, start, end) {
212
212
  }
213
213
  function withLocDeep(node, start, end, inplace) {
214
214
  node = withLoc(inplace ? node : { ...node }, start, end);
215
+ if (!inplace && node.origins) {
216
+ node.origins = [...node.origins];
217
+ }
215
218
  for (const key of mctreeTypeInfo[node.type].keys) {
216
219
  const value = node[key];
217
220
  if (!value)
@@ -546,6 +549,7 @@ __webpack_require__.d(__webpack_exports__, {
546
549
  "isLocal": () => (/* binding */ isLocal),
547
550
  "isLookupCandidate": () => (/* binding */ isLookupCandidate),
548
551
  "isStateNode": () => (/* binding */ isStateNode),
552
+ "lookupNext": () => (/* binding */ lookupNext),
549
553
  "markInvokeClassMethod": () => (/* binding */ markInvokeClassMethod),
550
554
  "parseSdkVersion": () => (/* binding */ parseSdkVersion),
551
555
  "sameLookupResult": () => (/* binding */ sameLookupResult),
@@ -869,7 +873,7 @@ function visit_resource_refs(state, doc, e) {
869
873
  start: addPositions(startPos, error.location.start),
870
874
  end: addPositions(startPos, error.location.end),
871
875
  };
872
- (0,external_api_cjs_.diagnostic)(state, location, ex.message, check || "WARNING");
876
+ (0,external_api_cjs_.diagnostic)(state, { type: "Identifier", loc: location, name: "" }, ex.message, check || "WARNING");
873
877
  }
874
878
  }
875
879
  }
@@ -1458,6 +1462,22 @@ function isLookupCandidate(node) {
1458
1462
  node.property.argument
1459
1463
  : node.property.type === "Identifier" && node.property;
1460
1464
  }
1465
+ function lookupNext(state, results, decls, property) {
1466
+ return results.reduce((current, lookupDef) => {
1467
+ const items = lookupDef.results
1468
+ .map((module) => {
1469
+ if (!isStateNode(module)) {
1470
+ return null;
1471
+ }
1472
+ const res = checkOne(state, module, decls, property);
1473
+ return res ? { parent: module, results: res } : null;
1474
+ })
1475
+ .filter((r) => r != null);
1476
+ if (!items.length)
1477
+ return current;
1478
+ return current ? current.concat(items) : items;
1479
+ }, null);
1480
+ }
1461
1481
  /**
1462
1482
  *
1463
1483
  * @param state - The ProgramState
@@ -1491,20 +1511,7 @@ function lookup(state, decls, node, name, maybeStack, nonlocal, ignoreImports) {
1491
1511
  break;
1492
1512
  if (!results)
1493
1513
  return [null, null];
1494
- result = results.reduce((current, lookupDef) => {
1495
- const items = lookupDef.results
1496
- .map((module) => {
1497
- if (!isStateNode(module)) {
1498
- return null;
1499
- }
1500
- const res = checkOne(state, module, decls, property);
1501
- return res ? { parent: module, results: res } : null;
1502
- })
1503
- .filter((r) => r != null);
1504
- if (!items.length)
1505
- return current;
1506
- return current ? current.concat(items) : items;
1507
- }, null);
1514
+ result = lookupNext(state, results, decls, property);
1508
1515
  if (!result &&
1509
1516
  results.some((ld) => ld.results.some((sn) => sn.type === "VariableDeclarator" ||
1510
1517
  sn.type === "Identifier" ||
@@ -1610,7 +1617,7 @@ function lookup(state, decls, node, name, maybeStack, nonlocal, ignoreImports) {
1610
1617
  if (imports) {
1611
1618
  if (imports.length > 1) {
1612
1619
  if (state.config?.checkInvalidSymbols !== "OFF") {
1613
- diagnostic(state, node.loc, `${formatAst(node)} is ambiguous and exists in multiple imported modules [${imports
1620
+ diagnostic(state, node, `${formatAst(node)} is ambiguous and exists in multiple imported modules [${imports
1614
1621
  .map(({ name }) => name)
1615
1622
  .join(", ")}]`, state.config?.checkInvalidSymbols || "WARNING");
1616
1623
  }
@@ -1623,7 +1630,10 @@ function lookup(state, decls, node, name, maybeStack, nonlocal, ignoreImports) {
1623
1630
  if (imports.length == 1) {
1624
1631
  if (decls !== "type_decls") {
1625
1632
  if (state.config?.checkCompilerLookupRules !== "OFF") {
1626
- diagnostic(state, node.loc, `${formatAst(node)} will only be found when compiled with compiler2 at -O1 or above`, state.config?.checkCompilerLookupRules || "WARNING");
1633
+ diagnostic(state, node, `${formatAst(node)} will only be found when compiled with compiler2 at -O1 or above`, state.config?.checkCompilerLookupRules || "WARNING", {
1634
+ uri: "https://github.com/markw65/monkeyc-optimizer/wiki/Compiler1-vs-Compiler2-lookup-rules",
1635
+ message: "more info",
1636
+ });
1627
1637
  }
1628
1638
  else if (state.lookupRules === "COMPILER1") {
1629
1639
  return [null, null];
@@ -1688,7 +1698,7 @@ function stateFuncs() {
1688
1698
  switch (node.type) {
1689
1699
  case "MemberExpression": {
1690
1700
  if (isLookupCandidate(node)) {
1691
- return (this.pre && this.pre(node, this)) || ["object"];
1701
+ return (this.pre && this.pre(node, this)) ?? ["object"];
1692
1702
  }
1693
1703
  break;
1694
1704
  }
@@ -2157,7 +2167,7 @@ function findUsing(state, stack, using) {
2157
2167
  return using.module;
2158
2168
  }
2159
2169
  if (state.config?.checkInvalidSymbols !== "OFF") {
2160
- diagnostic(state, using.node.id.loc, `Unable to resolve import of ${formatAst(using.node.id)}`, state.config?.checkInvalidSymbols || "WARNING");
2170
+ diagnostic(state, using.node.id, `Unable to resolve import of ${formatAst(using.node.id)}`, state.config?.checkInvalidSymbols || "WARNING");
2161
2171
  }
2162
2172
  return null;
2163
2173
  }
@@ -2216,7 +2226,8 @@ function markInvokeClassMethod(state, func) {
2216
2226
  function isLocal(v) {
2217
2227
  return v.stack[v.stack.length - 1]?.type === "BlockStatement";
2218
2228
  }
2219
- function diagnostic(state, loc, message, type = "INFO") {
2229
+ function diagnostic(state, node, message, type = "INFO", extra) {
2230
+ const loc = node.loc;
2220
2231
  if (!loc || !loc.source)
2221
2232
  return;
2222
2233
  const source = loc.source;
@@ -2228,11 +2239,38 @@ function diagnostic(state, loc, message, type = "INFO") {
2228
2239
  state.diagnostics[source] = [];
2229
2240
  }
2230
2241
  const diags = state.diagnostics[source];
2231
- let index = diags.findIndex((item) => item.loc === loc);
2242
+ let index = diags.findIndex((item) => item.loc.start.offset === loc.start.offset &&
2243
+ item.loc.end.offset === loc.end.offset &&
2244
+ (!item.related
2245
+ ? !node.origins
2246
+ : item.related.length === node.origins?.length &&
2247
+ item.related.every((r, i) => r.loc.start.offset === node.origins[i].loc.start.offset &&
2248
+ r.loc.end.offset === node.origins[i].loc.end.offset &&
2249
+ r.loc.source === node.origins[i].loc.source)));
2232
2250
  if (message) {
2233
2251
  if (index < 0)
2234
2252
  index = diags.length;
2235
- diags[index] = { type, loc, message };
2253
+ const diag = {
2254
+ type,
2255
+ loc,
2256
+ message,
2257
+ };
2258
+ if (extra) {
2259
+ diag.extra = extra;
2260
+ }
2261
+ if (node.origins) {
2262
+ diag.related = [];
2263
+ const related = diag.related;
2264
+ node.origins.forEach((origin) => {
2265
+ if (origin.loc.source) {
2266
+ related.push({
2267
+ loc: origin.loc,
2268
+ message: `inlined from ${origin.func}`,
2269
+ });
2270
+ }
2271
+ });
2272
+ }
2273
+ diags[index] = diag;
2236
2274
  }
2237
2275
  else if (index >= 0) {
2238
2276
  diags.splice(index, 1);