/*
* Copyright (c) 2013-2017 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;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_CLOSE_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COMMA;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_DOLLAR;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_DOT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_HAT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_SQUARE_BRACKET;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_ASSEMBLY;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_SIG_MATCH;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.REGEX_GROUP_ANY;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.REGEX_ONE_OR_MORE_SPACES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.REGEX_UNICODE_PACKAGE_NAME;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.REGEX_UNICODE_PARAM_NAME;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.REGEX_ZERO_OR_MORE_SPACES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLOSE_SQUARE_BRACKET;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_COMMA;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_VOID;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_ESCAPED_CLOSE_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_ESCAPED_CLOSE_SQUARE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_ESCAPED_OPEN_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_ESCAPED_OPEN_SQUARE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OPEN_SQUARE_BRACKET;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_POLYMORPHIC_SIGNATURE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_COMPILE_ID;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_COMPILE_KIND;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C2N;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyMethod;
import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction;
import org.adoptopenjdk.jitwatch.model.bytecode.ClassBC;
import org.adoptopenjdk.jitwatch.model.bytecode.MemberBytecode;
import org.adoptopenjdk.jitwatch.util.ParseUtil;
import org.adoptopenjdk.jitwatch.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractMetaMember implements IMetaMember, Comparable<IMetaMember>
{
protected static final Logger logger = LoggerFactory.getLogger(AbstractMetaMember.class);
protected MetaClass metaClass;
private List<Compilation> compilations;
private int selectedCompilation;
private boolean isCompiled = false;
protected boolean isVarArgs = false;
protected boolean isPolymorphicSignature = false;
protected int modifier; // bitset
private String memberName;
protected Class<?> returnType;
protected List<Class<?>> paramTypes;
public AbstractMetaMember(String memberName)
{
this.memberName = memberName;
compilations = new ArrayList<>();
}
protected void checkPolymorphicSignature(Method method)
{
for (Annotation anno : method.getAnnotations())
{
if (S_POLYMORPHIC_SIGNATURE.equals(anno.annotationType().getSimpleName()))
{
isPolymorphicSignature = true;
break;
}
}
}
@Override
public String getMemberName()
{
return memberName;
}
@Override
public String getFullyQualifiedMemberName()
{
return metaClass.getFullyQualifiedName() + C_DOT + memberName;
}
@Override
public String getAbbreviatedFullyQualifiedMemberName()
{
return metaClass.getAbbreviatedFullyQualifiedName() + C_DOT + memberName;
}
@Override
public int getModifier()
{
return modifier;
}
@Override
public String getModifierString()
{
return Modifier.toString(modifier);
}
private boolean nameMatches(MemberSignatureParts msp)
{
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("nameMatches this.memberName: '{}' fq: '{}' other '{}' fq: '{}'", memberName,
getFullyQualifiedMemberName(), msp.getMemberName(), msp.getFullyQualifiedClassName());
}
boolean match = memberName.equals(msp.getMemberName());
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("nameMatches {}", match);
}
return match;
}
private boolean returnTypeMatches(MemberSignatureParts msp) throws ClassNotFoundException
{
boolean matched = false;
String returnTypeClassName = msp.applyGenericSubstitutionsForClassLoading(msp.getReturnType());
if (returnTypeClassName != null)
{
Class<?> sigReturnType = ParseUtil.findClassForLogCompilationParameter(returnTypeClassName);
matched = returnType.equals(sigReturnType);
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("Return: '{}' === '{}' ? {}", returnType.getName(), sigReturnType.getName(), matched);
}
}
else
{
matched = (isConstructor());
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("Constructor found");
}
}
return matched;
}
@Override
public boolean matchesSignature(MemberSignatureParts msp, boolean matchTypesExactly)
{
boolean result = false;
if (nameMatches(msp))
{
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("Comparing:\n--------------\n{}\n--------------\n{}\n--------------", this, msp);
}
if (isPolymorphicSignature)
{
// assumption: method overloading not possible
// with polymorphic signatures so this is a match
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("Member has PolymorphicSignature");
}
result = true;
}
else
{
try
{
if (returnTypeMatches(msp))
{
List<Class<?>> mspClassTypes = getClassesForParamTypes(msp);
if (ParseUtil.paramClassesMatch(isVarArgs, this.paramTypes, mspClassTypes, matchTypesExactly))
{
result = true;
}
}
}
catch (ClassNotFoundException cnfe)
{
logger.error("Class not found while matching signature:\n{}", msp, cnfe);
}
}
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("Match: {}", result);
}
}
return result;
}
private List<Class<?>> getClassesForParamTypes(MemberSignatureParts msp) throws ClassNotFoundException
{
List<Class<?>> result = new ArrayList<>();
for (String param : msp.getParamTypes())
{
String paramClassName = msp.applyGenericSubstitutionsForClassLoading(param);
Class<?> clazz = ParseUtil.findClassForLogCompilationParameter(paramClassName);
result.add(clazz);
}
return result;
}
@Override
public String getReturnTypeName()
{
String result = null;
if (isConstructor() || returnType == null)
{
result = S_TYPE_NAME_VOID;
}
else
{
result = ParseUtil.expandParameterType(returnType.getName());
}
return result;
}
@Override
public String[] getParamTypeNames()
{
List<String> typeNames = new ArrayList<>();
for (Class<?> paramClass : paramTypes)
{
typeNames.add(ParseUtil.expandParameterType(paramClass.getName()));
}
return typeNames.toArray(new String[typeNames.size()]);
}
@Override
public MemberBytecode getMemberBytecode()
{
MemberBytecode result = null;
if (metaClass != null)
{
ClassBC classBytecode = metaClass.getClassBytecode();
if (classBytecode != null)
{
result = classBytecode.getMemberBytecode(this);
}
}
return result;
}
@Override
public List<BytecodeInstruction> getInstructions()
{
List<BytecodeInstruction> result = null;
MemberBytecode memberBytecode = getMemberBytecode();
if (memberBytecode != null)
{
result = memberBytecode.getInstructions();
}
else
{
result = new ArrayList<>();
}
return result;
}
@Override
public MetaClass getMetaClass()
{
return metaClass;
}
@Override
public String getQueuedAttribute(String key)
{
return getLastCompilation() == null ? null : getLastCompilation().getQueuedAttribute(key);
}
@Override
public String getCompiledAttribute(String key)
{
return getLastCompilation() == null ? null : getLastCompilation().getCompiledAttribute(key);
}
@Override
public void setTagTaskQueued(Tag tagTaskQueued)
{
Compilation compilation = createCompilation();
compilation.setTagTaskQueued(tagTaskQueued);
compilations.add(compilation);
}
@Override
public Compilation getCompilationByCompileID(String compileID)
{
Compilation result = null;
for (Compilation compilation : compilations)
{
if (compileID.equals(compilation.getCompileID()))
{
result = compilation;
break;
}
}
return result;
}
@Override
public Compilation getCompilationByNativeAddress(String address)
{
Compilation result = null;
for (Compilation compilation : compilations)
{
if (address.equals(compilation.getNativeAddress()))
{
result = compilation;
break;
}
}
return result;
}
@Override
public void setTagNMethod(Tag tagNMethod)
{
isCompiled = true;
String compileID = tagNMethod.getAttributes().get(ATTR_COMPILE_ID);
Compilation compilation = getCompilationByCompileID(compileID);
if (compilation != null)
{
compilation.setTagNMethod(tagNMethod);
}
else
{
// check if C2N stub
String compileKind = tagNMethod.getAttributes().get(ATTR_COMPILE_KIND);
if (C2N.equals(compileKind))
{
compilation = createCompilation();
compilation.setTagNMethod(tagNMethod);
storeCompilation(compilation);
}
else
{
logger.warn("Didn't find compilation with ID {}", compileID);
}
}
// inform package tree it contains class with a compiled method
getMetaClass().getPackage().setHasCompiledClasses();
}
private void storeCompilation(Compilation compilation)
{
compilations.add(compilation);
selectedCompilation = compilations.size() - 1;
}
private Compilation createCompilation()
{
int nextIndex = compilations.size();
selectedCompilation = nextIndex;
return new Compilation(this, nextIndex);
}
@Override
public void setTagTask(Task tagTask)
{
String compileID = tagTask.getAttributes().get(ATTR_COMPILE_ID);
Compilation compilation = getCompilationByCompileID(compileID);
if (compilation != null)
{
compilation.setTagTask(tagTask);
}
else
{
logger.warn("Didn't find compilation with ID {}", compileID);
}
}
@Override
public void setTagTaskDone(String compileID, Tag tagTaskDone)
{
Compilation compilation = getCompilationByCompileID(compileID);
if (compilation != null)
{
compilation.setTagTaskDone(tagTaskDone);
}
else
{
logger.warn("Didn't find compilation with ID {}", compileID);
}
}
@Override
public boolean isCompiled()
{
return isCompiled;
}
@Override
public Map<String, String> getQueuedAttributes()
{
return getLastCompilation() == null ? null : getLastCompilation().getQueuedAttributes();
}
@Override
public Map<String, String> getCompiledAttributes()
{
return getLastCompilation() == null ? null : getLastCompilation().getCompiledAttributes();
}
@Override
public String toStringUnqualifiedMethodName(boolean visibilityAndReturnType, boolean fqParamTypes)
{
StringBuilder builder = new StringBuilder();
if (visibilityAndReturnType)
{
if (modifier != 0)
{
builder.append(Modifier.toString(modifier)).append(C_SPACE);
}
if (!isConstructor() && returnType != null)
{
builder.append(expandParam(returnType.getName(), fqParamTypes)).append(C_SPACE);
}
}
builder.append(memberName);
builder.append(C_OPEN_PARENTHESES);
if (paramTypes.size() > 0)
{
for (Class<?> paramClass : paramTypes)
{
builder.append(expandParam(paramClass.getName(), fqParamTypes)).append(C_COMMA);
}
builder.deleteCharAt(builder.length() - 1);
}
builder.append(C_CLOSE_PARENTHESES);
return builder.toString();
}
@Override
public void addAssembly(AssemblyMethod asmMethod)
{
if (DEBUG_LOGGING_ASSEMBLY)
{
logger.debug("setAssembly on member {}", getFullyQualifiedMemberName());
}
Compilation compilation = getCompilationByNativeAddress(asmMethod.getNativeAddress());
if (compilation != null)
{
compilation.setAssembly(asmMethod);
}
else
{
logger.warn("Didn't find compilation to attach assembly for address {}", asmMethod.getNativeAddress());
}
}
@Override
public List<Compilation> getCompilations()
{
return compilations;
}
@Override
public boolean isConstructor()
{
return (this instanceof MetaConstructor);
}
@Override
public String getSourceMethodSignatureRegEx()
{
StringBuilder builder = new StringBuilder();
builder.append(C_HAT);
builder.append(REGEX_GROUP_ANY);
String modifiers = Modifier.toString(modifier);
if (modifiers.length() > 0)
{
builder.append(modifiers).append(C_SPACE);
}
// return type of constructor is not declared in signature
if (!isConstructor() && returnType != null)
{
String rt = expandParamRegEx(returnType.getName());
builder.append(rt);
builder.append(C_SPACE);
}
if (isConstructor())
{
builder.append(REGEX_UNICODE_PACKAGE_NAME);
builder.append(StringUtil.getUnqualifiedClassName(memberName));
}
else
{
builder.append(memberName);
}
// TODO return type and name should uniquely identify a method
/*
* builder.append(REGEX_ZERO_OR_MORE_SPACES);
*
* builder.append(S_ESCAPED_OPEN_PARENTHESES);
*
* if (paramTypes.size() > 0) { for (Class<?> paramClass : paramTypes) {
* builder.append(REGEX_ZERO_OR_MORE_SPACES);
*
* String paramType = expandParamRegEx(paramClass.getName());
*
* builder.append(paramType); builder.append(REGEX_ONE_OR_MORE_SPACES);
* builder.append(REGEX_UNICODE_PARAM_NAME); builder.append(S_COMMA); }
*
* builder.deleteCharAt(builder.length() - 1); }
*
* builder.append(REGEX_ZERO_OR_MORE_SPACES);
* builder.append(S_ESCAPED_CLOSE_PARENTHESES);
* builder.append(REGEX_GROUP_ANY); builder.append(C_DOLLAR);
*/
return builder.toString();
}
public static String expandParam(String inParamType, boolean fullyQualifiedType)
{
String paramType = inParamType;
if (paramType.charAt(0) == C_OPEN_SQUARE_BRACKET)
{
paramType = ParseUtil.expandParameterType(paramType);
}
if (paramType.contains(S_DOT) && !fullyQualifiedType)
{
paramType = StringUtil.getUnqualifiedClassName(paramType);
}
return paramType;
}
public static String expandParamRegEx(String inParamType)
{
String paramType = inParamType;
if (paramType.charAt(0) == C_OPEN_SQUARE_BRACKET)
{
paramType = ParseUtil.expandParameterType(paramType);
paramType = paramType.replace(S_OPEN_SQUARE_BRACKET, S_ESCAPED_OPEN_SQUARE).replace(S_CLOSE_SQUARE_BRACKET,
S_ESCAPED_CLOSE_SQUARE);
}
if (paramType.contains(S_DOT))
{
paramType = REGEX_UNICODE_PACKAGE_NAME + StringUtil.getUnqualifiedClassName(paramType);
}
return paramType;
}
@Override
public Compilation getLastCompilation()
{
int compilationCount = compilations.size();
Compilation result = null;
if (compilationCount > 0)
{
result = compilations.get(compilationCount - 1);
}
return result;
}
private int makeSafeIndex(int index)
{
return Math.max(0, Math.min(index, compilations.size() - 1));
}
public Compilation getCompilation(int index)
{
Compilation result = null;
if (index >= 0 && index < compilations.size())
{
result = compilations.get(index);
}
return result;
}
@Override
public void setSelectedCompilation(int index)
{
this.selectedCompilation = makeSafeIndex(index);
}
@Override
public Compilation getSelectedCompilation()
{
return getCompilation(selectedCompilation);
}
@Override
public int compareTo(IMetaMember other)
{
if (other == null)
{
return -1;
}
else
{
return getMemberName().compareTo(other.getMemberName());
}
}
}