Skip to content

Commit

Permalink
Populate default methods, when interfaces are in classpath and not in…
Browse files Browse the repository at this point in the history
… input directory
  • Loading branch information
teras committed Jun 15, 2016
1 parent 93ff6ad commit c506d63
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static void run(Config config) throws Throwable {

ClassAnalyzer analyzer = new ClassAnalyzer();
OutputDirectory outputDirectory = new OutputDirectory(outputDir);
outputDirectory.setClassNamePredicate(analyzer.getLibraryInterfaces().getIgnoreLibraryPredicate());
Transformers transformers = new Transformers(bytecodeVersion, defaultMethodsEnabled, analyzer);
LambdaClassSaver lambdaClassSaver = new LambdaClassSaver(outputDirectory, transformers);

Expand All @@ -61,6 +62,8 @@ protected void visitResource(Path relativePath, byte[] content) throws IOExcepti
outputDirectory.writeFile(relativePath, content);
}
});
for(byte[] interf : analyzer.getLibraryInterfaces().getMissingInterfaces(classpath))
analyzer.analyze(interf);

// Because Transformers.backportLambdaClass() analyzes the lambda class,
// adding it to the analyzer's list of classes, we must take care to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@

import java.io.IOException;
import java.nio.file.*;
import java.util.function.Predicate;

public class OutputDirectory {

private final Path outputDir;
private Predicate<String> classNamePredicate;

public OutputDirectory(Path outputDir) {
this.outputDir = outputDir;
Expand All @@ -22,13 +24,19 @@ public void writeClass(byte[] bytecode) throws IOException {
return;
}
ClassReader cr = new ClassReader(bytecode);
Path relativePath = outputDir.getFileSystem().getPath(cr.getClassName() + ".class");
writeFile(relativePath, bytecode);
if (classNamePredicate == null || classNamePredicate.test(cr.getClassName())) {
Path relativePath = outputDir.getFileSystem().getPath(cr.getClassName() + ".class");
writeFile(relativePath, bytecode);
}
}

public void writeFile(Path relativePath, byte[] content) throws IOException {
Path outputFile = outputDir.resolve(relativePath);
Files.createDirectories(outputFile.getParent());
Files.write(outputFile, content);
}

public void setClassNamePredicate(Predicate<String> classNamePredicate) {
this.classNamePredicate = classNamePredicate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class ClassAnalyzer {

private final Map<Type, ClassInfo> classes = new HashMap<>();
private final Map<MethodRef, MethodRef> relocatedMethods = new HashMap<>();
private final LibraryInterfaces libraryInterfaces = new LibraryInterfaces();

public void analyze(byte[] bytecode) {
analyze(new ClassReader(bytecode));
Expand All @@ -40,6 +41,7 @@ private void analyzeClass(ClassInfo c, ClassReader cr) {

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
libraryInterfaces.addRequiredInterfaces(interfaces);
this.owner = name;
}

Expand Down Expand Up @@ -70,6 +72,7 @@ private void analyzeInterface(ClassInfo c, ClassReader cr) {
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.owner = name;
this.companion = name + "$";
libraryInterfaces.addFoundInterface(name);
}

@Override
Expand Down Expand Up @@ -97,6 +100,10 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
}, ClassReader.SKIP_CODE);
}

public LibraryInterfaces getLibraryInterfaces() {
return libraryInterfaces;
}

public static boolean isDefaultMethod(int access) {
return !isAbstractMethod(access)
&& !isStaticMethod(access)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright © 2016 Panayotis Katsaloulis <www.panayotis.com>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package net.orfjackal.retrolambda.interfaces;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.function.Predicate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class LibraryInterfaces {

private final Collection<String> requiredInterfaces = new HashSet<>();
private final Collection<String> inputDirInterfaces = new HashSet<>();
private final Collection<String> resolvedInterfaces = new HashSet<>();

public void addRequiredInterfaces(String[] interfaceNames) {
requiredInterfaces.addAll(Arrays.asList(interfaceNames));
}

public void addFoundInterface(String interfaceName) {
inputDirInterfaces.add(interfaceName);
}

public Collection<byte[]> getMissingInterfaces(List<Path> classpath) {
Collection<String> missingInterfaces = new HashSet<>(requiredInterfaces);
missingInterfaces.removeAll(inputDirInterfaces);
Collection<String> justFound = new HashSet<>();
Collection<byte[]> result = new HashSet<>();

for (Path path : classpath) {
if (Files.isDirectory(path))
for (String missingName : missingInterfaces) {
Path possibleTarget = path.resolve(missingName + ".class");
if (Files.isRegularFile(possibleTarget))
try {
result.add(Files.readAllBytes(possibleTarget));
justFound.add(missingName);
} catch (IOException ex) {
}
}
else if (Files.isRegularFile(path) && path.getFileName().toString().toLowerCase().endsWith(".jar")) {
JarFile jar = null;
try {
jar = new JarFile(path.toFile());
} catch (IOException ex) {
}
if (jar != null)
for (String missingName : missingInterfaces) {
JarEntry entry = jar.getJarEntry(missingName + ".class");
if (entry != null)
try (InputStream inputStream = jar.getInputStream(entry)) {
byte[] bytes = getBytes(inputStream);
if (bytes != null)
result.add(bytes);
justFound.add(missingName);
} catch (IOException ex) {
}
}
}
missingInterfaces.removeAll(justFound);
resolvedInterfaces.addAll(justFound);
justFound.clear();
}
return result;
}

public Predicate<String> getIgnoreLibraryPredicate() {
return className -> !resolvedInterfaces.contains(className);
}

private byte[] getBytes(InputStream in) {
ByteArrayOutputStream out = new ByteArrayOutputStream(100);
int value;
try {
while ((value = in.read()) >= 0)
out.write(value);
} catch (IOException ex) {
return null;
}
return out.toByteArray();
}

}

0 comments on commit c506d63

Please sign in to comment.