rework FlowGraph
This commit is contained in:
@ -1,21 +1,36 @@
|
||||
package codegen.flowgraph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FlowBasicBlock implements Iterable<FlowInstruction> {
|
||||
|
||||
// Graph structure information
|
||||
private final String id;
|
||||
private final String label;
|
||||
private final List<FlowInstruction> instructions;
|
||||
private final Set<FlowBasicBlock> predecessors;
|
||||
private final Set<FlowBasicBlock> successors;
|
||||
|
||||
/**
|
||||
* Das Label ist das Jump-Label, über welches ein Block angesprungen werden kann.
|
||||
*/
|
||||
private final String label;
|
||||
|
||||
/**
|
||||
* Alle Instructions, welche zu einem Block gehören.
|
||||
* Diese werden immer sequentiell ohne Verzweigungen ausgeführt.
|
||||
*/
|
||||
private final List<FlowInstruction> instructions;
|
||||
|
||||
/**
|
||||
* Wird intern benutzt um die ID für eine hinzugefügte {@link FlowInstruction} zu ermitteln.
|
||||
*/
|
||||
private int instNr;
|
||||
|
||||
public FlowBasicBlock(String label) {
|
||||
@ -38,6 +53,8 @@ public class FlowBasicBlock implements Iterable<FlowInstruction> {
|
||||
return this.instructions.isEmpty() && this.label.isBlank();
|
||||
}
|
||||
|
||||
// Geteter, Setter
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
@ -48,84 +65,105 @@ public class FlowBasicBlock implements Iterable<FlowInstruction> {
|
||||
|
||||
public void addInstruction(String instruction, String... args) {
|
||||
this.instNr++;
|
||||
this.instructions.add(new FlowInstruction(String.valueOf(Long.parseLong(this.id) + this.instNr), this.id,
|
||||
this.instructions.add(new FlowInstruction(String.valueOf(Long.parseLong(this.id) + this.instNr),
|
||||
instruction, args));
|
||||
}
|
||||
|
||||
public void addSuccessor(FlowBasicBlock block) {
|
||||
this.successors.add(block);
|
||||
public Set<FlowBasicBlock> getBlockSuccessorSet() {
|
||||
return Collections.unmodifiableSet(this.successors);
|
||||
}
|
||||
|
||||
public void addPredecessor(FlowBasicBlock block) {
|
||||
this.predecessors.add(block);
|
||||
}
|
||||
|
||||
public Set<FlowBasicBlock> getSuccessorSet() {
|
||||
return this.successors;
|
||||
}
|
||||
|
||||
public Set<FlowInstruction> getPredecessors(FlowInstruction inst) {
|
||||
/**
|
||||
* Ermittelt alle Instructions, welche auf eine {@link FlowInstruction} folgen können.
|
||||
* Befindet sich die Instruction am Ende des Blockes, werden Instructions aus Successor-Blöcken gesucht.
|
||||
*/
|
||||
public Set<FlowInstruction> getInstructionSuccessorSet(FlowInstruction inst) {
|
||||
final int index = this.instructions.indexOf(inst);
|
||||
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (index > 0 && index <= this.instructions.size() - 1) {
|
||||
// Instruction is in the middle or end
|
||||
|
||||
return Set.of(this.instructions.get(index - 1));
|
||||
}
|
||||
|
||||
// Instruction is at the beginning
|
||||
return this.predecessors.stream()
|
||||
.map(FlowBasicBlock::getLastInst)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
public Set<FlowInstruction> getSuccessors(FlowInstruction inst) {
|
||||
final int index = this.instructions.indexOf(inst);
|
||||
|
||||
if (index == -1) {
|
||||
return null;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
if (index >= 0 && index < this.instructions.size() - 1) {
|
||||
// Instruction is in the beginning or middle
|
||||
// Instruction is in the beginning or in the middle
|
||||
|
||||
return Set.of(this.instructions.get(index + 1));
|
||||
}
|
||||
|
||||
// Instruction is at the end
|
||||
return this.successors.stream()
|
||||
.map(FlowBasicBlock::getFirstInst)
|
||||
.filter(Objects::nonNull)
|
||||
.map(FlowBasicBlock::getFirstInstruction)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
public Set<FlowBasicBlock> getPredecessorSet() {
|
||||
return this.predecessors;
|
||||
public boolean addSuccessorBlock(FlowBasicBlock successor) {
|
||||
return this.successors.add(successor);
|
||||
}
|
||||
|
||||
public FlowInstruction getFirstInst() {
|
||||
if (!this.instructions.isEmpty()) {
|
||||
return this.instructions.get(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
public boolean removeSuccessorBlock(FlowBasicBlock successor) {
|
||||
return this.successors.remove(successor);
|
||||
}
|
||||
|
||||
public FlowInstruction getLastInst() {
|
||||
if (!this.instructions.isEmpty()) {
|
||||
return this.instructions.get(this.instructions.size() - 1);
|
||||
public Set<FlowBasicBlock> getBlockPredecessorSet() {
|
||||
return Collections.unmodifiableSet(this.predecessors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ermittelt alle Instructions, welche Predecessor einer {@link FlowInstruction} sein können.
|
||||
* Befindet sich die Instruction am Anfang des Blockes, werden Instructions aus Predecessor-Blöcken gesucht.
|
||||
*/
|
||||
public Set<FlowInstruction> getInstructionPredecessorSet(FlowInstruction inst) {
|
||||
final int index = this.instructions.indexOf(inst);
|
||||
|
||||
if (index == -1) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
return null;
|
||||
if (index > 0 && index <= this.instructions.size() - 1) {
|
||||
// Instruction is in the middle or at the end
|
||||
|
||||
return Set.of(this.instructions.get(index - 1));
|
||||
}
|
||||
|
||||
// Instruction is at the beginning
|
||||
return this.predecessors.stream()
|
||||
.map(FlowBasicBlock::getLastInstruction)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
public boolean addPredecessorBlock(FlowBasicBlock predecessor) {
|
||||
return this.predecessors.add(predecessor);
|
||||
}
|
||||
|
||||
public boolean removePredecessorBlock(FlowBasicBlock predecessor) {
|
||||
return this.predecessors.remove(predecessor);
|
||||
}
|
||||
|
||||
public Optional<FlowInstruction> getFirstInstruction() {
|
||||
if (this.instructions.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(this.instructions.get(0));
|
||||
}
|
||||
|
||||
public Optional<FlowInstruction> getLastInstruction() {
|
||||
if (this.instructions.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(this.instructions.get(this.instructions.size() - 1));
|
||||
}
|
||||
|
||||
// Printing
|
||||
|
||||
/**
|
||||
* Diese Methode ist für das Printen mit Graphviz, {@link #toString()} für den Rest.
|
||||
*/
|
||||
public String printInst() {
|
||||
return this.instructions.stream()
|
||||
.map(inst -> inst.toString().trim() + "\\n")
|
||||
@ -139,16 +177,19 @@ public class FlowBasicBlock implements Iterable<FlowInstruction> {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.id);
|
||||
return Objects.hash(this.id, this.label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof FlowBasicBlock) {
|
||||
return this.id.equals(((FlowBasicBlock) obj).id);
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (o == null || this.getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final FlowBasicBlock that = (FlowBasicBlock) o;
|
||||
return this.id.equals(that.id) && this.label.equals(that.label);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,53 +1,79 @@
|
||||
package codegen.flowgraph;
|
||||
|
||||
import parser.ast.AST;
|
||||
import util.GraphvizCaller;
|
||||
import util.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Die Graph-Repräsentation des Programm, erzeugt aus einem {@link AST}.
|
||||
* Der Grundbaustein ist {@link FlowBasicBlock}, diese enthalten wiederum {@link FlowInstruction}.
|
||||
*/
|
||||
public class FlowGraph implements Iterable<FlowBasicBlock> {
|
||||
|
||||
private final FlowGraphHead head;
|
||||
private final List<FlowBasicBlock> basicBlocks;
|
||||
private final FlowGraphTail tail;
|
||||
|
||||
// If a new block has this label, the value in this map is a predecessor
|
||||
// Only for Export to Jasmin-Assembler
|
||||
private final FlowGraphHead exportHead;
|
||||
private final FlowGraphTail exportTail;
|
||||
|
||||
/**
|
||||
* Wenn ein neuer Block ein Label bekommt, welches in der Predecessor-Map vorhanden ist,
|
||||
* dann ist der hier gespeicherte Block ein Predecessor des neuen Blockes.
|
||||
* <p>
|
||||
* Einträge werden hier hinzugefügt, wenn ein Jump nach vorne passiert.
|
||||
* In diesem Fall ist der Jump-Successor noch nicht im Graph präsent.
|
||||
*/
|
||||
private final Map<String, FlowBasicBlock> predecessorMap;
|
||||
|
||||
public FlowGraph(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) {
|
||||
this.head = new FlowGraphHead(bytecodeVersion, source, clazz, stackSize, localCount);
|
||||
this.exportHead = new FlowGraphHead(bytecodeVersion, source, clazz, stackSize, localCount);
|
||||
this.basicBlocks = new ArrayList<>();
|
||||
this.tail = new FlowGraphTail();
|
||||
this.exportTail = new FlowGraphTail();
|
||||
this.predecessorMap = new HashMap<>();
|
||||
}
|
||||
|
||||
// Label marks beginning of block
|
||||
/**
|
||||
* Ein Label markiert den Beginn eines neuen Blockes.
|
||||
* Es werden Predecessor/Successor-Verbindungen zum letzten Block
|
||||
* und zu Blöcken aus der {@link #predecessorMap} hergestellt.
|
||||
*/
|
||||
public void addLabel(String label) {
|
||||
final FlowBasicBlock newBlock = new FlowBasicBlock(label);
|
||||
|
||||
// Resolve missing successors/predecessors from jumps
|
||||
if (this.predecessorMap.containsKey(label)) {
|
||||
this.predecessorMap.get(label).addSuccessor(newBlock);
|
||||
newBlock.addPredecessor(this.predecessorMap.get(label));
|
||||
this.predecessorMap.get(label).addSuccessorBlock(newBlock);
|
||||
newBlock.addPredecessorBlock(this.predecessorMap.get(label));
|
||||
}
|
||||
|
||||
newBlock.addPredecessor(this.getCurrentBlock()); // Obvious predecessor of new block
|
||||
this.getCurrentBlock().addSuccessor(newBlock); // Obvious successor of current block
|
||||
final Optional<FlowBasicBlock> currentBlock = this.getCurrentBlock();
|
||||
if (currentBlock.isPresent()) {
|
||||
newBlock.addPredecessorBlock(currentBlock.get()); // Obvious predecessor of new block
|
||||
currentBlock.get().addSuccessorBlock(newBlock); // Obvious successor of current block
|
||||
}
|
||||
|
||||
this.basicBlocks.add(newBlock);
|
||||
}
|
||||
|
||||
// Jump means end of block
|
||||
/**
|
||||
* Ein Jump markiert das Ende eines Blockes.
|
||||
* Es werden Predecessor/Successor-Verbindungen zum letzten Block hergestellt
|
||||
* und wenn nötig Einträge in der {@link #predecessorMap} angelegt.
|
||||
* <p>
|
||||
* Da GoTo immer springt, wird diese Sprunganweisung gesondert betrachtet.
|
||||
*
|
||||
* @param jumpInstruction Der verwendete Sprungbefehl.
|
||||
*/
|
||||
public void addJump(String jumpInstruction, String label) {
|
||||
this.addInstruction(jumpInstruction, label);
|
||||
|
||||
@ -56,21 +82,30 @@ public class FlowGraph implements Iterable<FlowBasicBlock> {
|
||||
if (!"goto".equals(jumpInstruction)) {
|
||||
// Goto always jumps, so we don't have a direct relation in order of the code
|
||||
|
||||
newBlock.addPredecessor(this.getCurrentBlock()); // Obvious predecessor of new block
|
||||
this.getCurrentBlock().addSuccessor(newBlock); // Obvious successor of current block
|
||||
final Optional<FlowBasicBlock> currentBlock = this.getCurrentBlock();
|
||||
if (currentBlock.isPresent()) {
|
||||
|
||||
newBlock.addPredecessorBlock(currentBlock.get()); // Obvious predecessor of new block
|
||||
currentBlock.get().addSuccessorBlock(newBlock); // Obvious successor of current block
|
||||
}
|
||||
}
|
||||
|
||||
// Jumped successor
|
||||
final FlowBasicBlock labelBlock = this.getBlockByLabel(label);
|
||||
if (labelBlock != null) {
|
||||
final Optional<FlowBasicBlock> labelBlock = this.getBlockByLabel(label);
|
||||
final Optional<FlowBasicBlock> currentBlock = this.getCurrentBlock();
|
||||
|
||||
if (labelBlock.isPresent()) {
|
||||
// Successor exists
|
||||
|
||||
this.getCurrentBlock().addSuccessor(labelBlock);
|
||||
labelBlock.addPredecessor(this.getCurrentBlock());
|
||||
if (currentBlock.isPresent()) {
|
||||
currentBlock.get().addSuccessorBlock(labelBlock.get());
|
||||
labelBlock.get().addPredecessorBlock(currentBlock.get());
|
||||
}
|
||||
} else {
|
||||
// Successor doesn't exist, so wait until it does
|
||||
|
||||
this.predecessorMap.put(label, this.getCurrentBlock()); // Current node is predecessor of label-block
|
||||
// Current node is predecessor of label-block
|
||||
currentBlock.ifPresent(flowBasicBlock -> this.predecessorMap.put(label, flowBasicBlock));
|
||||
}
|
||||
|
||||
this.basicBlocks.add(newBlock);
|
||||
@ -81,16 +116,20 @@ public class FlowGraph implements Iterable<FlowBasicBlock> {
|
||||
this.basicBlocks.add(new FlowBasicBlock("START")); // First block doesn't exist
|
||||
}
|
||||
|
||||
this.getCurrentBlock().addInstruction(instruction, args); // Add to last block
|
||||
final Optional<FlowBasicBlock> currentBlock = this.getCurrentBlock();
|
||||
|
||||
// Add to last block
|
||||
currentBlock.ifPresent(flowBasicBlock -> flowBasicBlock.addInstruction(instruction, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Entfernt leere Blöcke.
|
||||
* Ein Block ist "leer", wenn er kein Label und keine Instructions hat.
|
||||
*/
|
||||
public void purgeEmptyBlocks() {
|
||||
Logger.log("\nPurging empty blocks: ");
|
||||
|
||||
final Set<FlowBasicBlock> toRemove = new HashSet<>();
|
||||
final Collection<FlowBasicBlock> toRemove = new HashSet<>();
|
||||
|
||||
// Collect removable blocks
|
||||
for (FlowBasicBlock block : this.basicBlocks) {
|
||||
@ -103,52 +142,53 @@ public class FlowGraph implements Iterable<FlowBasicBlock> {
|
||||
// Remove blocks + reroute predecessors/successors
|
||||
for (FlowBasicBlock block : toRemove) {
|
||||
|
||||
for (FlowBasicBlock pred : block.getPredecessorSet()) {
|
||||
// Reroute
|
||||
for (FlowBasicBlock predecessor : block.getBlockPredecessorSet()) {
|
||||
for (FlowBasicBlock successor : block.getBlockSuccessorSet()) {
|
||||
|
||||
for (FlowBasicBlock succ : block.getSuccessorSet()) {
|
||||
|
||||
Logger.log("Rerouting Block " + this.basicBlocks.indexOf(pred) + " to Block " + this.basicBlocks.indexOf(succ));
|
||||
pred.addSuccessor(succ);
|
||||
succ.addPredecessor(pred);
|
||||
Logger.log("Rerouting Block " + this.basicBlocks.indexOf(predecessor)
|
||||
+ " to Block " + this.basicBlocks.indexOf(successor));
|
||||
predecessor.addSuccessorBlock(successor);
|
||||
successor.addPredecessorBlock(predecessor);
|
||||
}
|
||||
|
||||
pred.getSuccessorSet().remove(block);
|
||||
}
|
||||
|
||||
for (FlowBasicBlock succ : block.getSuccessorSet()) {
|
||||
succ.getPredecessorSet().remove(block);
|
||||
// Remove references
|
||||
for (FlowBasicBlock predecessor : block.getBlockPredecessorSet()) {
|
||||
predecessor.removeSuccessorBlock(block);
|
||||
}
|
||||
|
||||
for (FlowBasicBlock successor : block.getBlockSuccessorSet()) {
|
||||
successor.removePredecessorBlock(block);
|
||||
}
|
||||
}
|
||||
|
||||
this.basicBlocks.removeAll(toRemove);
|
||||
}
|
||||
|
||||
private FlowBasicBlock getBlockByLabel(String label) {
|
||||
private Optional<FlowBasicBlock> getBlockByLabel(String label) {
|
||||
return this.basicBlocks.stream()
|
||||
.filter(block -> block.getLabel().equals(label))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private FlowBasicBlock getCurrentBlock() {
|
||||
return this.basicBlocks.get(this.basicBlocks.size() - 1);
|
||||
/**
|
||||
* Der aktuelle Block ist immer der letzte Block.
|
||||
*/
|
||||
private Optional<FlowBasicBlock> getCurrentBlock() {
|
||||
if (this.basicBlocks.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(this.basicBlocks.get(this.basicBlocks.size() - 1));
|
||||
}
|
||||
|
||||
// Printing
|
||||
|
||||
public String print() {
|
||||
final String blocksString = this.basicBlocks.stream()
|
||||
.map(FlowBasicBlock::toString)
|
||||
.map(string -> string + "-".repeat(50) + "\n")
|
||||
.collect(Collectors.joining());
|
||||
|
||||
return this.head + "-".repeat(100) + "\n"
|
||||
+ "-".repeat(50) + "\n" + blocksString + "-".repeat(100) + "\n"
|
||||
+ this.tail;
|
||||
}
|
||||
|
||||
public String printToImage() {
|
||||
if (this.basicBlocks.isEmpty()) {
|
||||
final Optional<FlowBasicBlock> currentBlock = this.getCurrentBlock();
|
||||
|
||||
if (this.basicBlocks.isEmpty() || currentBlock.isEmpty()) {
|
||||
return "Empty Graph";
|
||||
}
|
||||
|
||||
@ -174,45 +214,19 @@ public class FlowGraph implements Iterable<FlowBasicBlock> {
|
||||
.append("END[label=\"END\"];\n");
|
||||
|
||||
dot.append("START -> ").append(this.basicBlocks.get(0).getId()).append(";\n");
|
||||
dot.append(this.getCurrentBlock().getId()).append(" -> END;\n");
|
||||
dot.append(currentBlock.get().getId()).append(" -> END;\n");
|
||||
|
||||
for (FlowBasicBlock block : this.basicBlocks) {
|
||||
// Successors
|
||||
for (FlowBasicBlock succ : block.getSuccessorSet()) {
|
||||
if (!dot.toString().contains(block.getId() + " -> " + succ.getId())) {
|
||||
// No duplicate arrows
|
||||
|
||||
dot.append(block.getId()).append(" -> ").append(succ.getId()).append(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Predecessors
|
||||
for (FlowBasicBlock pred : block.getPredecessorSet()) {
|
||||
if (!dot.toString().contains(pred.getId() + " -> " + block.getId())) {
|
||||
// No duplicate arrows
|
||||
|
||||
dot.append(pred.getId()).append(" -> ").append(block.getId()).append(";\n");
|
||||
}
|
||||
for (FlowBasicBlock successor : block.getBlockSuccessorSet()) {
|
||||
dot.append(block.getId()).append(" -> ").append(successor.getId()).append(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
dot.append("}");
|
||||
|
||||
final String dotOut = dot.toString();
|
||||
|
||||
final Path dotFile = Paths.get(System.getProperty("user.dir") + "/FlowGraph.dot");
|
||||
try {
|
||||
Files.writeString(dotFile, dotOut);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
final ProcessBuilder dotCompile = new ProcessBuilder("dot", "-Tsvg", "-oFlowGraph.svg", "FlowGraph.dot");
|
||||
try {
|
||||
dotCompile.start();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
GraphvizCaller.callGraphviz(dot, "FlowGraph");
|
||||
|
||||
return "Finished.";
|
||||
}
|
||||
@ -225,9 +239,9 @@ public class FlowGraph implements Iterable<FlowBasicBlock> {
|
||||
.map(FlowBasicBlock::toString)
|
||||
.collect(Collectors.joining());
|
||||
|
||||
return this.head
|
||||
return this.exportHead
|
||||
+ blocksString
|
||||
+ this.tail;
|
||||
+ this.exportTail;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,24 +1,26 @@
|
||||
package codegen.flowgraph;
|
||||
|
||||
/**
|
||||
* Repräsentiert eine Instruction im {@link FlowGraph}.
|
||||
*/
|
||||
public class FlowInstruction {
|
||||
|
||||
private final String id;
|
||||
private final String blockId;
|
||||
|
||||
/**
|
||||
* Die Instruction ist der Jasmin-Assembler Befehl.
|
||||
*/
|
||||
private final String instruction;
|
||||
private final String[] args;
|
||||
|
||||
public FlowInstruction(String id, String blockId, String instruction, String... args) {
|
||||
public FlowInstruction(String id, String instruction, String... args) {
|
||||
this.id = id;
|
||||
this.blockId = blockId;
|
||||
this.instruction = instruction;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String argsString = String.join(" ", this.args);
|
||||
|
||||
return "\t\t" + this.instruction + " " + argsString;
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getInstruction() {
|
||||
@ -29,11 +31,12 @@ public class FlowInstruction {
|
||||
return this.args;
|
||||
}
|
||||
|
||||
public String getBlockId() {
|
||||
return this.blockId;
|
||||
}
|
||||
// Overrides
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
@Override
|
||||
public String toString() {
|
||||
final String argsString = String.join(" ", this.args);
|
||||
|
||||
return "\t\t" + this.instruction + " " + argsString;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user