@gracexwho/model-card-generator
Version:
Tool for generating model cards for Jupyter Notebook.
548 lines • 26.2 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./python-parser", "./set", "./printNode"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ControlFlowGraph = exports.Block = void 0;
var ast = require("./python-parser");
var set_1 = require("./set");
var printNode_1 = require("./printNode");
var Block = /** @class */ (function () {
function Block(id, hint, statements, loopVariables) {
if (loopVariables === void 0) { loopVariables = []; }
this.id = id;
this.hint = hint;
this.statements = statements;
this.loopVariables = loopVariables;
}
Block.prototype.toString = function () {
return ("BLOCK " + this.id + " (" + this.hint + ")\n" +
this.statements
.map(function (s) { return s.location.first_line + ": " + printNode_1.printNode(s); })
.join('\n'));
};
return Block;
}());
exports.Block = Block;
var BlockSet = /** @class */ (function (_super) {
__extends(BlockSet, _super);
function BlockSet() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
return _super.apply(this, __spreadArrays([function (b) { return b.id.toString(); }], items)) || this;
}
return BlockSet;
}(set_1.Set));
var Context = /** @class */ (function () {
function Context(loopHead, loopExit, exceptionBlock) {
this.loopHead = loopHead;
this.loopExit = loopExit;
this.exceptionBlock = exceptionBlock;
}
Context.prototype.forLoop = function (loopHead, loopExit) {
return new Context(loopHead, loopExit, this.exceptionBlock);
};
Context.prototype.forExcepts = function (exceptionBlock) {
return new Context(this.loopHead, this.loopExit, exceptionBlock);
};
return Context;
}());
var ControlFlowGraph = /** @class */ (function () {
function ControlFlowGraph(node) {
var _a;
this._blocks = [];
this.globalId = 0;
this.successors = new set_1.Set(function (_a) {
var b1 = _a[0], b2 = _a[1];
return b1.id + ',' + b2.id;
});
this.loopVariables = [];
this.postdominators = new PostdominatorSet();
if (!node) {
throw 'argument undefined';
}
var statements = [];
if (node.type == ast.MODULE) {
statements = Array.isArray(node.code) ? node.code : [node.code];
}
else if (node.type == ast.DEF) {
statements = Array.isArray(node.code) ? node.code : [node.code];
}
_a = this.makeCFG('entry', statements, new Context(null, null, this.makeBlock('exceptional exit'))), this.entry = _a[0], this.exit = _a[1];
}
ControlFlowGraph.prototype.makeBlock = function (hint, statements) {
if (statements === void 0) { statements = []; }
var b = new Block(this.globalId++, hint, statements);
if (this.loopVariables.length) {
b.loopVariables = this.loopVariables[this.loopVariables.length - 1];
}
this._blocks.push(b);
return b;
};
Object.defineProperty(ControlFlowGraph.prototype, "blocks", {
get: function () {
var visited = [];
var toVisit = new BlockSet(this.entry);
var _loop_1 = function () {
var block = toVisit.take();
visited.push(block);
this_1.successors.items.forEach(function (_a) {
var pred = _a[0], succ = _a[1];
if (pred === block && visited.indexOf(succ) < 0) {
toVisit.add(succ);
}
});
};
var this_1 = this;
while (toVisit.size) {
_loop_1();
}
return visited;
},
enumerable: false,
configurable: true
});
ControlFlowGraph.prototype.getSuccessors = function (block) {
return this.successors.items
.filter(function (_a) {
var p = _a[0], _ = _a[1];
return p == block;
})
.map(function (_a) {
var _ = _a[0], s = _a[1];
return s;
});
};
ControlFlowGraph.prototype.getPredecessors = function (block) {
return this.successors.items
.filter(function (_a) {
var _ = _a[0], s = _a[1];
return s == block;
})
.map(function (_a) {
var p = _a[0], _ = _a[1];
return p;
});
};
ControlFlowGraph.prototype.print = function () {
var _this = this;
console.log('CFG', 'ENTRY:', this.entry.id, 'EXIT:', this.exit.id);
this.blocks.forEach(function (block) {
console.log(block.toString());
if (block === _this.exit) {
console.log(' EXIT');
}
else {
console.log(' SUCC', _this.getSuccessors(block)
.map(function (b) { return b.id.toString(); })
.join(','));
}
});
};
ControlFlowGraph.prototype.link = function () {
var blocks = [];
for (var _i = 0; _i < arguments.length; _i++) {
blocks[_i] = arguments[_i];
}
for (var i = 1; i < blocks.length; i++)
this.successors.add([blocks[i - 1], blocks[i]]);
};
ControlFlowGraph.prototype.handleIf = function (statement, last, context) {
var _this = this;
var ifCondBlock = this.makeBlock('if cond', [statement.cond]);
if (!statement.code)
console.log(statement);
var _a = this.makeCFG('if body', statement.code, context), bodyEntry = _a[0], bodyExit = _a[1];
this.link(last, ifCondBlock);
this.link(ifCondBlock, bodyEntry);
var joinBlock = this.makeBlock('conditional join');
this.link(bodyExit, joinBlock);
var lastCondBlock = ifCondBlock;
if (statement.elif) {
statement.elif.forEach(function (elif) {
var elifCondBlock = _this.makeBlock('elif cond', [elif.cond]);
_this.link(lastCondBlock, elifCondBlock);
var _a = _this.makeCFG('elif body', elif.code, context), elifEntry = _a[0], elifExit = _a[1];
_this.link(elifCondBlock, elifEntry);
_this.link(elifExit, joinBlock);
lastCondBlock = elifCondBlock;
});
}
if (statement.else) {
var elseStmt = statement.else;
if (elseStmt.code && elseStmt.code.length) {
// XXX: 'Else' isn't *really* a condition, though we're treating it like it is
// so we can mark a dependence between the body of the else and its header.
var elseCondBlock = this.makeBlock('else cond', [elseStmt]);
this.link(lastCondBlock, elseCondBlock);
var _b = this.makeCFG('else body', elseStmt.code, context), elseEntry = _b[0], elseExit = _b[1];
this.link(elseCondBlock, elseEntry);
this.link(elseExit, joinBlock);
lastCondBlock = elseCondBlock;
}
}
this.link(lastCondBlock, joinBlock);
return joinBlock;
};
ControlFlowGraph.prototype.handleWhile = function (statement, last, context) {
var loopHeadBlock = this.makeBlock('while loop head', [statement.cond]);
this.link(last, loopHeadBlock);
var afterLoop = this.makeBlock('while loop join');
this.loopVariables.push([statement.cond]);
var _a = this.makeCFG('while body', statement.code, context.forLoop(loopHeadBlock, afterLoop)), bodyEntry = _a[0], bodyExit = _a[1];
this.loopVariables.pop();
this.link(loopHeadBlock, bodyEntry);
this.link(bodyExit, loopHeadBlock); // back edge
this.link(loopHeadBlock, afterLoop);
return afterLoop;
};
ControlFlowGraph.prototype.handleFor = function (statement, last, context) {
var loopHeadBlock = this.makeBlock('for loop head',
// synthesize a statement to simulate using the iterator
[
{
type: ast.ASSIGN,
op: undefined,
sources: statement.iter,
targets: statement.target,
location: statement.decl_location,
},
]);
this.link(last, loopHeadBlock);
var afterLoop = this.makeBlock('for loop join');
this.loopVariables.push(statement.target);
var _a = this.makeCFG('for body', statement.code, context.forLoop(loopHeadBlock, afterLoop)), bodyEntry = _a[0], bodyExit = _a[1];
this.loopVariables.pop();
this.link(loopHeadBlock, bodyEntry);
this.link(bodyExit, loopHeadBlock); // back edge
this.link(loopHeadBlock, afterLoop);
return afterLoop;
};
ControlFlowGraph.prototype.handleWith = function (statement, last, context) {
var assignments = statement.items.map(function (_a) {
var w = _a.with, a = _a.as;
return ({
type: ast.ASSIGN,
targets: [a],
sources: [w],
location: w.location,
});
});
var resourceBlock = this.makeBlock('with', assignments);
this.link(last, resourceBlock);
var _a = this.makeCFG('with body', statement.code, context), bodyEntry = _a[0], bodyExit = _a[1];
this.link(resourceBlock, bodyEntry);
return bodyExit;
};
ControlFlowGraph.prototype.handleTry = function (statement, last, context) {
var _this = this;
var afterTry = this.makeBlock('try join');
var exnContext = context;
var handlerExits = [];
var handlerHead = undefined;
if (statement.excepts) {
handlerHead = this.makeBlock('handlers');
var handlerCfgs = statement.excepts.map(function (handler) {
return _this.makeCFG('handler body', handler.code, context);
});
handlerCfgs.forEach(function (_a) {
var exceptEntry = _a[0], _ = _a[1];
return _this.link(handlerHead, exceptEntry);
});
exnContext = context.forExcepts(handlerHead);
handlerExits = handlerCfgs.map(function (_a) {
var _ = _a[0], exceptExit = _a[1];
return exceptExit;
});
}
var _a = this.makeCFG('try body', statement.code, exnContext), bodyEntry = _a[0], bodyExit = _a[1];
this.link(last, bodyEntry);
var normalExit = bodyExit;
if (handlerHead) {
this.link(bodyExit, handlerHead);
}
if (statement.else) {
var _b = this.makeCFG('try else body', statement.else, context), elseEntry = _b[0], elseExit = _b[1];
this.link(normalExit, elseEntry);
normalExit = elseExit;
}
if (statement.finally) {
var _c = this.makeCFG('finally body', statement.finally, context), finallyEntry_1 = _c[0], finallyExit = _c[1];
this.link(normalExit, finallyEntry_1);
this.link(finallyExit, afterTry);
handlerExits.forEach(function (handlerExit) { return _this.link(handlerExit, finallyEntry_1); });
}
else {
handlerExits.forEach(function (handlerExit) { return _this.link(handlerExit, afterTry); });
this.link(normalExit, afterTry);
}
return afterTry;
};
ControlFlowGraph.prototype.makeCFG = function (hint, statements, context) {
var _this = this;
if (!hint) {
throw 'hint undefined';
}
if (!statements) {
throw 'statements undefined';
}
if (!context) {
throw 'context undefined';
}
var entry = this.makeBlock(hint);
var last = entry;
statements.forEach(function (statement) {
switch (statement.type) {
case ast.IF:
last = _this.handleIf(statement, last, context);
break;
case ast.WHILE:
last = _this.handleWhile(statement, last, context);
break;
case ast.FOR:
last = _this.handleFor(statement, last, context);
break;
case ast.WITH:
last = _this.handleWith(statement, last, context);
break;
case ast.TRY:
last = _this.handleTry(statement, last, context);
break;
case ast.RAISE:
_this.link(last, context.exceptionBlock);
return;
case ast.BREAK:
_this.link(last, context.loopExit);
return;
case ast.CONTINUE:
_this.link(last, context.loopHead);
return;
case ast.DEF:
case ast.CLASS:
default:
last.statements.push(statement);
break;
}
});
return [entry, last];
};
/**
* Based on the algorithm in "Engineering a Compiler", 2nd ed., Cooper and Torczon:
* - p479: computing dominance
* - p498-500: dominator trees and frontiers
* - p544: postdominance and reverse dominance frontier
*/
ControlFlowGraph.prototype.visitControlDependencies = function (visit) {
var blocks = this.blocks;
this.postdominators = this.findPostdominators(blocks);
this.immediatePostdominators = this.getImmediatePostdominators(this.postdominators.items);
this.reverseDominanceFrontiers = this.buildReverseDominanceFrontiers(blocks);
// Mine the dependencies.
for (var _i = 0, blocks_1 = blocks; _i < blocks_1.length; _i++) {
var block = blocks_1[_i];
if (this.reverseDominanceFrontiers.hasOwnProperty(block.id)) {
var frontier = this.reverseDominanceFrontiers[block.id];
for (var _a = 0, _b = frontier.items; _a < _b.length; _a++) {
var frontierBlock = _b[_a];
for (var _c = 0, _d = frontierBlock.statements; _c < _d.length; _c++) {
var controlStmt = _d[_c];
for (var _e = 0, _f = block.statements; _e < _f.length; _e++) {
var stmt = _f[_e];
visit(controlStmt, stmt);
}
}
}
}
}
};
ControlFlowGraph.prototype.postdominatorExists = function (block, postdominator) {
return (this.postdominators.filter(function (p) { return p.block == block && p.postdominator == postdominator; }).size > 0);
};
ControlFlowGraph.prototype.getImmediatePostdominator = function (block) {
var immediatePostdominators = this.immediatePostdominators.items.filter(function (p) { return p.block == block; });
return immediatePostdominators[0];
};
ControlFlowGraph.prototype.findPostdominators = function (blocks) {
var _this = this;
// Initially, every block has every other block as a postdominator, except for the last block.
var postdominators = {};
for (var _i = 0, blocks_2 = blocks; _i < blocks_2.length; _i++) {
var block = blocks_2[_i];
postdominators[block.id] = new PostdominatorSet();
for (var _a = 0, blocks_3 = blocks; _a < blocks_3.length; _a++) {
var otherBlock = blocks_3[_a];
var distance = block.id == otherBlock.id ? 0 : Infinity;
postdominators[block.id].add(new Postdominator(distance, block, otherBlock));
}
}
var lastBlock = blocks.filter(function (b) { return _this.getSuccessors(b).length == 0; })[0];
postdominators[lastBlock.id] = new PostdominatorSet(new Postdominator(0, lastBlock, lastBlock));
var changed = true;
while (changed == true) {
changed = false;
var _loop_2 = function (block) {
if (block == lastBlock)
return "continue";
var oldPostdominators = postdominators[block.id];
var successors = this_2.getSuccessors(block);
// Merge postdominators that appear in all of a block's successors.
var newPostdominators = new (PostdominatorSet.bind.apply(PostdominatorSet, __spreadArrays([void 0], []
.concat.apply([], successors.map(function (s) { return postdominators[s.id].items; })).reduce(function (pCounts, p) {
var countIndex = pCounts.findIndex(function (record) {
return record.p.postdominator == p.postdominator;
});
var countRecord;
if (countIndex == -1) {
countRecord = {
p: new Postdominator(p.distance + 1, block, p.postdominator),
count: 0,
};
pCounts.push(countRecord);
}
else {
countRecord = pCounts[countIndex];
pCounts[countIndex].p.distance = Math.min(pCounts[countIndex].p.distance, p.distance + 1);
}
countRecord.count++;
return pCounts;
}, [])
.filter(function (p) {
return p.count == successors.length;
})
.map(function (p) {
return p.p;
}))))();
// A block always postdominates itself.
newPostdominators.add(new Postdominator(0, block, block));
if (!oldPostdominators.equals(newPostdominators)) {
postdominators[block.id] = newPostdominators;
changed = true;
}
};
var this_2 = this;
for (var _b = 0, blocks_4 = blocks; _b < blocks_4.length; _b++) {
var block = blocks_4[_b];
_loop_2(block);
}
}
var result = new PostdominatorSet();
Object.keys(postdominators).forEach(function (blockId) {
result = result.union(postdominators[blockId]);
});
return result;
};
ControlFlowGraph.prototype.getImmediatePostdominators = function (postdominators) {
var postdominatorsByBlock = postdominators
.filter(function (p) { return p.block != p.postdominator; })
.reduce(function (dict, postdominator) {
if (!dict.hasOwnProperty(postdominator.block.id)) {
dict[postdominator.block.id] = [];
}
dict[postdominator.block.id].push(postdominator);
return dict;
}, {});
var immediatePostdominators = [];
Object.keys(postdominatorsByBlock).forEach(function (blockId) {
immediatePostdominators.push(postdominatorsByBlock[blockId].sort(function (a, b) {
return a.distance - b.distance;
})[0]);
});
return new (PostdominatorSet.bind.apply(PostdominatorSet, __spreadArrays([void 0], immediatePostdominators)))();
};
ControlFlowGraph.prototype.buildReverseDominanceFrontiers = function (blocks) {
var frontiers = {};
var _loop_3 = function (block) {
var successors = this_3.getSuccessors(block);
if (successors.length > 1) {
var workQueue_1 = successors;
var scheduled_1 = [];
var blockImmediatePostdominator = this_3.getImmediatePostdominator(block);
while (workQueue_1.length > 0) {
var item = workQueue_1.pop();
// A branch's successor might be a join point. These aren't dependencies.
if (this_3.postdominatorExists(block, item))
continue;
if (!frontiers.hasOwnProperty(item.id)) {
frontiers[item.id] = new BlockSet();
}
var frontier = frontiers[item.id];
frontier.add(block);
var immediatePostdominator = this_3.getImmediatePostdominator(item);
if (immediatePostdominator.postdominator !=
blockImmediatePostdominator.postdominator) {
this_3.getSuccessors(item).forEach(function (b) {
if (scheduled_1.indexOf(b) == -1) {
scheduled_1.push(b);
workQueue_1.push(b);
}
});
}
}
}
};
var this_3 = this;
for (var _i = 0, blocks_5 = blocks; _i < blocks_5.length; _i++) {
var block = blocks_5[_i];
_loop_3(block);
}
return frontiers;
};
return ControlFlowGraph;
}());
exports.ControlFlowGraph = ControlFlowGraph;
/**
* A block and another block that postdominates it. Distance is the length of the longest path
* from the block to its postdominator.
*/
var Postdominator = /** @class */ (function () {
function Postdominator(distance, block, postdominator) {
this.distance = distance;
this.block = block;
this.postdominator = postdominator;
}
return Postdominator;
}());
/**
* A set of postdominators
*/
var PostdominatorSet = /** @class */ (function (_super) {
__extends(PostdominatorSet, _super);
function PostdominatorSet() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
return _super.apply(this, __spreadArrays([function (p) { return p.block.id + ',' + p.postdominator.id; }], items)) || this;
}
return PostdominatorSet;
}(set_1.Set));
});
//# sourceMappingURL=control-flow.js.map