@markw65/monkeyc-optimizer 1.0.14 → 1.0.15

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
@@ -155,3 +155,9 @@ More fixes found via open source projects.
155
155
  - npm upgrade to pickup ts 4.7.2
156
156
  - Add types to package exports for ts 4.7.2
157
157
  - Better handling of program-logic errors
158
+
159
+ ### 1.0.15
160
+
161
+ - Bug fixes
162
+ - Inject the superclass name into the classes namespace
163
+ - Separate type vs value lookup, and use the correct one based on context.
package/build/api.cjs CHANGED
@@ -248,11 +248,21 @@ function processImports(allImports, lookup) {
248
248
  if (!parent.decls)
249
249
  parent.decls = {};
250
250
  const decls = parent.decls;
251
- if (!hasProperty(parent.decls, name))
252
- parent.decls[name] = [];
251
+ if (!hasProperty(decls, name))
252
+ decls[name] = [];
253
253
  module.forEach((m) => {
254
254
  if (isStateNode(m) && m.type == "ModuleDeclaration") {
255
255
  pushUnique(decls[name], m);
256
+ if (node.type == "ImportModule" && m.type_decls) {
257
+ if (!parent.type_decls)
258
+ parent.type_decls = {};
259
+ const tdecls = parent.type_decls;
260
+ Object.entries(m.type_decls).forEach(([name, decls]) => {
261
+ if (!hasProperty(tdecls, name))
262
+ tdecls[name] = [];
263
+ decls.forEach((decl) => pushUnique(tdecls[name], decl));
264
+ });
265
+ }
256
266
  }
257
267
  });
258
268
  }
