/* * 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; } }