Last active
January 2, 2016 12:32
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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