Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Last active January 2, 2016 12:32
Show Gist options
  • Save thomasdarimont/fa0d441c7a1662c20271 to your computer and use it in GitHub Desktop.
Save thomasdarimont/fa0d441c7a1662c20271 to your computer and use it in GitHub Desktop.
Quick ugly hack.. to diff Java 8 vs. Java 9 Runtime to find newly introduced Types / Methods.
mvn exec:java -q -Dexec.mainClass="training.Java9Changes" -Dexec.args="/usr/lib/jvm/java-8-oracle/jre/lib/" > classes-java8-2.txt
mvn exec:java -q -Dexec.mainClass="training.Java9Changes" -Dexec.args="/usr/lib/jvm/java-9-oracle/lib/modules/" > classes-java9.txt
sort -g < classes-java8.txt > classes-java8-sorted.txt
sort -g < classes-java9.txt > classes-java9-sorted.txt
meld classes-java8-sorted.txt classes-java9-sorted.txt
package training;
import jdk.internal.jimage.ImageLocation;
import jdk.internal.jimage.ImageReader;
import jdk.internal.jimage.ImageReaderFactory;
import org.objectweb.asm.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
public class Java9Changes {
public static void main(String[] args) throws Exception {
JavaRuntimeAnalyzer analyzer = new DelegatingJavaRuntimeAnalyzer();
Files.newDirectoryStream(Paths.get(args[0])).forEach(analyzer::analyze);
//Files.newDirectoryStream(Paths.get("/usr/lib/jvm/java-9-oracle/lib/modules/")).forEach(analyzer::analyze);
//Files.newDirectoryStream(Paths.get("/usr/lib/jvm/java-8-oracle/jre/lib/")).forEach(analyzer::analyze);
}
interface JavaRuntimeAnalyzer {
void analyze(Path path);
}
static class DelegatingJavaRuntimeAnalyzer implements JavaRuntimeAnalyzer {
JavaRuntimeAnalyzer java8Analyzer = new Java8RuntimeAnalyzer();
JavaRuntimeAnalyzer java9Analyzer = new Java9RuntimeAnalyzer();
@Override
public void analyze(Path path) {
if (path.getFileName().toString().endsWith(".jimage")) {
java9Analyzer.analyze(path);
} else {
java8Analyzer.analyze(path);
}
}
}
static class Java8RuntimeAnalyzer implements JavaRuntimeAnalyzer {
protected void analyzeClassBytes(byte[] classBytes) {
ClassReader cr = new ClassReader(classBytes);
List<MethodInfo> methodInfos = new ArrayList<>();
TypeInfo typeInfo = new TypeInfo();
cr.accept(new TypeInfoCollectingClassVisitor(typeInfo, methodInfos), Opcodes.ASM5);
System.out.println(typeInfo.toMethodLines());
}
public void analyze(Path jarPath) {
if (!jarPath.getFileName().toString().endsWith(".jar")) {
return;
}
try (JarFile jarFile = new JarFile(jarPath.toFile())) {
jarFile.stream().filter(entry -> entry.getName().endsWith(".class")).forEach(entry -> {
try (InputStream is = jarFile.getInputStream(entry)) {
analyzeClassBytes(is.readAllBytes());
} catch (IOException e) {
e.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
static class Java9RuntimeAnalyzer extends Java8RuntimeAnalyzer {
public void analyze(Path modulePath) {
ImageReader imageReader = getImageReader(modulePath);
Arrays.stream(imageReader.getEntryNames()).forEach(entryName -> analyzeEntry(imageReader, entryName));
}
private void analyzeEntry(ImageReader imageReader, String entryName) {
if (!entryName.endsWith(".class")) {
//skipping non class files
return;
}
ImageLocation entryLocation = imageReader.findLocation(entryName);
byte[] classBytes = imageReader.getResource(entryLocation);
analyzeClassBytes(classBytes);
}
private ImageReader getImageReader(Path modulePath) {
ImageReader imageReader = null;
try {
imageReader = ImageReaderFactory.get(modulePath);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (imageReader != null) {
try {
imageReader.close();
} catch (IOException ignore) {
ignore.printStackTrace();
}
}
}
return imageReader;
}
}
static class ClassVisitorAdapter extends ClassVisitor {
public ClassVisitorAdapter() {
super(Opcodes.ASM5);
}
}
static class TypeInfoCollectingClassVisitor extends ClassVisitorAdapter {
private final TypeInfo typeInfo;
private final List<MethodInfo> methodInfos;
TypeInfoCollectingClassVisitor(TypeInfo typeInfo, List<MethodInfo> methodInfos) {
this.typeInfo = typeInfo;
this.methodInfos = methodInfos;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
int packageClassNameSplit = name.lastIndexOf("/");
typeInfo.setTypeName(name);
typeInfo.setSimpleName(name.substring(packageClassNameSplit + 1));
typeInfo.setPackageName(name.substring(0, packageClassNameSplit).replace("/", "."));
typeInfo.setMethodInfos(methodInfos);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
String methodName = name;
Type[] argumentTypes = Type.getArgumentTypes(desc);
Type returnType = Type.getReturnType(desc);
if (methodName.equals("<init>")) {
returnType = Type.VOID_TYPE;
}
String argumentTypesString = Arrays.stream(argumentTypes).map(t -> t.getClassName()).collect(Collectors.joining(", "));
String returnTypeString = returnType.getClassName();
methodInfos.add(new MethodInfo(methodName, argumentTypesString, returnTypeString));
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
static class TypeInfo {
String typeName;
String simpleName;
String packageName;
List<MethodInfo> methodInfos;
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public List<MethodInfo> getMethodInfos() {
return methodInfos;
}
public void setMethodInfos(List<MethodInfo> methodInfos) {
this.methodInfos = methodInfos;
}
public String getSimpleName() {
return simpleName;
}
public void setSimpleName(String simpleName) {
this.simpleName = simpleName;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String toMethodLines() {
String type = getPackageName() + " " + getSimpleName();
String result = methodInfos.stream().map(m -> type + " " + m.getName() + " " + m.getReturnType() + " (" + m.getArgumentTypes() + ")").collect(Collectors.joining("\n"));
return result;
}
public String toString() {
String type = getPackageName() + "." + getSimpleName();
String methods = methodInfos.stream().map(s -> " " + s).collect(Collectors.joining("\n"));
String result = "Type:\n" + type + "\n" + "Methods:\n" + methods + "\n###";
return result;
}
}
static class MethodInfo {
String name;
String argumentTypes;
String returnType;
public MethodInfo(String name, String argumentTypes, String returnType) {
this.name = name;
this.argumentTypes = argumentTypes;
this.returnType = returnType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getArgumentTypes() {
return argumentTypes;
}
public void setArgumentTypes(String argumentTypes) {
this.argumentTypes = argumentTypes;
}
public String getReturnType() {
return returnType;
}
public void setReturnType(String returnType) {
this.returnType = returnType;
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.jugsaar</groupId>
<artifactId>training-java9</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.0.3</version>
</dependency>
</dependencies>
</project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment