@abaplint/core
Version:
abaplint - Core API
342 lines • 17.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StatementFlow = void 0;
const nodes_1 = require("../nodes");
const Structures = require("../3_structures/structures");
const Statements = require("../2_statements/statements");
const Expressions = require("../2_statements/expressions");
const flow_graph_1 = require("./flow_graph");
const objects_1 = require("../../objects");
const selection_events_1 = require("./selection_events");
const virtual_position_1 = require("../../virtual_position");
class StatementFlow {
constructor() {
this.counter = 0;
}
build(stru, obj) {
var _a, _b, _c, _d;
const ret = [];
let name = "";
const structures = stru.findAllStructuresMulti([
Structures.Form, Structures.ClassImplementation, Structures.FunctionModule, Structures.Module
]);
for (const s of structures) {
if (s.get() instanceof Structures.Form) {
name = "FORM " + ((_a = s.findFirstExpression(Expressions.FormName)) === null || _a === void 0 ? void 0 : _a.concatTokens());
ret.push(this.run(s, name));
}
else if (s.get() instanceof Structures.ClassImplementation) {
const className = (_b = s.findFirstExpression(Expressions.ClassName)) === null || _b === void 0 ? void 0 : _b.concatTokens();
for (const method of s.findDirectStructures(Structures.Method)) {
const methodName = (_c = method.findFirstExpression(Expressions.MethodName)) === null || _c === void 0 ? void 0 : _c.concatTokens();
name = "METHOD " + methodName + ", CLASS " + className;
ret.push(this.run(method, name));
}
}
else if (s.get() instanceof Structures.FunctionModule) {
name = "FUNCTION " + ((_d = s.findFirstExpression(Expressions.Field)) === null || _d === void 0 ? void 0 : _d.concatTokens());
ret.push(this.run(s, name));
}
else if (s.get() instanceof Structures.Module) {
name = s.getFirstStatement().concatTokens().toUpperCase();
ret.push(this.run(s, name));
}
else {
throw new Error("StatementFlow, unknown structure");
}
}
if (obj instanceof objects_1.Program || obj instanceof objects_1.FunctionGroup) {
// find the top level events
let inEvent = false;
let collected = [];
for (const s of stru.getChildren() || []) {
if (selection_events_1.SELECTION_EVENTS.some(f => s.get() instanceof f)) {
if (inEvent === true) {
ret.push(this.runEvent(collected, name));
}
collected = [];
inEvent = true;
name = s.concatTokens();
}
else if (s.get() instanceof Structures.Normal) {
collected.push(s);
}
else {
if (inEvent === true) {
ret.push(this.runEvent(collected, name));
inEvent = false;
}
collected = [];
}
}
if (inEvent === true) {
ret.push(this.runEvent(collected, name));
}
else if (collected.length > 0
&& !(obj instanceof objects_1.FunctionGroup)) {
// implicit START-OF-SELECTION
ret.push(this.runEvent(collected, "START-OF-SELECTION."));
}
}
return ret.map(f => f.reduce());
}
////////////////////
runEvent(s, name) {
this.counter = 1;
const graph = this.traverseBody(s, { procedureEnd: "end#1" });
graph.setLabel(name);
return graph;
}
run(s, name) {
this.counter = 1;
const graph = this.traverseBody(this.findBody(s), { procedureEnd: "end#1" });
graph.setLabel(name);
return graph;
}
findBody(f) {
var _a;
return ((_a = f.findDirectStructure(Structures.Body)) === null || _a === void 0 ? void 0 : _a.getChildren()) || [];
}
// note: it must handle macros and chained statements
static buildName(statement) {
let token = undefined;
const colon = statement.getColon();
if (colon === undefined) {
token = statement.getFirstToken();
}
else {
for (const t of statement.getTokens()) {
if (t.getStart().isAfter(colon.getEnd())) {
token = t;
break;
}
}
}
let extra = "";
const tStart = token === null || token === void 0 ? void 0 : token.getStart();
if (tStart instanceof virtual_position_1.VirtualPosition) {
extra += "$" + tStart.vrow;
extra += "," + tStart.vcol;
}
if (token === undefined) {
return "tokenError";
}
return statement.get().constructor.name +
":" + token.getRow() +
"," + token.getCol() + extra;
}
traverseBody(children, context) {
const graph = new flow_graph_1.FlowGraph(this.counter++);
if (children.length === 0) {
graph.addEdge(graph.getStart(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
return graph;
}
let current = graph.getStart();
for (const c of children) {
if (c.get() instanceof Structures.Normal) {
const firstChild = c.getFirstChild(); // "Normal" only has one child
if (firstChild instanceof nodes_1.StatementNode) {
const name = StatementFlow.buildName(firstChild);
graph.addEdge(current, name, flow_graph_1.FLOW_EDGE_TYPE.undefined);
current = name;
if (firstChild.get() instanceof Statements.Check) {
if (context.loopStart) {
graph.addEdge(name, context.loopStart, flow_graph_1.FLOW_EDGE_TYPE.false);
}
else {
graph.addEdge(name, context.procedureEnd, flow_graph_1.FLOW_EDGE_TYPE.false);
}
}
else if (firstChild.get() instanceof Statements.Assert) {
graph.addEdge(name, context.procedureEnd, flow_graph_1.FLOW_EDGE_TYPE.false);
}
else if (firstChild.get() instanceof Statements.Continue && context.loopStart) {
graph.addEdge(name, context.loopStart, flow_graph_1.FLOW_EDGE_TYPE.undefined);
return graph;
}
else if (firstChild.get() instanceof Statements.Exit) {
if (context.loopEnd) {
graph.addEdge(name, context.loopEnd, flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
else {
graph.addEdge(name, context.procedureEnd, flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
return graph;
}
else if (firstChild.get() instanceof Statements.Return) {
graph.addEdge(name, context.procedureEnd, flow_graph_1.FLOW_EDGE_TYPE.undefined);
return graph;
}
}
else if (firstChild instanceof nodes_1.StructureNode) {
const sub = this.traverseStructure(firstChild, context);
current = graph.addGraph(current, sub, flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
}
}
graph.addEdge(current, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
return graph;
}
traverseStructure(n, context) {
const graph = new flow_graph_1.FlowGraph(this.counter++);
if (n === undefined) {
return graph;
}
let current = graph.getStart();
const type = n.get();
if (type instanceof Structures.If) {
const ifName = StatementFlow.buildName(n.findDirectStatement(Statements.If));
const sub = this.traverseBody(this.findBody(n), context);
graph.addEdge(current, ifName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addGraph(ifName, sub, flow_graph_1.FLOW_EDGE_TYPE.true);
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
current = ifName;
for (const e of n.findDirectStructures(Structures.ElseIf)) {
const elseifst = e.findDirectStatement(Statements.ElseIf);
if (elseifst === undefined) {
continue;
}
const elseIfName = StatementFlow.buildName(elseifst);
const sub = this.traverseBody(this.findBody(e), context);
graph.addEdge(current, elseIfName, flow_graph_1.FLOW_EDGE_TYPE.false);
graph.addGraph(elseIfName, sub, flow_graph_1.FLOW_EDGE_TYPE.true);
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
current = elseIfName;
}
const els = n.findDirectStructure(Structures.Else);
const elsest = els === null || els === void 0 ? void 0 : els.findDirectStatement(Statements.Else);
if (els && elsest) {
const elseName = StatementFlow.buildName(elsest);
const sub = this.traverseBody(this.findBody(els), context);
graph.addEdge(current, elseName, flow_graph_1.FLOW_EDGE_TYPE.false);
graph.addGraph(elseName, sub, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
else {
graph.addEdge(ifName, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.false);
}
}
else if (type instanceof Structures.Loop
|| type instanceof Structures.While
|| type instanceof Structures.With
|| type instanceof Structures.Provide
|| type instanceof Structures.Select
|| type instanceof Structures.EnhancementSection
|| type instanceof Structures.LoopAtScreen
|| type instanceof Structures.Do) {
const loopName = StatementFlow.buildName(n.getFirstStatement());
const sub = this.traverseBody(this.findBody(n), Object.assign(Object.assign({}, context), { loopStart: loopName, loopEnd: graph.getEnd() }));
graph.addEdge(current, loopName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addGraph(loopName, sub, flow_graph_1.FLOW_EDGE_TYPE.true);
graph.addEdge(sub.getEnd(), loopName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(loopName, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.false);
}
else if (type instanceof Structures.Data
|| type instanceof Structures.Constants
|| type instanceof Structures.Statics
|| type instanceof Structures.ExecSQL
|| type instanceof Structures.Types) {
// these doesnt affect control flow, so just take the first statement
const statement = n.getFirstStatement();
const name = StatementFlow.buildName(statement);
graph.addEdge(current, name, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(name, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
else if (type instanceof Structures.TestSeam) {
const name = StatementFlow.buildName(n.getFirstStatement());
const sub = this.traverseBody(this.findBody(n), context);
graph.addEdge(current, name, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addGraph(name, sub, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
else if (type instanceof Structures.AtFirst
|| type instanceof Structures.AtLast
|| type instanceof Structures.At
|| type instanceof Structures.OnChange) {
const name = StatementFlow.buildName(n.getFirstStatement());
const body = this.traverseBody(this.findBody(n), context);
graph.addEdge(current, name, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addGraph(name, body, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(body.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(current, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
else if (type instanceof Structures.Try) {
const tryName = StatementFlow.buildName(n.getFirstStatement());
const body = this.traverseBody(this.findBody(n), context);
graph.addEdge(current, tryName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addGraph(tryName, body, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(body.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
for (const c of n.findDirectStructures(Structures.Catch)) {
const catchName = StatementFlow.buildName(c.getFirstStatement());
const catchBody = this.traverseBody(this.findBody(c), context);
// TODO: this does not take exceptions into account
graph.addEdge(body.getEnd(), catchName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addGraph(catchName, catchBody, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(catchBody.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
// TODO, handle CLEANUP
}
else if (type instanceof Structures.CatchSystemExceptions) {
// TODO: this is not completely correct
const catchName = StatementFlow.buildName(n.getFirstStatement());
const body = this.traverseBody(this.findBody(n), context);
graph.addEdge(current, catchName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addGraph(catchName, body, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(body.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
else if (type instanceof Structures.Case) {
const caseName = StatementFlow.buildName(n.getFirstStatement());
graph.addEdge(current, caseName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
let othersFound = false;
for (const w of n.findDirectStructures(Structures.When)) {
const first = w.getFirstStatement();
if (first === undefined) {
continue;
}
if (first.get() instanceof Statements.WhenOthers) {
othersFound = true;
}
const firstName = StatementFlow.buildName(first);
const sub = this.traverseBody(this.findBody(w), context);
graph.addEdge(caseName, firstName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addGraph(firstName, sub, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
if (othersFound === false) {
graph.addEdge(caseName, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
}
else if (type instanceof Structures.CaseType) {
const caseName = StatementFlow.buildName(n.getFirstStatement());
graph.addEdge(current, caseName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
let othersFound = false;
for (const w of n.findDirectStructures(Structures.WhenType)) {
const first = w.getFirstStatement();
if (first === undefined) {
continue;
}
if (first.get() instanceof Statements.WhenOthers) {
othersFound = true;
}
const firstName = StatementFlow.buildName(first);
const sub = this.traverseBody(this.findBody(w), context);
graph.addEdge(caseName, firstName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addGraph(firstName, sub, flow_graph_1.FLOW_EDGE_TYPE.undefined);
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
if (othersFound === false) {
graph.addEdge(caseName, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
}
}
else if (type instanceof Structures.Define
|| type instanceof Structures.TestInjection) {
// do nothing
}
else {
console.dir("StatementFlow,todo, " + n.get().constructor.name);
}
return graph;
}
}
exports.StatementFlow = StatementFlow;
//# sourceMappingURL=statement_flow.js.map