@markw65/monkeyc-optimizer 1.0.43 → 1.0.45
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 +28 -0
- package/build/api.cjs +1241 -739
- package/build/optimizer.cjs +665 -251
- package/build/sdk-util.cjs +400 -1
- package/build/src/api.d.ts +3 -2
- package/build/src/ast.d.ts +30 -1
- package/build/src/data-flow.d.ts +45 -0
- package/build/src/function-info.d.ts +2 -0
- package/build/src/inliner.d.ts +2 -2
- package/build/src/interp-binary.d.ts +4 -0
- package/build/src/interp-call.d.ts +3 -0
- package/build/src/interp.d.ts +23 -0
- package/build/src/jungles.d.ts +2 -1
- package/build/src/manifest.d.ts +1 -0
- package/build/src/mc-types.d.ts +166 -0
- package/build/src/optimizer-types.d.ts +12 -3
- package/build/src/pre.d.ts +1 -1
- package/build/src/projects.d.ts +2 -1
- package/build/src/resources.d.ts +3 -4
- package/build/src/type-flow.d.ts +4 -0
- package/build/src/util.d.ts +6 -1
- package/build/src/worker-pool.d.ts +4 -0
- package/build/src/worker-task.d.ts +29 -0
- package/build/src/xml-util.d.ts +19 -7
- package/build/util.cjs +58 -2
- package/package.json +4 -3
- package/build/src/estree-types.d.ts +0 -324
package/build/optimizer.cjs
CHANGED
|
@@ -3585,6 +3585,17 @@ function manifestAnnotations(manifest) {
|
|
|
3585
3585
|
.children("iq:annotation")
|
|
3586
3586
|
.text();
|
|
3587
3587
|
}
|
|
3588
|
+
function manifestLanguages(manifest) {
|
|
3589
|
+
if (manifest.body instanceof Error) {
|
|
3590
|
+
throw manifest.body;
|
|
3591
|
+
}
|
|
3592
|
+
return manifest.body
|
|
3593
|
+
.children()
|
|
3594
|
+
.filter((c) => c.name === "iq:application" || c.name === "iq:barrel")
|
|
3595
|
+
.children("iq:languages")
|
|
3596
|
+
.children("iq:language")
|
|
3597
|
+
.text();
|
|
3598
|
+
}
|
|
3588
3599
|
async function checkManifest(manifest, products) {
|
|
3589
3600
|
if (manifest.body instanceof Error) {
|
|
3590
3601
|
throw manifest.body;
|
|
@@ -3973,10 +3984,6 @@ async function resolve_literals(qualifier, default_source, deviceInfo, cache) {
|
|
|
3973
3984
|
const lang = qualifier["lang"];
|
|
3974
3985
|
if (lang) {
|
|
3975
3986
|
await Promise.all(Object.keys(lang).map((key) => {
|
|
3976
|
-
if (!(0,external_api_cjs_namespaceObject.hasProperty)(deviceInfo.languages, key)) {
|
|
3977
|
-
delete lang[key];
|
|
3978
|
-
return null;
|
|
3979
|
-
}
|
|
3980
3987
|
return resolve_one_file_list(lang, key);
|
|
3981
3988
|
}));
|
|
3982
3989
|
if (Object.keys(lang).length === 0)
|
|
@@ -4479,6 +4486,7 @@ async function get_jungle_and_barrels(jungleFiles, defaultProducts, options, cac
|
|
|
4479
4486
|
jungles,
|
|
4480
4487
|
resources,
|
|
4481
4488
|
buildDependencies,
|
|
4489
|
+
devices,
|
|
4482
4490
|
};
|
|
4483
4491
|
}
|
|
4484
4492
|
catch (e) {
|
|
@@ -4509,17 +4517,22 @@ const external_child_process_namespaceObject = require("child_process");
|
|
|
4509
4517
|
|
|
4510
4518
|
|
|
4511
4519
|
async function launchSimulator(force = true) {
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
|
|
4515
|
-
const child = (0,external_child_process_namespaceObject.execFile)(external_path_.resolve(sdk, "bin", external_sdk_util_cjs_namespaceObject.isWin ? "simulator" : "connectiq"));
|
|
4516
|
-
child.unref();
|
|
4517
|
-
for (let i = 0;; i++) {
|
|
4518
|
-
if (await checkIfSimulatorRunning())
|
|
4519
|
-
return;
|
|
4520
|
-
if (i === 5)
|
|
4520
|
+
try {
|
|
4521
|
+
if (!force && (await checkIfSimulatorRunning()))
|
|
4521
4522
|
return;
|
|
4522
|
-
|
|
4523
|
+
const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
|
|
4524
|
+
const child = (0,external_child_process_namespaceObject.execFile)(external_path_.resolve(sdk, "bin", external_sdk_util_cjs_namespaceObject.isWin ? "simulator" : "connectiq"));
|
|
4525
|
+
child.unref();
|
|
4526
|
+
for (let i = 0;; i++) {
|
|
4527
|
+
if (await checkIfSimulatorRunning())
|
|
4528
|
+
return;
|
|
4529
|
+
if (i === 5)
|
|
4530
|
+
return;
|
|
4531
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
catch (e) {
|
|
4535
|
+
console.log(e);
|
|
4523
4536
|
}
|
|
4524
4537
|
}
|
|
4525
4538
|
function checkIfSimulatorRunning() {
|
|
@@ -4534,7 +4547,7 @@ function checkIfSimulatorRunningOn(port) {
|
|
|
4534
4547
|
socket.on("end", () => resolve(listening));
|
|
4535
4548
|
socket.connect(port, "localhost");
|
|
4536
4549
|
socket.end();
|
|
4537
|
-
});
|
|
4550
|
+
}).catch(() => false);
|
|
4538
4551
|
}
|
|
4539
4552
|
function simulateProgram(prg, device, test = false, logger) {
|
|
4540
4553
|
const args = [prg, device];
|
|
@@ -4789,13 +4802,73 @@ function getNodeValue(node) {
|
|
|
4789
4802
|
return [node, "Long"];
|
|
4790
4803
|
}
|
|
4791
4804
|
if (type === "string") {
|
|
4792
|
-
return
|
|
4805
|
+
return node.raw.startsWith("'")
|
|
4806
|
+
? [node, "Char"]
|
|
4807
|
+
: [node, "String"];
|
|
4793
4808
|
}
|
|
4794
4809
|
if (type === "boolean") {
|
|
4795
4810
|
return [node, "Boolean"];
|
|
4796
4811
|
}
|
|
4797
4812
|
throw new Error(`Literal has unknown type '${type}'`);
|
|
4798
4813
|
}
|
|
4814
|
+
function wrap(node, loc) {
|
|
4815
|
+
if (loc) {
|
|
4816
|
+
node.loc = loc;
|
|
4817
|
+
node.start = loc.start.offset;
|
|
4818
|
+
node.end = loc.end.offset;
|
|
4819
|
+
}
|
|
4820
|
+
return node;
|
|
4821
|
+
}
|
|
4822
|
+
function locRange(start, end) {
|
|
4823
|
+
return {
|
|
4824
|
+
source: start.source || end.source,
|
|
4825
|
+
start: start.start,
|
|
4826
|
+
end: end.end,
|
|
4827
|
+
};
|
|
4828
|
+
}
|
|
4829
|
+
function adjustLoc(loc, start = 1, end = -1) {
|
|
4830
|
+
return {
|
|
4831
|
+
source: loc.source,
|
|
4832
|
+
start: {
|
|
4833
|
+
offset: loc.start.offset + start,
|
|
4834
|
+
line: loc.start.line,
|
|
4835
|
+
column: loc.start.column + start,
|
|
4836
|
+
},
|
|
4837
|
+
end: {
|
|
4838
|
+
offset: loc.end.offset + end,
|
|
4839
|
+
line: loc.end.line,
|
|
4840
|
+
column: loc.end.column + end,
|
|
4841
|
+
},
|
|
4842
|
+
};
|
|
4843
|
+
}
|
|
4844
|
+
function makeIdentifier(name, loc) {
|
|
4845
|
+
return wrap({ type: "Identifier", name }, loc);
|
|
4846
|
+
}
|
|
4847
|
+
function makeMemberExpression(object, property) {
|
|
4848
|
+
return wrap({
|
|
4849
|
+
type: "MemberExpression",
|
|
4850
|
+
object,
|
|
4851
|
+
property,
|
|
4852
|
+
computed: false,
|
|
4853
|
+
}, object.loc && locRange(object.loc, property.loc));
|
|
4854
|
+
}
|
|
4855
|
+
function makeScopedName(dotted, l) {
|
|
4856
|
+
const loc = l && adjustLoc(l, 0, l.start.offset - l.end.offset);
|
|
4857
|
+
const result = dotted.split(/\s*\.\s*/).reduce(({ cur, offset }, next) => {
|
|
4858
|
+
const id = makeIdentifier(next, loc && adjustLoc(loc, offset, offset + next.length));
|
|
4859
|
+
if (!cur) {
|
|
4860
|
+
cur = id;
|
|
4861
|
+
}
|
|
4862
|
+
else {
|
|
4863
|
+
cur = makeMemberExpression(cur, id);
|
|
4864
|
+
}
|
|
4865
|
+
offset += next.length + 1;
|
|
4866
|
+
return { cur, offset };
|
|
4867
|
+
}, { cur: null, offset: 0 }).cur;
|
|
4868
|
+
if (!result)
|
|
4869
|
+
throw new Error("Failed to make a ScopedName");
|
|
4870
|
+
return result;
|
|
4871
|
+
}
|
|
4799
4872
|
|
|
4800
4873
|
;// CONCATENATED MODULE: ./src/function-info.ts
|
|
4801
4874
|
|
|
@@ -4848,6 +4921,8 @@ function recordCalledFuncs(func, callees) {
|
|
|
4848
4921
|
}
|
|
4849
4922
|
function functionMayModify(state, func, decl) {
|
|
4850
4923
|
const info = func.info;
|
|
4924
|
+
if (info === false)
|
|
4925
|
+
return false;
|
|
4851
4926
|
if (!info || info.modifiedUnknown)
|
|
4852
4927
|
return true;
|
|
4853
4928
|
if (info.resolvedDecls) {
|
|
@@ -4860,7 +4935,7 @@ function functionMayModify(state, func, decl) {
|
|
|
4860
4935
|
const visited = new Set();
|
|
4861
4936
|
const resolved = new Set();
|
|
4862
4937
|
const resolveDecls = (f) => {
|
|
4863
|
-
if (visited.has(f))
|
|
4938
|
+
if (f.info === false || visited.has(f))
|
|
4864
4939
|
return true;
|
|
4865
4940
|
if (!f.info)
|
|
4866
4941
|
return false;
|
|
@@ -4922,6 +4997,16 @@ function findCalleesForNew(lookupDefs) {
|
|
|
4922
4997
|
.flatMap(initializer)
|
|
4923
4998
|
.filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
|
|
4924
4999
|
}
|
|
5000
|
+
function findCalleesByNode(state, callee) {
|
|
5001
|
+
const name = callee.type === "Identifier"
|
|
5002
|
+
? callee.name
|
|
5003
|
+
: callee.type === "MemberExpression" && !callee.computed
|
|
5004
|
+
? callee.property.name
|
|
5005
|
+
: null;
|
|
5006
|
+
if (!name)
|
|
5007
|
+
return null;
|
|
5008
|
+
return ((hasProperty(state.allFunctions, name) && state.allFunctions[name]) || null);
|
|
5009
|
+
}
|
|
4925
5010
|
|
|
4926
5011
|
;// CONCATENATED MODULE: ./src/optimizer-types.ts
|
|
4927
5012
|
var StateNodeAttributes;
|
|
@@ -5885,13 +5970,17 @@ function pragmaChecker(state, ast, diagnostics) {
|
|
|
5885
5970
|
if (quote == '"') {
|
|
5886
5971
|
return haystack.includes(needle);
|
|
5887
5972
|
}
|
|
5888
|
-
const re = new RegExp(needle.replace(/@([
|
|
5973
|
+
const re = new RegExp(needle.replace(/@([-\d.\w]+|"[^"]*")/g, (_match, pat) => `(?:${pat}|pre_${pat.replace(/\W/g, "_")}(?:_\\d+)?)`));
|
|
5889
5974
|
return re.test(haystack);
|
|
5890
5975
|
};
|
|
5891
5976
|
next();
|
|
5892
5977
|
traverseAst(ast, (node) => {
|
|
5893
|
-
if (index >= comments.length
|
|
5978
|
+
if (index >= comments.length ||
|
|
5979
|
+
node.type === "Line" ||
|
|
5980
|
+
node.type === "Block" ||
|
|
5981
|
+
node.type === "MultiLine") {
|
|
5894
5982
|
return false;
|
|
5983
|
+
}
|
|
5895
5984
|
if (node.start && node.start >= (comment.end || Infinity)) {
|
|
5896
5985
|
const { kind, quote, needle } = matchers.shift();
|
|
5897
5986
|
if (kind === "match") {
|
|
@@ -6037,7 +6126,7 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6037
6126
|
try {
|
|
6038
6127
|
const localState = new LocalState(func.node);
|
|
6039
6128
|
const ret = localState.curBlock;
|
|
6040
|
-
state.stack = func.stack;
|
|
6129
|
+
state.stack = [...func.stack];
|
|
6041
6130
|
const stmtStack = [func.node];
|
|
6042
6131
|
let tryActive = 0;
|
|
6043
6132
|
state.pre = (node) => {
|
|
@@ -6052,6 +6141,8 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6052
6141
|
stmtStack.push(node);
|
|
6053
6142
|
}
|
|
6054
6143
|
switch (node.type) {
|
|
6144
|
+
case "FunctionDeclaration":
|
|
6145
|
+
return ["body"];
|
|
6055
6146
|
case "AttributeList":
|
|
6056
6147
|
return [];
|
|
6057
6148
|
case "SwitchStatement": {
|
|
@@ -6268,11 +6359,6 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6268
6359
|
}
|
|
6269
6360
|
case "VariableDeclarator":
|
|
6270
6361
|
return ["init"];
|
|
6271
|
-
case "MemberExpression":
|
|
6272
|
-
if (!node.computed) {
|
|
6273
|
-
return ["object"];
|
|
6274
|
-
}
|
|
6275
|
-
break;
|
|
6276
6362
|
case "UnaryExpression":
|
|
6277
6363
|
if (node.operator === ":") {
|
|
6278
6364
|
return [];
|
|
@@ -6308,6 +6394,14 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6308
6394
|
case "ContinueStatement":
|
|
6309
6395
|
localState.terminal(node.type);
|
|
6310
6396
|
return [];
|
|
6397
|
+
case "CallExpression":
|
|
6398
|
+
if (node.callee.type === "Identifier") {
|
|
6399
|
+
const extra = state.stack.splice(func.stack.length);
|
|
6400
|
+
state.traverse(node.callee);
|
|
6401
|
+
state.stack.push(...extra);
|
|
6402
|
+
return ["arguments"];
|
|
6403
|
+
}
|
|
6404
|
+
break;
|
|
6311
6405
|
}
|
|
6312
6406
|
return null;
|
|
6313
6407
|
};
|
|
@@ -6469,142 +6563,94 @@ function getPreOrder(head) {
|
|
|
6469
6563
|
|
|
6470
6564
|
// EXTERNAL MODULE: ./node_modules/priorityqueuejs/index.js
|
|
6471
6565
|
var priorityqueuejs = __webpack_require__(2789);
|
|
6472
|
-
;// CONCATENATED MODULE: ./src/
|
|
6566
|
+
;// CONCATENATED MODULE: ./src/data-flow.ts
|
|
6567
|
+
|
|
6473
6568
|
|
|
6474
6569
|
|
|
6475
6570
|
|
|
6476
6571
|
|
|
6477
6572
|
|
|
6478
|
-
/**
|
|
6479
|
-
* This implements a pseudo Partial Redundancy Elimination
|
|
6480
|
-
* pass. It isn't quite like traditional PRE because we're
|
|
6481
|
-
* aiming to minimize size, not dynamic instructions. So
|
|
6482
|
-
* for us, its worthwhile to take something like:
|
|
6483
|
-
*
|
|
6484
|
-
* switch (x) {
|
|
6485
|
-
* case 1: foo(A.B); break;
|
|
6486
|
-
* case 2: foo(C); break;
|
|
6487
|
-
* case 3: bar(A.B); break;
|
|
6488
|
-
* }
|
|
6489
|
-
*
|
|
6490
|
-
* and rewrite it as
|
|
6491
|
-
*
|
|
6492
|
-
* var tmp = A.B;
|
|
6493
|
-
* switch (x) {
|
|
6494
|
-
* case 1: foo(tmp); break;
|
|
6495
|
-
* case 2: foo(C); break;
|
|
6496
|
-
* case 3: bar(tmp); break;
|
|
6497
|
-
* }
|
|
6498
|
-
*
|
|
6499
|
-
* because even though A.B wasn't used on all paths where we
|
|
6500
|
-
* inserted the temporary, we still reduced the code size.
|
|
6501
|
-
*/
|
|
6502
|
-
const logging = false;
|
|
6503
6573
|
function declFullName(decl) {
|
|
6574
|
+
if (Array.isArray(decl)) {
|
|
6575
|
+
decl = decl[0];
|
|
6576
|
+
}
|
|
6577
|
+
if (decl.type === "Literal") {
|
|
6578
|
+
return decl.raw || decl.value?.toString() || "null";
|
|
6579
|
+
}
|
|
6580
|
+
if ((0,external_api_cjs_namespaceObject.isStateNode)(decl))
|
|
6581
|
+
return decl.fullName;
|
|
6504
6582
|
switch (decl.type) {
|
|
6505
|
-
case "
|
|
6506
|
-
return decl.
|
|
6507
|
-
case "
|
|
6508
|
-
return decl.
|
|
6583
|
+
case "BinaryExpression":
|
|
6584
|
+
return decl.left.name;
|
|
6585
|
+
case "EnumStringMember":
|
|
6586
|
+
return decl.init
|
|
6587
|
+
? `${decl.id.name}:${(0,external_api_cjs_namespaceObject.formatAst)(decl.init)}`
|
|
6588
|
+
: decl.id.name;
|
|
6509
6589
|
default:
|
|
6510
6590
|
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
6511
6591
|
}
|
|
6512
6592
|
}
|
|
6513
6593
|
function declName(decl) {
|
|
6594
|
+
if (Array.isArray(decl)) {
|
|
6595
|
+
decl = decl[0];
|
|
6596
|
+
}
|
|
6597
|
+
if (decl.type === "Literal") {
|
|
6598
|
+
return (decl.raw || decl.value?.toString() || "null").replace(/[^\w]/g, "_");
|
|
6599
|
+
}
|
|
6600
|
+
if ((0,external_api_cjs_namespaceObject.isStateNode)(decl))
|
|
6601
|
+
return decl.name;
|
|
6514
6602
|
switch (decl.type) {
|
|
6515
|
-
case "
|
|
6516
|
-
return
|
|
6517
|
-
case "
|
|
6518
|
-
return decl.name;
|
|
6603
|
+
case "BinaryExpression":
|
|
6604
|
+
return decl.left.name;
|
|
6605
|
+
case "EnumStringMember":
|
|
6606
|
+
return decl.id.name;
|
|
6519
6607
|
default:
|
|
6520
6608
|
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
6521
6609
|
}
|
|
6522
6610
|
}
|
|
6523
|
-
function logAntState(s, decl) {
|
|
6524
|
-
const defs = Array.from(s.ant).reduce((defs, event) => {
|
|
6525
|
-
if (event.type === "def" || event.type === "mod")
|
|
6526
|
-
defs++;
|
|
6527
|
-
return defs;
|
|
6528
|
-
}, 0);
|
|
6529
|
-
console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
|
|
6530
|
-
console.log(` - members: ${Array.from(s.members)
|
|
6531
|
-
.map(([block, live]) => block.order + (live ? "t" : "f"))
|
|
6532
|
-
.join(", ")}`);
|
|
6533
|
-
}
|
|
6534
|
-
function logAntDecls(antDecls) {
|
|
6535
|
-
antDecls.forEach(logAntState);
|
|
6536
|
-
}
|
|
6537
|
-
function sizeBasedPRE(state, func) {
|
|
6538
|
-
if (!func.node.body)
|
|
6539
|
-
return;
|
|
6540
|
-
if (!state.config ||
|
|
6541
|
-
!state.config.sizeBasedPRE ||
|
|
6542
|
-
(typeof state.config.sizeBasedPRE === "string" &&
|
|
6543
|
-
state.config.sizeBasedPRE !== func.fullName)) {
|
|
6544
|
-
return;
|
|
6545
|
-
}
|
|
6546
|
-
const { graph: head, identifiers } = buildPREGraph(state, func);
|
|
6547
|
-
const candidates = computeAttributes(state, head);
|
|
6548
|
-
if (candidates) {
|
|
6549
|
-
if (logging) {
|
|
6550
|
-
console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
|
|
6551
|
-
logAntDecls(candidates);
|
|
6552
|
-
}
|
|
6553
|
-
const nodeMap = new Map();
|
|
6554
|
-
const declMap = new Map();
|
|
6555
|
-
const variableDecl = withLoc({
|
|
6556
|
-
type: "VariableDeclaration",
|
|
6557
|
-
declarations: [],
|
|
6558
|
-
kind: "var",
|
|
6559
|
-
}, func.node.body);
|
|
6560
|
-
variableDecl.end = variableDecl.start;
|
|
6561
|
-
variableDecl.loc.end = variableDecl.loc.start;
|
|
6562
|
-
candidates.forEach((s, decl) => {
|
|
6563
|
-
let name;
|
|
6564
|
-
let i = 0;
|
|
6565
|
-
do {
|
|
6566
|
-
name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
|
|
6567
|
-
if (!identifiers.has(name)) {
|
|
6568
|
-
identifiers.add(name);
|
|
6569
|
-
break;
|
|
6570
|
-
}
|
|
6571
|
-
i++;
|
|
6572
|
-
} while (true);
|
|
6573
|
-
declMap.set(decl, name);
|
|
6574
|
-
variableDecl.declarations.push(withLoc({
|
|
6575
|
-
type: "VariableDeclarator",
|
|
6576
|
-
id: withLoc({ type: "Identifier", name }, variableDecl),
|
|
6577
|
-
kind: "var",
|
|
6578
|
-
}, variableDecl));
|
|
6579
|
-
s.ant.forEach((event) => {
|
|
6580
|
-
const events = nodeMap.get(event.node);
|
|
6581
|
-
if (!events) {
|
|
6582
|
-
nodeMap.set(event.node, [event]);
|
|
6583
|
-
}
|
|
6584
|
-
else {
|
|
6585
|
-
events.push(event);
|
|
6586
|
-
}
|
|
6587
|
-
});
|
|
6588
|
-
});
|
|
6589
|
-
applyReplacements(func.node, nodeMap, declMap);
|
|
6590
|
-
func.node.body.body.unshift(variableDecl);
|
|
6591
|
-
}
|
|
6592
|
-
}
|
|
6593
6611
|
function unhandledExpression(node) {
|
|
6594
6612
|
throw new Error(`Unhandled expression type: ${node.type}`);
|
|
6595
6613
|
}
|
|
6596
|
-
function
|
|
6614
|
+
function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wantsAllRefs) {
|
|
6615
|
+
const uniqueDeclMap = new Map();
|
|
6597
6616
|
const findDecl = (node) => {
|
|
6598
6617
|
if (node.type === "Identifier" ||
|
|
6599
6618
|
(node.type === "MemberExpression" && !node.computed)) {
|
|
6600
6619
|
const [, results] = state.lookup(node);
|
|
6601
|
-
|
|
6602
|
-
results.
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6620
|
+
const decls = (results &&
|
|
6621
|
+
results.reduce((decls, result) => result.results.reduce((decls, result) => {
|
|
6622
|
+
if (!wantsAllRefs &&
|
|
6623
|
+
(result.type !== "VariableDeclarator" || (0,external_api_cjs_namespaceObject.isLocal)(result))) {
|
|
6624
|
+
return decls;
|
|
6625
|
+
}
|
|
6626
|
+
if (!decls)
|
|
6627
|
+
return result;
|
|
6628
|
+
if (Array.isArray(decls)) {
|
|
6629
|
+
decls.push(result);
|
|
6630
|
+
return decls;
|
|
6631
|
+
}
|
|
6632
|
+
else {
|
|
6633
|
+
return [decls, result];
|
|
6634
|
+
}
|
|
6635
|
+
}, decls), null)) ||
|
|
6636
|
+
null;
|
|
6637
|
+
if (!Array.isArray(decls))
|
|
6638
|
+
return decls;
|
|
6639
|
+
// We use EventDecl as a Map key, so we need to
|
|
6640
|
+
// uniquify it. Note that from any given function,
|
|
6641
|
+
// if state.lookup finds a set of non locals, it will
|
|
6642
|
+
// always find the same set, so we can use the first
|
|
6643
|
+
// such as the unique identifier.
|
|
6644
|
+
const canon = uniqueDeclMap.get(decls[0]);
|
|
6645
|
+
if (!canon) {
|
|
6646
|
+
uniqueDeclMap.set(decls[0], decls);
|
|
6647
|
+
return decls;
|
|
6648
|
+
}
|
|
6649
|
+
if (canon.length != decls.length ||
|
|
6650
|
+
!canon.every((v, i) => v === decls[i])) {
|
|
6651
|
+
throw new Error(`Canonical representation of ${declFullName(canon)} did not match`);
|
|
6652
|
+
}
|
|
6653
|
+
return canon;
|
|
6608
6654
|
}
|
|
6609
6655
|
return null;
|
|
6610
6656
|
};
|
|
@@ -6612,18 +6658,22 @@ function buildPREGraph(state, func) {
|
|
|
6612
6658
|
const identifiers = new Set();
|
|
6613
6659
|
const liveDefs = new Map();
|
|
6614
6660
|
const liveStmts = new Map();
|
|
6615
|
-
const liveDef =
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6661
|
+
const liveDef = trackInsertionPoints
|
|
6662
|
+
? (def, stmt) => {
|
|
6663
|
+
let curNodes = liveDefs.get(def);
|
|
6664
|
+
if (!curNodes) {
|
|
6665
|
+
liveDefs.set(def, (curNodes = new Set()));
|
|
6666
|
+
}
|
|
6667
|
+
curNodes.add(stmt);
|
|
6668
|
+
let defs = liveStmts.get(stmt);
|
|
6669
|
+
if (!defs) {
|
|
6670
|
+
liveStmts.set(stmt, (defs = new Map()));
|
|
6671
|
+
}
|
|
6672
|
+
defs.set(def, (defs.get(def) || 0) + 1);
|
|
6624
6673
|
}
|
|
6625
|
-
|
|
6626
|
-
|
|
6674
|
+
: () => {
|
|
6675
|
+
/* do nothing */
|
|
6676
|
+
};
|
|
6627
6677
|
return {
|
|
6628
6678
|
identifiers,
|
|
6629
6679
|
graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
|
|
@@ -6658,7 +6708,7 @@ function buildPREGraph(state, func) {
|
|
|
6658
6708
|
case "ParenthesizedExpression":
|
|
6659
6709
|
break;
|
|
6660
6710
|
case "Literal":
|
|
6661
|
-
if (
|
|
6711
|
+
if (wantsLiteral(node)) {
|
|
6662
6712
|
const result = getNodeValue(node);
|
|
6663
6713
|
const key = result[1] +
|
|
6664
6714
|
(result[0].value === null
|
|
@@ -6680,34 +6730,40 @@ function buildPREGraph(state, func) {
|
|
|
6680
6730
|
case "Identifier":
|
|
6681
6731
|
identifiers.add(node.name);
|
|
6682
6732
|
// fall through
|
|
6683
|
-
case "MemberExpression":
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
if (
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6733
|
+
case "MemberExpression": {
|
|
6734
|
+
const decls = findDecl(node);
|
|
6735
|
+
if (!decls)
|
|
6736
|
+
break;
|
|
6737
|
+
if (trackInsertionPoints &&
|
|
6738
|
+
(0,external_util_cjs_namespaceObject.some)(decls, (decl) => {
|
|
6739
|
+
if (decl.type === "VariableDeclarator") {
|
|
6740
|
+
const defStmts = (decl.node.kind === "var" && liveDefs.get(null)) ||
|
|
6741
|
+
liveDefs.get(decl);
|
|
6742
|
+
if (defStmts) {
|
|
6743
|
+
return true;
|
|
6744
|
+
/*
|
|
6745
|
+
// hold off on this for now. we need to communicate
|
|
6746
|
+
// which defs need to be fixed, which involves yet-another
|
|
6747
|
+
// table.
|
|
6748
|
+
|
|
6749
|
+
if (defStmts.size !== 1) break;
|
|
6750
|
+
const fixable = isFixableStmt([...defStmts][0]);
|
|
6751
|
+
if (fixable === false) break;
|
|
6752
|
+
cost += fixable;
|
|
6753
|
+
*/
|
|
6754
|
+
}
|
|
6701
6755
|
}
|
|
6702
|
-
return
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
decl,
|
|
6706
|
-
mayThrow,
|
|
6707
|
-
};
|
|
6708
|
-
}
|
|
6756
|
+
return false;
|
|
6757
|
+
})) {
|
|
6758
|
+
break;
|
|
6709
6759
|
}
|
|
6710
|
-
|
|
6760
|
+
return {
|
|
6761
|
+
type: "ref",
|
|
6762
|
+
node,
|
|
6763
|
+
decl: decls,
|
|
6764
|
+
mayThrow,
|
|
6765
|
+
};
|
|
6766
|
+
}
|
|
6711
6767
|
case "VariableDeclarator": {
|
|
6712
6768
|
const decl = findDecl(node.id.type === "BinaryExpression" ? node.id.left : node.id);
|
|
6713
6769
|
if (decl) {
|
|
@@ -6755,8 +6811,10 @@ function buildPREGraph(state, func) {
|
|
|
6755
6811
|
}
|
|
6756
6812
|
case "CallExpression": {
|
|
6757
6813
|
liveDef(null, stmt);
|
|
6758
|
-
const [, results] = state.
|
|
6759
|
-
const callees = results
|
|
6814
|
+
const [, results] = state.lookupNonlocal(node.callee);
|
|
6815
|
+
const callees = results
|
|
6816
|
+
? findCallees(results)
|
|
6817
|
+
: findCalleesByNode(state, node.callee);
|
|
6760
6818
|
return { type: "mod", node, mayThrow, callees };
|
|
6761
6819
|
}
|
|
6762
6820
|
default:
|
|
@@ -6771,6 +6829,132 @@ function buildPREGraph(state, func) {
|
|
|
6771
6829
|
}),
|
|
6772
6830
|
};
|
|
6773
6831
|
}
|
|
6832
|
+
class DataflowQueue {
|
|
6833
|
+
constructor() {
|
|
6834
|
+
this.enqueued = new Set();
|
|
6835
|
+
this.queue = new priorityqueuejs((b, a) => (a.order || 0) - (b.order || 0));
|
|
6836
|
+
}
|
|
6837
|
+
enqueue(block) {
|
|
6838
|
+
if (!this.enqueued.has(block)) {
|
|
6839
|
+
this.enqueued.add(block);
|
|
6840
|
+
this.queue.enq(block);
|
|
6841
|
+
}
|
|
6842
|
+
}
|
|
6843
|
+
dequeue() {
|
|
6844
|
+
const block = this.queue.deq();
|
|
6845
|
+
this.enqueued.delete(block);
|
|
6846
|
+
return block;
|
|
6847
|
+
}
|
|
6848
|
+
empty() {
|
|
6849
|
+
return this.queue.isEmpty();
|
|
6850
|
+
}
|
|
6851
|
+
}
|
|
6852
|
+
|
|
6853
|
+
;// CONCATENATED MODULE: ./src/pre.ts
|
|
6854
|
+
|
|
6855
|
+
|
|
6856
|
+
|
|
6857
|
+
|
|
6858
|
+
|
|
6859
|
+
|
|
6860
|
+
/**
|
|
6861
|
+
* This implements a pseudo Partial Redundancy Elimination
|
|
6862
|
+
* pass. It isn't quite like traditional PRE because we're
|
|
6863
|
+
* aiming to minimize size, not dynamic instructions. So
|
|
6864
|
+
* for us, its worthwhile to take something like:
|
|
6865
|
+
*
|
|
6866
|
+
* switch (x) {
|
|
6867
|
+
* case 1: foo(A.B); break;
|
|
6868
|
+
* case 2: foo(C); break;
|
|
6869
|
+
* case 3: bar(A.B); break;
|
|
6870
|
+
* }
|
|
6871
|
+
*
|
|
6872
|
+
* and rewrite it as
|
|
6873
|
+
*
|
|
6874
|
+
* var tmp = A.B;
|
|
6875
|
+
* switch (x) {
|
|
6876
|
+
* case 1: foo(tmp); break;
|
|
6877
|
+
* case 2: foo(C); break;
|
|
6878
|
+
* case 3: bar(tmp); break;
|
|
6879
|
+
* }
|
|
6880
|
+
*
|
|
6881
|
+
* because even though A.B wasn't used on all paths where we
|
|
6882
|
+
* inserted the temporary, we still reduced the code size.
|
|
6883
|
+
*/
|
|
6884
|
+
const logging = false;
|
|
6885
|
+
function logAntState(s, decl) {
|
|
6886
|
+
const defs = Array.from(s.ant).reduce((defs, event) => {
|
|
6887
|
+
if (event.type === "def" || event.type === "mod")
|
|
6888
|
+
defs++;
|
|
6889
|
+
return defs;
|
|
6890
|
+
}, 0);
|
|
6891
|
+
console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
|
|
6892
|
+
console.log(` - members: ${Array.from(s.members)
|
|
6893
|
+
.map(([block, live]) => block.order + (live ? "t" : "f"))
|
|
6894
|
+
.join(", ")}`);
|
|
6895
|
+
}
|
|
6896
|
+
function logAntDecls(antDecls) {
|
|
6897
|
+
antDecls.forEach(logAntState);
|
|
6898
|
+
}
|
|
6899
|
+
function sizeBasedPRE(state, func) {
|
|
6900
|
+
if (!func.node.body)
|
|
6901
|
+
return;
|
|
6902
|
+
if (!state.config ||
|
|
6903
|
+
!state.config.sizeBasedPRE ||
|
|
6904
|
+
(typeof state.config.sizeBasedPRE === "string" &&
|
|
6905
|
+
state.config.sizeBasedPRE !== func.fullName)) {
|
|
6906
|
+
return;
|
|
6907
|
+
}
|
|
6908
|
+
const { graph: head, identifiers } = buildPREGraph(state, func);
|
|
6909
|
+
const candidates = computeAttributes(state, head);
|
|
6910
|
+
if (candidates) {
|
|
6911
|
+
if (logging) {
|
|
6912
|
+
console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
|
|
6913
|
+
logAntDecls(candidates);
|
|
6914
|
+
}
|
|
6915
|
+
const nodeMap = new Map();
|
|
6916
|
+
const declMap = new Map();
|
|
6917
|
+
const variableDecl = withLoc({
|
|
6918
|
+
type: "VariableDeclaration",
|
|
6919
|
+
declarations: [],
|
|
6920
|
+
kind: "var",
|
|
6921
|
+
}, func.node.body);
|
|
6922
|
+
variableDecl.end = variableDecl.start;
|
|
6923
|
+
variableDecl.loc.end = variableDecl.loc.start;
|
|
6924
|
+
candidates.forEach((s, decl) => {
|
|
6925
|
+
let name;
|
|
6926
|
+
let i = 0;
|
|
6927
|
+
do {
|
|
6928
|
+
name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
|
|
6929
|
+
if (!identifiers.has(name)) {
|
|
6930
|
+
identifiers.add(name);
|
|
6931
|
+
break;
|
|
6932
|
+
}
|
|
6933
|
+
i++;
|
|
6934
|
+
} while (true);
|
|
6935
|
+
declMap.set(decl, name);
|
|
6936
|
+
variableDecl.declarations.push(withLoc({
|
|
6937
|
+
type: "VariableDeclarator",
|
|
6938
|
+
id: withLoc({ type: "Identifier", name }, variableDecl),
|
|
6939
|
+
kind: "var",
|
|
6940
|
+
}, variableDecl));
|
|
6941
|
+
s.ant.forEach((event) => {
|
|
6942
|
+
const events = nodeMap.get(event.node);
|
|
6943
|
+
if (!events) {
|
|
6944
|
+
nodeMap.set(event.node, [event]);
|
|
6945
|
+
}
|
|
6946
|
+
else {
|
|
6947
|
+
events.push(event);
|
|
6948
|
+
}
|
|
6949
|
+
});
|
|
6950
|
+
});
|
|
6951
|
+
applyReplacements(func.node, nodeMap, declMap);
|
|
6952
|
+
func.node.body.body.unshift(variableDecl);
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6955
|
+
function buildPREGraph(state, func) {
|
|
6956
|
+
return buildDataFlowGraph(state, func, (literal) => refCost(literal) > LocalRefCost, true, false);
|
|
6957
|
+
}
|
|
6774
6958
|
function anticipatedDecls() {
|
|
6775
6959
|
return new Map();
|
|
6776
6960
|
}
|
|
@@ -6926,26 +7110,18 @@ function computeAttributes(state, head) {
|
|
|
6926
7110
|
.join(", ")}`);
|
|
6927
7111
|
if (block.events) {
|
|
6928
7112
|
block.events.forEach((event) => event.type !== "exn" &&
|
|
6929
|
-
console.log(` ${event.type}: ${event.decl
|
|
7113
|
+
console.log(` ${event.type}: ${event.decl
|
|
7114
|
+
? declFullName(event.decl)
|
|
7115
|
+
: event.node
|
|
7116
|
+
? (0,external_api_cjs_namespaceObject.formatAst)(event.node)
|
|
7117
|
+
: "??"}`));
|
|
6930
7118
|
}
|
|
6931
7119
|
console.log(`Succs: ${(block.succs || [])
|
|
6932
7120
|
.map((block) => block.order)
|
|
6933
7121
|
.join(", ")} ExSucc: ${block.exsucc ? block.exsucc.order : ""}`);
|
|
6934
7122
|
});
|
|
6935
7123
|
}
|
|
6936
|
-
const
|
|
6937
|
-
const queue = new priorityqueuejs((b, a) => (a.order || 0) - (b.order || 0));
|
|
6938
|
-
const enqueue = (block) => {
|
|
6939
|
-
if (!enqueued.has(block)) {
|
|
6940
|
-
enqueued.add(block);
|
|
6941
|
-
queue.enq(block);
|
|
6942
|
-
}
|
|
6943
|
-
};
|
|
6944
|
-
const dequeue = () => {
|
|
6945
|
-
const block = queue.deq();
|
|
6946
|
-
enqueued.delete(block);
|
|
6947
|
-
return block;
|
|
6948
|
-
};
|
|
7124
|
+
const queue = new DataflowQueue();
|
|
6949
7125
|
const blockStates = [];
|
|
6950
7126
|
/*
|
|
6951
7127
|
Algorithm
|
|
@@ -6979,9 +7155,9 @@ function computeAttributes(state, head) {
|
|
|
6979
7155
|
}
|
|
6980
7156
|
return result;
|
|
6981
7157
|
};
|
|
6982
|
-
order.forEach((block) => enqueue(block));
|
|
6983
|
-
while (queue.
|
|
6984
|
-
const top = dequeue();
|
|
7158
|
+
order.forEach((block) => queue.enqueue(block));
|
|
7159
|
+
while (!queue.empty()) {
|
|
7160
|
+
const top = queue.dequeue();
|
|
6985
7161
|
if (top.order === undefined) {
|
|
6986
7162
|
throw new Error(`Unreachable block was visited!`);
|
|
6987
7163
|
}
|
|
@@ -7020,13 +7196,13 @@ function computeAttributes(state, head) {
|
|
|
7020
7196
|
break;
|
|
7021
7197
|
}
|
|
7022
7198
|
case "mod": {
|
|
7023
|
-
curState.forEach((candidates,
|
|
7024
|
-
if (decl.type === "VariableDeclarator" &&
|
|
7199
|
+
curState.forEach((candidates, decls) => {
|
|
7200
|
+
if ((0,external_util_cjs_namespaceObject.some)(decls, (decl) => decl.type === "VariableDeclarator" &&
|
|
7025
7201
|
decl.node.kind === "var" &&
|
|
7026
7202
|
candidates.live &&
|
|
7027
7203
|
(!event.callees ||
|
|
7028
|
-
event.callees.some((callee) => functionMayModify(state, callee, decl)))) {
|
|
7029
|
-
candidates.ant.add(getMod(event,
|
|
7204
|
+
event.callees.some((callee) => functionMayModify(state, callee, decl))))) {
|
|
7205
|
+
candidates.ant.add(getMod(event, decls, candidates.node));
|
|
7030
7206
|
candidates.live = false;
|
|
7031
7207
|
}
|
|
7032
7208
|
});
|
|
@@ -7078,7 +7254,7 @@ function computeAttributes(state, head) {
|
|
|
7078
7254
|
logAntDecls(curState);
|
|
7079
7255
|
}
|
|
7080
7256
|
if (top.preds) {
|
|
7081
|
-
top.preds.forEach((pred) => enqueue(pred));
|
|
7257
|
+
top.preds.forEach((pred) => queue.enqueue(pred));
|
|
7082
7258
|
}
|
|
7083
7259
|
}
|
|
7084
7260
|
const candidateDecls = anticipatedDecls();
|
|
@@ -7832,7 +8008,7 @@ function getLiteralNode(node) {
|
|
|
7832
8008
|
case "-": {
|
|
7833
8009
|
const [arg, type] = getNodeValue(node.argument);
|
|
7834
8010
|
if (type === "Number" || type === "Long") {
|
|
7835
|
-
return replacementLiteral(
|
|
8011
|
+
return replacementLiteral(node, -arg.value, type);
|
|
7836
8012
|
}
|
|
7837
8013
|
}
|
|
7838
8014
|
}
|
|
@@ -7882,8 +8058,14 @@ function isBooleanExpression(state, node) {
|
|
|
7882
8058
|
}
|
|
7883
8059
|
return false;
|
|
7884
8060
|
}
|
|
8061
|
+
function roundToFloat(value) {
|
|
8062
|
+
return new Float32Array([value])[0];
|
|
8063
|
+
}
|
|
7885
8064
|
function replacementLiteral(arg, value, type) {
|
|
7886
|
-
if (
|
|
8065
|
+
if (value === null) {
|
|
8066
|
+
type = "Null";
|
|
8067
|
+
}
|
|
8068
|
+
else if (typeof value === "boolean") {
|
|
7887
8069
|
type = "Boolean";
|
|
7888
8070
|
}
|
|
7889
8071
|
else if (type === "Number") {
|
|
@@ -7892,32 +8074,213 @@ function replacementLiteral(arg, value, type) {
|
|
|
7892
8074
|
else if (type === "Long") {
|
|
7893
8075
|
value = BigInt.asIntN(64, BigInt(value));
|
|
7894
8076
|
}
|
|
8077
|
+
else if (type === "Float") {
|
|
8078
|
+
value = roundToFloat(Number(value));
|
|
8079
|
+
}
|
|
8080
|
+
let raw = type === "String"
|
|
8081
|
+
? JSON.stringify(value)
|
|
8082
|
+
: type === "Char"
|
|
8083
|
+
? value === "'"
|
|
8084
|
+
? "'\\''"
|
|
8085
|
+
: "'" + JSON.stringify(value).slice(1, -1) + "'"
|
|
8086
|
+
: value == null
|
|
8087
|
+
? "null"
|
|
8088
|
+
: value.toString();
|
|
8089
|
+
if (type === "Long") {
|
|
8090
|
+
raw += "l";
|
|
8091
|
+
}
|
|
8092
|
+
else if (type === "Double") {
|
|
8093
|
+
raw += "d";
|
|
8094
|
+
}
|
|
8095
|
+
else if (type === "Float") {
|
|
8096
|
+
if (prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.test(raw)) {
|
|
8097
|
+
raw += "f";
|
|
8098
|
+
}
|
|
8099
|
+
else {
|
|
8100
|
+
const match = raw.match(/^(-)?(\d*)\.(\d+)(e\d+)?/);
|
|
8101
|
+
if (match && match[2].length + match[3].length > 9) {
|
|
8102
|
+
for (let l = 9 - match[2].length; l > 0; l--) {
|
|
8103
|
+
const s = `${match[1] || ""}${match[2]}.${match[3].substring(0, l)}${match[4] || ""}`;
|
|
8104
|
+
if (value !== roundToFloat(parseFloat(s)))
|
|
8105
|
+
break;
|
|
8106
|
+
raw = s;
|
|
8107
|
+
}
|
|
8108
|
+
}
|
|
8109
|
+
}
|
|
8110
|
+
}
|
|
8111
|
+
const { start, end, loc } = arg;
|
|
7895
8112
|
return {
|
|
7896
|
-
|
|
8113
|
+
type: "Literal",
|
|
7897
8114
|
value,
|
|
7898
|
-
raw
|
|
8115
|
+
raw,
|
|
8116
|
+
start,
|
|
8117
|
+
end,
|
|
8118
|
+
loc,
|
|
7899
8119
|
};
|
|
7900
8120
|
}
|
|
8121
|
+
function classify(arg) {
|
|
8122
|
+
switch (arg) {
|
|
8123
|
+
case "Number":
|
|
8124
|
+
return { big: false, int: true };
|
|
8125
|
+
case "Long":
|
|
8126
|
+
return { big: true, int: true };
|
|
8127
|
+
case "Float":
|
|
8128
|
+
return { big: false, int: false };
|
|
8129
|
+
case "Double":
|
|
8130
|
+
return { big: true, int: false };
|
|
8131
|
+
}
|
|
8132
|
+
return null;
|
|
8133
|
+
}
|
|
8134
|
+
function common_arith_types(left, right) {
|
|
8135
|
+
const l = classify(left);
|
|
8136
|
+
if (!l)
|
|
8137
|
+
return null;
|
|
8138
|
+
const r = classify(right);
|
|
8139
|
+
if (!r)
|
|
8140
|
+
return null;
|
|
8141
|
+
if (l.big || r.big) {
|
|
8142
|
+
return l.int && r.int
|
|
8143
|
+
? ["Long", (v) => BigInt.asIntN(64, BigInt(v))]
|
|
8144
|
+
: ["Double", (v) => Number(v)];
|
|
8145
|
+
}
|
|
8146
|
+
else {
|
|
8147
|
+
return l.int && r.int
|
|
8148
|
+
? ["Number", (v) => BigInt.asIntN(32, BigInt(v))]
|
|
8149
|
+
: ["Float", (v) => roundToFloat(Number(v))];
|
|
8150
|
+
}
|
|
8151
|
+
}
|
|
8152
|
+
function common_bitwise_types(left, right) {
|
|
8153
|
+
if (left === "Boolean" && right === "Boolean") {
|
|
8154
|
+
return ["Boolean", (v) => (v ? true : false)];
|
|
8155
|
+
}
|
|
8156
|
+
const l = classify(left);
|
|
8157
|
+
if (!l)
|
|
8158
|
+
return null;
|
|
8159
|
+
const r = classify(right);
|
|
8160
|
+
if (!r)
|
|
8161
|
+
return null;
|
|
8162
|
+
if (!l.int || !r.int)
|
|
8163
|
+
return null;
|
|
8164
|
+
return l.big || r.big
|
|
8165
|
+
? ["Long", (v) => BigInt.asIntN(64, BigInt(v))]
|
|
8166
|
+
: ["Number", (v) => Number(BigInt.asIntN(32, BigInt(v)))];
|
|
8167
|
+
}
|
|
8168
|
+
function plus_types(left, right) {
|
|
8169
|
+
if (left === "String" || right === "String") {
|
|
8170
|
+
// Boolean + String is an error, and
|
|
8171
|
+
// Float/Double + String is legal, but its hard to predict
|
|
8172
|
+
// the way the float will be formatted (and it won't match
|
|
8173
|
+
// what javascript would do by default)
|
|
8174
|
+
if (/Float|Double|Boolean/.test(left + right)) {
|
|
8175
|
+
return null;
|
|
8176
|
+
}
|
|
8177
|
+
return ["String", String];
|
|
8178
|
+
}
|
|
8179
|
+
if (left === "Char" || right === "Char") {
|
|
8180
|
+
if (left === right) {
|
|
8181
|
+
// adding two chars produces a string
|
|
8182
|
+
return ["String", String];
|
|
8183
|
+
}
|
|
8184
|
+
if (/Number|Long/.test(left + right)) {
|
|
8185
|
+
return ["Char", (v) => v];
|
|
8186
|
+
}
|
|
8187
|
+
}
|
|
8188
|
+
return common_arith_types(left, right);
|
|
8189
|
+
}
|
|
8190
|
+
function shift_mod_types(left, right) {
|
|
8191
|
+
const result = common_bitwise_types(left, right);
|
|
8192
|
+
if (result && result[0] === "Boolean") {
|
|
8193
|
+
return null;
|
|
8194
|
+
}
|
|
8195
|
+
return result;
|
|
8196
|
+
}
|
|
8197
|
+
function equalsFn(left, right) {
|
|
8198
|
+
const lt = typeof left;
|
|
8199
|
+
const rt = typeof right;
|
|
8200
|
+
return lt === "string" || rt === "string"
|
|
8201
|
+
? // two string literals will compare unequal, becuase string
|
|
8202
|
+
// equality is object equality.
|
|
8203
|
+
false
|
|
8204
|
+
: (lt === "number" || lt === "bigint") &&
|
|
8205
|
+
(rt === "number" || rt === "bigint")
|
|
8206
|
+
? // numeric types are compared for value equality
|
|
8207
|
+
left == right
|
|
8208
|
+
: // otherwise types and values must match
|
|
8209
|
+
left === right;
|
|
8210
|
+
}
|
|
7901
8211
|
const operators = {
|
|
7902
|
-
"+":
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
"
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
"
|
|
7919
|
-
|
|
7920
|
-
|
|
8212
|
+
"+": {
|
|
8213
|
+
typeFn: plus_types,
|
|
8214
|
+
valueFn: (left, right) => typeof left === "string" && typeof right !== "string"
|
|
8215
|
+
? String.fromCharCode(left.charCodeAt(0) + Number(right))
|
|
8216
|
+
: typeof left !== "string" && typeof right === "string"
|
|
8217
|
+
? String.fromCharCode(right.charCodeAt(0) + Number(left))
|
|
8218
|
+
: left + right,
|
|
8219
|
+
},
|
|
8220
|
+
"-": {
|
|
8221
|
+
typeFn: common_arith_types,
|
|
8222
|
+
valueFn: (left, right) => left - right,
|
|
8223
|
+
},
|
|
8224
|
+
"*": {
|
|
8225
|
+
typeFn: common_arith_types,
|
|
8226
|
+
valueFn: (left, right) => left * right,
|
|
8227
|
+
},
|
|
8228
|
+
"/": {
|
|
8229
|
+
typeFn: common_arith_types,
|
|
8230
|
+
valueFn: (left, right) => left / right,
|
|
8231
|
+
},
|
|
8232
|
+
"%": {
|
|
8233
|
+
typeFn: shift_mod_types,
|
|
8234
|
+
valueFn: (left, right) => left % right,
|
|
8235
|
+
},
|
|
8236
|
+
"&": {
|
|
8237
|
+
typeFn: common_bitwise_types,
|
|
8238
|
+
valueFn: (left, right) => left & right,
|
|
8239
|
+
},
|
|
8240
|
+
"|": {
|
|
8241
|
+
typeFn: common_bitwise_types,
|
|
8242
|
+
valueFn: (left, right) => left | right,
|
|
8243
|
+
},
|
|
8244
|
+
"^": {
|
|
8245
|
+
typeFn: common_bitwise_types,
|
|
8246
|
+
valueFn: (left, right) => left ^ right,
|
|
8247
|
+
},
|
|
8248
|
+
"<<": {
|
|
8249
|
+
typeFn: shift_mod_types,
|
|
8250
|
+
valueFn: (left, right) => typeof right === "bigint"
|
|
8251
|
+
? left << right
|
|
8252
|
+
: left << right,
|
|
8253
|
+
},
|
|
8254
|
+
">>": {
|
|
8255
|
+
typeFn: shift_mod_types,
|
|
8256
|
+
valueFn: (left, right) => typeof right === "bigint"
|
|
8257
|
+
? left >> right
|
|
8258
|
+
: left >> right,
|
|
8259
|
+
},
|
|
8260
|
+
"==": {
|
|
8261
|
+
typeFn: () => ["Boolean", (v) => v],
|
|
8262
|
+
valueFn: equalsFn,
|
|
8263
|
+
},
|
|
8264
|
+
"!=": {
|
|
8265
|
+
typeFn: () => ["Boolean", (v) => v],
|
|
8266
|
+
valueFn: (left, right) => !equalsFn(left, right),
|
|
8267
|
+
},
|
|
8268
|
+
"<=": {
|
|
8269
|
+
typeFn: common_arith_types,
|
|
8270
|
+
valueFn: (left, right) => left <= right,
|
|
8271
|
+
},
|
|
8272
|
+
">=": {
|
|
8273
|
+
typeFn: common_arith_types,
|
|
8274
|
+
valueFn: (left, right) => left >= right,
|
|
8275
|
+
},
|
|
8276
|
+
"<": {
|
|
8277
|
+
typeFn: common_arith_types,
|
|
8278
|
+
valueFn: (left, right) => left < right,
|
|
8279
|
+
},
|
|
8280
|
+
">": {
|
|
8281
|
+
typeFn: common_arith_types,
|
|
8282
|
+
valueFn: (left, right) => left > right,
|
|
8283
|
+
},
|
|
7921
8284
|
as: null,
|
|
7922
8285
|
instanceof: null,
|
|
7923
8286
|
has: null,
|
|
@@ -7930,23 +8293,31 @@ function optimizeNode(state, node) {
|
|
|
7930
8293
|
break;
|
|
7931
8294
|
switch (node.operator) {
|
|
7932
8295
|
case "+":
|
|
7933
|
-
if (type === "Number" ||
|
|
8296
|
+
if (type === "Number" ||
|
|
8297
|
+
type === "Long" ||
|
|
8298
|
+
type === "Float" ||
|
|
8299
|
+
type === "Double" ||
|
|
8300
|
+
type === "Char" ||
|
|
8301
|
+
type === "String") {
|
|
7934
8302
|
return arg;
|
|
7935
8303
|
}
|
|
7936
8304
|
break;
|
|
7937
8305
|
case "-":
|
|
7938
|
-
if (type === "Number" ||
|
|
7939
|
-
|
|
8306
|
+
if (type === "Number" ||
|
|
8307
|
+
type === "Long" ||
|
|
8308
|
+
type === "Float" ||
|
|
8309
|
+
type === "Double") {
|
|
8310
|
+
return replacementLiteral(node, -arg.value, type);
|
|
7940
8311
|
}
|
|
7941
8312
|
break;
|
|
7942
8313
|
case "!":
|
|
7943
8314
|
case "~":
|
|
7944
8315
|
{
|
|
7945
8316
|
if (type === "Number" || type === "Long") {
|
|
7946
|
-
return replacementLiteral(
|
|
8317
|
+
return replacementLiteral(node, ~BigInt(arg.value), type);
|
|
7947
8318
|
}
|
|
7948
8319
|
if (type === "Boolean" && node.operator == "!") {
|
|
7949
|
-
return replacementLiteral(
|
|
8320
|
+
return replacementLiteral(node, !arg.value, type);
|
|
7950
8321
|
}
|
|
7951
8322
|
}
|
|
7952
8323
|
break;
|
|
@@ -7960,23 +8331,13 @@ function optimizeNode(state, node) {
|
|
|
7960
8331
|
const [right, right_type] = getNodeValue(node.right);
|
|
7961
8332
|
if (!left || !right)
|
|
7962
8333
|
break;
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
if (node.operator !== "==" && node.operator !== "!=") {
|
|
7968
|
-
break;
|
|
7969
|
-
}
|
|
7970
|
-
value = operators[node.operator](left.value, right.value);
|
|
7971
|
-
type = "Boolean";
|
|
7972
|
-
}
|
|
7973
|
-
else {
|
|
7974
|
-
type = left_type;
|
|
7975
|
-
value = op(BigInt(left.value), BigInt(right.value));
|
|
7976
|
-
}
|
|
8334
|
+
const type = op.typeFn(left_type, right_type);
|
|
8335
|
+
if (!type)
|
|
8336
|
+
break;
|
|
8337
|
+
const value = op.valueFn(type[1](left.value), type[1](right.value));
|
|
7977
8338
|
if (value === null)
|
|
7978
8339
|
break;
|
|
7979
|
-
return replacementLiteral(
|
|
8340
|
+
return replacementLiteral(node, value, type[0]);
|
|
7980
8341
|
}
|
|
7981
8342
|
break;
|
|
7982
8343
|
}
|
|
@@ -8429,7 +8790,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8429
8790
|
if (!state.currentFunction) {
|
|
8430
8791
|
throw new Error(`Finished function ${state.stack.slice(-1)[0].fullName}, but it was not marked current`);
|
|
8431
8792
|
}
|
|
8432
|
-
state.currentFunction.info = state.currentFunction.next_info;
|
|
8793
|
+
state.currentFunction.info = state.currentFunction.next_info || false;
|
|
8433
8794
|
delete state.currentFunction.next_info;
|
|
8434
8795
|
delete state.currentFunction;
|
|
8435
8796
|
break;
|
|
@@ -8992,7 +9353,7 @@ async function createLocalBarrels(targets, options) {
|
|
|
8992
9353
|
async function generateOptimizedProject(options) {
|
|
8993
9354
|
const config = await getConfig(options);
|
|
8994
9355
|
const workspace = config.workspace;
|
|
8995
|
-
const { manifest, targets, xml,
|
|
9356
|
+
const { manifest, targets, xml, devices, resources, buildDependencies } = await get_jungle(config.jungleFiles, config);
|
|
8996
9357
|
if (xml.body instanceof Error) {
|
|
8997
9358
|
throw xml.body;
|
|
8998
9359
|
}
|
|
@@ -9004,7 +9365,7 @@ async function generateOptimizedProject(options) {
|
|
|
9004
9365
|
xml.body.elements[0].loc || undefined;
|
|
9005
9366
|
throw error;
|
|
9006
9367
|
}
|
|
9007
|
-
const dependencyFiles =
|
|
9368
|
+
const dependencyFiles = Object.keys(resources).concat(Object.keys(buildDependencies));
|
|
9008
9369
|
await createLocalBarrels(targets, config);
|
|
9009
9370
|
const buildConfigs = {};
|
|
9010
9371
|
const products = {};
|
|
@@ -9102,13 +9463,35 @@ async function generateOptimizedProject(options) {
|
|
|
9102
9463
|
relative_manifest = "manifest.xml";
|
|
9103
9464
|
}
|
|
9104
9465
|
const parts = [`project.manifest=${relative_manifest}`];
|
|
9105
|
-
const
|
|
9466
|
+
const map_field = (base, name, mapper = null) => {
|
|
9106
9467
|
const obj = base[name];
|
|
9107
9468
|
if (!obj)
|
|
9108
|
-
return;
|
|
9469
|
+
return null;
|
|
9109
9470
|
const map_one = (s) => (mapper ? mapper(s) : s);
|
|
9110
9471
|
const map = (s) => Array.isArray(s) ? `[${s.map(map_one).join(";")}]` : map_one(s);
|
|
9111
|
-
|
|
9472
|
+
return `${obj.map(map).join(";")}`;
|
|
9473
|
+
};
|
|
9474
|
+
const process_field = (prefix, base, name, mapper = null) => {
|
|
9475
|
+
const mapped = map_field(base, name, mapper);
|
|
9476
|
+
if (!mapped)
|
|
9477
|
+
return;
|
|
9478
|
+
parts.push(`${prefix}${name} = ${mapped}`);
|
|
9479
|
+
};
|
|
9480
|
+
const languagesToInclude = Object.fromEntries((manifestLanguages(xml) || []).map((lang) => [lang, true]));
|
|
9481
|
+
const unsupportedLangsCache = {};
|
|
9482
|
+
let availableDefaults = null;
|
|
9483
|
+
const nextAvailableDefault = () => {
|
|
9484
|
+
if (!availableDefaults) {
|
|
9485
|
+
availableDefaults = Object.keys(Object.values(devices).reduce((m, d) => {
|
|
9486
|
+
m[d.deviceFamily] = true;
|
|
9487
|
+
const match = d.deviceFamily.match(/^(\w+)-\d+x\d+/);
|
|
9488
|
+
if (match)
|
|
9489
|
+
m[match[1]] = true;
|
|
9490
|
+
return m;
|
|
9491
|
+
}, {})).sort();
|
|
9492
|
+
availableDefaults.unshift("base");
|
|
9493
|
+
}
|
|
9494
|
+
return availableDefaults.shift();
|
|
9112
9495
|
};
|
|
9113
9496
|
targets.forEach((target) => {
|
|
9114
9497
|
if (!buildConfigs[configKey(target)])
|
|
@@ -9145,8 +9528,39 @@ async function generateOptimizedProject(options) {
|
|
|
9145
9528
|
process_field(prefix, qualifier, "excludeAnnotations");
|
|
9146
9529
|
const qlang = qualifier.lang;
|
|
9147
9530
|
if (qlang) {
|
|
9531
|
+
const devLang = devices[product].languages;
|
|
9532
|
+
const unsupportedLangs = Object.keys(qlang)
|
|
9533
|
+
.sort()
|
|
9534
|
+
.map((key) => {
|
|
9535
|
+
if ((0,external_api_cjs_namespaceObject.hasProperty)(devLang, key) ||
|
|
9536
|
+
!(0,external_api_cjs_namespaceObject.hasProperty)(languagesToInclude, key)) {
|
|
9537
|
+
return null;
|
|
9538
|
+
}
|
|
9539
|
+
const mapped = map_field(qlang, key, relative_path);
|
|
9540
|
+
if (!mapped)
|
|
9541
|
+
return null;
|
|
9542
|
+
return [key, mapped];
|
|
9543
|
+
})
|
|
9544
|
+
.filter((a) => a != null);
|
|
9545
|
+
let keysToSkip = null;
|
|
9546
|
+
if (unsupportedLangs.length) {
|
|
9547
|
+
const key = JSON.stringify(unsupportedLangs);
|
|
9548
|
+
if (!(0,external_api_cjs_namespaceObject.hasProperty)(unsupportedLangsCache, key)) {
|
|
9549
|
+
const base = nextAvailableDefault();
|
|
9550
|
+
if (base) {
|
|
9551
|
+
unsupportedLangs.forEach(([key, value]) => parts.push(`${base}.lang.${key} = ${value}`));
|
|
9552
|
+
unsupportedLangsCache[key] = `${base}.lang`;
|
|
9553
|
+
}
|
|
9554
|
+
}
|
|
9555
|
+
if ((0,external_api_cjs_namespaceObject.hasProperty)(unsupportedLangsCache, key)) {
|
|
9556
|
+
keysToSkip = Object.fromEntries(unsupportedLangs);
|
|
9557
|
+
parts.push(`${prefix}lang = $(${unsupportedLangsCache[key]})`);
|
|
9558
|
+
}
|
|
9559
|
+
}
|
|
9148
9560
|
Object.keys(qlang).forEach((key) => {
|
|
9149
|
-
|
|
9561
|
+
(0,external_api_cjs_namespaceObject.hasProperty)(keysToSkip, key) ||
|
|
9562
|
+
!(0,external_api_cjs_namespaceObject.hasProperty)(languagesToInclude, key) ||
|
|
9563
|
+
process_field(`${prefix}lang.`, qlang, key, relative_path);
|
|
9150
9564
|
});
|
|
9151
9565
|
}
|
|
9152
9566
|
});
|
|
@@ -9266,7 +9680,7 @@ async function generateOneConfig(buildConfig, manifestXML, dependencyFiles, conf
|
|
|
9266
9680
|
// the oldest optimized file, we don't need to regenerate
|
|
9267
9681
|
const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
|
|
9268
9682
|
const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
|
|
9269
|
-
if (source_time < opt_time &&
|
|
9683
|
+
if (source_time < opt_time && 1672601827382 < opt_time) {
|
|
9270
9684
|
return { hasTests, diagnostics: prevDiagnostics };
|
|
9271
9685
|
}
|
|
9272
9686
|
}
|
|
@@ -9293,7 +9707,7 @@ async function generateOneConfig(buildConfig, manifestXML, dependencyFiles, conf
|
|
|
9293
9707
|
return promises_namespaceObject.writeFile(external_path_.join(output, "build-info.json"), JSON.stringify({
|
|
9294
9708
|
hasTests,
|
|
9295
9709
|
diagnostics,
|
|
9296
|
-
optimizerVersion: "1.0.
|
|
9710
|
+
optimizerVersion: "1.0.45",
|
|
9297
9711
|
...Object.fromEntries(configOptionsToCheck.map((option) => [option, config[option]])),
|
|
9298
9712
|
}))
|
|
9299
9713
|
.then(() => ({ hasTests, diagnostics }));
|