All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gr.gousiosg.javacg.stat.DynamicCallManager Maven / Gradle / Ivy

The newest version!
/*
 * Written in 2018 by Matthieu Vergne 
 *
 *     To the extent possible under law, the author(s) have dedicated all
 *     copyright and related and neighboring rights to this software to
 *     the public domain worldwide. This software is distributed without
 *     any warranty.
 *
 *     You should have received a copy of the CC0 Public Domain Dedication
 *     along with this software. If not,
 *     see .
 */

package gr.gousiosg.javacg.stat;

import org.apache.bcel.classfile.*;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * {@link DynamicCallManager} provides facilities to retrieve information about
 * dynamic calls statically.
 * 

* Most of the time, call relationships are explicit, which allows to properly * build the call graph statically. But in the case of dynamic linking, i.e. * invokedynamic instructions, this relationship might be unknown * until the code is actually executed. Indeed, bootstrap methods are used to * dynamically link the code at first call. One can read details about the * invokedynamic * instruction to know more about this mechanism. *

* Nested lambdas are particularly subject to such absence of concrete caller, * which lead us to produce method names like lambda$null$0, which * breaks the call graph. This information can however be retrieved statically * through the code of the bootstrap method called. *

* In {@link #retrieveCalls(Method, JavaClass)}, we retrieve the (called, * caller) relationships by analyzing the code of the caller {@link Method}. * This information is then used in {@link #linkCalls(Method)} to rename the * called {@link Method} properly. * * @author Matthieu Vergne */ public class DynamicCallManager { private static final Pattern BOOTSTRAP_CALL_PATTERN = Pattern .compile("invokedynamic\t(\\d+):\\S+ \\S+ \\(\\d+\\)"); private static final int CALL_HANDLE_INDEX_ARGUMENT = 1; private final Map dynamicCallers = new HashMap<>(); // added by adrninistrator private Set lambdaMethodNameSet = new HashSet<>(); // added end /** * Retrieve dynamic call relationships based on the code of the provided * {@link Method}. * * @param method {@link Method} to analyze the code * @param jc {@link JavaClass} info, which contains the bootstrap methods * @see #linkCalls(Method) */ public void retrieveCalls(Method method, JavaClass jc) { if (method.isAbstract() || method.isNative()) { // No code to consider return; } ConstantPool cp = method.getConstantPool(); BootstrapMethod[] boots = getBootstrapMethods(jc); String code = method.getCode().toString(); Matcher matcher = BOOTSTRAP_CALL_PATTERN.matcher(code); while (matcher.find()) { int bootIndex = Integer.parseInt(matcher.group(1)); BootstrapMethod bootMethod = boots[bootIndex]; int calledIndex = bootMethod.getBootstrapArguments()[CALL_HANDLE_INDEX_ARGUMENT]; String calledName = getMethodNameFromHandleIndex(cp, calledIndex); // added by adrninistrator if (calledName.startsWith("lambda$") && !calledName.contains("lambda$null$") && !lambdaMethodNameSet.contains(calledName)) { lambdaMethodNameSet.add(calledName); } // added end String callerName = method.getName(); dynamicCallers.put(calledName, callerName); } } private String getMethodNameFromHandleIndex(ConstantPool cp, int callIndex) { ConstantMethodHandle handle = (ConstantMethodHandle) cp.getConstant(callIndex); ConstantCP ref = (ConstantCP) cp.getConstant(handle.getReferenceIndex()); ConstantNameAndType nameAndType = (ConstantNameAndType) cp.getConstant(ref.getNameAndTypeIndex()); return nameAndType.getName(cp); } /** * Link the {@link Method}'s name to its concrete caller if required. * * @param method {@link Method} to analyze * @see #retrieveCalls(Method, JavaClass) */ public void linkCalls(Method method) { int nameIndex = method.getNameIndex(); ConstantPool cp = method.getConstantPool(); String methodName = ((ConstantUtf8) cp.getConstant(nameIndex)).getBytes(); String linkedName = methodName; String callerName = methodName; while (linkedName.matches("(lambda\\$)+null(\\$\\d+)+")) { callerName = dynamicCallers.get(callerName); linkedName = linkedName.replace("null", callerName); } cp.setConstant(nameIndex, new ConstantUtf8(linkedName)); } private BootstrapMethod[] getBootstrapMethods(JavaClass jc) { for (Attribute attribute : jc.getAttributes()) { if (attribute instanceof BootstrapMethods) { return ((BootstrapMethods) attribute).getBootstrapMethods(); } } return new BootstrapMethod[]{}; } public Set getLambdaMethodNameSet() { return lambdaMethodNameSet; } public void clearLambdaMethodNameSet() { lambdaMethodNameSet.clear(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy