use of de.mirkosertic.bytecoder.core.BytecodeLocalVariableTableAttributeInfo in project Bytecoder by mirkosertic.
the class NaiveProgramGenerator method generateFrom.
@Override
public Program generateFrom(BytecodeClass aOwningClass, BytecodeMethod aMethod) {
BytecodeCodeAttributeInfo theCode = aMethod.getCode(aOwningClass);
Program theProgram = new Program();
// Initialize programm arguments
BytecodeLocalVariableTableAttributeInfo theDebugInfos = null;
if (theCode != null) {
theDebugInfos = theCode.attributeByType(BytecodeLocalVariableTableAttributeInfo.class);
}
int theCurrentIndex = 0;
if (!aMethod.getAccessFlags().isStatic()) {
theProgram.addArgument(new LocalVariableDescription(theCurrentIndex), Variable.createThisRef());
theCurrentIndex++;
}
BytecodeTypeRef[] theTypes = aMethod.getSignature().getArguments();
for (int i = 0; i < theTypes.length; i++) {
BytecodeTypeRef theRef = theTypes[i];
if (theDebugInfos != null) {
BytecodeLocalVariableTableEntry theEntry = theDebugInfos.matchingEntryFor(BytecodeOpcodeAddress.START_AT_ZERO, theCurrentIndex);
if (theEntry != null) {
String theVariableName = theDebugInfos.resolveVariableName(theEntry);
theProgram.addArgument(new LocalVariableDescription(theCurrentIndex), Variable.createMethodParameter(i + 1, theVariableName, TypeRef.toType(theTypes[i])));
} else {
theProgram.addArgument(new LocalVariableDescription(theCurrentIndex), Variable.createMethodParameter(i + 1, TypeRef.toType(theTypes[i])));
}
} else {
theProgram.addArgument(new LocalVariableDescription(theCurrentIndex), Variable.createMethodParameter(i + 1, TypeRef.toType(theTypes[i])));
}
theCurrentIndex++;
if (theRef == BytecodePrimitiveTypeRef.LONG || theRef == BytecodePrimitiveTypeRef.DOUBLE) {
theCurrentIndex++;
}
}
List<BytecodeBasicBlock> theBlocks = new ArrayList<>();
Function<BytecodeOpcodeAddress, BytecodeBasicBlock> theBasicBlockByAddress = aValue -> {
for (BytecodeBasicBlock theBlock : theBlocks) {
if (Objects.equals(aValue, theBlock.getStartAddress())) {
return theBlock;
}
}
throw new IllegalStateException("No Block for " + aValue.getAddress());
};
if (aMethod.getAccessFlags().isAbstract() || aMethod.getAccessFlags().isNative()) {
return theProgram;
}
BytecodeProgram theBytecode = theCode.getProgramm();
Set<BytecodeOpcodeAddress> theJumpTarget = theBytecode.getJumpTargets();
BytecodeBasicBlock currentBlock = null;
for (BytecodeInstruction theInstruction : theBytecode.getInstructions()) {
if (theJumpTarget.contains(theInstruction.getOpcodeAddress())) {
// Jump target, start a new basic block
currentBlock = null;
}
if (theBytecode.isStartOfTryBlock(theInstruction.getOpcodeAddress())) {
// start of try block, hence new basic block
currentBlock = null;
}
if (currentBlock == null) {
BytecodeBasicBlock.Type theType = BytecodeBasicBlock.Type.NORMAL;
for (BytecodeExceptionTableEntry theHandler : theBytecode.getExceptionHandlers()) {
if (Objects.equals(theHandler.getHandlerPc(), theInstruction.getOpcodeAddress())) {
if (theHandler.isFinally()) {
theType = BytecodeBasicBlock.Type.FINALLY;
} else {
theType = BytecodeBasicBlock.Type.EXCEPTION_HANDLER;
}
}
}
BytecodeBasicBlock theCurrentTemp = currentBlock;
currentBlock = new BytecodeBasicBlock(theType);
if (theCurrentTemp != null && !theCurrentTemp.endsWithReturn() && !theCurrentTemp.endsWithThrow() && theCurrentTemp.endsWithGoto() && !theCurrentTemp.endsWithConditionalJump()) {
theCurrentTemp.addSuccessor(currentBlock);
}
theBlocks.add(currentBlock);
}
currentBlock.addInstruction(theInstruction);
if (theInstruction.isJumpSource()) {
// conditional or unconditional jump, start new basic block
currentBlock = null;
} else if (theInstruction instanceof BytecodeInstructionRET) {
// returning, start new basic block
currentBlock = null;
} else if (theInstruction instanceof BytecodeInstructionRETURN) {
// returning, start new basic block
currentBlock = null;
} else if (theInstruction instanceof BytecodeInstructionObjectRETURN) {
// returning, start new basic block
currentBlock = null;
} else if (theInstruction instanceof BytecodeInstructionGenericRETURN) {
// returning, start new basic block
currentBlock = null;
} else if (theInstruction instanceof BytecodeInstructionATHROW) {
// thowing an exception, start new basic block
currentBlock = null;
} else if (theInstruction instanceof BytecodeInstructionInvoke) {
// invocation, start new basic block
// currentBlock = null;
}
}
// Now, we have to build the successors of each block
for (int i = 0; i < theBlocks.size(); i++) {
BytecodeBasicBlock theBlock = theBlocks.get(i);
if (!theBlock.endsWithReturn() && !theBlock.endsWithThrow()) {
if (theBlock.endsWithJump()) {
for (BytecodeInstruction theInstruction : theBlock.getInstructions()) {
if (theInstruction.isJumpSource()) {
for (BytecodeOpcodeAddress theBlockJumpTarget : theInstruction.getPotentialJumpTargets()) {
theBlock.addSuccessor(theBasicBlockByAddress.apply(theBlockJumpTarget));
}
}
}
if (theBlock.endsWithConditionalJump()) {
if (i < theBlocks.size() - 1) {
theBlock.addSuccessor(theBlocks.get(i + 1));
} else {
throw new IllegalStateException("Block at end with no jump target!");
}
}
} else {
if (i < theBlocks.size() - 1) {
theBlock.addSuccessor(theBlocks.get(i + 1));
} else {
throw new IllegalStateException("Block at end with no jump target!");
}
}
}
}
// Ok, now we transform it to GraphNodes with yet empty content
Map<BytecodeBasicBlock, RegionNode> theCreatedBlocks = new HashMap<>();
ControlFlowGraph theGraph = theProgram.getControlFlowGraph();
for (BytecodeBasicBlock theBlock : theBlocks) {
RegionNode theSingleAssignmentBlock;
switch(theBlock.getType()) {
case NORMAL:
theSingleAssignmentBlock = theGraph.createAt(theBlock.getStartAddress(), RegionNode.BlockType.NORMAL);
break;
case EXCEPTION_HANDLER:
theSingleAssignmentBlock = theGraph.createAt(theBlock.getStartAddress(), RegionNode.BlockType.EXCEPTION_HANDLER);
break;
case FINALLY:
theSingleAssignmentBlock = theGraph.createAt(theBlock.getStartAddress(), RegionNode.BlockType.FINALLY);
break;
default:
throw new IllegalStateException("Unsupported block type : " + theBlock.getType());
}
theCreatedBlocks.put(theBlock, theSingleAssignmentBlock);
}
// Initialize Block dependency graph
for (Map.Entry<BytecodeBasicBlock, RegionNode> theEntry : theCreatedBlocks.entrySet()) {
for (BytecodeBasicBlock theSuccessor : theEntry.getKey().getSuccessors()) {
RegionNode theSuccessorBlock = theCreatedBlocks.get(theSuccessor);
if (theSuccessorBlock == null) {
throw new IllegalStateException("Cannot find successor block");
}
theEntry.getValue().addSuccessor(theSuccessorBlock);
}
}
// And add dependencies for exception handlers and finally blocks
for (BytecodeExceptionTableEntry theHandler : theBytecode.getExceptionHandlers()) {
RegionNode theStart = theProgram.getControlFlowGraph().nodeStartingAt(theHandler.getStartPC());
RegionNode theHandlerNode = theProgram.getControlFlowGraph().nodeStartingAt(theHandler.getHandlerPc());
theStart.addSuccessor(theHandlerNode);
}
// Now we can add the SSA instructions to the graph nodes
Set<RegionNode> theVisited = new HashSet<>();
RegionNode theStart = theProgram.getControlFlowGraph().startNode();
// First of all, we need to mark the back-edges of the graph
theProgram.getControlFlowGraph().calculateReachabilityAndMarkBackEdges();
try {
// Now we can continue to create the program flow
ParsingHelperCache theParsingHelperCache = new ParsingHelperCache(theProgram, aMethod, theStart, theDebugInfos);
// This will traverse the CFG from bottom to top
for (RegionNode theNode : theProgram.getControlFlowGraph().finalNodes()) {
initializeBlock(theProgram, aOwningClass, aMethod, theNode, theVisited, theParsingHelperCache, theBasicBlockByAddress);
}
// finally blocks
for (Map.Entry<BytecodeBasicBlock, RegionNode> theEntry : theCreatedBlocks.entrySet()) {
RegionNode theBlock = theEntry.getValue();
if (theBlock.getType() != RegionNode.BlockType.NORMAL) {
initializeBlock(theProgram, aOwningClass, aMethod, theBlock, theVisited, theParsingHelperCache, theBasicBlockByAddress);
}
}
// Additionally, we have to add gotos
for (RegionNode theNode : theProgram.getControlFlowGraph().getKnownNodes()) {
ExpressionList theCurrentList = theNode.getExpressions();
Expression theLast = theCurrentList.lastExpression();
if (theLast instanceof GotoExpression) {
GotoExpression theGoto = (GotoExpression) theLast;
if (Objects.equals(theGoto.getJumpTarget(), theNode.getStartAddress())) {
theCurrentList.remove(theGoto);
}
}
if (!theNode.getExpressions().endWithNeverReturningExpression()) {
Map<RegionNode.Edge, RegionNode> theSuccessors = theNode.getSuccessors();
for (Expression theExpression : theCurrentList.toList()) {
if (theExpression instanceof IFExpression) {
IFExpression theIF = (IFExpression) theExpression;
BytecodeOpcodeAddress theGoto = theIF.getGotoAddress();
theSuccessors = theSuccessors.entrySet().stream().filter(t -> !Objects.equals(t.getValue().getStartAddress(), theGoto)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
}
List<RegionNode> theSuccessorRegions = theSuccessors.values().stream().filter(t -> t.getType() == RegionNode.BlockType.NORMAL).collect(Collectors.toList());
if (theSuccessorRegions.size() == 1) {
theNode.getExpressions().add(new GotoExpression(theSuccessorRegions.get(0).getStartAddress()).withComment("Resolving pass thru direct"));
} else {
// Special case, the node includes gotos and a fall thru to the same node
theSuccessors = theNode.getSuccessors();
theSuccessorRegions = theSuccessors.values().stream().filter(t -> t.getType() == RegionNode.BlockType.NORMAL).collect(Collectors.toList());
if (theSuccessorRegions.size() == 1) {
theNode.getExpressions().add(new GotoExpression(theSuccessorRegions.get(0).getStartAddress()).withComment("Resolving pass thru direct"));
} else {
throw new IllegalStateException("Invalid number of successors : " + theSuccessors.size() + " for " + theNode.getStartAddress().getAddress());
}
}
}
}
// Check that all PHI-propagations for back-edges are set
for (RegionNode theNode : theProgram.getControlFlowGraph().getKnownNodes()) {
ParsingHelper theHelper = theParsingHelperCache.resolveFinalStateForNode(theNode);
for (Map.Entry<RegionNode.Edge, RegionNode> theEdge : theNode.getSuccessors().entrySet()) {
if (theEdge.getKey().getType() == RegionNode.EdgeType.BACK) {
RegionNode theReceiving = theEdge.getValue();
BlockState theReceivingState = theReceiving.toStartState();
for (Map.Entry<VariableDescription, Value> theEntry : theReceivingState.getPorts().entrySet()) {
Value theExportingValue = theHelper.requestValue(theEntry.getKey());
if (theExportingValue == null) {
throw new IllegalStateException("No value for " + theEntry.getKey() + " to jump from " + theNode.getStartAddress().getAddress() + " to " + theReceiving.getStartAddress().getAddress());
}
Variable theReceivingTarget = (Variable) theEntry.getValue();
theReceivingTarget.initializeWith(theExportingValue);
}
}
}
}
// Make sure that all jump conditions are met
for (RegionNode theNode : theProgram.getControlFlowGraph().getKnownNodes()) {
forEachExpressionOf(theNode, aPoint -> {
if (aPoint.expression instanceof GotoExpression) {
GotoExpression theGoto = (GotoExpression) aPoint.expression;
RegionNode theGotoNode = theProgram.getControlFlowGraph().nodeStartingAt(theGoto.getJumpTarget());
BlockState theImportingState = theGotoNode.toStartState();
for (Map.Entry<VariableDescription, Value> theImporting : theImportingState.getPorts().entrySet()) {
ParsingHelper theHelper = theParsingHelperCache.resolveFinalStateForNode(theNode);
Value theExportingValue = theHelper.requestValue(theImporting.getKey());
if (theExportingValue == null) {
throw new IllegalStateException("No value for " + theImporting.getKey() + " to jump from " + theNode.getStartAddress().getAddress() + " to " + theGotoNode.getStartAddress().getAddress());
}
}
}
});
}
// Insert PHI value resolving at required places
for (RegionNode theNode : theProgram.getControlFlowGraph().getKnownNodes()) {
forEachExpressionOf(theNode, aPoint -> {
if (aPoint.expression instanceof GotoExpression) {
GotoExpression theGoto = (GotoExpression) aPoint.expression;
RegionNode theGotoNode = theProgram.getControlFlowGraph().nodeStartingAt(theGoto.getJumpTarget());
BlockState theImportingState = theGotoNode.toStartState();
String theComments = "";
for (Map.Entry<VariableDescription, Value> theImporting : theImportingState.getPorts().entrySet()) {
theComments = theComments + theImporting.getKey() + " is of type " + theImporting.getValue().resolveType().resolve() + " with values " + theImporting.getValue().incomingDataFlows();
Value theReceivingValue = theImporting.getValue();
ParsingHelper theHelper = theParsingHelperCache.resolveFinalStateForNode(theNode);
Value theExportingValue = theHelper.requestValue(theImporting.getKey());
if (theExportingValue == null) {
throw new IllegalStateException("No value for " + theImporting.getKey() + " to jump from " + theNode.getStartAddress().getAddress() + " to " + theGotoNode.getStartAddress().getAddress());
}
if (theReceivingValue != theExportingValue) {
VariableAssignmentExpression theInit = new VariableAssignmentExpression((Variable) theReceivingValue, theExportingValue);
aPoint.expressionList.addBefore(theInit, theGoto);
}
}
theGoto.withComment(theComments);
}
});
}
} catch (Exception e) {
throw new ControlFlowProcessingException("Error processing CFG for " + aOwningClass.getThisInfo().getConstant().stringValue() + "." + aMethod.getName().stringValue(), e, theProgram.getControlFlowGraph());
}
return theProgram;
}
Aggregations