@@ -261,11 +271,39 @@ function processImports(allImports, lookup) {
261
271
  function collectClassInfo(state) {
262
272
  state.allClasses.forEach((elm) => {
263
273
  if (elm.node.superClass) {
264
- const [, classes] = state.lookup(elm.node.superClass, null, elm.stack);
274
+ const [name, classes] = state.lookup(elm.node.superClass, null, elm.stack);
265
275
  const superClass = classes &&
266
276
  classes.filter((c) => isStateNode(c) && c.type === "ClassDeclaration");
267
277
  // set it "true" if there is a superClass, but we can't find it.
268
278
  elm.superClass = superClass && superClass.length ? superClass : true;
279
+ if (name && elm.superClass !== true) {
280
+ /*
281
+ * The runtime behavior of monkeyc is strange. Lookups
282
+ * of the name of the superclass, either bare, or via self.<name>
283
+ * always find the superclass, even if there's a member variable
284
+ * or method of the same name. So its ok to just overwrite
285
+ * elm.decls[name] here.
286
+ *
287
+ * ie
288
+ *
289
+ * class A { function foo() as Number { return 1; } }
290
+ * class B { function foo() as String { return "B"; } }
291
+ * class C extends A {
292
+ * var A as B = new B();
293
+ * function initialize() {
294
+ * A.initialize(); // class A's initialize
295
+ * A.foo(); // returns 1
296
+ * self.A.foo(); // still returns 1
297
+ * }
298
+ * }
299
+ *
300
+ * The typechecker seems to get confused in some circumstances
301
+ * though (ie it doesn't always use the same rules)
302
+ */
303
+ if (!elm.decls)
304
+ elm.decls = {};
305
+ elm.decls[name] = elm.superClass;
306
+ }
269
307
  }
270
308
  });
271
309
  const markOverrides = (cls, scls) => {
@@ -700,11 +738,12 @@ async function optimizeMonkeyC(fnMap) {
700
738
  * anywhere in its superClass chain.
701
739
  */
702
740
  const checkInherited = (elm, name) => elm.superClass === true ||
703
- elm.superClass.some((sc) => (hasProperty(sc.decls, name) &&
704
- sc.decls[name].some((f) => isStateNode(f) &&
705
- f.type == "FunctionDeclaration" &&
706
- maybeCalled(f.node))) ||
707
- (sc.superClass && checkInherited(sc, name)));
741
+ (elm.superClass != null &&
742
+ elm.superClass.some((sc) => (hasProperty(sc.decls, name) &&
743
+ sc.decls[name].some((f) => isStateNode(f) &&
744
+ f.type == "FunctionDeclaration" &&
745
+ maybeCalled(f.node))) ||
746
+ (sc.superClass && checkInherited(sc, name))));
708
747
  state.pre = (node) => {
709
748
  switch (node.type) {
710
749
  case "ConditionalExpression":
@@ -1181,6 +1220,57 @@ function api_isStateNode(node) {
1181
1220
  function api_variableDeclarationName(node) {
1182
1221
  return ("left" in node ? node.left : node).name;
1183
1222
  }
1223
+ function checkOne(ns, decls, name) {
1224
+ if (api_isStateNode(ns) && api_hasProperty(ns[decls], name)) {
1225
+ return ns[decls][name];
1226
+ }
1227
+ return null;
1228
+ }
1229
+ function lookup(state, decls, node, name, stack) {
1230
+ stack || (stack = state.stack);
1231
+ switch (node.type) {
1232
+ case "MemberExpression": {
1233
+ if (node.property.type != "Identifier" || node.computed)
1234
+ break;
1235
+ const [, module, where] = state.lookup(node.object, name, stack);
1236
+ if (module && module.length === 1) {
1237
+ const result = checkOne(module[0], decls, node.property.name);
1238
+ if (result) {
1239
+ return [
1240
+ name || node.property.name,
1241
+ result,
1242
+ where.concat(module[0]),
1243
+ ];
1244
+ }
1245
+ }
1246
+ break;
1247
+ }
1248
+ case "ThisExpression": {
1249
+ for (let i = stack.length; i--;) {
1250
+ const si = stack[i];
1251
+ if (si.type == "ModuleDeclaration" ||
1252
+ si.type == "ClassDeclaration" ||
1253
+ !i) {
1254
+ return [name || si.name, [si], stack.slice(0, i)];
1255
+ }
1256
+ }
1257
+ break;
1258
+ }
1259
+ case "Identifier": {
1260
+ if (node.name == "$") {
1261
+ return [name || node.name, [stack[0]], []];
1262
+ }
1263
+ for (let i = stack.length; i--;) {
1264
+ const result = checkOne(stack[i], decls, node.name);
1265
+ if (result) {
1266
+ return [name || node.name, result, stack.slice(0, i + 1)];
1267
+ }
1268
+ }
1269
+ break;
1270
+ }
1271
+ }
1272
+ return [null, null, null];
1273
+ }
1184
1274
  function api_collectNamespaces(ast, stateIn) {
1185
1275
  const state = (stateIn || {});
1186
1276
  if (!state.index)
@@ -1190,12 +1280,6 @@ function api_collectNamespaces(ast, stateIn) {
1190
1280
  { type: "Program", name: "$", fullName: "$", node: undefined },
1191
1281
  ];
1192
1282
  }
1193
- const checkOne = (ns, name) => {
1194
- if (api_isStateNode(ns) && api_hasProperty(ns.decls, name)) {
1195
- return ns.decls[name];
1196
- }
1197
- return null;
1198
- };
1199
1283
  state.removeNodeComments = (node, ast) => {
1200
1284
  if (node.start && node.end && ast.comments && ast.comments.length) {
1201
1285
  let low = 0, high = ast.comments.length;
@@ -1220,51 +1304,10 @@ function api_collectNamespaces(ast, stateIn) {
1220
1304
  }
1221
1305
  }
1222
1306
  };
1223
- state.lookup = (node, name, stack) => {
1224
- stack || (stack = state.stack);
1225
- switch (node.type) {
1226
- case "MemberExpression": {
1227
- if (node.property.type != "Identifier" || node.computed)
1228
- break;
1229
- const [, module, where] = state.lookup(node.object, name, stack);
1230
- if (module && module.length === 1) {
1231
- const result = checkOne(module[0], node.property.name);
1232
- if (result) {
1233
- return [
1234
- name || node.property.name,
1235
- result,
1236
- where.concat(module[0]),
1237
- ];
1238
- }
1239
- }
1240
- break;
1241
- }
1242
- case "ThisExpression": {
1243
- for (let i = stack.length; i--;) {
1244
- const si = stack[i];
1245
- if (si.type == "ModuleDeclaration" ||
1246
- si.type == "ClassDeclaration" ||
1247
- !i) {
1248
- return [name || si.name, [si], stack.slice(0, i)];
1249
- }
1250
- }
1251
- break;
1252
- }
1253
- case "Identifier": {
1254
- if (node.name == "$") {
1255
- return [name || node.name, [stack[0]], []];
1256
- }
1257
- for (let i = stack.length; i--;) {
1258
- const result = checkOne(stack[i], node.name);
1259
- if (result) {
1260
- return [name || node.name, result, stack.slice(0, i + 1)];
1261
- }
1262
- }
1263
- break;
1264
- }
1265
- }
1266
- return [null, null, null];
1267
- };
1307
+ state.lookup = (node, name, stack) => lookup(state, state.inType ? "type_decls" : "decls", node, name, stack);
1308
+ state.lookupValue = (node, name, stack) => lookup(state, "decls", node, name, stack);
1309
+ state.lookupType = (node, name, stack) => lookup(state, "type_decls", node, name, stack);
1310
+ state.inType = false;
1268
1311
  state.traverse = (root) => api_traverseAst(root, (node) => {
1269
1312
  try {
1270
1313
  if (state.shouldExclude && state.shouldExclude(node)) {
@@ -1277,6 +1320,9 @@ function api_collectNamespaces(ast, stateIn) {
1277
1320
  throw new Error("Unexpected stack length for Program node");
1278
1321
  }
1279
1322
  break;
1323
+ case "TypeSpecList":
1324
+ state.inType = true;
1325
+ break;
1280
1326
  case "BlockStatement": {
1281
1327
  const [parent] = state.stack.slice(-1);
1282
1328
  if (parent.type != "FunctionDeclaration" &&
@@ -1289,7 +1335,7 @@ function api_collectNamespaces(ast, stateIn) {
1289
1335
  case "FunctionDeclaration":
1290
1336
  case "ModuleDeclaration": {
1291
1337
  const [parent] = state.stack.slice(-1);
1292
- const name = "id" in node ? node.id && node.id.name : null;
1338
+ const name = "id" in node ? node.id && node.id.name : undefined;
1293
1339
  const fullName = state.stack
1294
1340
  .map((e) => e.name)
1295
1341
  .concat(name)
@@ -1324,6 +1370,15 @@ function api_collectNamespaces(ast, stateIn) {
1324
1370
  node.params.forEach((p) => (decls[api_variableDeclarationName(p)] = [p]));
1325
1371
  }
1326
1372
  parent.decls[name].push(elm);
1373
+ if (node.type == "ModuleDeclaration" ||
1374
+ node.type == "ClassDeclaration") {
1375
+ if (!parent.type_decls)
1376
+ parent.type_decls = {};
1377
+ if (!api_hasProperty(parent.type_decls, name)) {
1378
+ parent.type_decls[name] = [];
1379
+ }
1380
+ parent.type_decls[name].push(elm);
1381
+ }
1327
1382
  }
1328
1383
  break;
1329
1384
  }
@@ -1338,14 +1393,15 @@ function api_collectNamespaces(ast, stateIn) {
1338
1393
  }
1339
1394
  // fall through
1340
1395
  case "TypedefDeclaration": {
1396
+ state.inType = true;
1341
1397
  const name = node.id.name;
1342
1398
  const [parent] = state.stack.slice(-1);
1343
- if (!parent.decls)
1344
- parent.decls = {};
1345
- if (!api_hasProperty(parent.decls, name)) {
1346
- parent.decls[name] = [];
1399
+ if (!parent.type_decls)
1400
+ parent.type_decls = {};
1401
+ if (!api_hasProperty(parent.type_decls, name)) {
1402
+ parent.type_decls[name] = [];
1347
1403
  }
1348
- (0,external_util_cjs_namespaceObject.pushUnique)(parent.decls[name], node);
1404
+ (0,external_util_cjs_namespaceObject.pushUnique)(parent.type_decls[name], node);
1349
1405
  break;
1350
1406
  }
1351
1407
  case "VariableDeclaration": {
@@ -1370,6 +1426,7 @@ function api_collectNamespaces(ast, stateIn) {
1370
1426
  break;
1371
1427
  }
1372
1428
  case "EnumStringBody": {
1429
+ state.inType = false;
1373
1430
  const [parent] = state.stack.slice(-1);
1374
1431
  const values = parent.decls || (parent.decls = {});
1375
1432
  let prev = -1;
@@ -1433,8 +1490,19 @@ function api_collectNamespaces(ast, stateIn) {
1433
1490
  ret = false;
1434
1491
  }
1435
1492
  else {
1493
+ const type = node.type;
1436
1494
  if (state.post)
1437
1495
  ret = state.post(node, state);
1496
+ switch (type) {
1497
+ case "TypeSpecList":
1498
+ case "TypedefDeclaration":
1499
+ case "EnumDeclaration":
1500
+ state.inType = false;
1501
+ break;
1502
+ case "EnumStringBody":
1503
+ state.inType = true;
1504
+ break;
1505
+ }
1438
1506
  if (state.stack.slice(-1).pop()?.node === node) {
1439
1507
  state.stack.pop();
1440
1508
  }
@@ -1538,7 +1606,7 @@ function handleException(state, node, exception) {
1538
1606
  try {
1539
1607
  const fullName = state.stack
1540
1608
  .map((e) => e.name)
1541
- .concat("name" in node && typeof node.name === "string" ? node.name : null)
1609
+ .concat("name" in node && typeof node.name === "string" ? node.name : undefined)
1542
1610
  .filter((e) => e != null)
1543
1611
  .join(".");
1544
1612
  const location = node.loc && node.loc.source
@@ -10804,11 +10804,21 @@ function processImports(allImports, lookup) {
10804
10804
  if (!parent.decls)
10805
10805
  parent.decls = {};
10806
10806
  const decls = parent.decls;
10807
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(parent.decls, name))
10808
- parent.decls[name] = [];
10807
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(decls, name))
10808
+ decls[name] = [];
10809
10809
  module.forEach((m) => {
10810
10810
  if ((0,external_api_cjs_namespaceObject.isStateNode)(m) && m.type == "ModuleDeclaration") {
10811
10811
  (0,external_util_cjs_namespaceObject.pushUnique)(decls[name], m);
10812
+ if (node.type == "ImportModule" && m.type_decls) {
10813
+ if (!parent.type_decls)
10814
+ parent.type_decls = {};
10815
+ const tdecls = parent.type_decls;
10816
+ Object.entries(m.type_decls).forEach(([name, decls]) => {
10817
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(tdecls, name))
10818
+ tdecls[name] = [];
10819
+ decls.forEach((decl) => (0,external_util_cjs_namespaceObject.pushUnique)(tdecls[name], decl));
10820
+ });
10821
+ }
10812
10822
  }
10813
10823
  });
10814
10824
  }
@@ -10817,11 +10827,39 @@ function processImports(allImports, lookup) {
10817
10827
  function collectClassInfo(state) {
10818
10828
  state.allClasses.forEach((elm) => {
10819
10829
  if (elm.node.superClass) {
10820
- const [, classes] = state.lookup(elm.node.superClass, null, elm.stack);
10830
+ const [name, classes] = state.lookup(elm.node.superClass, null, elm.stack);
10821
10831
  const superClass = classes &&
10822
10832
  classes.filter((c) => (0,external_api_cjs_namespaceObject.isStateNode)(c) && c.type === "ClassDeclaration");
10823
10833
  // set it "true" if there is a superClass, but we can't find it.
10824
10834
  elm.superClass = superClass && superClass.length ? superClass : true;
10835
+ if (name && elm.superClass !== true) {
10836
+ /*
10837
+ * The runtime behavior of monkeyc is strange. Lookups
10838
+ * of the name of the superclass, either bare, or via self.<name>
10839
+ * always find the superclass, even if there's a member variable
10840
+ * or method of the same name. So its ok to just overwrite
10841
+ * elm.decls[name] here.
10842
+ *
10843
+ * ie
10844
+ *
10845
+ * class A { function foo() as Number { return 1; } }
10846
+ * class B { function foo() as String { return "B"; } }
10847
+ * class C extends A {
10848
+ * var A as B = new B();
10849
+ * function initialize() {
10850
+ * A.initialize(); // class A's initialize
10851
+ * A.foo(); // returns 1
10852
+ * self.A.foo(); // still returns 1
10853
+ * }
10854
+ * }
10855
+ *
10856
+ * The typechecker seems to get confused in some circumstances
10857
+ * though (ie it doesn't always use the same rules)
10858
+ */
10859
+ if (!elm.decls)
10860
+ elm.decls = {};
10861
+ elm.decls[name] = elm.superClass;
10862
+ }
10825
10863
  }
10826
10864
  });
10827
10865
  const markOverrides = (cls, scls) => {
@@ -11255,11 +11293,12 @@ async function optimizeMonkeyC(fnMap) {
11255
11293
  * anywhere in its superClass chain.
11256
11294
  */
11257
11295
  const checkInherited = (elm, name) => elm.superClass === true ||
11258
- elm.superClass.some((sc) => ((0,external_api_cjs_namespaceObject.hasProperty)(sc.decls, name) &&
11259
- sc.decls[name].some((f) => (0,external_api_cjs_namespaceObject.isStateNode)(f) &&
11260
- f.type == "FunctionDeclaration" &&
11261
- maybeCalled(f.node))) ||
11262
- (sc.superClass && checkInherited(sc, name)));
11296
+ (elm.superClass != null &&
11297
+ elm.superClass.some((sc) => ((0,external_api_cjs_namespaceObject.hasProperty)(sc.decls, name) &&
11298
+ sc.decls[name].some((f) => (0,external_api_cjs_namespaceObject.isStateNode)(f) &&
11299
+ f.type == "FunctionDeclaration" &&
11300
+ maybeCalled(f.node))) ||
11301
+ (sc.superClass && checkInherited(sc, name))));
11263
11302
  state.pre = (node) => {
11264
11303
  switch (node.type) {
11265
11304
  case "ConditionalExpression":
@@ -12025,7 +12064,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12025
12064
  // the oldest optimized file, we don't need to regenerate
12026
12065
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
12027
12066
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
12028
- if (source_time < opt_time && 1653864408816 < opt_time) {
12067
+ if (source_time < opt_time && 1653954367574 < opt_time) {
12029
12068
  return hasTests;
12030
12069
  }
12031
12070
  }
@@ -43,47 +43,50 @@ declare global {
43
43
  type StateNodeDecls = {
44
44
  [key: string]: StateNodeDecl[];
45
45
  };
46
- type ProgramStateNode = {
46
+ interface BaseStateNode {
47
+ type: string;
48
+ node: mctree.Node | null | undefined;
49
+ name: string | null | undefined;
50
+ fullName: string | null | undefined;
51
+ decls?: StateNodeDecls | undefined;
52
+ type_decls?: StateNodeDecls | undefined;
53
+ stack?: ProgramStateStack | undefined;
54
+ }
55
+ interface ProgramStateNode extends BaseStateNode {
47
56
  type: "Program";
48
57
  node: null | undefined;
49
58
  name: "$";
50
59
  fullName: "$";
51
- decls?: StateNodeDecls;
52
- stack?: null | undefined;
53
- };
54
- type ModuleStateNode = {
60
+ stack?: undefined;
61
+ }
62
+ interface ModuleStateNode extends BaseStateNode {
55
63
  type: "ModuleDeclaration";
64
+ node: mctree.ModuleDeclaration;
56
65
  name: string;
57
66
  fullName: string;
58
- node: mctree.ModuleDeclaration;
59
- stack?: ProgramStateStack;
60
- decls?: StateNodeDecls;
61
- };
62
- type ClassStateNode = {
67
+ }
68
+ interface ClassStateNode extends BaseStateNode {
63
69
  type: "ClassDeclaration";
70
+ node: mctree.ClassDeclaration;
64
71
  name: string;
65
72
  fullName: string;
66
- node: mctree.ClassDeclaration;
67
- decls?: StateNodeDecls;
68
- stack?: ProgramStateStack;
69
- superClass: ClassStateNode[] | true;
70
- };
71
- type FunctionStateNode = {
73
+ superClass?: ClassStateNode[] | true;
74
+ }
75
+ interface FunctionStateNode extends BaseStateNode {
72
76
  type: "FunctionDeclaration";
77
+ node: mctree.FunctionDeclaration;
73
78
  name: string;
74
79
  fullName: string;
75
- node: mctree.FunctionDeclaration;
76
80
  stack?: ProgramStateStack;
77
81
  decls?: undefined;
78
- };
79
- type BlockStateNode = {
82
+ }
83
+ interface BlockStateNode extends BaseStateNode {
80
84
  type: "BlockStatement";
81
- name?: null | undefined;
82
- fullName?: null | undefined;
85
+ name: undefined;
86
+ fullName: undefined;
83
87
  node: mctree.BlockStatement;
84
- decls?: StateNodeDecls;
85
- stack?: null | undefined;
86
- };
88
+ stack?: undefined;
89
+ }
87
90
  type StateNode = ProgramStateNode | FunctionStateNode | BlockStateNode | ClassStateNode | ModuleStateNode;
88
91
  type ProgramStateStack = StateNode[];
89
92
  export type ProgramState = {
@@ -95,7 +98,10 @@ declare global {
95
98
  pre?: (node: mctree.Node, state: ProgramStateLive) => null | false | (keyof mctree.NodeAll)[];
96
99
  post?: (node: mctree.Node, state: ProgramStateLive) => null | false | mctree.Node;
97
100
  lookup?: (node: mctree.Node, name?: string | null, stack?: ProgramStateStack) => [string, StateNodeDecl[], ProgramStateStack] | [null, null, null];
101
+ lookupValue?: (node: mctree.Node, name?: string | null, stack?: ProgramStateStack) => [string, StateNodeDecl[], ProgramStateStack] | [null, null, null];
102
+ lookupType?: (node: mctree.Node, name?: string | null, stack?: ProgramStateStack) => [string, StateNodeDecl[], ProgramStateStack] | [null, null, null];
98
103
  traverse?: (node: mctree.Node) => void | null | false | mctree.Node;
104
+ inType?: boolean;
99
105
  exposed?: {
100
106
  [key: string]: true;
101
107
  };
@@ -121,7 +127,7 @@ declare global {
121
127
  type Finalized<T, Keys extends keyof T> = T & {
122
128
  [key in Keys]-?: NonNullable<T[key]>;
123
129
  };
124
- export type ProgramStateLive = Finalized<ProgramState, "stack" | "lookup" | "traverse" | "index" | "constants" | "removeNodeComments">;
130
+ export type ProgramStateLive = Finalized<ProgramState, "stack" | "lookup" | "lookupValue" | "lookupType" | "traverse" | "index" | "constants" | "removeNodeComments" | "inType">;
125
131
  export type ProgramStateAnalysis = Finalized<ProgramStateLive, "allClasses" | "allFunctions">;
126
132
  export type ProgramStateOptimizer = Finalized<ProgramStateAnalysis, "localsStack" | "exposed" | "calledFunctions">;
127
133
  type ExcludeAnnotationsMap = {
@@ -175,7 +181,7 @@ declare type RequiredNonNull<T> = {
175
181
  export declare type Analysis = {
176
182
  fnMap: RequiredNonNull<FilesToOptimizeMap>;
177
183
  paths: string[];
178
- state: ProgramState;
184
+ state: ProgramStateAnalysis;
179
185
  };
180
186
  export declare function getProjectAnalysis(targets: Target[], analysis: PreAnalysis | null, options: BuildConfig): Promise<Analysis | PreAnalysis>;
181
187
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@markw65/monkeyc-optimizer",
3
3
  "type": "module",
4
- "version": "1.0.14",
4
+ "version": "1.0.15",
5
5
  "description": "Source to source optimizer for Garmin Monkey C code",
6
6
  "main": "build/optimizer.cjs",
7
7
  "types": "build/src/optimizer.d.ts",