/* * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ package org.adoptopenjdk.jitwatch.model.bytecode; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COLON; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COMMA; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_BRACE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_BYTECODE_INTERFACEMETHOD_COMMENT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_BYTECODE_METHOD_COMMENT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_BYTECODE_INVOKEDYNAMIC_COMMENT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLOSE_BRACE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; import java.util.ArrayList; import java.util.List; import org.adoptopenjdk.jitwatch.util.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BytecodeInstruction { private int offset; private Opcode opcode; private List<IBytecodeParam> parameters = new ArrayList<>(); private boolean hasComment; private String comment; private static final Logger logger = LoggerFactory.getLogger(BytecodeInstruction.class); public int getOffset() { return offset; } public void setOffset(int offset) { this.offset = offset; } public Opcode getOpcode() { return opcode; } public void setOpcode(Opcode opcode) { this.opcode = opcode; } public List<IBytecodeParam> getParameters() { return parameters; } public void addParameter(IBytecodeParam parameter) { this.parameters.add(parameter); } public boolean hasParameters() { return parameters.size() > 0; } public String getComment() { return comment; } //TODO unit test INDY public String getCommentWithMemberPrefixStripped() { String stripped = comment; if (comment != null) { if (comment.startsWith(S_BYTECODE_METHOD_COMMENT)) { return comment.substring(S_BYTECODE_METHOD_COMMENT.length()).trim(); } else if (comment.startsWith(S_BYTECODE_INTERFACEMETHOD_COMMENT)) { return comment.substring(S_BYTECODE_INTERFACEMETHOD_COMMENT.length()).trim(); } else if (comment.startsWith(S_BYTECODE_INVOKEDYNAMIC_COMMENT)) { // InvokeDynamic #1:run:(Ljavafx/embed/swt/FXCanvas$HostContainer;)Ljava/lang/Runnable; int firstColon = comment.indexOf(C_COLON); if (firstColon != -1) { stripped = comment.substring(firstColon+1, comment.length()); } } } return stripped; } public void setComment(String comment) { this.comment = comment; hasComment = true; } public boolean hasComment() { return hasComment; } @Override public String toString() { return toString(0, 0); } public int getLabelLines() { int result = 1; if (opcode.isSwitch() && parameters.size() == 1) { BCParamSwitch switchParam = (BCParamSwitch) parameters.get(0); result = 2 + switchParam.getSize(); } return result; } public String toStringComplete() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < getLabelLines(); i++) { builder.append(toString(1000, i)).append(S_NEWLINE); } builder.deleteCharAt(builder.length() - 1); return builder.toString(); } public String toString(int maxOffset, int line) { if (opcode.isSwitch()) { return toStringSwitch(maxOffset, line); } else { return toStringNonSwitch(maxOffset); } } private String toStringNonSwitch(int maxOffset) { StringBuilder toStringBuilder = new StringBuilder(); int offsetWidth = Integer.toString(maxOffset).length(); toStringBuilder.append(StringUtil.alignRight(offset, offsetWidth)).append(C_COLON).append(C_SPACE); if (opcode != null) { String mnemonic = opcode.getMnemonic(); toStringBuilder.append(StringUtil.alignLeft(mnemonic, 16)); } if (hasParameters()) { StringBuilder paramBuilder = new StringBuilder(); for (IBytecodeParam parameter : parameters) { paramBuilder.append(parameter.toString()).append(C_COMMA).append(C_SPACE); } int paramLength = paramBuilder.length(); paramBuilder.delete(paramLength - 2, paramLength); String paramString = StringUtil.alignLeft(paramBuilder.toString(), 5); toStringBuilder.append(paramString); } if (hasComment) { toStringBuilder.append(comment); } return toStringBuilder.toString(); } private String toStringSwitch(int maxOffset, int line) { int maxLines = getLabelLines(); StringBuilder toStringBuilder = new StringBuilder(); int offsetWidth = Integer.toString(maxOffset).length(); if (line == 0) { toStringBuilder.append(StringUtil.alignRight(offset, offsetWidth)).append(C_COLON).append(C_SPACE); toStringBuilder.append(opcode.getMnemonic()); toStringBuilder.append(C_SPACE).append(C_OPEN_BRACE); if (hasComment) { toStringBuilder.append(C_SPACE).append(comment); } } else if (line == maxLines - 1) { toStringBuilder.append(StringUtil.alignRight(S_CLOSE_BRACE, offsetWidth + 3)); } else { if (parameters.size() == 1 && parameters.get(0) instanceof BCParamSwitch) { BCParamSwitch param = (BCParamSwitch) parameters.get(0); toStringBuilder.append(param.toString(line - 1)); } else { logger.error("Bad parameters set on tableswitch or lookupswitch, cannot create toString()"); } } return toStringBuilder.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((comment == null) ? 0 : comment.hashCode()); result = prime * result + (hasComment ? 1231 : 1237); result = prime * result + offset; result = prime * result + ((opcode == null) ? 0 : opcode.hashCode()); result = prime * result + ((parameters == null) ? 0 : parameters.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } BytecodeInstruction other = (BytecodeInstruction) obj; if (comment == null) { if (other.comment != null) { return false; } } else if (!comment.equals(other.comment)) { return false; } if (hasComment != other.hasComment) { return false; } if (offset != other.offset) { return false; } if (opcode != other.opcode) { return false; } if (parameters == null) { if (other.parameters != null) { return false; } } else if (!parameters.equals(other.parameters)) { return false; } return true; } }