Skip to content

Commit

Permalink
Fix JavapTask fallback for JDK17 reflection failure (module system)
Browse files Browse the repository at this point in the history
  • Loading branch information
chriswhocodes committed Jan 4, 2021
1 parent 7a913d8 commit bcb5ba8
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SLASH;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SPACE;

import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -71,8 +72,8 @@ public final class BytecodeLoader
{
private static final Logger logger = LoggerFactory.getLogger(BytecodeLoader.class);

private static final Pattern PATTERN_BYTECODE_INSTRUCTION = Pattern
.compile("^([0-9]+):\\s([0-9a-z_]+)\\s?([#0-9a-z,\\- ]+)?\\s?\\{?\\s?(//.*)?");
private static final Pattern PATTERN_BYTECODE_INSTRUCTION = Pattern.compile(
"^([0-9]+):\\s([0-9a-z_]+)\\s?([#0-9a-z,\\- ]+)?\\s?\\{?\\s?(//.*)?");

enum BytecodeSection
{
Expand Down Expand Up @@ -114,7 +115,7 @@ public static ClassBC fetchBytecodeForClass(List<String> classLocations, String

public static ClassBC fetchBytecodeForClass(List<String> classLocations, String fqClassName, Path javapPath,
boolean cacheBytecode)
{
{
if (DEBUG_LOGGING_BYTECODE)
{
logger.debug("fetchBytecodeForClass: {}", fqClassName);
Expand All @@ -123,45 +124,59 @@ public static ClassBC fetchBytecodeForClass(List<String> classLocations, String
}

ClassBC classBytecode = null;


String byteCodeString = null;

try
{
String byteCodeString = null;

{
if (ReflectionJavap.canUseReflectionJavap())
{
//TimerUtil.timerStart("javap");
byteCodeString = ReflectionJavap.getBytecode(classLocations, fqClassName);
//TimerUtil.timerEnd("javap");
}
else
{
JavapProcess javapProcess;

if (javapPath != null)
try
{
javapProcess = new JavapProcess(javapPath);
byteCodeString = ReflectionJavap.getBytecode(classLocations, fqClassName);
}
else
catch (Exception e)
{
javapProcess = new JavapProcess();
logger.info("Could not fetch bytecode via reflection, trying Process");

byteCodeString = getBytecodeStringViaProcess(classLocations, fqClassName, javapPath);
}

javapProcess.execute(classLocations, fqClassName);

byteCodeString = javapProcess.getOutputStream();
}

classBytecode = parseByteCodeFromString(fqClassName, byteCodeString, cacheBytecode);
else
{
byteCodeString = getBytecodeStringViaProcess(classLocations, fqClassName, javapPath);
}

classBytecode = parseByteCodeFromString(fqClassName, byteCodeString, cacheBytecode);

}
catch (Exception e)
{
logger.error("Could not fetch bytecode for {}", fqClassName, e);
}

return classBytecode;
}

private static String getBytecodeStringViaProcess(List<String> classLocations, String fqClassName, Path javapPath)
throws IOException
{
JavapProcess javapProcess;

if (javapPath != null)
{
javapProcess = new JavapProcess(javapPath);
}
else
{
javapProcess = new JavapProcess();
}

javapProcess.execute(classLocations, fqClassName);

return javapProcess.getOutputStream();
}

private static ClassBC parseByteCodeFromString(String fqClassName, String byteCodeString, boolean cacheBytecode)
{
ClassBC result = null;
Expand Down Expand Up @@ -197,7 +212,7 @@ public static ClassBC parse(String fqClassName, String[] bytecodeLines, boolean
MemberSignatureParts msp = null;

MemberBytecode memberBytecode = null;

while (pos < bytecodeLines.length)
{
String line = bytecodeLines[pos].trim();
Expand Down Expand Up @@ -362,7 +377,7 @@ else if (line.startsWith(S_BYTECODE_SOURCE_FILE))

pos++;
}

return classBytecode;
}

Expand Down Expand Up @@ -549,8 +564,8 @@ private static boolean isStackMapTable(final String line)

private static boolean couldBeMemberSignature(String line)
{
return line.endsWith(");") || line.contains(" throws ") && line.endsWith(S_SEMICOLON)
|| line.startsWith(S_BYTECODE_STATIC_INITIALISER_SIGNATURE);
return line.endsWith(");") || line.contains(" throws ") && line.endsWith(S_SEMICOLON) || line.startsWith(
S_BYTECODE_STATIC_INITIALISER_SIGNATURE);
}

private static void sectionFinished(String fqClassName, BytecodeSection lastSection, MemberSignatureParts msp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ protected boolean runCommands(List<String> commands, File workingDirectory, Map<
cmdBuilder.append(part).append(C_SPACE);
}

logger.info("Process: {}", cmdBuilder.toString());

if (logListener != null)
{
logListener.handleLogEntry("Running: " + cmdBuilder.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import java.util.List;

import org.adoptopenjdk.jitwatch.process.AbstractProcess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JavapProcess extends AbstractProcess
{
Expand All @@ -26,6 +28,8 @@ public class JavapProcess extends AbstractProcess
public JavapProcess(Path executablePath)
{
this.executablePath = executablePath;

logger.info("JavapProcess({})", executablePath);
}

public JavapProcess() throws FileNotFoundException
Expand All @@ -47,6 +51,8 @@ public JavapProcess() throws FileNotFoundException
}

executablePath = executablePath.normalize();

logger.info("JavapProcess() executablePath: {}", executablePath);
}

private List<String> getJavapCommands(Collection<String> classLocations, String fqClassName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,23 @@ public class ReflectionJavap
private static final Logger logger = LoggerFactory.getLogger(ReflectionJavap.class);

private static final String JAVAPTASK_CLASS = "com.sun.tools.javap.JavapTask";

private static final int BUFFER_SIZE = 64 * 1024;

private static boolean hasCheckedForToolsJar = false;

private static boolean canUseReflectionJavap = false;

private static Class<?> classJavapTask;

private static URL locateToolsJar()
{
Path javaHome = Paths.get(System.getProperty("java.home"));

Path toolsJarPath = Paths.get(javaHome.toString(), "..", "lib", "tools.jar").normalize();

URL result = null;

if (toolsJarPath.toFile().exists())
{
try
Expand All @@ -55,7 +55,7 @@ private static URL locateToolsJar()
e.printStackTrace();
}
}

return result;
}

Expand All @@ -64,23 +64,23 @@ public static boolean canUseReflectionJavap()
if (!canUseReflectionJavap)
{
boolean available = false;

try
{
classJavapTask = Class.forName(JAVAPTASK_CLASS);
}
catch (ClassNotFoundException cnfe)
{
URL toolsJarURL = locateToolsJar();

if (toolsJarURL != null)
{
List<URL> urls = new ArrayList<>();

urls.add(toolsJarURL);

DisposableURLClassLoader disposableClassLoader = new DisposableURLClassLoader(urls);

try
{
classJavapTask = Class.forName(JAVAPTASK_CLASS, false, disposableClassLoader);
Expand All @@ -91,30 +91,30 @@ public static boolean canUseReflectionJavap()
}
}
}

if (classJavapTask != null)
{
try
{
classJavapTask.getMethod("setLog", new Class[] { OutputStream.class });
classJavapTask.getMethod("handleOptions", new Class[] { String[].class });
classJavapTask.getMethod("call", new Class[] {});

available = true;
}
catch (NoSuchMethodException | SecurityException e)
{
e.printStackTrace();
}
}

canUseReflectionJavap = available;
}

return canUseReflectionJavap;
}

public static String getBytecode(List<String> classLocations, String fqClassName)
public static String getBytecode(List<String> classLocations, String fqClassName) throws Exception
{
String[] args = buildClassPathFromClassLocations(classLocations, fqClassName);

Expand All @@ -123,33 +123,27 @@ public static String getBytecode(List<String> classLocations, String fqClassName
return byteCodeString;
}

private static String createJavapTaskFromArguments(String fqClassName, String[] args)
private static String createJavapTaskFromArguments(String fqClassName, String[] args) throws Exception
{
String byteCodeString = null;

if (classJavapTask != null)
{
try
{
Constructor<?> constructor = classJavapTask.getDeclaredConstructor();
Object javapObject = constructor.newInstance();

Method methodSetLog = classJavapTask.getMethod("setLog", new Class[] { OutputStream.class });
Method methodHandleOptions = classJavapTask.getMethod("handleOptions", new Class[] { String[].class });
Method methodCall = classJavapTask.getMethod("call", new Class[] {});

try (ByteArrayOutputStream baos = new ByteArrayOutputStream(BUFFER_SIZE))
{
methodSetLog.invoke(javapObject, baos);
methodHandleOptions.invoke(javapObject, new Object[] {args});
methodCall.invoke(javapObject);

byteCodeString = baos.toString();
}
}
catch (Exception e)

Constructor<?> constructor = classJavapTask.getDeclaredConstructor();
Object javapObject = constructor.newInstance();

Method methodSetLog = classJavapTask.getMethod("setLog", new Class[] { OutputStream.class });
Method methodHandleOptions = classJavapTask.getMethod("handleOptions", new Class[] { String[].class });
Method methodCall = classJavapTask.getMethod("call", new Class[] {});

try (ByteArrayOutputStream baos = new ByteArrayOutputStream(BUFFER_SIZE))
{
logger.error("Could not load bytecode via reflection", e);
methodSetLog.invoke(javapObject, baos);
methodHandleOptions.invoke(javapObject, new Object[] { args });
methodCall.invoke(javapObject);

byteCodeString = baos.toString();
}
}
return byteCodeString;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ private boolean executeClass(String fqClassName, IRuntime runtime, boolean intel

List<String> options = new ArrayList<>();
options.add("-XX:+UnlockDiagnosticVMOptions");
options.add("-XX:+TraceClassLoading");
//options.add("-XX:+TraceClassLoading");
options.add("-XX:+LogCompilation");
options.add("-XX:LogFile=" + sandboxLogFile.getCanonicalPath());

Expand Down

0 comments on commit bcb5ba8

Please sign in to comment.