@markw65/monkeyc-optimizer 1.0.23 → 1.0.26
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 +17 -0
- package/build/api.cjs +65 -39
- package/build/optimizer.cjs +60 -40
- package/build/src/pre.d.ts +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -275,3 +275,20 @@ More fixes found via open source projects.
|
|
|
275
275
|
- Tests
|
|
276
276
|
- Various new tests for module/class/local resolution of symbols
|
|
277
277
|
- Make tests fail by default if the optimizer reports any undefined symbols, and add `@expects` or `checkInvalidSymbols=WARNING` as needed to prevent test failures.
|
|
278
|
+
|
|
279
|
+
### 1.0.24
|
|
280
|
+
|
|
281
|
+
- Bug fix
|
|
282
|
+
- The new ast.ts didn't pick up child elements that could be either a string or a node. This resulted in it missing the name in TypeSpecPart.
|
|
283
|
+
|
|
284
|
+
### 1.0.25
|
|
285
|
+
|
|
286
|
+
- Bug fix
|
|
287
|
+
- estree-types was missing the returnType on FunctionDeclaration. Update to latest prettier-plugin, and fix ast.ts.
|
|
288
|
+
|
|
289
|
+
### 1.0.26
|
|
290
|
+
|
|
291
|
+
- Bug fixes
|
|
292
|
+
- Use `self.` rather than `ClassName.` to qualify names that would otherwise collide with locals, since that works with both public and private variables
|
|
293
|
+
- Fix a bug that caused the inliner to fail to qualify certain names, even if there was a collision with an existing local variables
|
|
294
|
+
- Fix some name lookup issues relating to whether the lookup is done as a type or a value.
|
package/build/api.cjs
CHANGED
|
@@ -108,7 +108,7 @@ const mctreeTypeInfo = {
|
|
|
108
108
|
EnumStringMember: ["id", "init"],
|
|
109
109
|
ExpressionStatement: ["expression"],
|
|
110
110
|
ForStatement: ["init", "test", "body", "update"],
|
|
111
|
-
FunctionDeclaration: ["attrs", "id", "params", "body"],
|
|
111
|
+
FunctionDeclaration: ["attrs", "id", "params", "returnType", "body"],
|
|
112
112
|
Identifier: [],
|
|
113
113
|
IfStatement: ["test", "consequent", "alternate"],
|
|
114
114
|
ImportModule: ["id"],
|
|
@@ -135,7 +135,7 @@ const mctreeTypeInfo = {
|
|
|
135
135
|
TryStatement: ["block", "handler", "finalizer"],
|
|
136
136
|
TypedefDeclaration: ["attrs", "id", "ts"],
|
|
137
137
|
TypeSpecList: ["ts"],
|
|
138
|
-
TypeSpecPart: ["body", "callspec", "generics"],
|
|
138
|
+
TypeSpecPart: ["name", "body", "callspec", "generics"],
|
|
139
139
|
UnaryExpression: ["argument"],
|
|
140
140
|
UpdateExpression: ["argument"],
|
|
141
141
|
Using: ["id", "as"],
|
|
@@ -441,11 +441,28 @@ function inliner_shouldInline(state, func, call, context) {
|
|
|
441
441
|
}
|
|
442
442
|
return false;
|
|
443
443
|
}
|
|
444
|
-
|
|
444
|
+
// We still need to keep track of every local name that was
|
|
445
|
+
// already in use before we started inlining, but we don't want to
|
|
446
|
+
// use any of its renames. We also want to know whether a local is
|
|
447
|
+
// from the function being inlined, or the calling function, so
|
|
448
|
+
// set every element to false.
|
|
449
|
+
function fixupLocalsMap(state) {
|
|
450
|
+
if (!state.localsStack)
|
|
451
|
+
throw new Error("No local variable map!");
|
|
452
|
+
const locals = state.localsStack[state.localsStack.length - 1];
|
|
453
|
+
const { map } = locals;
|
|
454
|
+
if (!map)
|
|
455
|
+
throw new Error("No local variable map!");
|
|
456
|
+
const original = { ...map };
|
|
457
|
+
Object.keys(map).forEach((key) => (map[key] = false));
|
|
458
|
+
return original;
|
|
459
|
+
}
|
|
460
|
+
function processInlineBody(state, func, call, root, params) {
|
|
445
461
|
let failed = false;
|
|
446
462
|
const pre = state.pre;
|
|
447
463
|
const post = state.post;
|
|
448
464
|
state.inlining = true;
|
|
465
|
+
let insertedVariableDecls = null;
|
|
449
466
|
try {
|
|
450
467
|
state.pre = (node) => {
|
|
451
468
|
if (failed)
|
|
@@ -457,15 +474,10 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
|
|
|
457
474
|
return false;
|
|
458
475
|
const result = pre(node, state);
|
|
459
476
|
if (!insertedVariableDecls && node.type === "BlockStatement") {
|
|
477
|
+
// the block just created a new locals map, so we don't
|
|
478
|
+
// need to restore it at the end.
|
|
479
|
+
fixupLocalsMap(state);
|
|
460
480
|
const locals = state.localsStack[state.localsStack.length - 1];
|
|
461
|
-
const { map } = locals;
|
|
462
|
-
if (!map)
|
|
463
|
-
throw new Error("No local variable map!");
|
|
464
|
-
// We still need to keep track of every local name that was
|
|
465
|
-
// already in use, but we don't want to use any of its renames.
|
|
466
|
-
// We also want to know whether a local is from the function being
|
|
467
|
-
// inlined, or the calling function, so set every element to false.
|
|
468
|
-
Object.keys(map).forEach((key) => (map[key] = false));
|
|
469
481
|
const declarations = func.node.params
|
|
470
482
|
.map((param, i) => {
|
|
471
483
|
const paramName = variableDeclarationName(param);
|
|
@@ -542,6 +554,8 @@ function inliner_unused(expression, top) {
|
|
|
542
554
|
return [];
|
|
543
555
|
case "Identifier":
|
|
544
556
|
return [];
|
|
557
|
+
case "ThisExpression":
|
|
558
|
+
return [];
|
|
545
559
|
case "BinaryExpression":
|
|
546
560
|
if (expression.operator === "as") {
|
|
547
561
|
return inliner_unused(expression.left);
|
|
@@ -644,7 +658,7 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
644
658
|
const name = variableDeclarationName(param);
|
|
645
659
|
return [name, argnum];
|
|
646
660
|
}));
|
|
647
|
-
if (!processInlineBody(state, func, call, body,
|
|
661
|
+
if (!processInlineBody(state, func, call, body, params)) {
|
|
648
662
|
return null;
|
|
649
663
|
}
|
|
650
664
|
inliner_diagnostic(state, call.loc, null);
|
|
@@ -676,7 +690,10 @@ function inliner_inlineFunction(state, func, call, context) {
|
|
|
676
690
|
}
|
|
677
691
|
const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
|
|
678
692
|
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
679
|
-
|
|
693
|
+
const map = fixupLocalsMap(state);
|
|
694
|
+
const ret = processInlineBody(state, func, call, retArg, params);
|
|
695
|
+
state.localsStack[state.localsStack.length - 1].map = map;
|
|
696
|
+
return ret;
|
|
680
697
|
}
|
|
681
698
|
function applyTypeIfNeeded(node) {
|
|
682
699
|
if ("enumType" in node && node.enumType) {
|
|
@@ -729,6 +746,15 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
729
746
|
return "";
|
|
730
747
|
}))
|
|
731
748
|
.flat();
|
|
749
|
+
const member = (object, property) => ({
|
|
750
|
+
type: "MemberExpression",
|
|
751
|
+
object,
|
|
752
|
+
property,
|
|
753
|
+
computed: false,
|
|
754
|
+
start: node.start,
|
|
755
|
+
end: node.end,
|
|
756
|
+
loc: node.loc,
|
|
757
|
+
});
|
|
732
758
|
if (prefixes.length &&
|
|
733
759
|
prefixes[0].startsWith("$.") &&
|
|
734
760
|
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
@@ -742,38 +768,32 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
742
768
|
found = true;
|
|
743
769
|
return current;
|
|
744
770
|
}
|
|
745
|
-
const object =
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
name,
|
|
749
|
-
start: node.start,
|
|
750
|
-
end: node.end,
|
|
751
|
-
loc: node.loc,
|
|
752
|
-
}
|
|
753
|
-
: name;
|
|
754
|
-
let root = null;
|
|
755
|
-
let property = current;
|
|
756
|
-
while (property.type !== "Identifier") {
|
|
757
|
-
root = property;
|
|
758
|
-
property = property.object;
|
|
759
|
-
}
|
|
760
|
-
const mb = {
|
|
761
|
-
type: "MemberExpression",
|
|
762
|
-
object,
|
|
763
|
-
property,
|
|
764
|
-
computed: false,
|
|
771
|
+
const object = {
|
|
772
|
+
type: "Identifier",
|
|
773
|
+
name,
|
|
765
774
|
start: node.start,
|
|
766
775
|
end: node.end,
|
|
767
776
|
loc: node.loc,
|
|
768
777
|
};
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
778
|
+
let root = null;
|
|
779
|
+
let property = current;
|
|
780
|
+
do {
|
|
781
|
+
root = property;
|
|
782
|
+
property = property.object;
|
|
783
|
+
} while (property.type === "MemberExpression");
|
|
784
|
+
if (property.type === "ThisExpression") {
|
|
785
|
+
root.object = object;
|
|
786
|
+
return root;
|
|
774
787
|
}
|
|
788
|
+
root.object = member(object, property);
|
|
775
789
|
return current;
|
|
776
|
-
},
|
|
790
|
+
}, member({
|
|
791
|
+
type: "ThisExpression",
|
|
792
|
+
text: "self",
|
|
793
|
+
start: node.start,
|
|
794
|
+
end: node.end,
|
|
795
|
+
loc: node.loc,
|
|
796
|
+
}, node));
|
|
777
797
|
}
|
|
778
798
|
return null;
|
|
779
799
|
}
|
|
@@ -2213,6 +2233,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2213
2233
|
state.stack[0].node = node;
|
|
2214
2234
|
break;
|
|
2215
2235
|
case "TypeSpecList":
|
|
2236
|
+
case "TypeSpecPart":
|
|
2216
2237
|
state.inType = true;
|
|
2217
2238
|
break;
|
|
2218
2239
|
case "ImportModule":
|
|
@@ -2454,6 +2475,11 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2454
2475
|
if (state.post)
|
|
2455
2476
|
ret = state.post(node, state);
|
|
2456
2477
|
switch (type) {
|
|
2478
|
+
// Don't clear inType for TypeSpecPart, since they
|
|
2479
|
+
// generally occur in TypeSpecLists. But do clear it for
|
|
2480
|
+
// SizedArrayExpression, since thats the only place they
|
|
2481
|
+
// happen on their own.
|
|
2482
|
+
case "SizedArrayExpression":
|
|
2457
2483
|
case "TypeSpecList":
|
|
2458
2484
|
case "TypedefDeclaration":
|
|
2459
2485
|
case "EnumDeclaration":
|
package/build/optimizer.cjs
CHANGED
|
@@ -10834,7 +10834,7 @@ const mctreeTypeInfo = {
|
|
|
10834
10834
|
EnumStringMember: ["id", "init"],
|
|
10835
10835
|
ExpressionStatement: ["expression"],
|
|
10836
10836
|
ForStatement: ["init", "test", "body", "update"],
|
|
10837
|
-
FunctionDeclaration: ["attrs", "id", "params", "body"],
|
|
10837
|
+
FunctionDeclaration: ["attrs", "id", "params", "returnType", "body"],
|
|
10838
10838
|
Identifier: [],
|
|
10839
10839
|
IfStatement: ["test", "consequent", "alternate"],
|
|
10840
10840
|
ImportModule: ["id"],
|
|
@@ -10861,7 +10861,7 @@ const mctreeTypeInfo = {
|
|
|
10861
10861
|
TryStatement: ["block", "handler", "finalizer"],
|
|
10862
10862
|
TypedefDeclaration: ["attrs", "id", "ts"],
|
|
10863
10863
|
TypeSpecList: ["ts"],
|
|
10864
|
-
TypeSpecPart: ["body", "callspec", "generics"],
|
|
10864
|
+
TypeSpecPart: ["name", "body", "callspec", "generics"],
|
|
10865
10865
|
UnaryExpression: ["argument"],
|
|
10866
10866
|
UpdateExpression: ["argument"],
|
|
10867
10867
|
Using: ["id", "as"],
|
|
@@ -11165,11 +11165,28 @@ function shouldInline(state, func, call, context) {
|
|
|
11165
11165
|
}
|
|
11166
11166
|
return false;
|
|
11167
11167
|
}
|
|
11168
|
-
|
|
11168
|
+
// We still need to keep track of every local name that was
|
|
11169
|
+
// already in use before we started inlining, but we don't want to
|
|
11170
|
+
// use any of its renames. We also want to know whether a local is
|
|
11171
|
+
// from the function being inlined, or the calling function, so
|
|
11172
|
+
// set every element to false.
|
|
11173
|
+
function fixupLocalsMap(state) {
|
|
11174
|
+
if (!state.localsStack)
|
|
11175
|
+
throw new Error("No local variable map!");
|
|
11176
|
+
const locals = state.localsStack[state.localsStack.length - 1];
|
|
11177
|
+
const { map } = locals;
|
|
11178
|
+
if (!map)
|
|
11179
|
+
throw new Error("No local variable map!");
|
|
11180
|
+
const original = { ...map };
|
|
11181
|
+
Object.keys(map).forEach((key) => (map[key] = false));
|
|
11182
|
+
return original;
|
|
11183
|
+
}
|
|
11184
|
+
function processInlineBody(state, func, call, root, params) {
|
|
11169
11185
|
let failed = false;
|
|
11170
11186
|
const pre = state.pre;
|
|
11171
11187
|
const post = state.post;
|
|
11172
11188
|
state.inlining = true;
|
|
11189
|
+
let insertedVariableDecls = null;
|
|
11173
11190
|
try {
|
|
11174
11191
|
state.pre = (node) => {
|
|
11175
11192
|
if (failed)
|
|
@@ -11181,15 +11198,10 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
|
|
|
11181
11198
|
return false;
|
|
11182
11199
|
const result = pre(node, state);
|
|
11183
11200
|
if (!insertedVariableDecls && node.type === "BlockStatement") {
|
|
11201
|
+
// the block just created a new locals map, so we don't
|
|
11202
|
+
// need to restore it at the end.
|
|
11203
|
+
fixupLocalsMap(state);
|
|
11184
11204
|
const locals = state.localsStack[state.localsStack.length - 1];
|
|
11185
|
-
const { map } = locals;
|
|
11186
|
-
if (!map)
|
|
11187
|
-
throw new Error("No local variable map!");
|
|
11188
|
-
// We still need to keep track of every local name that was
|
|
11189
|
-
// already in use, but we don't want to use any of its renames.
|
|
11190
|
-
// We also want to know whether a local is from the function being
|
|
11191
|
-
// inlined, or the calling function, so set every element to false.
|
|
11192
|
-
Object.keys(map).forEach((key) => (map[key] = false));
|
|
11193
11205
|
const declarations = func.node.params
|
|
11194
11206
|
.map((param, i) => {
|
|
11195
11207
|
const paramName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
|
|
@@ -11266,6 +11278,8 @@ function unused(expression, top) {
|
|
|
11266
11278
|
return [];
|
|
11267
11279
|
case "Identifier":
|
|
11268
11280
|
return [];
|
|
11281
|
+
case "ThisExpression":
|
|
11282
|
+
return [];
|
|
11269
11283
|
case "BinaryExpression":
|
|
11270
11284
|
if (expression.operator === "as") {
|
|
11271
11285
|
return unused(expression.left);
|
|
@@ -11368,7 +11382,7 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
11368
11382
|
const name = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
|
|
11369
11383
|
return [name, argnum];
|
|
11370
11384
|
}));
|
|
11371
|
-
if (!processInlineBody(state, func, call, body,
|
|
11385
|
+
if (!processInlineBody(state, func, call, body, params)) {
|
|
11372
11386
|
return null;
|
|
11373
11387
|
}
|
|
11374
11388
|
diagnostic(state, call.loc, null);
|
|
@@ -11400,7 +11414,10 @@ function inlineFunction(state, func, call, context) {
|
|
|
11400
11414
|
}
|
|
11401
11415
|
const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
|
|
11402
11416
|
const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
|
|
11403
|
-
|
|
11417
|
+
const map = fixupLocalsMap(state);
|
|
11418
|
+
const ret = processInlineBody(state, func, call, retArg, params);
|
|
11419
|
+
state.localsStack[state.localsStack.length - 1].map = map;
|
|
11420
|
+
return ret;
|
|
11404
11421
|
}
|
|
11405
11422
|
function applyTypeIfNeeded(node) {
|
|
11406
11423
|
if ("enumType" in node && node.enumType) {
|
|
@@ -11453,6 +11470,15 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
11453
11470
|
return "";
|
|
11454
11471
|
}))
|
|
11455
11472
|
.flat();
|
|
11473
|
+
const member = (object, property) => ({
|
|
11474
|
+
type: "MemberExpression",
|
|
11475
|
+
object,
|
|
11476
|
+
property,
|
|
11477
|
+
computed: false,
|
|
11478
|
+
start: node.start,
|
|
11479
|
+
end: node.end,
|
|
11480
|
+
loc: node.loc,
|
|
11481
|
+
});
|
|
11456
11482
|
if (prefixes.length &&
|
|
11457
11483
|
prefixes[0].startsWith("$.") &&
|
|
11458
11484
|
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
@@ -11466,38 +11492,32 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
11466
11492
|
found = true;
|
|
11467
11493
|
return current;
|
|
11468
11494
|
}
|
|
11469
|
-
const object =
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
name,
|
|
11473
|
-
start: node.start,
|
|
11474
|
-
end: node.end,
|
|
11475
|
-
loc: node.loc,
|
|
11476
|
-
}
|
|
11477
|
-
: name;
|
|
11478
|
-
let root = null;
|
|
11479
|
-
let property = current;
|
|
11480
|
-
while (property.type !== "Identifier") {
|
|
11481
|
-
root = property;
|
|
11482
|
-
property = property.object;
|
|
11483
|
-
}
|
|
11484
|
-
const mb = {
|
|
11485
|
-
type: "MemberExpression",
|
|
11486
|
-
object,
|
|
11487
|
-
property,
|
|
11488
|
-
computed: false,
|
|
11495
|
+
const object = {
|
|
11496
|
+
type: "Identifier",
|
|
11497
|
+
name,
|
|
11489
11498
|
start: node.start,
|
|
11490
11499
|
end: node.end,
|
|
11491
11500
|
loc: node.loc,
|
|
11492
11501
|
};
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11502
|
+
let root = null;
|
|
11503
|
+
let property = current;
|
|
11504
|
+
do {
|
|
11505
|
+
root = property;
|
|
11506
|
+
property = property.object;
|
|
11507
|
+
} while (property.type === "MemberExpression");
|
|
11508
|
+
if (property.type === "ThisExpression") {
|
|
11509
|
+
root.object = object;
|
|
11510
|
+
return root;
|
|
11498
11511
|
}
|
|
11512
|
+
root.object = member(object, property);
|
|
11499
11513
|
return current;
|
|
11500
|
-
},
|
|
11514
|
+
}, member({
|
|
11515
|
+
type: "ThisExpression",
|
|
11516
|
+
text: "self",
|
|
11517
|
+
start: node.start,
|
|
11518
|
+
end: node.end,
|
|
11519
|
+
loc: node.loc,
|
|
11520
|
+
}, node));
|
|
11501
11521
|
}
|
|
11502
11522
|
return null;
|
|
11503
11523
|
}
|
|
@@ -13085,7 +13105,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
|
|
|
13085
13105
|
// the oldest optimized file, we don't need to regenerate
|
|
13086
13106
|
const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
|
|
13087
13107
|
const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
|
|
13088
|
-
if (source_time < opt_time &&
|
|
13108
|
+
if (source_time < opt_time && 1655771632397 < opt_time) {
|
|
13089
13109
|
return { hasTests, diagnostics: prevDiagnostics };
|
|
13090
13110
|
}
|
|
13091
13111
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sizeBasedPRE(state: ProgramStateAnalysis, func: FunctionStateNode): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markw65/monkeyc-optimizer",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.26",
|
|
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",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"author": "markw65",
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@markw65/prettier-plugin-monkeyc": "^1.0.
|
|
40
|
+
"@markw65/prettier-plugin-monkeyc": "^1.0.28"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/glob": "^7.2.0",
|