/*
* 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.util;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_ARGUMENTS;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_HOLDER;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_NAME;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_RETURN;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_STAMP;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_STAMP_COMPLETED;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_CLOSE_ANGLE;
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_DOT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OBJECT_REF;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_ANGLE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_SQUARE_BRACKET;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_QUOTE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SEMICOLON;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SLASH;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_OVC;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_SIG_MATCH;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_ARRAY_BRACKET_PAIR;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLASS;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLASS_AUTOGENERATED_LAMBDA;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLOSE_ANGLE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLOSE_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOUBLE_QUOTE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_EMPTY;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OBJECT_ARRAY_DEF;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OPEN_ANGLE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OPEN_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OPEN_SQUARE_BRACKET;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_PACKAGE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SEMICOLON;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SLASH;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SPACE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_BOOLEAN;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_BYTE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_CHARACTER;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_DOUBLE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_FLOAT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_INTEGER;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_LONG;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_SHORT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_TYPE_NAME_VOID;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_VARARGS_DOTS;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.adoptopenjdk.jitwatch.core.JITWatchConstants;
import org.adoptopenjdk.jitwatch.model.IMetaMember;
import org.adoptopenjdk.jitwatch.model.IParseDictionary;
import org.adoptopenjdk.jitwatch.model.IReadOnlyJITDataModel;
import org.adoptopenjdk.jitwatch.model.LogParseException;
import org.adoptopenjdk.jitwatch.model.MemberSignatureParts;
import org.adoptopenjdk.jitwatch.model.MetaClass;
import org.adoptopenjdk.jitwatch.model.PackageManager;
import org.adoptopenjdk.jitwatch.model.Tag;
import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class ParseUtil
{
private static final Logger logger = LoggerFactory.getLogger(ParseUtil.class);
// class<SPACE>METHOD<SPACE>(PARAMS)RETURN
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2
public static String CLASS_NAME_REGEX_GROUP = "([\\p{L}0-9$=_{};\\.\\[/<>]+)";
public static String METHOD_NAME_REGEX_GROUP = "([^;\\[/]+)";
public static String PARAM_REGEX_GROUP = "(\\(.*\\))";
public static String RETURN_REGEX_GROUP = "(.*)";
private static final Pattern PATTERN_LOG_SIGNATURE = Pattern
.compile("^" + CLASS_NAME_REGEX_GROUP + S_SPACE + METHOD_NAME_REGEX_GROUP + S_SPACE + PARAM_REGEX_GROUP + RETURN_REGEX_GROUP);
public static final char TYPE_SHORT = 'S';
public static final char TYPE_CHARACTER = 'C';
public static final char TYPE_BYTE = 'B';
public static final char TYPE_VOID = 'V';
public static final char TYPE_LONG = 'J';
public static final char TYPE_DOUBLE = 'D';
public static final char TYPE_BOOLEAN = 'Z';
public static final char TYPE_INTEGER = 'I';
public static final char TYPE_FLOAT = 'F';
private ParseUtil()
{
}
public static long parseStamp(String stamp)
{
long result = 0;
if (stamp != null)
{
double number = parseLocaleSafeDouble(stamp);
result = (long) (number * 1000);
}
else
{
logger.warn("Could not parse null stamp");
Thread.dumpStack();
}
return result;
}
public static long parseStampFromTag(Tag tag)
{
String attrValue = tag.getAttributes().get(ATTR_STAMP);
long result = 0;
if (attrValue != null)
{
result = parseStamp(attrValue);
}
else
{
logger.error("attribute {} missing from tag {}", ATTR_STAMP, tag.toString(true));
}
return result;
}
public static long parseLongAttributeFromTag(Tag tag, String attrName)
{
String attrValue = tag.getAttributes().get(attrName);
long result = 0;
if (attrValue != null)
{
result = Long.parseLong(attrValue);
}
else
{
logger.error("attribute {} missing from tag {}", attrName, tag.toString(true));
}
return result;
}
public static long getStamp(Map<String, String> attrs)
{
long result = 0;
if (attrs.containsKey(ATTR_STAMP_COMPLETED))
{
result = parseStamp(attrs.get(ATTR_STAMP_COMPLETED));
}
else if (attrs.containsKey(ATTR_STAMP))
{
result = parseStamp(attrs.get(ATTR_STAMP));
}
return result;
}
public static double parseLocaleSafeDouble(String str)
{
NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
double result = 0;
try
{
result = nf.parse(str).doubleValue();
}
catch (ParseException pe)
{
logger.warn("Could not parse {} as a Double", pe);
}
return result;
}
public static Class<?> getPrimitiveClass(char c)
{
switch (c)
{
case TYPE_SHORT:
return Short.TYPE;
case TYPE_CHARACTER:
return Character.TYPE;
case TYPE_BYTE:
return Byte.TYPE;
case TYPE_VOID:
return Void.TYPE;
case TYPE_LONG:
return Long.TYPE;
case TYPE_DOUBLE:
return Double.TYPE;
case TYPE_BOOLEAN:
return Boolean.TYPE;
case TYPE_INTEGER:
return Integer.TYPE;
case TYPE_FLOAT:
return Float.TYPE;
}
throw new RuntimeException("Unknown class for " + c);
}
public static int getArrayDepth(String input)
{
int result = 0;
for (int i = 0; i < input.length(); i++)
{
char c = input.charAt(i);
if (c == C_OPEN_SQUARE_BRACKET)
{
result++;
}
else
{
break;
}
}
return result;
}
// I => int
// [C => char[]
// [[I => int[][]
// [Ljava.lang.Object; => java.lang.Object[]
public static String expandParameterType(String name)
{
StringBuilder builder = new StringBuilder();
int arrayDepth = getArrayDepth(name);
int nameLengthWithoutArrayDepth = name.length() - arrayDepth;
int nameStart = arrayDepth;
if (nameLengthWithoutArrayDepth == 1)
{
char c = name.charAt(nameStart);
switch (c)
{
case TYPE_SHORT:
builder.append(S_TYPE_NAME_SHORT);
break;
case TYPE_CHARACTER:
builder.append(S_TYPE_NAME_CHARACTER);
break;
case TYPE_BYTE:
builder.append(S_TYPE_NAME_BYTE);
break;
case TYPE_LONG:
builder.append(S_TYPE_NAME_LONG);
break;
case TYPE_DOUBLE:
builder.append(S_TYPE_NAME_DOUBLE);
break;
case TYPE_BOOLEAN:
builder.append(S_TYPE_NAME_BOOLEAN);
break;
case TYPE_INTEGER:
builder.append(S_TYPE_NAME_INTEGER);
break;
case TYPE_FLOAT:
builder.append(S_TYPE_NAME_FLOAT);
break;
}
}
else if (name.charAt(nameStart) == C_OBJECT_REF && name.endsWith(S_SEMICOLON))
{
builder.append(name.substring(nameStart + 1, name.length() - 1));
}
else
{
builder.append(name.substring(nameStart));
}
for (int i = 0; i < arrayDepth; i++)
{
builder.append(S_ARRAY_BRACKET_PAIR);
}
return builder.toString();
}
public static String[] splitLogSignatureWithRegex(final String logSignature) throws LogParseException
{
String sig = logSignature;
sig = StringUtil.replaceXMLEntities(sig);
Matcher matcher = PATTERN_LOG_SIGNATURE.matcher(sig);
if (matcher.find())
{
String className = matcher.group(1);
String methodName = matcher.group(2);
String paramTypes = matcher.group(3).replace(S_OPEN_PARENTHESES, S_EMPTY).replace(S_CLOSE_PARENTHESES, S_EMPTY);
String returnType = matcher.group(4);
return new String[] { className, methodName, paramTypes, returnType };
}
logger.debug("Could not apply {} to {}", PATTERN_LOG_SIGNATURE, logSignature);
throw new LogParseException("Could not split signature with regex: '" + logSignature + C_QUOTE);
}
public static IMetaMember findMemberWithSignature(IReadOnlyJITDataModel model, String logSignature) throws LogParseException
{
IMetaMember metaMember = null;
if (logSignature != null)
{
MemberSignatureParts msp = MemberSignatureParts.fromLogCompilationSignature(logSignature);
metaMember = model.findMetaMember(msp);
if (metaMember == null)
{
throw new LogParseException("MetaMember not found for " + logSignature);
}
}
return metaMember;
}
public static Class<?>[] getClassTypes(String typesString) throws LogParseException
{
List<Class<?>> classes = null;
try
{
classes = findClassesForTypeString(typesString);
}
catch (Throwable t)
{
throw new LogParseException("Could not parse types: " + typesString, t);
}
return classes.toArray(new Class<?>[classes.size()]);
}
public static Class<?> findClassForLogCompilationParameter(String param) throws ClassNotFoundException
{
StringBuilder builder = new StringBuilder();
if (isPrimitive(param))
{
return classForPrimitive(param);
}
else
{
int arrayBracketCount = getArrayBracketCount(param);
if (param.contains(S_CLOSE_ANGLE))
{
param = stripGenerics(param);
}
if (arrayBracketCount == 0)
{
if (param.endsWith(S_VARARGS_DOTS))
{
String partBeforeDots = param.substring(0, param.length() - S_VARARGS_DOTS.length());
if (isPrimitive(partBeforeDots))
{
builder.append(S_OPEN_ANGLE).append(classForPrimitive(partBeforeDots));
}
else
{
builder.append(S_OBJECT_ARRAY_DEF).append(partBeforeDots);
builder.append(C_SEMICOLON);
}
}
else
{
builder.append(param);
}
}
else
{
int arrayBracketChars = 2 * arrayBracketCount;
String partBeforeArrayBrackets = param.substring(0, param.length() - arrayBracketChars);
for (int i = 0; i < arrayBracketCount - 1; i++)
{
builder.append(C_OPEN_SQUARE_BRACKET);
}
if (isPrimitive(partBeforeArrayBrackets))
{
builder.append(C_OPEN_SQUARE_BRACKET);
builder.append(getClassTypeCharForPrimitiveTypeString(partBeforeArrayBrackets));
}
else
{
builder.append(S_OBJECT_ARRAY_DEF);
builder.append(param);
builder.delete(builder.length() - arrayBracketChars, builder.length());
builder.append(C_SEMICOLON);
}
}
return ClassUtil.loadClassWithoutInitialising(builder.toString());
}
}
public static String stripGenerics(String param)
{
String result = param;
if (param != null)
{
int firstOpenAngle = param.indexOf(C_OPEN_ANGLE);
int lastCloseAngle = param.lastIndexOf(C_CLOSE_ANGLE);
if (firstOpenAngle != -1 && lastCloseAngle != -1 && firstOpenAngle < lastCloseAngle)
{
result = param.substring(0, firstOpenAngle) + param.substring(lastCloseAngle + 1);
}
}
return result;
}
public static boolean paramClassesMatch(boolean memberHasVarArgs, List<Class<?>> memberParamClasses,
List<Class<?>> signatureParamClasses, boolean matchTypesExactly)
{
boolean result = true;
final int memberParamCount = memberParamClasses.size();
final int signatureParamCount = signatureParamClasses.size();
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("MemberParamCount:{} SignatureParamCount:{} varArgs:{}", memberParamCount, signatureParamCount,
memberHasVarArgs);
}
if (memberParamCount == 0 && signatureParamCount == 0)
{
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("both have zero params");
}
result = true;
}
else if (signatureParamCount < memberParamCount)
{
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("signature has less params than method");
}
result = false;
}
else if (signatureParamCount > memberParamCount && !memberHasVarArgs)
{
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("signature has more params than non-varargs method");
}
result = false;
}
else
{
// signature params >= memberParams
int memPos = 0;
for (int sigPos = 0; sigPos < signatureParamCount; sigPos++)
{
Class<?> sigParamClass = signatureParamClasses.get(sigPos);
Class<?> memParamClass = memberParamClasses.get(memPos);
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("Comparing member param[{}] {} to sig param[{}] {}", memPos, memParamClass, sigPos, sigParamClass);
}
boolean classMatch = false;
if (matchTypesExactly)
{
classMatch = memParamClass.equals(sigParamClass);
}
else
{
classMatch = memParamClass.isAssignableFrom(sigParamClass);
}
if (classMatch)
{
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("{} equals/isAssignableFrom {}", memParamClass, sigParamClass);
}
}
else
{
boolean memberParamCouldBeVarArgs = false;
boolean isLastParameter = (memPos == memberParamCount - 1);
if (memberHasVarArgs && isLastParameter)
{
memberParamCouldBeVarArgs = true;
}
if (memberParamCouldBeVarArgs)
{
// check assignable
Class<?> componentType = memParamClass.getComponentType();
if (componentType.isAssignableFrom(sigParamClass))
{
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("vararg member param {} equals/isAssignableFrom {}", memParamClass, sigParamClass);
}
}
else
{
result = false;
break;
}
}
else
{
result = false;
break;
}
} // if classMatch
memPos++;
if (memPos >= memberParamCount)
{
memPos = memberParamCount - 1;
}
} // for
}
return result;
}
public static boolean typeIsVarArgs(String type)
{
return type != null && type.endsWith(S_VARARGS_DOTS);
}
public static char getClassTypeCharForPrimitiveTypeString(String type)
{
switch (type)
{
case S_TYPE_NAME_INTEGER:
return TYPE_INTEGER;
case S_TYPE_NAME_BOOLEAN:
return TYPE_BOOLEAN;
case S_TYPE_NAME_LONG:
return TYPE_LONG;
case S_TYPE_NAME_DOUBLE:
return TYPE_DOUBLE;
case S_TYPE_NAME_FLOAT:
return TYPE_FLOAT;
case S_TYPE_NAME_SHORT:
return TYPE_SHORT;
case S_TYPE_NAME_BYTE:
return TYPE_BYTE;
case S_TYPE_NAME_CHARACTER:
return TYPE_CHARACTER;
case S_TYPE_NAME_VOID:
return TYPE_VOID;
}
throw new RuntimeException(type + " is not a primitive type");
}
public static boolean isPrimitive(String type)
{
boolean result = false;
if (type != null)
{
switch (type)
{
case S_TYPE_NAME_INTEGER:
case S_TYPE_NAME_BOOLEAN:
case S_TYPE_NAME_LONG:
case S_TYPE_NAME_DOUBLE:
case S_TYPE_NAME_FLOAT:
case S_TYPE_NAME_SHORT:
case S_TYPE_NAME_BYTE:
case S_TYPE_NAME_CHARACTER:
case S_TYPE_NAME_VOID:
result = true;
}
}
return result;
}
public static Class<?> classForPrimitive(String primitiveType)
{
if (primitiveType != null)
{
switch (primitiveType)
{
case S_TYPE_NAME_INTEGER:
return int.class;
case S_TYPE_NAME_BOOLEAN:
return boolean.class;
case S_TYPE_NAME_LONG:
return long.class;
case S_TYPE_NAME_DOUBLE:
return double.class;
case S_TYPE_NAME_FLOAT:
return float.class;
case S_TYPE_NAME_SHORT:
return short.class;
case S_TYPE_NAME_BYTE:
return byte.class;
case S_TYPE_NAME_CHARACTER:
return char.class;
case S_TYPE_NAME_VOID:
return void.class;
}
}
throw new RuntimeException(primitiveType + " is not a primitive type");
}
public static int getArrayBracketCount(String param)
{
int count = 0;
if (param != null)
{
int index = param.indexOf(S_ARRAY_BRACKET_PAIR, 0);
while (index != -1)
{
count++;
index = param.indexOf(S_ARRAY_BRACKET_PAIR, index + 2);
}
}
return count;
}
public static List<String> parseTypeString(final String typesString)
{
List<String> result = new ArrayList<>();
String toParse = typesString.replace(C_SLASH, C_DOT);
int pos = 0;
StringBuilder builder = new StringBuilder();
final int stringLen = toParse.length();
while (pos < stringLen)
{
char c = toParse.charAt(pos);
switch (c)
{
case C_OPEN_SQUARE_BRACKET:
// Could be
// [Ljava.lang.String; Object array
// [I primitive array
// [..[I multidimensional primitive array
// [..[Ljava.lang.String multidimensional Object array
builder.delete(0, builder.length());
builder.append(c);
pos++;
c = toParse.charAt(pos);
while (c == C_OPEN_SQUARE_BRACKET)
{
builder.append(c);
pos++;
c = toParse.charAt(pos);
}
if (c == C_OBJECT_REF)
{
// array of ref type
while (pos < stringLen)
{
c = toParse.charAt(pos++);
builder.append(c);
if (c == C_SEMICOLON)
{
break;
}
}
}
else
{
// array of primitive
builder.append(c);
pos++;
}
result.add(builder.toString());
builder.delete(0, builder.length());
break;
case C_OBJECT_REF:
// ref type
while (pos < stringLen - 1)
{
pos++;
c = toParse.charAt(pos);
if (c == C_SEMICOLON)
{
pos++;
break;
}
builder.append(c);
}
result.add(builder.toString());
builder.delete(0, builder.length());
break;
default:
// primitive
result.add(Character.toString(c));
pos++;
} // end switch
} // end while
return result;
}
/*
* Converts (III[Ljava.lang.String;) into a list of Class<?>
*/
public static List<Class<?>> findClassesForTypeString(final String typesString) throws ClassNotFoundException
{
List<Class<?>> result = new ArrayList<>();
List<String> typeNames = parseTypeString(typesString);
for (String typeName : typeNames)
{
Class<?> clazz = null;
if (typeName.length() == 1)
{
clazz = getPrimitiveClass(typeName.charAt(0));
}
else
{
clazz = ClassUtil.loadClassWithoutInitialising(typeName);
}
result.add(clazz);
}
return result;
}
public static String findBestMatchForMemberSignature(IMetaMember member, List<String> lines)
{
String match = null;
if (lines != null)
{
int index = findBestLineMatchForMemberSignature(member, lines);
if (index > 0 && index < lines.size())
{
match = lines.get(index);
}
}
return match;
}
public static int findBestLineMatchForMemberSignature(IMetaMember member, List<String> lines)
{
int bestScoreLine = 0;
if (lines != null)
{
String memberName = member.getMemberName();
int modifier = member.getModifier();
String returnTypeName = member.getReturnTypeName();
String[] paramTypeNames = member.getParamTypeNames();
int bestScore = 0;
for (int i = 0; i < lines.size(); i++)
{
String line = lines.get(i);
int score = 0;
if (line.contains(memberName))
{
if (DEBUG_LOGGING_SIG_MATCH)
{
logger.debug("Comparing {} with {}", line, member);
}
MemberSignatureParts msp = MemberSignatureParts
.fromBytecodeSignature(member.getMetaClass().getFullyQualifiedName(), line);
if (!memberName.equals(msp.getMemberName()))
{
continue;
}
// modifiers matched
if (msp.getModifier() != modifier)
{
continue;
}
List<String> mspParamTypes = msp.getParamTypes();
if (mspParamTypes.size() != paramTypeNames.length)
{
continue;
}
int pos = 0;
for (String memberParamType : paramTypeNames)
{
String mspParamType = msp.getParamTypes().get(pos++);
if (compareTypeEquality(memberParamType, mspParamType, msp.getGenerics()))
{
score++;
}
}
// return type matched
if (compareTypeEquality(returnTypeName, msp.getReturnType(), msp.getGenerics()))
{
score++;
}
if (score > bestScore)
{
bestScoreLine = i;
bestScore = score;
}
}
}
}
return bestScoreLine;
}
private static boolean compareTypeEquality(String memberTypeName, String inMspTypeName, Map<String, String> genericsMap)
{
String mspTypeName = inMspTypeName;
if (memberTypeName != null && memberTypeName.equals(mspTypeName))
{
return true;
}
else if (mspTypeName != null)
{
// Substitute generics to match with non-generic signature
// public static <T extends java.lang.Object, U extends
// java.lang.Object> T[] copyOf(U[], int, java.lang.Class<? extends
// T[]>)";
// U[] -> java.lang.Object[]
String mspTypeNameWithoutArray = getParamTypeWithoutArrayBrackets(mspTypeName);
String genericSubstitution = genericsMap.get(mspTypeNameWithoutArray);
if (genericSubstitution != null)
{
mspTypeName = mspTypeName.replace(mspTypeNameWithoutArray, genericSubstitution);
if (memberTypeName != null && memberTypeName.equals(mspTypeName))
{
return true;
}
}
}
return false;
}
public static String getParamTypeWithoutArrayBrackets(String paramType)
{
int bracketsIndex = paramType.indexOf(S_ARRAY_BRACKET_PAIR);
if (bracketsIndex != -1)
{
return paramType.substring(0, bracketsIndex);
}
else
{
return paramType;
}
}
public static String getMethodTagReturn(Tag methodTag, IParseDictionary parseDictionary)
{
String returnTypeId = methodTag.getAttributes().get(ATTR_RETURN);
String returnType = lookupType(returnTypeId, parseDictionary);
return returnType;
}
public static List<String> getMethodTagArguments(Tag methodTag, IParseDictionary parseDictionary)
{
List<String> result = new ArrayList<>();
String arguments = methodTag.getAttributes().get(ATTR_ARGUMENTS);
if (arguments != null)
{
String[] typeIDs = arguments.split(S_SPACE);
for (String typeID : typeIDs)
{
result.add(lookupType(typeID, parseDictionary));
}
}
return result;
}
public static String getMethodName(String methodID, IParseDictionary parseDictionary)
{
String result = null;
Tag methodTag = parseDictionary.getMethod(methodID);
if (methodTag != null)
{
String methodName = methodTag.getAttributes().get(ATTR_NAME);
result = StringUtil.replaceXMLEntities(methodName);
}
return result;
}
public static String lookupMetaClassName(String methodId, IParseDictionary parseDictionary)
{
String metaClassName = null;
Tag methodTag = parseDictionary.getMethod(methodId);
if (methodTag != null)
{
Map<String, String> methodTagAttributes = methodTag.getAttributes();
String klassId = methodTagAttributes.get(ATTR_HOLDER);
Tag klassTag = parseDictionary.getKlass(klassId);
metaClassName = klassTag.getAttributes().get(ATTR_NAME).replace(S_SLASH, S_DOT);
}
return metaClassName;
}
public static String lookupMethodName(String methodId, IParseDictionary parseDictionary)
{
String methodName = null;
Tag methodTag = parseDictionary.getMethod(methodId);
if (methodTag != null)
{
Map<String, String> methodTagAttributes = methodTag.getAttributes();
methodName = methodTagAttributes.get(ATTR_NAME);
methodName = StringUtil.replaceXMLEntities(methodName);
}
return methodName;
}
public static IMetaMember lookupMember(String methodId, IParseDictionary parseDictionary, IReadOnlyJITDataModel model)
{
IMetaMember result = null;
Tag methodTag = parseDictionary.getMethod(methodId);
if (methodTag != null)
{
String metaClassName = lookupMetaClassName(methodId, parseDictionary);
PackageManager pm = model.getPackageManager();
MetaClass metaClass = pm.getMetaClass(metaClassName);
if (metaClass == null)
{
metaClass = lateLoadMetaClass(model, metaClassName);
}
if (metaClass != null)
{
String methodName = lookupMethodName(methodId, parseDictionary);
String returnType = getMethodTagReturn(methodTag, parseDictionary);
List<String> argumentTypes = getMethodTagArguments(methodTag, parseDictionary);
MemberSignatureParts msp = MemberSignatureParts.fromParts(metaClass.getFullyQualifiedName(), methodName, returnType,
argumentTypes);
result = metaClass.getMemberForSignature(msp);
}
else if (!possibleLambdaMethod(metaClassName))
{
logger.error("metaClass not found: {}", metaClassName);
}
}
return result;
}
public static MetaClass lateLoadMetaClass(IReadOnlyJITDataModel model, String metaClassName)
{
if (DEBUG_LOGGING)
{
logger.debug("metaClass not found: {}. Attempting classload", metaClassName);
}
MetaClass metaClass = null;
try
{
Class<?> clazz = ClassUtil.loadClassWithoutInitialising(metaClassName);
if (clazz != null)
{
metaClass = model.buildAndGetMetaClass(clazz);
}
}
catch (ClassNotFoundException cnf)
{
if (!possibleLambdaMethod(metaClassName))
{
logger.error("ClassNotFoundException: '" + metaClassName + C_QUOTE);
}
}
catch (NoClassDefFoundError ncdf)
{
logger.error("NoClassDefFoundError: '" + metaClassName + C_SPACE + ncdf.getMessage() + C_QUOTE);
}
return metaClass;
}
public static boolean possibleLambdaMethod(String fqClassName)
{
if (fqClassName.contains(S_CLASS_AUTOGENERATED_LAMBDA))
{
return true;
}
else
{
for (String prefix : JITWatchConstants.getAutoGeneratedClassPrefixes())
{
if (fqClassName.startsWith(prefix))
{
return true;
}
}
return false;
}
}
public static String lookupType(String typeOrKlassID, IParseDictionary parseDictionary)
{
String result = null;
boolean isType = true;
if (typeOrKlassID != null)
{
Tag typeTag = parseDictionary.getType(typeOrKlassID);
if (typeTag == null)
{
typeTag = parseDictionary.getKlass(typeOrKlassID);
isType = false;
}
if (typeTag != null)
{
String typeAttrName = typeTag.getAttributes().get(ATTR_NAME);
if (typeAttrName != null)
{
typeAttrName = typeAttrName.replace(S_SLASH, S_DOT);
if (isType || typeAttrName.startsWith(S_OPEN_SQUARE_BRACKET))
{
result = expandParseDictionaryTypeName(typeAttrName);
}
else
{
result = typeAttrName;
}
}
}
}
return result;
}
public static String expandParseDictionaryTypeName(String typeName)
{
String result = null;
if (typeName != null)
{
result = typeName.replace(S_SLASH, S_DOT);
result = expandParameterType(result);
}
return result;
}
public static String getPackageFromSource(String source)
{
String result = null;
String[] lines = source.split(S_NEWLINE);
for (String line : lines)
{
line = line.trim();
if (line.startsWith(S_PACKAGE) && line.endsWith(S_SEMICOLON))
{
result = line.substring(S_PACKAGE.length(), line.length() - 1).trim();
}
}
if (result == null)
{
result = S_EMPTY;
}
return result;
}
public static String getClassFromSource(String source)
{
String result = null;
String[] lines = source.split(S_NEWLINE);
String classToken = S_SPACE + S_CLASS + S_SPACE;
for (String line : lines)
{
line = line.trim();
int classTokenPos = line.indexOf(classToken);
if (classTokenPos != -1)
{
result = line.substring(classTokenPos + classToken.length());
}
}
if (result == null)
{
result = "";
}
return result;
}
public static String bytecodeMethodCommentToReadableString(String className, String comment)
{
StringBuilder builder = new StringBuilder();
if (bytecodeMethodCommentHasNoClassPrefix(comment))
{
comment = className.replace(S_DOT, S_SLASH) + C_DOT + comment;
}
String logCompilationSignature = bytecodeCommentSignatureToLogCompilationSignature(comment);
try
{
String[] parts = ParseUtil.splitLogSignatureWithRegex(logCompilationSignature);
String fullyQualifiedClassName = parts[0];
String memberName = parts[1];
String paramTypes = parts[2];
builder.append(fullyQualifiedClassName).append(S_DOT);
builder.append(memberName).append(S_OPEN_PARENTHESES);
List<String> paramTypeNames = parseTypeString(paramTypes);
if (paramTypeNames.size() > 0)
{
for (String paramTypeName : paramTypeNames)
{
builder.append(expandParameterType(paramTypeName));
builder.append(C_COMMA);
}
builder.deleteCharAt(builder.length() - 1);
}
builder.append(S_CLOSE_PARENTHESES);
}
catch (LogParseException e)
{
e.printStackTrace();
}
return builder.toString();
}
public static boolean bytecodeMethodCommentHasNoClassPrefix(String comment)
{
return (comment.indexOf(C_DOT) == -1);
}
private static String prependCurrentMember(String comment, IMetaMember member)
{
String currentClass = member.getMetaClass().getFullyQualifiedName();
currentClass = currentClass.replace(C_DOT, C_SLASH);
return currentClass + C_DOT + comment;
}
public static String bytecodeCommentSignatureToLogCompilationSignature(String bytcodeCommentSignature)
{
return bytcodeCommentSignature.replace(C_DOT, C_SPACE).replace(C_COLON, C_SPACE).replace(C_SLASH, C_DOT)
.replace(S_DOUBLE_QUOTE, S_EMPTY);
}
public static IMetaMember getMemberFromBytecodeComment(IReadOnlyJITDataModel model, IMetaMember currentMember,
BytecodeInstruction instruction) throws LogParseException
{
IMetaMember result = null;
if (DEBUG_LOGGING_OVC)
{
logger.debug("Looking for member in {} using {}", currentMember, instruction);
}
if (instruction != null)
{
String comment = instruction.getCommentWithMemberPrefixStripped();
if (comment != null)
{
if (bytecodeMethodCommentHasNoClassPrefix(comment) && currentMember != null)
{
comment = prependCurrentMember(comment, currentMember);
}
MemberSignatureParts msp = MemberSignatureParts.fromBytecodeComment(comment);
result = model.findMetaMember(msp);
}
}
return result;
}
}