Adds utility that checks which classes are implemented in JCL emulation

and produces JSON report
This commit is contained in:
konsoletyper 2014-03-05 17:47:47 +04:00
parent d714e880d3
commit a29318668e
4 changed files with 293 additions and 1 deletions

View File

@ -58,6 +58,34 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
<phase>process-test-classes</phase>
</execution>
</executions>
<configuration>
<mainClass>org.teavm.classlib.impl.JCLComparisonBuilder</mainClass>
<arguments>
<argument>java.lang</argument>
<argument>java.lang.annotation</argument>
<argument>java.lang.reflect</argument>
<argument>java.io</argument>
<argument>java.net</argument>
<argument>java.util</argument>
<argument>java.util.logging</argument>
<argument>java.util.concurrent</argument>
<argument>-output</argument>
<argument>${project.build.directory}/jcl-report/jcl-comparision.json</argument>
</arguments>
</configuration>
</plugin>
</plugins> </plugins>
<pluginManagement> <pluginManagement>
<plugins> <plugins>

View File

@ -0,0 +1,121 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.impl;
import java.io.*;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.objectweb.asm.ClassReader;
import org.teavm.parsing.ClasspathClassHolderSource;
/**
*
* @author Alexey Andreev
*/
public class JCLComparisonBuilder {
private static final String JAR_PREFIX = "jar:file:";
private static final String JAR_SUFFIX = "!/java/lang/Object.class";
private static final String CLASS_SUFFIX = ".class";
private Set<String> packages = new HashSet<>();
private ClassLoader classLoader = JCLComparisonBuilder.class.getClassLoader();
private JCLComparisonVisitor visitor;
private String outputFile;
public ClassLoader getClassLoader() {
return classLoader;
}
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public Set<String> getPackages() {
return packages;
}
public String getOutputFile() {
return outputFile;
}
public void setOutputFile(String outputFile) {
this.outputFile = outputFile;
}
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.err.println("Usage: package.name [package.name2 ...]");
System.exit(1);
}
JCLComparisonBuilder builder = new JCLComparisonBuilder();
for (int i = 0; i < args.length; ++i) {
if (args[i].equals("-output")) {
builder.setOutputFile(args[++i]);
} else {
builder.getPackages().add(args[i].replace('.', '/'));
}
}
builder.buildComparisonReport();
}
public void buildComparisonReport() throws IOException {
URL url = classLoader.getResource("java/lang/Object" + CLASS_SUFFIX);
String path = url.toString();
if (!path.startsWith(JAR_PREFIX) || !path.endsWith(JAR_SUFFIX)) {
throw new RuntimeException("Can't find JCL classes");
}
ClasspathClassHolderSource classSource = new ClasspathClassHolderSource(classLoader);
path = path.substring(JAR_PREFIX.length(), path.length() - JAR_SUFFIX.length());
File outDir = new File(outputFile).getParentFile();
if (!outDir.exists()) {
outDir.mkdirs();
}
try (JarInputStream jar = new JarInputStream(new FileInputStream(path));
PrintStream out = new PrintStream(new FileOutputStream(outputFile))) {
out.println("{");
visitor = new JCLComparisonVisitor(classSource, out);
while (true) {
JarEntry entry = jar.getNextJarEntry();
if (entry == null) {
break;
}
if (validateName(entry.getName())) {
compareClass(jar);
}
jar.closeEntry();
}
out.println();
out.println("}");
}
}
private boolean validateName(String name) {
if (!name.endsWith(CLASS_SUFFIX)) {
return false;
}
int slashIndex = name.lastIndexOf('/');
return packages.contains(name.substring(0, slashIndex));
}
private void compareClass(InputStream input) throws IOException {
ClassReader reader = new ClassReader(input);
reader.accept(visitor, 0);
}
}

View File

@ -0,0 +1,143 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.impl;
import java.io.PrintStream;
import org.objectweb.asm.*;
import org.teavm.model.*;
import org.teavm.model.ClassReader;
/**
*
* @author Alexey Andreev
*/
class JCLComparisonVisitor implements ClassVisitor {
private PrintStream out;
private ClassReaderSource classSource;
private boolean first = true;
private boolean firstItem;
private boolean pass;
private boolean ended;
private ClassReader classReader;
public JCLComparisonVisitor(ClassReaderSource classSource, PrintStream out) {
this.classSource = classSource;
this.out = out;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
if ((access & Opcodes.ACC_PUBLIC) == 0) {
return;
}
String javaName = name.replace('/', '.');
if (!first) {
out.println(",");
}
first = false;
out.println(" \"" + javaName + "\" : {");
classReader = classSource.get(javaName);
if (classReader == null) {
out.println(" \"implemented\" : false");
pass = true;
} else {
out.println(" \"implemented\" : true,");
out.println(" \"items\" : [");
pass = false;
}
ended = false;
firstItem = true;
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if (pass) {
return null;
}
if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
return null;
}
if (!firstItem) {
out.println(",");
}
firstItem = false;
out.println(" {");
out.println(" \"type\" : \"field\",");
out.println(" \"name\" : \"" + name + "\",");
out.println(" \"descriptor\" : \"" + desc + "\",");
FieldReader field = classReader.getField(name);
out.println(" \"implemented\" : \"" + (field != null ? "true" : "false") + "\",");
out.print(" }");
return null;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (pass) {
return null;
}
if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
return null;
}
if (!firstItem) {
out.println(",");
}
firstItem = false;
out.println(" {");
out.println(" \"type\" : \"method\",");
out.println(" \"name\" : \"" + name + "\",");
out.println(" \"descriptor\" : \"" + desc + "\",");
MethodReader method = classReader.getMethod(MethodDescriptor.parse(name + desc));
out.println(" \"implemented\" : \"" + (method != null ? "true" : "false") + "\",");
out.print(" }");
return null;
}
@Override
public void visitSource(String source, String debug) {
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
@Override
public void visitAttribute(Attribute attr) {
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
}
@Override
public void visitEnd() {
if (!ended) {
if (!pass) {
if (!firstItem) {
out.println();
}
out.println(" ]");
}
out.print(" }");
ended = true;
}
}
}

View File

@ -93,7 +93,7 @@ public class ClasspathResourceMapper implements Mapper<String, ClassHolder> {
if (name.startsWith(transformation.packageName)) { if (name.startsWith(transformation.packageName)) {
int index = name.lastIndexOf('.'); int index = name.lastIndexOf('.');
String className = name.substring(index + 1); String className = name.substring(index + 1);
String packageName = name.substring(0, index); String packageName = index > 0 ? name.substring(0, index) : "";
ClassHolder classHolder = innerMapper.map(transformation.packagePrefix + packageName + ClassHolder classHolder = innerMapper.map(transformation.packagePrefix + packageName +
"." + transformation.classPrefix + className); "." + transformation.classPrefix + className);
if (classHolder != null) { if (classHolder != null) {