@markw65/monkeyc-optimizer 1.0.20 → 1.0.23
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 +40 -0
- package/build/api.cjs +770 -286
- package/build/optimizer.cjs +552 -201
- package/build/src/api.d.ts +7 -3
- package/build/src/ast.d.ts +2 -0
- package/build/src/inliner.d.ts +1 -5
- package/build/src/mc-rewrite.d.ts +4 -4
- package/build/src/optimizer.d.ts +29 -11
- package/build/src/pragma-checker.d.ts +1 -1
- package/build/src/util.d.ts +1 -0
- package/build/src/visitor.d.ts +2 -0
- package/build/util.cjs +10 -4
- package/package.json +9 -4
package/build/api.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
0 && (module.exports = {collectNamespaces,formatAst,getApiMapping,hasProperty,isStateNode,traverseAst,variableDeclarationName});
|
|
1
|
+
0 && (module.exports = {collectNamespaces,findUsingForNode,formatAst,getApiMapping,hasProperty,isStateNode,sameLookupResult,traverseAst,variableDeclarationName,visitReferences});
|
|
2
2
|
/******/ (() => { // webpackBootstrap
|
|
3
3
|
/******/ "use strict";
|
|
4
4
|
/******/ // The require scope
|
|
@@ -53,12 +53,15 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
53
53
|
// EXPORTS
|
|
54
54
|
__webpack_require__.d(__webpack_exports__, {
|
|
55
55
|
"collectNamespaces": () => (/* binding */ api_collectNamespaces),
|
|
56
|
-
"
|
|
56
|
+
"findUsingForNode": () => (/* binding */ findUsingForNode),
|
|
57
|
+
"formatAst": () => (/* binding */ api_formatAst),
|
|
57
58
|
"getApiMapping": () => (/* binding */ api_getApiMapping),
|
|
58
59
|
"hasProperty": () => (/* binding */ api_hasProperty),
|
|
59
60
|
"isStateNode": () => (/* binding */ api_isStateNode),
|
|
60
|
-
"
|
|
61
|
-
"
|
|
61
|
+
"sameLookupResult": () => (/* binding */ api_sameLookupResult),
|
|
62
|
+
"traverseAst": () => (/* reexport */ ast_traverseAst),
|
|
63
|
+
"variableDeclarationName": () => (/* binding */ api_variableDeclarationName),
|
|
64
|
+
"visitReferences": () => (/* reexport */ visitor_visitReferences)
|
|
62
65
|
});
|
|
63
66
|
|
|
64
67
|
;// CONCATENATED MODULE: external "@markw65/prettier-plugin-monkeyc"
|
|
@@ -68,10 +71,148 @@ var prettier_plugin_monkeyc_default = /*#__PURE__*/__webpack_require__.n(prettie
|
|
|
68
71
|
const promises_namespaceObject = require("fs/promises");
|
|
69
72
|
;// CONCATENATED MODULE: external "prettier"
|
|
70
73
|
const external_prettier_namespaceObject = require("prettier");
|
|
74
|
+
;// CONCATENATED MODULE: ./src/ast.ts
|
|
75
|
+
/*
|
|
76
|
+
* This ensures that mctreeTypeInfo has every key of MCTreeTypeInfo,
|
|
77
|
+
* and that the corresponding arrays contain every element of the
|
|
78
|
+
* corresponding type.
|
|
79
|
+
*
|
|
80
|
+
* ie, any time mctree.Node changes, we'll get errors here if
|
|
81
|
+
* mctreeTypeInfo needs updating.
|
|
82
|
+
*
|
|
83
|
+
*/
|
|
84
|
+
function _check(x) {
|
|
85
|
+
const y = x;
|
|
86
|
+
x = y;
|
|
87
|
+
}
|
|
88
|
+
const mctreeTypeInfo = {
|
|
89
|
+
ArrayExpression: ["elements"],
|
|
90
|
+
AssignmentExpression: ["left", "right"],
|
|
91
|
+
AttributeList: ["attributes"],
|
|
92
|
+
Attributes: ["elements"],
|
|
93
|
+
BinaryExpression: ["left", "right"],
|
|
94
|
+
Block: [],
|
|
95
|
+
BlockStatement: ["body", "innerComments"],
|
|
96
|
+
BreakStatement: [],
|
|
97
|
+
CallExpression: ["callee", "arguments"],
|
|
98
|
+
CatchClause: ["param", "body"],
|
|
99
|
+
CatchClauses: ["catches"],
|
|
100
|
+
ClassBody: ["body"],
|
|
101
|
+
ClassDeclaration: ["attrs", "id", "superClass", "body"],
|
|
102
|
+
ClassElement: ["item"],
|
|
103
|
+
ConditionalExpression: ["test", "consequent", "alternate"],
|
|
104
|
+
ContinueStatement: [],
|
|
105
|
+
DoWhileStatement: ["body", "test"],
|
|
106
|
+
EnumDeclaration: ["attrs", "id", "body"],
|
|
107
|
+
EnumStringBody: ["members"],
|
|
108
|
+
EnumStringMember: ["id", "init"],
|
|
109
|
+
ExpressionStatement: ["expression"],
|
|
110
|
+
ForStatement: ["init", "test", "body", "update"],
|
|
111
|
+
FunctionDeclaration: ["attrs", "id", "params", "body"],
|
|
112
|
+
Identifier: [],
|
|
113
|
+
IfStatement: ["test", "consequent", "alternate"],
|
|
114
|
+
ImportModule: ["id"],
|
|
115
|
+
InstanceOfCase: ["id"],
|
|
116
|
+
Line: [],
|
|
117
|
+
Literal: [],
|
|
118
|
+
LogicalExpression: ["left", "right"],
|
|
119
|
+
MemberExpression: ["object", "property"],
|
|
120
|
+
MethodDefinition: ["params", "returnType"],
|
|
121
|
+
ModuleDeclaration: ["attrs", "id", "body"],
|
|
122
|
+
MultiLine: [],
|
|
123
|
+
NewExpression: ["callee", "arguments"],
|
|
124
|
+
ObjectExpression: ["properties"],
|
|
125
|
+
ParenthesizedExpression: ["expression"],
|
|
126
|
+
Program: ["body", "comments"],
|
|
127
|
+
Property: ["key", "value"],
|
|
128
|
+
ReturnStatement: ["argument"],
|
|
129
|
+
SequenceExpression: ["expressions"],
|
|
130
|
+
SizedArrayExpression: ["size", "ts"],
|
|
131
|
+
SwitchCase: ["test", "consequent"],
|
|
132
|
+
SwitchStatement: ["discriminant", "cases"],
|
|
133
|
+
ThisExpression: [],
|
|
134
|
+
ThrowStatement: ["argument"],
|
|
135
|
+
TryStatement: ["block", "handler", "finalizer"],
|
|
136
|
+
TypedefDeclaration: ["attrs", "id", "ts"],
|
|
137
|
+
TypeSpecList: ["ts"],
|
|
138
|
+
TypeSpecPart: ["body", "callspec", "generics"],
|
|
139
|
+
UnaryExpression: ["argument"],
|
|
140
|
+
UpdateExpression: ["argument"],
|
|
141
|
+
Using: ["id", "as"],
|
|
142
|
+
VariableDeclaration: ["attrs", "declarations"],
|
|
143
|
+
VariableDeclarator: ["id", "init"],
|
|
144
|
+
WhileStatement: ["test", "body"],
|
|
145
|
+
};
|
|
146
|
+
function isMCTreeNode(node) {
|
|
147
|
+
return node ? typeof node === "object" && "type" in node : false;
|
|
148
|
+
}
|
|
149
|
+
/*
|
|
150
|
+
* Traverse the ast rooted at node, calling pre before
|
|
151
|
+
* visiting each node, and post after.
|
|
152
|
+
*
|
|
153
|
+
* - if pre returns false, the node is not traversed, and
|
|
154
|
+
* post is not called;
|
|
155
|
+
* - if pre returns a list of child nodes, only those will
|
|
156
|
+
* be traversed
|
|
157
|
+
* - otherwise all child nodes are traversed
|
|
158
|
+
*
|
|
159
|
+
* - if post returns false, the node it was called on is
|
|
160
|
+
* removed.
|
|
161
|
+
*/
|
|
162
|
+
function ast_traverseAst(node, pre, post) {
|
|
163
|
+
const nodes = pre && pre(node);
|
|
164
|
+
if (nodes === false)
|
|
165
|
+
return;
|
|
166
|
+
if (!mctreeTypeInfo[node.type]) {
|
|
167
|
+
throw new Error("what?");
|
|
168
|
+
}
|
|
169
|
+
for (const key of nodes || mctreeTypeInfo[node.type]) {
|
|
170
|
+
const value = node[key];
|
|
171
|
+
if (!value)
|
|
172
|
+
continue;
|
|
173
|
+
if (Array.isArray(value)) {
|
|
174
|
+
const values = value;
|
|
175
|
+
const deletions = values.reduce((state, obj, i) => {
|
|
176
|
+
if (isMCTreeNode(obj)) {
|
|
177
|
+
const repl = ast_traverseAst(obj, pre, post);
|
|
178
|
+
if (repl === false) {
|
|
179
|
+
if (!state)
|
|
180
|
+
state = {};
|
|
181
|
+
state[i] = true;
|
|
182
|
+
}
|
|
183
|
+
else if (repl != null) {
|
|
184
|
+
if (!state)
|
|
185
|
+
state = {};
|
|
186
|
+
values[i] = repl;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return state;
|
|
190
|
+
}, null);
|
|
191
|
+
if (deletions) {
|
|
192
|
+
values.splice(0, values.length, ...values.filter((obj, i) => deletions[i] !== true).flat(1));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else if (isMCTreeNode(value)) {
|
|
196
|
+
const repl = ast_traverseAst(value, pre, post);
|
|
197
|
+
if (repl === false) {
|
|
198
|
+
delete node[key];
|
|
199
|
+
}
|
|
200
|
+
else if (repl != null) {
|
|
201
|
+
if (Array.isArray(repl)) {
|
|
202
|
+
throw new Error("Array returned by traverseAst in Node context");
|
|
203
|
+
}
|
|
204
|
+
node[key] = repl;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return post && post(node);
|
|
209
|
+
}
|
|
210
|
+
|
|
71
211
|
;// CONCATENATED MODULE: external "./api.cjs"
|
|
72
212
|
const external_api_cjs_namespaceObject = require("./api.cjs");
|
|
73
213
|
;// CONCATENATED MODULE: ./src/variable-renamer.ts
|
|
74
214
|
|
|
215
|
+
|
|
75
216
|
function variable_renamer_renameVariable(state, locals, declName) {
|
|
76
217
|
const map = locals.map;
|
|
77
218
|
if (!hasProperty(map, declName))
|
|
@@ -128,27 +269,37 @@ function variable_renamer_renameVariable(state, locals, declName) {
|
|
|
128
269
|
;// CONCATENATED MODULE: ./src/inliner.ts
|
|
129
270
|
|
|
130
271
|
|
|
272
|
+
|
|
131
273
|
function getArgSafety(state, func, args, requireAll) {
|
|
132
274
|
// determine whether decl might be changed by a function call
|
|
133
275
|
// or assignment during the evaluation of FunctionStateNode.
|
|
134
276
|
const getSafety = (decl) => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (decl.type === "VariableDeclarator") {
|
|
139
|
-
// constants also can't change
|
|
140
|
-
if (decl.node.kind === "const")
|
|
277
|
+
switch (decl.type) {
|
|
278
|
+
// enums are constant, they cant change
|
|
279
|
+
case "EnumStringMember":
|
|
141
280
|
return true;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (!state.stack[i] || decl.stack[i] !== state.stack[i])
|
|
146
|
-
return false;
|
|
147
|
-
if (state.stack[i].type === "FunctionDeclaration")
|
|
281
|
+
case "VariableDeclarator": {
|
|
282
|
+
// constants also can't change
|
|
283
|
+
if (decl.node.kind === "const")
|
|
148
284
|
return true;
|
|
285
|
+
// if decl is a local, it also can't be changed
|
|
286
|
+
// by a call to another function.
|
|
287
|
+
for (let i = 0;; i++) {
|
|
288
|
+
if (!state.stack[i] || decl.stack[i] !== state.stack[i])
|
|
289
|
+
return false;
|
|
290
|
+
if (state.stack[i].type === "FunctionDeclaration")
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
149
293
|
}
|
|
294
|
+
case "Identifier":
|
|
295
|
+
case "BinaryExpression":
|
|
296
|
+
// This is a parameter of the calling function.
|
|
297
|
+
// It also can't be changed during the execution
|
|
298
|
+
// of the inlined function
|
|
299
|
+
return true;
|
|
300
|
+
default:
|
|
301
|
+
return null;
|
|
150
302
|
}
|
|
151
|
-
return null;
|
|
152
303
|
};
|
|
153
304
|
const safeArgs = [];
|
|
154
305
|
let allSafe = true;
|
|
@@ -160,11 +311,13 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
160
311
|
case "Identifier":
|
|
161
312
|
case "MemberExpression": {
|
|
162
313
|
const [, results] = state.lookup(arg);
|
|
163
|
-
if (!results ||
|
|
314
|
+
if (!results ||
|
|
315
|
+
results.length !== 1 ||
|
|
316
|
+
results[0].results.length !== 1) {
|
|
164
317
|
safeArgs.push(null);
|
|
165
318
|
return !requireAll;
|
|
166
319
|
}
|
|
167
|
-
const safety = getSafety(results[0]);
|
|
320
|
+
const safety = getSafety(results[0].results[0]);
|
|
168
321
|
safeArgs.push(safety);
|
|
169
322
|
if (!safety) {
|
|
170
323
|
allSafe = false;
|
|
@@ -184,25 +337,12 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
184
337
|
if (allSafe && requireAll)
|
|
185
338
|
return true;
|
|
186
339
|
let callSeen = false;
|
|
187
|
-
let ok = true;
|
|
188
340
|
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
189
|
-
const getLoc = (node) => (Array.isArray(node) ? node[0].start : node.start) || 0;
|
|
190
341
|
// look for uses of "unsafe" args that occur after a call.
|
|
191
342
|
// use post to do the checking, because arguments are evaluated
|
|
192
343
|
// prior to the call, so eg "return f(x.y);" is fine, but
|
|
193
344
|
// "return f()+x.y" is not.
|
|
194
|
-
|
|
195
|
-
// We also have to use a "pre" to ensure that child nodes are
|
|
196
|
-
// visited in source order (otherwise we could visit x.y before f()
|
|
197
|
-
// in the above example)
|
|
198
|
-
traverseAst(func.node.body, (node) => {
|
|
199
|
-
return Object.entries(node)
|
|
200
|
-
.filter((kv) => Array.isArray(kv[1])
|
|
201
|
-
? kv[1].length !== 0 && hasProperty(kv[1][0], "type")
|
|
202
|
-
: hasProperty(kv[1], "type"))
|
|
203
|
-
.sort(([, a], [, b]) => getLoc(a) - getLoc(b))
|
|
204
|
-
.map(([key]) => key);
|
|
205
|
-
}, (node) => {
|
|
345
|
+
traverseAst(func.node.body, null, (node) => {
|
|
206
346
|
switch (node.type) {
|
|
207
347
|
case "AssignmentExpression":
|
|
208
348
|
case "UpdateExpression": {
|
|
@@ -256,27 +396,23 @@ function inliningLooksUseful(func, node) {
|
|
|
256
396
|
}
|
|
257
397
|
return false;
|
|
258
398
|
}
|
|
259
|
-
var InlineStatus;
|
|
260
|
-
(function (InlineStatus) {
|
|
261
|
-
InlineStatus[InlineStatus["Never"] = 0] = "Never";
|
|
262
|
-
InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
|
|
263
|
-
InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
|
|
264
|
-
})(InlineStatus || (InlineStatus = {}));
|
|
265
399
|
function inlineRequested(state, func) {
|
|
266
400
|
const excludeAnnotations = (func.node.loc?.source &&
|
|
267
401
|
state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
|
|
268
402
|
{};
|
|
269
403
|
if (func.node.attrs &&
|
|
270
|
-
func.node.attrs.
|
|
271
|
-
func.node.attrs.
|
|
404
|
+
func.node.attrs.attributes &&
|
|
405
|
+
func.node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" &&
|
|
272
406
|
(attr.argument.name === "inline" ||
|
|
273
407
|
(attr.argument.name.startsWith("inline_") &&
|
|
274
|
-
hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
|
|
408
|
+
!hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
|
|
275
409
|
return true;
|
|
276
410
|
}
|
|
277
411
|
return false;
|
|
278
412
|
}
|
|
279
413
|
function inliner_shouldInline(state, func, call, context) {
|
|
414
|
+
if (state.inlining)
|
|
415
|
+
return false;
|
|
280
416
|
let autoInline = false;
|
|
281
417
|
let inlineAsExpression = false;
|
|
282
418
|
const args = call.arguments;
|
|
@@ -309,6 +445,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
|
|
|
309
445
|
let failed = false;
|
|
310
446
|
const pre = state.pre;
|
|
311
447
|
const post = state.post;
|
|
448
|
+
state.inlining = true;
|
|
312
449
|
try {
|
|
313
450
|
state.pre = (node) => {
|
|
314
451
|
if (failed)
|
|
@@ -354,7 +491,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
|
|
|
354
491
|
};
|
|
355
492
|
state.post = (node) => {
|
|
356
493
|
if (failed)
|
|
357
|
-
return
|
|
494
|
+
return post(node, state);
|
|
358
495
|
let replacement = null;
|
|
359
496
|
switch (node.type) {
|
|
360
497
|
case "Identifier": {
|
|
@@ -371,12 +508,13 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
|
|
|
371
508
|
if (!replacement) {
|
|
372
509
|
failed = true;
|
|
373
510
|
inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
|
|
374
|
-
return
|
|
511
|
+
return post(node, state);
|
|
375
512
|
}
|
|
376
513
|
break;
|
|
377
514
|
}
|
|
378
515
|
}
|
|
379
|
-
|
|
516
|
+
const ret = post(replacement || node, state);
|
|
517
|
+
return ret === false || ret ? ret : replacement;
|
|
380
518
|
};
|
|
381
519
|
let ret = state.traverse(root);
|
|
382
520
|
if (failed) {
|
|
@@ -395,6 +533,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
|
|
|
395
533
|
finally {
|
|
396
534
|
state.pre = pre;
|
|
397
535
|
state.post = post;
|
|
536
|
+
delete state.inlining;
|
|
398
537
|
}
|
|
399
538
|
}
|
|
400
539
|
function inliner_unused(expression, top) {
|
|
@@ -436,13 +575,13 @@ function inliner_unused(expression, top) {
|
|
|
436
575
|
},
|
|
437
576
|
];
|
|
438
577
|
}
|
|
439
|
-
function
|
|
578
|
+
function inliner_diagnostic(state, loc, message, type = "INFO") {
|
|
440
579
|
if (!loc || !loc.source)
|
|
441
580
|
return;
|
|
442
581
|
const source = loc.source;
|
|
443
582
|
if (!state.diagnostics)
|
|
444
583
|
state.diagnostics = {};
|
|
445
|
-
if (!hasProperty(state.diagnostics, source)) {
|
|
584
|
+
if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.diagnostics, source)) {
|
|
446
585
|
if (!message)
|
|
447
586
|
return;
|
|
448
587
|
state.diagnostics[source] = [];
|
|
@@ -452,7 +591,7 @@ function diagnostic(state, loc, message) {
|
|
|
452
591
|
if (message) {
|
|
453
592
|
if (index < 0)
|
|
454
593
|
index = diags.length;
|
|
455
|
-
diags[index] = { type
|
|
594
|
+
diags[index] = { type, loc, message };
|
|
456
595
|
}
|
|
457
596
|
else if (index >= 0) {
|
|
458
597
|
diags.splice(index, 1);
|
|
@@ -460,7 +599,7 @@ function diagnostic(state, loc, message) {
|
|
|
460
599
|
}
|
|
461
600
|
function inlineDiagnostic(state, func, call, message) {
|
|
462
601
|
if (inlineRequested(state, func)) {
|
|
463
|
-
|
|
602
|
+
inliner_diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
|
|
464
603
|
}
|
|
465
604
|
}
|
|
466
605
|
function inlineWithArgs(state, func, call, context) {
|
|
@@ -508,7 +647,7 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
508
647
|
if (!processInlineBody(state, func, call, body, func.node.params.length ? false : true, params)) {
|
|
509
648
|
return null;
|
|
510
649
|
}
|
|
511
|
-
|
|
650
|
+
inliner_diagnostic(state, call.loc, null);
|
|
512
651
|
if (context.type !== "ReturnStatement" && retStmtCount) {
|
|
513
652
|
const last = body.body[body.body.length - 1];
|
|
514
653
|
if (last.type != "ReturnStatement") {
|
|
@@ -571,23 +710,25 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
571
710
|
// With a bit more work, we could find the guaranteed shortest
|
|
572
711
|
// reference, and then use this to optimize *all* symbols, not
|
|
573
712
|
// just fix inlined ones.
|
|
574
|
-
if (current &&
|
|
575
|
-
current.length === original.length &&
|
|
576
|
-
current.every((item, index) => item == original[index])) {
|
|
713
|
+
if (current && sameLookupResult(original, current)) {
|
|
577
714
|
return lookupNode;
|
|
578
715
|
}
|
|
579
716
|
const node = lookupNode.type === "Identifier"
|
|
580
717
|
? lookupNode
|
|
581
718
|
: lookupNode.property;
|
|
582
|
-
if (original.length === 1 &&
|
|
583
|
-
|
|
719
|
+
if (original.length === 1 &&
|
|
720
|
+
original[0].results.length === 1 &&
|
|
721
|
+
original[0].results[0].type === "EnumStringMember") {
|
|
722
|
+
return applyTypeIfNeeded(original[0].results[0].init);
|
|
584
723
|
}
|
|
585
|
-
const prefixes = original
|
|
724
|
+
const prefixes = original
|
|
725
|
+
.map((lookupDef) => lookupDef.results.map((sn) => {
|
|
586
726
|
if (isStateNode(sn) && sn.fullName) {
|
|
587
727
|
return sn.fullName;
|
|
588
728
|
}
|
|
589
729
|
return "";
|
|
590
|
-
})
|
|
730
|
+
}))
|
|
731
|
+
.flat();
|
|
591
732
|
if (prefixes.length &&
|
|
592
733
|
prefixes[0].startsWith("$.") &&
|
|
593
734
|
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
@@ -597,9 +738,7 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
597
738
|
if (found)
|
|
598
739
|
return current;
|
|
599
740
|
const [, results] = state.lookup(current);
|
|
600
|
-
if (results &&
|
|
601
|
-
results.length === original.length &&
|
|
602
|
-
results.every((result, i) => result === original[i])) {
|
|
741
|
+
if (results && sameLookupResult(original, results)) {
|
|
603
742
|
found = true;
|
|
604
743
|
return current;
|
|
605
744
|
}
|
|
@@ -641,6 +780,99 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
641
780
|
|
|
642
781
|
;// CONCATENATED MODULE: external "./util.cjs"
|
|
643
782
|
const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
783
|
+
;// CONCATENATED MODULE: ./src/visitor.ts
|
|
784
|
+
|
|
785
|
+
function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
786
|
+
const checkResults = ([name, results], node) => {
|
|
787
|
+
if (name && results) {
|
|
788
|
+
if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
|
|
789
|
+
if (callback(node, results, false) === false) {
|
|
790
|
+
return [];
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
else if (defn === false) {
|
|
795
|
+
if (callback(node, [], results === null) === false) {
|
|
796
|
+
return [];
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
return null;
|
|
800
|
+
};
|
|
801
|
+
state.pre = (node) => {
|
|
802
|
+
switch (node.type) {
|
|
803
|
+
case "AttributeList":
|
|
804
|
+
return [];
|
|
805
|
+
case "UnaryExpression":
|
|
806
|
+
// a bare symbol isn't a reference
|
|
807
|
+
if (node.operator === ":")
|
|
808
|
+
return [];
|
|
809
|
+
break;
|
|
810
|
+
case "BinaryExpression":
|
|
811
|
+
/*
|
|
812
|
+
* `expr has :symbol` can be treated as a reference
|
|
813
|
+
* to expr.symbol.
|
|
814
|
+
*/
|
|
815
|
+
if (node.operator === "has") {
|
|
816
|
+
if (node.right.type === "UnaryExpression" &&
|
|
817
|
+
node.right.operator === ":") {
|
|
818
|
+
if (!name || node.right.argument.name === name) {
|
|
819
|
+
return checkResults(state.lookup({
|
|
820
|
+
type: "MemberExpression",
|
|
821
|
+
object: node.left,
|
|
822
|
+
property: node.right.argument,
|
|
823
|
+
computed: false,
|
|
824
|
+
}), node.right.argument);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
break;
|
|
829
|
+
case "CallExpression":
|
|
830
|
+
// A call expression whose callee is an identifier is looked
|
|
831
|
+
// up as a non-local. ie even if there's a same named local,
|
|
832
|
+
// it will be ignored, and the lookup will start as if the
|
|
833
|
+
// call had been written self.foo() rather than foo().
|
|
834
|
+
if (node.callee.type === "Identifier") {
|
|
835
|
+
if (!name || node.callee.name === name) {
|
|
836
|
+
/* ignore return value */
|
|
837
|
+
checkResults(state.lookupNonlocal(node.callee), node.callee);
|
|
838
|
+
}
|
|
839
|
+
return ["arguments"];
|
|
840
|
+
}
|
|
841
|
+
break;
|
|
842
|
+
case "Identifier":
|
|
843
|
+
if (!name || node.name === name) {
|
|
844
|
+
return checkResults(state.lookup(node), node);
|
|
845
|
+
}
|
|
846
|
+
break;
|
|
847
|
+
case "MemberExpression":
|
|
848
|
+
if (!node.computed && node.property.type === "Identifier") {
|
|
849
|
+
if (!name || node.property.name === name) {
|
|
850
|
+
return checkResults(state.lookup(node), node) || ["object"];
|
|
851
|
+
}
|
|
852
|
+
return ["object"];
|
|
853
|
+
}
|
|
854
|
+
break;
|
|
855
|
+
case "MethodDefinition": {
|
|
856
|
+
if (!state.inType) {
|
|
857
|
+
throw new Error("Method definition outside of type!");
|
|
858
|
+
}
|
|
859
|
+
if (node.params) {
|
|
860
|
+
node.params.forEach((param) => {
|
|
861
|
+
if (param.type == "BinaryExpression") {
|
|
862
|
+
state.traverse(param.right);
|
|
863
|
+
state.inType = true;
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
return ["returnType"];
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return null;
|
|
871
|
+
};
|
|
872
|
+
(0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
|
|
873
|
+
delete state.pre;
|
|
874
|
+
}
|
|
875
|
+
|
|
644
876
|
;// CONCATENATED MODULE: ./src/mc-rewrite.ts
|
|
645
877
|
|
|
646
878
|
|
|
@@ -648,43 +880,25 @@ const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
|
648
880
|
|
|
649
881
|
|
|
650
882
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const [name, module] = lookup(node.id, ("as" in node && node.as && node.as.name) || null, stack);
|
|
654
|
-
if (name && module) {
|
|
655
|
-
const [parent] = stack.slice(-1);
|
|
656
|
-
if (!parent.decls)
|
|
657
|
-
parent.decls = {};
|
|
658
|
-
const decls = parent.decls;
|
|
659
|
-
if (!hasProperty(decls, name))
|
|
660
|
-
decls[name] = [];
|
|
661
|
-
module.forEach((m) => {
|
|
662
|
-
if (isStateNode(m) && m.type == "ModuleDeclaration") {
|
|
663
|
-
pushUnique(decls[name], m);
|
|
664
|
-
if (!parent.type_decls)
|
|
665
|
-
parent.type_decls = {};
|
|
666
|
-
const tdecls = parent.type_decls;
|
|
667
|
-
if (!hasProperty(tdecls, name))
|
|
668
|
-
tdecls[name] = [];
|
|
669
|
-
pushUnique(tdecls[name], m);
|
|
670
|
-
if (node.type == "ImportModule" && m.type_decls) {
|
|
671
|
-
Object.entries(m.type_decls).forEach(([name, decls]) => {
|
|
672
|
-
if (!hasProperty(tdecls, name))
|
|
673
|
-
tdecls[name] = [];
|
|
674
|
-
decls.forEach((decl) => pushUnique(tdecls[name], decl));
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
});
|
|
679
|
-
}
|
|
680
|
-
});
|
|
681
|
-
}
|
|
883
|
+
|
|
884
|
+
|
|
682
885
|
function collectClassInfo(state) {
|
|
886
|
+
const toybox = state.stack[0].decls["Toybox"][0];
|
|
887
|
+
const lang = toybox.decls["Lang"][0];
|
|
888
|
+
const object = lang.decls["Object"];
|
|
683
889
|
state.allClasses.forEach((elm) => {
|
|
890
|
+
if (elm.stack[elm.stack.length - 1].type === "ClassDeclaration") {
|
|
891
|
+
// nested classes don't get access to their contained
|
|
892
|
+
// context. Put them in the global scope instead.
|
|
893
|
+
elm.stack = elm.stack.slice(0, 1);
|
|
894
|
+
}
|
|
684
895
|
if (elm.node.superClass) {
|
|
685
|
-
const [name,
|
|
686
|
-
const superClass =
|
|
687
|
-
|
|
896
|
+
const [name, lookupDefns] = state.lookup(elm.node.superClass, null, elm.stack);
|
|
897
|
+
const superClass = lookupDefns &&
|
|
898
|
+
lookupDefns
|
|
899
|
+
.map((lookupDefn) => lookupDefn.results)
|
|
900
|
+
.flat()
|
|
901
|
+
.filter((c) => isStateNode(c) && c.type === "ClassDeclaration");
|
|
688
902
|
// set it "true" if there is a superClass, but we can't find it.
|
|
689
903
|
elm.superClass = superClass && superClass.length ? superClass : true;
|
|
690
904
|
if (name && elm.superClass !== true) {
|
|
@@ -716,6 +930,9 @@ function collectClassInfo(state) {
|
|
|
716
930
|
elm.decls[name] = elm.superClass;
|
|
717
931
|
}
|
|
718
932
|
}
|
|
933
|
+
else if (elm !== object[0]) {
|
|
934
|
+
elm.superClass = object;
|
|
935
|
+
}
|
|
719
936
|
});
|
|
720
937
|
const markOverrides = (cls, scls) => {
|
|
721
938
|
if (scls === true)
|
|
@@ -746,7 +963,9 @@ function getFileSources(fnMap) {
|
|
|
746
963
|
fs
|
|
747
964
|
.readFile(name)
|
|
748
965
|
.then((data) => (value.monkeyCSource = data.toString().replace(/\r\n/g, "\n"))));
|
|
749
|
-
})).then(() => {
|
|
966
|
+
})).then(() => {
|
|
967
|
+
return;
|
|
968
|
+
});
|
|
750
969
|
}
|
|
751
970
|
function getFileASTs(fnMap) {
|
|
752
971
|
return getFileSources(fnMap).then(() => Object.entries(fnMap).reduce((ok, [name, value]) => {
|
|
@@ -769,22 +988,23 @@ function getFileASTs(fnMap) {
|
|
|
769
988
|
return ok;
|
|
770
989
|
}, true));
|
|
771
990
|
}
|
|
772
|
-
async function analyze(fnMap) {
|
|
991
|
+
async function analyze(fnMap, barrelList, config) {
|
|
773
992
|
let hasTests = false;
|
|
774
|
-
|
|
993
|
+
let markApi = true;
|
|
775
994
|
const preState = {
|
|
776
995
|
fnMap,
|
|
996
|
+
config,
|
|
777
997
|
allFunctions: [],
|
|
778
998
|
allClasses: [],
|
|
779
999
|
shouldExclude(node) {
|
|
780
1000
|
if ("attrs" in node &&
|
|
781
1001
|
node.attrs &&
|
|
782
|
-
"
|
|
783
|
-
node.attrs.
|
|
1002
|
+
"attributes" in node.attrs &&
|
|
1003
|
+
node.attrs.attributes &&
|
|
784
1004
|
node.loc?.source) {
|
|
785
1005
|
const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
|
|
786
1006
|
if (excludeAnnotations) {
|
|
787
|
-
return node.attrs.
|
|
1007
|
+
return node.attrs.attributes.elements.reduce((drop, attr) => {
|
|
788
1008
|
if (attr.type != "UnaryExpression")
|
|
789
1009
|
return drop;
|
|
790
1010
|
if (attr.argument.type != "Identifier")
|
|
@@ -801,45 +1021,38 @@ async function analyze(fnMap) {
|
|
|
801
1021
|
}
|
|
802
1022
|
return false;
|
|
803
1023
|
},
|
|
804
|
-
|
|
1024
|
+
pre(node, state) {
|
|
805
1025
|
switch (node.type) {
|
|
806
1026
|
case "FunctionDeclaration":
|
|
1027
|
+
if (markApi) {
|
|
1028
|
+
node.body = null;
|
|
1029
|
+
break;
|
|
1030
|
+
}
|
|
1031
|
+
// falls through
|
|
1032
|
+
case "ModuleDeclaration":
|
|
807
1033
|
case "ClassDeclaration": {
|
|
808
1034
|
const [scope] = state.stack.slice(-1);
|
|
809
|
-
|
|
810
|
-
scope.stack = stack;
|
|
1035
|
+
scope.stack = state.stackClone().slice(0, -1);
|
|
811
1036
|
if (scope.type == "FunctionDeclaration") {
|
|
1037
|
+
scope.isStatic =
|
|
1038
|
+
scope.stack.slice(-1)[0].type !== "ClassDeclaration" ||
|
|
1039
|
+
(scope.node.attrs &&
|
|
1040
|
+
scope.node.attrs.access &&
|
|
1041
|
+
scope.node.attrs.access.includes("static"));
|
|
812
1042
|
state.allFunctions.push(scope);
|
|
813
1043
|
}
|
|
814
|
-
else {
|
|
1044
|
+
else if (scope.type === "ClassDeclaration") {
|
|
815
1045
|
state.allClasses.push(scope);
|
|
816
1046
|
}
|
|
817
|
-
|
|
1047
|
+
break;
|
|
818
1048
|
}
|
|
819
|
-
case "Using":
|
|
820
|
-
case "ImportModule":
|
|
821
|
-
allImports.push({ node, stack: state.stack.slice() });
|
|
822
|
-
return null;
|
|
823
|
-
default:
|
|
824
|
-
return null;
|
|
825
1049
|
}
|
|
1050
|
+
return null;
|
|
826
1051
|
},
|
|
827
1052
|
};
|
|
828
|
-
await getApiMapping(preState);
|
|
1053
|
+
await getApiMapping(preState, barrelList);
|
|
1054
|
+
markApi = false;
|
|
829
1055
|
const state = preState;
|
|
830
|
-
// Mark all functions from api.mir as "special" by
|
|
831
|
-
// setting their bodies to null. In api.mir, they're
|
|
832
|
-
// all empty, which makes it look like they're
|
|
833
|
-
// do-nothing functions.
|
|
834
|
-
const markApi = (node) => {
|
|
835
|
-
if (node.type == "FunctionDeclaration") {
|
|
836
|
-
node.node.body = null;
|
|
837
|
-
}
|
|
838
|
-
if (isStateNode(node) && node.decls) {
|
|
839
|
-
Object.values(node.decls).forEach((v) => v.forEach(markApi));
|
|
840
|
-
}
|
|
841
|
-
};
|
|
842
|
-
markApi(state.stack[0]);
|
|
843
1056
|
await getFileASTs(fnMap);
|
|
844
1057
|
Object.entries(fnMap).forEach(([name, value]) => {
|
|
845
1058
|
const { ast, parserError } = value;
|
|
@@ -852,8 +1065,28 @@ async function analyze(fnMap) {
|
|
|
852
1065
|
});
|
|
853
1066
|
delete state.shouldExclude;
|
|
854
1067
|
delete state.post;
|
|
855
|
-
processImports(allImports, state.lookup);
|
|
856
1068
|
collectClassInfo(state);
|
|
1069
|
+
const diagnosticType = config?.checkInvalidSymbols !== "OFF"
|
|
1070
|
+
? config?.checkInvalidSymbols || "WARNING"
|
|
1071
|
+
: null;
|
|
1072
|
+
if (diagnosticType &&
|
|
1073
|
+
!config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
|
|
1074
|
+
const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
|
|
1075
|
+
Object.entries(fnMap).forEach(([, v]) => {
|
|
1076
|
+
visitReferences(state, v.ast, null, false, (node, results, error) => {
|
|
1077
|
+
if (!error)
|
|
1078
|
+
return undefined;
|
|
1079
|
+
const nodeStr = formatAst(node);
|
|
1080
|
+
if (state.inType) {
|
|
1081
|
+
if (!checkTypes || nodeStr.match(/^Void|Null$/)) {
|
|
1082
|
+
return undefined;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
diagnostic(state, node.loc, `Undefined symbol ${nodeStr}`, diagnosticType);
|
|
1086
|
+
return false;
|
|
1087
|
+
});
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
857
1090
|
return state;
|
|
858
1091
|
}
|
|
859
1092
|
function compareLiteralLike(a, b) {
|
|
@@ -863,11 +1096,11 @@ function compareLiteralLike(a, b) {
|
|
|
863
1096
|
b = b.left;
|
|
864
1097
|
return a.type === "Literal" && b.type === "Literal" && a.value === b.value;
|
|
865
1098
|
}
|
|
866
|
-
function getLiteralFromDecls(
|
|
867
|
-
if (!
|
|
1099
|
+
function getLiteralFromDecls(lookupDefns) {
|
|
1100
|
+
if (!lookupDefns.length)
|
|
868
1101
|
return null;
|
|
869
1102
|
let result = null;
|
|
870
|
-
if (
|
|
1103
|
+
if (lookupDefns.every((lookupDefn) => lookupDefn.results.every((d) => {
|
|
871
1104
|
if (d.type === "EnumStringMember" ||
|
|
872
1105
|
(d.type === "VariableDeclarator" && d.node.kind === "const")) {
|
|
873
1106
|
const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
|
|
@@ -882,7 +1115,7 @@ function getLiteralFromDecls(decls) {
|
|
|
882
1115
|
}
|
|
883
1116
|
}
|
|
884
1117
|
return false;
|
|
885
|
-
})) {
|
|
1118
|
+
}))) {
|
|
886
1119
|
return result;
|
|
887
1120
|
}
|
|
888
1121
|
return null;
|
|
@@ -1093,14 +1326,23 @@ function markFunctionCalled(state, func) {
|
|
|
1093
1326
|
}
|
|
1094
1327
|
pushUnique(state.calledFunctions[func.id.name], func);
|
|
1095
1328
|
}
|
|
1096
|
-
async function optimizeMonkeyC(fnMap) {
|
|
1329
|
+
async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
1097
1330
|
const state = {
|
|
1098
|
-
...(await analyze(fnMap)),
|
|
1331
|
+
...(await analyze(fnMap, barrelList, config)),
|
|
1099
1332
|
localsStack: [{}],
|
|
1100
1333
|
exposed: {},
|
|
1101
1334
|
calledFunctions: {},
|
|
1102
1335
|
};
|
|
1103
|
-
const replace = (node,
|
|
1336
|
+
const replace = (node, old) => {
|
|
1337
|
+
if (node === false || node === null)
|
|
1338
|
+
return node;
|
|
1339
|
+
const rep = state.traverse(node);
|
|
1340
|
+
if (rep === false || Array.isArray(rep))
|
|
1341
|
+
return rep;
|
|
1342
|
+
return { ...(rep || node), loc: old.loc, start: old.start, end: old.end };
|
|
1343
|
+
};
|
|
1344
|
+
const inPlaceReplacement = (node, obj) => {
|
|
1345
|
+
const { start, end, loc } = node;
|
|
1104
1346
|
for (const k of Object.keys(node)) {
|
|
1105
1347
|
delete node[k];
|
|
1106
1348
|
}
|
|
@@ -1108,13 +1350,16 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1108
1350
|
obj = {
|
|
1109
1351
|
type: "BinaryExpression",
|
|
1110
1352
|
operator: "as",
|
|
1111
|
-
left: obj,
|
|
1353
|
+
left: { ...obj, start, end, loc },
|
|
1112
1354
|
right: { type: "TypeSpecList", ts: [obj.enumType] },
|
|
1113
1355
|
};
|
|
1114
1356
|
}
|
|
1115
1357
|
for (const [k, v] of Object.entries(obj)) {
|
|
1116
1358
|
node[k] = v;
|
|
1117
1359
|
}
|
|
1360
|
+
node.loc = loc;
|
|
1361
|
+
node.start = start;
|
|
1362
|
+
node.end = end;
|
|
1118
1363
|
};
|
|
1119
1364
|
const lookupAndReplace = (node) => {
|
|
1120
1365
|
const [, objects] = state.lookup(node);
|
|
@@ -1125,7 +1370,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1125
1370
|
if (!obj) {
|
|
1126
1371
|
return false;
|
|
1127
1372
|
}
|
|
1128
|
-
|
|
1373
|
+
inPlaceReplacement(node, obj);
|
|
1129
1374
|
return true;
|
|
1130
1375
|
};
|
|
1131
1376
|
const topLocals = () => state.localsStack[state.localsStack.length - 1];
|
|
@@ -1141,8 +1386,8 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1141
1386
|
if (hasProperty(state.exposed, func.id.name))
|
|
1142
1387
|
return true;
|
|
1143
1388
|
if (func.attrs &&
|
|
1144
|
-
func.attrs.
|
|
1145
|
-
func.attrs.
|
|
1389
|
+
func.attrs.attributes &&
|
|
1390
|
+
func.attrs.attributes.elements.some((attr) => {
|
|
1146
1391
|
if (attr.type != "UnaryExpression")
|
|
1147
1392
|
return false;
|
|
1148
1393
|
if (attr.argument.type != "Identifier")
|
|
@@ -1172,7 +1417,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1172
1417
|
case "ConditionalExpression":
|
|
1173
1418
|
case "IfStatement":
|
|
1174
1419
|
case "DoWhileStatement":
|
|
1175
|
-
case "WhileStatement":
|
|
1420
|
+
case "WhileStatement": {
|
|
1176
1421
|
const test = (state.traverse(node.test) ||
|
|
1177
1422
|
node.test);
|
|
1178
1423
|
const [value, type] = getNodeValue(test);
|
|
@@ -1204,6 +1449,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1204
1449
|
}
|
|
1205
1450
|
}
|
|
1206
1451
|
return null;
|
|
1452
|
+
}
|
|
1207
1453
|
case "EnumDeclaration":
|
|
1208
1454
|
return false;
|
|
1209
1455
|
case "ForStatement": {
|
|
@@ -1233,6 +1479,37 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1233
1479
|
}
|
|
1234
1480
|
return ["init"];
|
|
1235
1481
|
}
|
|
1482
|
+
case "CatchClause":
|
|
1483
|
+
if (node.param) {
|
|
1484
|
+
state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
|
|
1485
|
+
const locals = topLocals();
|
|
1486
|
+
const map = locals.map;
|
|
1487
|
+
const declName = variableDeclarationName(node.param);
|
|
1488
|
+
const name = renameVariable(state, locals, declName);
|
|
1489
|
+
if (name) {
|
|
1490
|
+
if (node.param.type === "Identifier") {
|
|
1491
|
+
node.param.name = name;
|
|
1492
|
+
}
|
|
1493
|
+
else {
|
|
1494
|
+
node.param.left.name = name;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
else {
|
|
1498
|
+
map[declName] = true;
|
|
1499
|
+
}
|
|
1500
|
+
return ["body"];
|
|
1501
|
+
}
|
|
1502
|
+
break;
|
|
1503
|
+
case "BinaryExpression":
|
|
1504
|
+
if (node.operator === "has") {
|
|
1505
|
+
if (node.right.type === "UnaryExpression" &&
|
|
1506
|
+
node.right.operator === ":") {
|
|
1507
|
+
// Using `expr has :symbol` doesn't "expose"
|
|
1508
|
+
// symbol. So skip the right operand.
|
|
1509
|
+
return ["left"];
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
break;
|
|
1236
1513
|
case "UnaryExpression":
|
|
1237
1514
|
if (node.operator == ":") {
|
|
1238
1515
|
// If we produce a Symbol, for a given name,
|
|
@@ -1315,8 +1592,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1315
1592
|
}
|
|
1316
1593
|
const opt = optimizeNode(node);
|
|
1317
1594
|
if (opt) {
|
|
1318
|
-
replace(
|
|
1319
|
-
return null;
|
|
1595
|
+
return replace(opt, node);
|
|
1320
1596
|
}
|
|
1321
1597
|
switch (node.type) {
|
|
1322
1598
|
case "ConditionalExpression":
|
|
@@ -1326,7 +1602,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1326
1602
|
const rep = node.test.value ? node.consequent : node.alternate;
|
|
1327
1603
|
if (!rep)
|
|
1328
1604
|
return false;
|
|
1329
|
-
replace(
|
|
1605
|
+
return replace(rep, rep);
|
|
1330
1606
|
}
|
|
1331
1607
|
break;
|
|
1332
1608
|
case "WhileStatement":
|
|
@@ -1341,15 +1617,11 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1341
1617
|
break;
|
|
1342
1618
|
case "ReturnStatement":
|
|
1343
1619
|
if (node.argument && node.argument.type === "CallExpression") {
|
|
1344
|
-
return optimizeCall(state, node.argument, node);
|
|
1620
|
+
return replace(optimizeCall(state, node.argument, node), node.argument);
|
|
1345
1621
|
}
|
|
1346
1622
|
break;
|
|
1347
1623
|
case "CallExpression": {
|
|
1348
|
-
|
|
1349
|
-
if (ret) {
|
|
1350
|
-
replace(node, ret);
|
|
1351
|
-
}
|
|
1352
|
-
break;
|
|
1624
|
+
return replace(optimizeCall(state, node, null), node);
|
|
1353
1625
|
}
|
|
1354
1626
|
case "AssignmentExpression":
|
|
1355
1627
|
if (node.operator === "=" &&
|
|
@@ -1361,7 +1633,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1361
1633
|
break;
|
|
1362
1634
|
case "ExpressionStatement":
|
|
1363
1635
|
if (node.expression.type === "CallExpression") {
|
|
1364
|
-
return optimizeCall(state, node.expression, node);
|
|
1636
|
+
return replace(optimizeCall(state, node.expression, node), node.expression);
|
|
1365
1637
|
}
|
|
1366
1638
|
else if (node.expression.type === "AssignmentExpression") {
|
|
1367
1639
|
if (node.expression.right.type === "CallExpression") {
|
|
@@ -1373,14 +1645,10 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1373
1645
|
}
|
|
1374
1646
|
if (!ok && node.expression.operator == "=") {
|
|
1375
1647
|
const [, result] = state.lookup(node.expression.left);
|
|
1376
|
-
ok = result
|
|
1648
|
+
ok = !!result;
|
|
1377
1649
|
}
|
|
1378
1650
|
if (ok) {
|
|
1379
|
-
|
|
1380
|
-
if (ret && ret.type === "BlockStatement") {
|
|
1381
|
-
const r2 = state.traverse(ret);
|
|
1382
|
-
return r2 === false || r2 ? r2 : ret;
|
|
1383
|
-
}
|
|
1651
|
+
return replace(optimizeCall(state, node.expression.right, node.expression), node.expression.right);
|
|
1384
1652
|
}
|
|
1385
1653
|
}
|
|
1386
1654
|
}
|
|
@@ -1388,12 +1656,9 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1388
1656
|
const ret = unused(node.expression, true);
|
|
1389
1657
|
if (ret) {
|
|
1390
1658
|
return ret
|
|
1391
|
-
.map((
|
|
1392
|
-
const r2 = state.traverse(s);
|
|
1393
|
-
return r2 === false || r2 ? r2 : s;
|
|
1394
|
-
})
|
|
1659
|
+
.map((r) => replace(r, r))
|
|
1395
1660
|
.flat(1)
|
|
1396
|
-
.filter((s) => s
|
|
1661
|
+
.filter((s) => !!s);
|
|
1397
1662
|
}
|
|
1398
1663
|
}
|
|
1399
1664
|
break;
|
|
@@ -1412,6 +1677,9 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1412
1677
|
delete state.post;
|
|
1413
1678
|
const cleanup = (node) => {
|
|
1414
1679
|
switch (node.type) {
|
|
1680
|
+
case "ThisExpression":
|
|
1681
|
+
node.text = "self";
|
|
1682
|
+
break;
|
|
1415
1683
|
case "EnumStringBody":
|
|
1416
1684
|
if (node.members.every((m) => {
|
|
1417
1685
|
const name = "name" in m ? m.name : m.id.name;
|
|
@@ -1436,19 +1704,24 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1436
1704
|
if (!node.body.members.length) {
|
|
1437
1705
|
if (!node.id)
|
|
1438
1706
|
return false;
|
|
1439
|
-
if (!node.body
|
|
1707
|
+
if (!node.body.enumType) {
|
|
1440
1708
|
throw new Error("Missing enumType on optimized enum");
|
|
1441
1709
|
}
|
|
1442
|
-
|
|
1710
|
+
return {
|
|
1443
1711
|
type: "TypedefDeclaration",
|
|
1444
1712
|
id: node.id,
|
|
1445
1713
|
ts: {
|
|
1446
1714
|
type: "UnaryExpression",
|
|
1447
|
-
argument: {
|
|
1715
|
+
argument: {
|
|
1716
|
+
type: "TypeSpecList",
|
|
1717
|
+
ts: [
|
|
1718
|
+
node.body.enumType,
|
|
1719
|
+
],
|
|
1720
|
+
},
|
|
1448
1721
|
prefix: true,
|
|
1449
1722
|
operator: " as",
|
|
1450
1723
|
},
|
|
1451
|
-
}
|
|
1724
|
+
};
|
|
1452
1725
|
}
|
|
1453
1726
|
break;
|
|
1454
1727
|
case "VariableDeclaration": {
|
|
@@ -1471,6 +1744,19 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1471
1744
|
return false;
|
|
1472
1745
|
}
|
|
1473
1746
|
break;
|
|
1747
|
+
case "ClassDeclaration":
|
|
1748
|
+
case "ModuleDeclaration":
|
|
1749
|
+
// none of the attributes means anything on classes and
|
|
1750
|
+
// modules, and the new compiler complains about some
|
|
1751
|
+
// of them. Just drop them all.
|
|
1752
|
+
if (node.attrs && node.attrs.access) {
|
|
1753
|
+
if (node.attrs.attributes) {
|
|
1754
|
+
delete node.attrs.access;
|
|
1755
|
+
}
|
|
1756
|
+
else {
|
|
1757
|
+
delete node.attrs;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1474
1760
|
}
|
|
1475
1761
|
return null;
|
|
1476
1762
|
};
|
|
@@ -1486,9 +1772,12 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1486
1772
|
return state.diagnostics;
|
|
1487
1773
|
}
|
|
1488
1774
|
function optimizeCall(state, node, context) {
|
|
1489
|
-
const [name, results] = state.
|
|
1775
|
+
const [name, results] = state.lookupNonlocal(node.callee);
|
|
1490
1776
|
const callees = results &&
|
|
1491
|
-
results
|
|
1777
|
+
results
|
|
1778
|
+
.map((r) => r.results)
|
|
1779
|
+
.flat()
|
|
1780
|
+
.filter((c) => c.type === "FunctionDeclaration");
|
|
1492
1781
|
if (!callees || !callees.length) {
|
|
1493
1782
|
const n = name ||
|
|
1494
1783
|
("name" in node.callee && node.callee.name) ||
|
|
@@ -1593,6 +1882,10 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
|
|
|
1593
1882
|
|
|
1594
1883
|
|
|
1595
1884
|
|
|
1885
|
+
|
|
1886
|
+
|
|
1887
|
+
|
|
1888
|
+
|
|
1596
1889
|
/*
|
|
1597
1890
|
* This is an unfortunate hack. I want to be able to extract things
|
|
1598
1891
|
* like the types of all of a Class's variables (in particular the type
|
|
@@ -1603,16 +1896,30 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
|
|
|
1603
1896
|
* but those are at least in a standard format.
|
|
1604
1897
|
*/
|
|
1605
1898
|
// Extract all enum values from api.mir
|
|
1606
|
-
async function api_getApiMapping(state) {
|
|
1899
|
+
async function api_getApiMapping(state, barrelList) {
|
|
1607
1900
|
// get the path to the currently active sdk
|
|
1608
1901
|
const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
|
|
1609
1902
|
const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
|
|
1903
|
+
const rezDecl = `module Rez { ${[
|
|
1904
|
+
"Drawables",
|
|
1905
|
+
"Fonts",
|
|
1906
|
+
"JsonData",
|
|
1907
|
+
"Layouts",
|
|
1908
|
+
"Menus",
|
|
1909
|
+
"Strings",
|
|
1910
|
+
]
|
|
1911
|
+
.map((s) => ` module ${s} {}\n`)
|
|
1912
|
+
.join("")}}`;
|
|
1610
1913
|
const api = (await promises_namespaceObject.readFile(`${sdk}bin/api.mir`))
|
|
1611
1914
|
.toString()
|
|
1612
1915
|
.replace(/\r\n/g, "\n")
|
|
1613
1916
|
.replace(/^\s*\[.*?\]\s*$/gm, "")
|
|
1614
1917
|
//.replace(/(COLOR_TRANSPARENT|LAYOUT_[HV]ALIGN_\w+) = (\d+)/gm, "$1 = -$2")
|
|
1615
|
-
.replace(/^(\s*type)\s/gm, "$1def ")
|
|
1918
|
+
.replace(/^(\s*type)\s/gm, "$1def ") +
|
|
1919
|
+
(barrelList || [])
|
|
1920
|
+
.map((name) => `module ${name} { ${rezDecl} }`)
|
|
1921
|
+
.concat(rezDecl)
|
|
1922
|
+
.join("");
|
|
1616
1923
|
try {
|
|
1617
1924
|
const result = api_collectNamespaces(parser.parse(api, null, {
|
|
1618
1925
|
filepath: "api.mir",
|
|
@@ -1626,23 +1933,24 @@ async function api_getApiMapping(state) {
|
|
|
1626
1933
|
return decls[0];
|
|
1627
1934
|
}, result);
|
|
1628
1935
|
const value = api_isStateNode(vs) ? vs.node : vs;
|
|
1629
|
-
if (value
|
|
1630
|
-
(value.type !== "
|
|
1936
|
+
if (!value ||
|
|
1937
|
+
(value.type !== "EnumStringMember" &&
|
|
1938
|
+
(value.type !== "VariableDeclarator" || value.kind != "const"))) {
|
|
1631
1939
|
throw `Negative constant ${fixup} did not refer to a constant`;
|
|
1632
1940
|
}
|
|
1633
|
-
const init = value.init;
|
|
1941
|
+
const init = getLiteralNode(value.init);
|
|
1634
1942
|
if (!init || init.type !== "Literal") {
|
|
1635
1943
|
throw `Negative constant ${fixup} was not a Literal`;
|
|
1636
1944
|
}
|
|
1637
1945
|
if (typeof init.value !== "number") {
|
|
1638
|
-
console.log(`Negative fixup ${fixup} was
|
|
1946
|
+
console.log(`Negative fixup ${fixup} was not a number!`);
|
|
1639
1947
|
}
|
|
1640
1948
|
else if (init.value > 0) {
|
|
1641
1949
|
init.value = -init.value;
|
|
1642
1950
|
init.raw = "-" + init.raw;
|
|
1643
1951
|
}
|
|
1644
1952
|
else {
|
|
1645
|
-
console.log(`Negative fixup ${fixup} was already negative!`);
|
|
1953
|
+
// console.log(`Negative fixup ${fixup} was already negative!`);
|
|
1646
1954
|
}
|
|
1647
1955
|
});
|
|
1648
1956
|
return result;
|
|
@@ -1661,67 +1969,197 @@ function api_isStateNode(node) {
|
|
|
1661
1969
|
function api_variableDeclarationName(node) {
|
|
1662
1970
|
return ("left" in node ? node.left : node).name;
|
|
1663
1971
|
}
|
|
1664
|
-
function
|
|
1972
|
+
function lookupToStateNodeDecls(results) {
|
|
1973
|
+
return results.reduce((result, current) => current.results.length
|
|
1974
|
+
? result
|
|
1975
|
+
? result.concat(current.results)
|
|
1976
|
+
: current.results
|
|
1977
|
+
: result, null);
|
|
1978
|
+
}
|
|
1979
|
+
function checkOne(state, ns, decls, node, isStatic) {
|
|
1980
|
+
// follow the superchain, looking up node in each class
|
|
1981
|
+
const superChain = (cls) => {
|
|
1982
|
+
if (!cls.superClass || cls.superClass === true) {
|
|
1983
|
+
return null;
|
|
1984
|
+
}
|
|
1985
|
+
return cls.superClass.reduce((result, sup) => {
|
|
1986
|
+
const sdecls = sup[decls];
|
|
1987
|
+
const next = api_hasProperty(sdecls, node.name)
|
|
1988
|
+
? sdecls[node.name]
|
|
1989
|
+
: superChain(sup);
|
|
1990
|
+
return next ? (result ? result.concat(next) : next) : result;
|
|
1991
|
+
}, null);
|
|
1992
|
+
};
|
|
1993
|
+
const lookupInContext = (ns) => {
|
|
1994
|
+
const [, lkup] = lookup(state, decls, node, null, ns.stack);
|
|
1995
|
+
return lkup && lookupToStateNodeDecls(lkup);
|
|
1996
|
+
};
|
|
1997
|
+
// follow the superchain, looking up node in each class's scope
|
|
1998
|
+
const superChainScopes = (ns) => {
|
|
1999
|
+
const result = lookupInContext(ns);
|
|
2000
|
+
if (result)
|
|
2001
|
+
return result;
|
|
2002
|
+
if (!ns.superClass || ns.superClass === true) {
|
|
2003
|
+
return null;
|
|
2004
|
+
}
|
|
2005
|
+
return ns.superClass.reduce((result, sup) => {
|
|
2006
|
+
const next = superChainScopes(sup);
|
|
2007
|
+
return next ? (result ? result.concat(next) : next) : result;
|
|
2008
|
+
}, null);
|
|
2009
|
+
};
|
|
1665
2010
|
if (api_isStateNode(ns)) {
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
2011
|
+
const ndecls = ns[decls];
|
|
2012
|
+
if (api_hasProperty(ndecls, node.name)) {
|
|
2013
|
+
return ndecls[node.name];
|
|
2014
|
+
}
|
|
2015
|
+
switch (ns.type) {
|
|
2016
|
+
case "ClassDeclaration":
|
|
2017
|
+
if (!isStatic) {
|
|
2018
|
+
return superChain(ns) || superChainScopes(ns) || false;
|
|
2019
|
+
}
|
|
2020
|
+
// fall through
|
|
2021
|
+
case "ModuleDeclaration":
|
|
2022
|
+
return lookupInContext(ns) || false;
|
|
1677
2023
|
}
|
|
1678
2024
|
}
|
|
1679
2025
|
return null;
|
|
1680
2026
|
}
|
|
1681
|
-
function
|
|
1682
|
-
|
|
2027
|
+
function sameStateNodeDecl(a, b) {
|
|
2028
|
+
if (a === b)
|
|
2029
|
+
return true;
|
|
2030
|
+
if (!a || !b)
|
|
2031
|
+
return false;
|
|
2032
|
+
if (!api_isStateNode(a) || a.type !== b.type)
|
|
2033
|
+
return false;
|
|
2034
|
+
return (a.node === b.node ||
|
|
2035
|
+
a.type === "Program" ||
|
|
2036
|
+
(a.type === "ModuleDeclaration" && a.fullName === b.fullName));
|
|
2037
|
+
}
|
|
2038
|
+
function sameLookupDefinition(a, b) {
|
|
2039
|
+
return (
|
|
2040
|
+
// sameStateNodeDecl(a.parent, b.parent) &&
|
|
2041
|
+
(0,external_util_cjs_namespaceObject.sameArrays)(a.results, b.results, (ar, br) => sameStateNodeDecl(ar, br)));
|
|
2042
|
+
}
|
|
2043
|
+
function api_sameLookupResult(a, b) {
|
|
2044
|
+
return (0,external_util_cjs_namespaceObject.sameArrays)(a, b, sameLookupDefinition);
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2047
|
+
*
|
|
2048
|
+
* @param state - The ProgramState
|
|
2049
|
+
* @param decls - The field to use to look things up. either "decls" or "type_decls"
|
|
2050
|
+
* @param node - The node to lookup
|
|
2051
|
+
* @param name - Overrides the name of the node.
|
|
2052
|
+
* @param stack - if provided, use this stack, rather than the current
|
|
2053
|
+
* state.stack for the lookup
|
|
2054
|
+
* @param nonlocal - when true, a plain identifier will be looked up as a
|
|
2055
|
+
* non-local. This is needed when looking up a callee.
|
|
2056
|
+
* If the callee is a MemberExpression, the flag is ignored.
|
|
2057
|
+
* @returns
|
|
2058
|
+
* - [string, LookupDefinition[]] - if the lookup succeeds
|
|
2059
|
+
* - [false, false] - if the lookup fails, but its not an error because its not the kind of expression we can lookup
|
|
2060
|
+
* - [null, null] - if the lookup fails unexpectedly.
|
|
2061
|
+
*/
|
|
2062
|
+
function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
2063
|
+
const stack = maybeStack || state.stack;
|
|
1683
2064
|
switch (node.type) {
|
|
1684
2065
|
case "MemberExpression": {
|
|
1685
2066
|
if (node.property.type != "Identifier" || node.computed)
|
|
1686
2067
|
break;
|
|
1687
|
-
const
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
2068
|
+
const property = node.property;
|
|
2069
|
+
let result;
|
|
2070
|
+
if (node.object.type === "ThisExpression") {
|
|
2071
|
+
[, result] = lookup(state, decls, node.property, name, stack, true);
|
|
2072
|
+
}
|
|
2073
|
+
else {
|
|
2074
|
+
const [, results] = lookup(state, decls, node.object, name, stack, false);
|
|
2075
|
+
if (results === false)
|
|
2076
|
+
break;
|
|
2077
|
+
if (!results)
|
|
2078
|
+
return [null, null];
|
|
2079
|
+
result = results.reduce((current, lookupDef) => {
|
|
2080
|
+
const items = lookupDef.results
|
|
2081
|
+
.map((module) => {
|
|
2082
|
+
const res = checkOne(state, module, decls, property, false);
|
|
2083
|
+
return res ? { parent: module, results: res } : null;
|
|
2084
|
+
})
|
|
2085
|
+
.filter((r) => r != null);
|
|
2086
|
+
if (!items.length)
|
|
2087
|
+
return current;
|
|
2088
|
+
return current ? current.concat(items) : items;
|
|
2089
|
+
}, null);
|
|
2090
|
+
if (!result &&
|
|
2091
|
+
results.some((ld) => ld.results.some((sn) => (api_isStateNode(sn) && sn.fullName?.match(/^\$\.(\w+\.)?Rez\./)) ||
|
|
2092
|
+
sn.type === "VariableDeclarator" ||
|
|
2093
|
+
sn.type === "Identifier" ||
|
|
2094
|
+
sn.type === "BinaryExpression" ||
|
|
2095
|
+
(sn.type === "ClassDeclaration" &&
|
|
2096
|
+
property.name === "initialize")))) {
|
|
2097
|
+
// - The Rez module can contain lots of things from the resource
|
|
2098
|
+
// compiler which we don't track.
|
|
2099
|
+
// - Variables, and formal parameters would require type tracking
|
|
2100
|
+
// which we don't yet do
|
|
2101
|
+
// - Its ok to call an undeclared initialize method.
|
|
2102
|
+
// Report them all as "expected failures".
|
|
2103
|
+
return [false, false];
|
|
1696
2104
|
}
|
|
1697
2105
|
}
|
|
1698
|
-
|
|
2106
|
+
if (!result)
|
|
2107
|
+
return [null, null];
|
|
2108
|
+
return [name || property.name, result];
|
|
1699
2109
|
}
|
|
1700
2110
|
case "ThisExpression": {
|
|
1701
|
-
for (let i = stack.length
|
|
2111
|
+
for (let i = stack.length;;) {
|
|
1702
2112
|
const si = stack[i];
|
|
1703
2113
|
if (si.type == "ModuleDeclaration" ||
|
|
1704
2114
|
si.type == "ClassDeclaration" ||
|
|
1705
2115
|
!i) {
|
|
1706
|
-
return [
|
|
2116
|
+
return [
|
|
2117
|
+
name || si.name,
|
|
2118
|
+
[{ parent: i ? stack[i - 1] : null, results: [si] }],
|
|
2119
|
+
];
|
|
1707
2120
|
}
|
|
1708
2121
|
}
|
|
1709
|
-
break;
|
|
1710
2122
|
}
|
|
1711
2123
|
case "Identifier": {
|
|
1712
2124
|
if (node.name == "$") {
|
|
1713
|
-
return [name || node.name, [stack[0]]
|
|
2125
|
+
return [name || node.name, [{ parent: null, results: [stack[0]] }]];
|
|
1714
2126
|
}
|
|
2127
|
+
let inStatic = false;
|
|
2128
|
+
let checkedImports = false;
|
|
1715
2129
|
for (let i = stack.length; i--;) {
|
|
1716
|
-
const
|
|
1717
|
-
|
|
1718
|
-
|
|
2130
|
+
const si = stack[i];
|
|
2131
|
+
switch (si.type) {
|
|
2132
|
+
case "ClassDeclaration":
|
|
2133
|
+
case "ModuleDeclaration":
|
|
2134
|
+
case "Program":
|
|
2135
|
+
if (!checkedImports) {
|
|
2136
|
+
checkedImports = true;
|
|
2137
|
+
const results = findUsingForNode(state, stack, i, node, decls === "type_decls");
|
|
2138
|
+
if (results) {
|
|
2139
|
+
return [name || node.name, [{ parent: si, results }]];
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
break;
|
|
2143
|
+
case "FunctionDeclaration":
|
|
2144
|
+
inStatic = si.isStatic === true;
|
|
2145
|
+
// fall through
|
|
2146
|
+
default:
|
|
2147
|
+
if (nonlocal)
|
|
2148
|
+
continue;
|
|
2149
|
+
break;
|
|
2150
|
+
}
|
|
2151
|
+
const results = checkOne(state, si, decls, node, inStatic);
|
|
2152
|
+
if (results) {
|
|
2153
|
+
return [name || node.name, [{ parent: si, results }]];
|
|
2154
|
+
}
|
|
2155
|
+
else if (results === false) {
|
|
2156
|
+
break;
|
|
1719
2157
|
}
|
|
1720
2158
|
}
|
|
1721
|
-
|
|
2159
|
+
return [null, null];
|
|
1722
2160
|
}
|
|
1723
2161
|
}
|
|
1724
|
-
return [
|
|
2162
|
+
return [false, false];
|
|
1725
2163
|
}
|
|
1726
2164
|
function api_collectNamespaces(ast, stateIn) {
|
|
1727
2165
|
const state = (stateIn || {});
|
|
@@ -1737,14 +2175,15 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1737
2175
|
let low = 0, high = ast.comments.length;
|
|
1738
2176
|
while (high > low) {
|
|
1739
2177
|
const mid = (low + high) >> 1;
|
|
1740
|
-
if (ast.comments[mid].start < node.start) {
|
|
2178
|
+
if ((ast.comments[mid].start || 0) < node.start) {
|
|
1741
2179
|
low = mid + 1;
|
|
1742
2180
|
}
|
|
1743
2181
|
else {
|
|
1744
2182
|
high = mid;
|
|
1745
2183
|
}
|
|
1746
2184
|
}
|
|
1747
|
-
while (high < ast.comments.length &&
|
|
2185
|
+
while (high < ast.comments.length &&
|
|
2186
|
+
(ast.comments[high].end || 0) < node.end) {
|
|
1748
2187
|
high++;
|
|
1749
2188
|
}
|
|
1750
2189
|
if (high > low) {
|
|
@@ -1753,10 +2192,14 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1753
2192
|
}
|
|
1754
2193
|
};
|
|
1755
2194
|
state.lookup = (node, name, stack) => lookup(state, state.inType ? "type_decls" : "decls", node, name, stack);
|
|
2195
|
+
state.lookupNonlocal = (node, name, stack) => lookup(state, "decls", node, name, stack, true);
|
|
1756
2196
|
state.lookupValue = (node, name, stack) => lookup(state, "decls", node, name, stack);
|
|
1757
2197
|
state.lookupType = (node, name, stack) => lookup(state, "type_decls", node, name, stack);
|
|
2198
|
+
state.stackClone = () => state.stack.map((elm) => elm.type === "ModuleDeclaration" || elm.type === "Program"
|
|
2199
|
+
? { ...elm }
|
|
2200
|
+
: elm);
|
|
1758
2201
|
state.inType = false;
|
|
1759
|
-
state.traverse = (root) =>
|
|
2202
|
+
state.traverse = (root) => ast_traverseAst(root, (node) => {
|
|
1760
2203
|
try {
|
|
1761
2204
|
if (state.shouldExclude && state.shouldExclude(node)) {
|
|
1762
2205
|
// don't visit any children, but do call post
|
|
@@ -1767,14 +2210,60 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1767
2210
|
if (state.stack.length != 1) {
|
|
1768
2211
|
throw new Error("Unexpected stack length for Program node");
|
|
1769
2212
|
}
|
|
2213
|
+
state.stack[0].node = node;
|
|
1770
2214
|
break;
|
|
1771
2215
|
case "TypeSpecList":
|
|
1772
2216
|
state.inType = true;
|
|
1773
2217
|
break;
|
|
2218
|
+
case "ImportModule":
|
|
2219
|
+
case "Using": {
|
|
2220
|
+
const [parent] = state.stack.slice(-1);
|
|
2221
|
+
if (!parent.usings) {
|
|
2222
|
+
parent.usings = {};
|
|
2223
|
+
}
|
|
2224
|
+
const name = (node.type === "Using" && node.as && node.as.name) ||
|
|
2225
|
+
(node.id.type === "Identifier"
|
|
2226
|
+
? node.id.name
|
|
2227
|
+
: node.id.property.name);
|
|
2228
|
+
const using = { node };
|
|
2229
|
+
parent.usings[name] = using;
|
|
2230
|
+
if (node.type == "ImportModule") {
|
|
2231
|
+
if (!parent.imports) {
|
|
2232
|
+
parent.imports = [using];
|
|
2233
|
+
}
|
|
2234
|
+
else {
|
|
2235
|
+
const index = parent.imports.findIndex((using) => (using.node.id.type === "Identifier"
|
|
2236
|
+
? using.node.id.name
|
|
2237
|
+
: using.node.id.property.name) === name);
|
|
2238
|
+
if (index >= 0)
|
|
2239
|
+
parent.imports.splice(index, 1);
|
|
2240
|
+
parent.imports.push(using);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
break;
|
|
2244
|
+
}
|
|
2245
|
+
case "CatchClause":
|
|
2246
|
+
if (node.param) {
|
|
2247
|
+
const [parent] = state.stack.slice(-1);
|
|
2248
|
+
if (!parent.decls)
|
|
2249
|
+
parent.decls = {};
|
|
2250
|
+
const id = node.param.type === "Identifier"
|
|
2251
|
+
? node.param
|
|
2252
|
+
: node.param.left;
|
|
2253
|
+
state.stack.push({
|
|
2254
|
+
type: "BlockStatement",
|
|
2255
|
+
fullName: undefined,
|
|
2256
|
+
name: undefined,
|
|
2257
|
+
node: node.body,
|
|
2258
|
+
decls: { [id.name]: [id] },
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
break;
|
|
1774
2262
|
case "BlockStatement": {
|
|
1775
2263
|
const [parent] = state.stack.slice(-1);
|
|
1776
|
-
if (parent.
|
|
1777
|
-
parent.type != "
|
|
2264
|
+
if (parent.node === node ||
|
|
2265
|
+
(parent.type != "FunctionDeclaration" &&
|
|
2266
|
+
parent.type != "BlockStatement")) {
|
|
1778
2267
|
break;
|
|
1779
2268
|
}
|
|
1780
2269
|
// fall through
|
|
@@ -1820,6 +2309,9 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1820
2309
|
parent.decls[name].push(elm);
|
|
1821
2310
|
if (node.type == "ModuleDeclaration" ||
|
|
1822
2311
|
node.type == "ClassDeclaration") {
|
|
2312
|
+
// Inject the class/module name into itself,
|
|
2313
|
+
// so you can say Graphics.Graphics.Graphics.COLOR_RED
|
|
2314
|
+
elm.decls = { [name]: [elm] };
|
|
1823
2315
|
if (!parent.type_decls)
|
|
1824
2316
|
parent.type_decls = {};
|
|
1825
2317
|
if (!api_hasProperty(parent.type_decls, name)) {
|
|
@@ -1867,7 +2359,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1867
2359
|
if (!parent.decls)
|
|
1868
2360
|
parent.decls = {};
|
|
1869
2361
|
const decls = parent.decls;
|
|
1870
|
-
const stack = state.
|
|
2362
|
+
const stack = state.stackClone();
|
|
1871
2363
|
node.declarations.forEach((decl) => {
|
|
1872
2364
|
const name = api_variableDeclarationName(decl.id);
|
|
1873
2365
|
if (!api_hasProperty(decls, name)) {
|
|
@@ -1971,8 +2463,14 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1971
2463
|
state.inType = true;
|
|
1972
2464
|
break;
|
|
1973
2465
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
2466
|
+
const [parent] = state.stack.slice(-1);
|
|
2467
|
+
if (parent.node === node ||
|
|
2468
|
+
(node.type === "CatchClause" && parent.node === node.body)) {
|
|
2469
|
+
delete parent.usings;
|
|
2470
|
+
delete parent.imports;
|
|
2471
|
+
if (node.type != "Program") {
|
|
2472
|
+
state.stack.pop();
|
|
2473
|
+
}
|
|
1976
2474
|
}
|
|
1977
2475
|
}
|
|
1978
2476
|
if (ret === false) {
|
|
@@ -1993,77 +2491,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1993
2491
|
}
|
|
1994
2492
|
return state.stack[0];
|
|
1995
2493
|
}
|
|
1996
|
-
function
|
|
1997
|
-
return node ? typeof node === "object" && "type" in node : false;
|
|
1998
|
-
}
|
|
1999
|
-
/*
|
|
2000
|
-
* Traverse the ast rooted at node, calling pre before
|
|
2001
|
-
* visiting each node, and post after.
|
|
2002
|
-
*
|
|
2003
|
-
* - if pre returns false, the node is not traversed, and
|
|
2004
|
-
* post is not called;
|
|
2005
|
-
* - if pre returns a list of child nodes, only those will
|
|
2006
|
-
* be traversed
|
|
2007
|
-
* - otherwise all child nodes are traversed
|
|
2008
|
-
*
|
|
2009
|
-
* - if post returns false, the node it was called on is
|
|
2010
|
-
* removed.
|
|
2011
|
-
*/
|
|
2012
|
-
function api_traverseAst(node, pre, post) {
|
|
2013
|
-
const nodes = pre && pre(node);
|
|
2014
|
-
if (nodes === false)
|
|
2015
|
-
return;
|
|
2016
|
-
for (const key of nodes || Object.keys(node)) {
|
|
2017
|
-
const value = node[key];
|
|
2018
|
-
if (!value)
|
|
2019
|
-
continue;
|
|
2020
|
-
if (Array.isArray(value)) {
|
|
2021
|
-
const values = value;
|
|
2022
|
-
const deletions = values.reduce((state, obj, i) => {
|
|
2023
|
-
if (isMCTreeNode(obj)) {
|
|
2024
|
-
const repl = api_traverseAst(obj, pre, post);
|
|
2025
|
-
if (repl === false) {
|
|
2026
|
-
if (!state)
|
|
2027
|
-
state = {};
|
|
2028
|
-
state[i] = true;
|
|
2029
|
-
}
|
|
2030
|
-
else if (repl != null) {
|
|
2031
|
-
if (!state)
|
|
2032
|
-
state = {};
|
|
2033
|
-
values[i] = repl;
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
return state;
|
|
2037
|
-
}, null);
|
|
2038
|
-
if (deletions) {
|
|
2039
|
-
values.splice(0, values.length, ...values.filter((obj, i) => deletions[i] !== true).flat(1));
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
else if (isMCTreeNode(value)) {
|
|
2043
|
-
const repl = api_traverseAst(value, pre, post);
|
|
2044
|
-
if (repl === false) {
|
|
2045
|
-
delete node[key];
|
|
2046
|
-
}
|
|
2047
|
-
else if (repl != null) {
|
|
2048
|
-
if (Array.isArray(repl)) {
|
|
2049
|
-
throw new Error("Array returned by traverseAst in Node context");
|
|
2050
|
-
}
|
|
2051
|
-
node[key] = repl;
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
return post && post(node);
|
|
2056
|
-
}
|
|
2057
|
-
function formatAst(node, monkeyCSource = null) {
|
|
2058
|
-
if ("comments" in node && !monkeyCSource) {
|
|
2059
|
-
// Prettier inserts comments by using the source location to
|
|
2060
|
-
// find the original comment, rather than using the contents
|
|
2061
|
-
// of the comment as reported by the comment nodes themselves.
|
|
2062
|
-
// If all we've got is the ast, rather than the actual
|
|
2063
|
-
// source code, this goes horribly wrong, so just drop all
|
|
2064
|
-
// the comments.
|
|
2065
|
-
delete node.comments;
|
|
2066
|
-
}
|
|
2494
|
+
function api_formatAst(node, monkeyCSource = null) {
|
|
2067
2495
|
/*
|
|
2068
2496
|
* The estree printer sometimes looks at the parent node without
|
|
2069
2497
|
* checking that there *is* a parent node (eg it assumes all
|
|
@@ -2104,7 +2532,7 @@ function handleException(state, node, exception) {
|
|
|
2104
2532
|
.filter((e) => e != null)
|
|
2105
2533
|
.join(".");
|
|
2106
2534
|
const location = node.loc && node.loc.source
|
|
2107
|
-
? `${node.loc.source}:${node.start || 0}:${node.end || 0}`
|
|
2535
|
+
? `${node.loc.source}:${node.loc.start.line || 0}:${node.loc.end.line || 0}`
|
|
2108
2536
|
: "<unknown>";
|
|
2109
2537
|
const message = `Got exception \`${exception instanceof Error
|
|
2110
2538
|
? exception.message
|
|
@@ -2116,9 +2544,65 @@ function handleException(state, node, exception) {
|
|
|
2116
2544
|
exception = new Error(message);
|
|
2117
2545
|
}
|
|
2118
2546
|
}
|
|
2119
|
-
|
|
2547
|
+
catch (ex) {
|
|
2120
2548
|
throw exception;
|
|
2121
2549
|
}
|
|
2550
|
+
throw exception;
|
|
2551
|
+
}
|
|
2552
|
+
function findUsing(state, stack, using) {
|
|
2553
|
+
if (using.module)
|
|
2554
|
+
return using.module;
|
|
2555
|
+
let module = stack[0];
|
|
2556
|
+
const find = (node) => {
|
|
2557
|
+
let name;
|
|
2558
|
+
if (node.type == "Identifier") {
|
|
2559
|
+
name = node.name;
|
|
2560
|
+
}
|
|
2561
|
+
else {
|
|
2562
|
+
find(node.object);
|
|
2563
|
+
name = node.property.name;
|
|
2564
|
+
}
|
|
2565
|
+
if (api_hasProperty(module.decls, name)) {
|
|
2566
|
+
const decls = module.decls[name];
|
|
2567
|
+
if (decls &&
|
|
2568
|
+
decls.length === 1 &&
|
|
2569
|
+
decls[0].type === "ModuleDeclaration") {
|
|
2570
|
+
module = decls[0];
|
|
2571
|
+
return true;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
return false;
|
|
2575
|
+
};
|
|
2576
|
+
if (find(using.node.id)) {
|
|
2577
|
+
using.module = module;
|
|
2578
|
+
return using.module;
|
|
2579
|
+
}
|
|
2580
|
+
if (state.config?.checkInvalidSymbols !== "OFF") {
|
|
2581
|
+
inliner_diagnostic(state, using.node.id.loc, `Unable to resolve import of ${api_formatAst(using.node.id)}`, state.config?.checkInvalidSymbols || "WARNING");
|
|
2582
|
+
}
|
|
2583
|
+
return null;
|
|
2584
|
+
}
|
|
2585
|
+
function findUsingForNode(state, stack, i, node, isType) {
|
|
2586
|
+
while (i >= 0) {
|
|
2587
|
+
const si = stack[i--];
|
|
2588
|
+
if (api_hasProperty(si.usings, node.name)) {
|
|
2589
|
+
const using = si.usings[node.name];
|
|
2590
|
+
const module = findUsing(state, stack, using);
|
|
2591
|
+
return module && [module];
|
|
2592
|
+
}
|
|
2593
|
+
if (si.imports && isType) {
|
|
2594
|
+
for (let j = si.imports.length; j--;) {
|
|
2595
|
+
const using = si.imports[j];
|
|
2596
|
+
const module = findUsing(state, stack, using);
|
|
2597
|
+
if (module) {
|
|
2598
|
+
if (api_hasProperty(module.type_decls, node.name)) {
|
|
2599
|
+
return module.type_decls[node.name];
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
return null;
|
|
2122
2606
|
}
|
|
2123
2607
|
|
|
2124
2608
|
var __webpack_export_target__ = exports;
|