UNPKG

@abaplint/core

Version:
342 lines • 17.5 kB
"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