package org.adoptopenjdk.jitwatch.model.assembly; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_ASSEMBLY; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.NATIVE_CODE_ENTRY_POINT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_ENTITY_APOS; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HASH; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HEX_POSTFIX; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HEX_PREFIX; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OPEN_SQUARE_BRACKET; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_QUOTE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SEMICOLON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AbstractAssemblyParser implements IAssemblyParser { protected static final Logger logger = LoggerFactory.getLogger(AbstractAssemblyParser.class); protected Architecture architecture; public AbstractAssemblyParser(Architecture architecture) { this.architecture = architecture; } public Architecture getArchitecture() { return architecture; } // TODO this is too much work // save the string blocks and parse on demand public AssemblyMethod parseAssembly(final String assemblyString) { final AssemblyLabels labels = new AssemblyLabels(); String[] lines = assemblyString.split(S_NEWLINE); StringBuilder headerBuilder = new StringBuilder(); AssemblyBlock currentBlock = new AssemblyBlock(); currentBlock.setTitle(NATIVE_CODE_ENTRY_POINT); AssemblyInstruction lastInstruction = null; String lastLine = null; AssemblyMethod method = null; for (int i = 0; i < lines.length; i++) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("line: '{}'", lines[i]); } if (i == 0) { method = new AssemblyMethod(lines[i], architecture); } String line = lines[i].replace(S_ENTITY_APOS, S_QUOTE); line = line.replaceFirst("^ +", ""); if (line.startsWith(S_HASH)) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Assembly header: {}", line); } headerBuilder.append(line).append(S_NEWLINE); } else if (line.startsWith(S_OPEN_SQUARE_BRACKET)) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("new AssemblyBlock: {}", line); } method.addBlock(currentBlock); currentBlock = new AssemblyBlock(); currentBlock.setTitle(line); } else if (line.startsWith(S_SEMICOLON)) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Extended comment? '{}'", line); } if (lastInstruction != null) { lastInstruction.addCommentLine(line); } } else { AssemblyInstruction instr = createInstruction(labels, line); if (instr == null && lastLine.trim().startsWith(S_HASH) && !line.startsWith(S_HEX_PREFIX) && !line.contains(' ' + S_HEX_PREFIX)) { // remove last newline headerBuilder.setLength(headerBuilder.length() - S_NEWLINE.length()); headerBuilder.append(line).append(S_NEWLINE); // update untrimmedLine since it is used to update // lastUntrimmedLine at end of loop line = lastLine + line; } else if (instr == null && lastLine.trim().startsWith(S_SEMICOLON) && lastInstruction != null) { lastInstruction.appendToLastCommentLine(line); // update untrimmedLine since it is used to update // lastUntrimmedLine at end of loop line = lastLine + line; } else { boolean replaceLast = false; if (instr == null && i < lines.length - 1) { // try appending current and next lines together String nextUntrimmedLine = lines[i + 1].replace(S_ENTITY_APOS, S_QUOTE); instr = createInstruction(labels, line + nextUntrimmedLine); if (instr != null) { i++; } } if (instr == null && lastInstruction != null) { // try appending last and current lines together instr = createInstruction(labels, lastLine + line); if (instr != null) { replaceLast = true; } } if (instr != null) { if (replaceLast) { currentBlock.replaceLastInstruction(instr); } else { currentBlock.addInstruction(instr); } lastInstruction = instr; } else { logger.error("Could not parse assembly: {}", line); } } } lastLine = line; } method.addBlock(currentBlock); method.setHeader(headerBuilder.toString()); labels.buildLabels(); return method; } @Override public boolean isAddress(String mnemonic, String operand) { return (operand.startsWith(S_HEX_PREFIX) || operand.endsWith(S_HEX_POSTFIX)) && isJump(mnemonic); } }