Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added retrolambda.javaHacks configuration option #143

Merged
merged 3 commits into from
Aug 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ Configurable system properties:
Alternative to retrolambda.includedFiles for avoiding the command line
length limit. The file must list one file per line with UTF-8 encoding.

retrolambda.javacHacks
Attempts to fix javac bugs (type-annotation emission for local variables).
Disabled by default. Enable by setting to "true"

retrolambda.quiet
Reduces the amount of logging.
Disabled by default. Enable by setting to "true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ public class RetrolambdaApi {
public static final String INPUT_DIR = PREFIX + "inputDir";
public static final String DEFAULT_METHODS = PREFIX + "defaultMethods";
public static final String BYTECODE_VERSION = PREFIX + "bytecodeVersion";
public static final String JAVAC_HACKS = PREFIX + "javacHacks";
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ abstract class ProcessClassesMojo extends AbstractMojo {
@Parameter(defaultValue = "false", property = "retrolambdaDefaultMethods", required = true)
public boolean defaultMethods;

/**
* Whether to apply experimental javac issues workarounds.
*
* @since 2.5.5
*/
@Parameter(defaultValue = "false", property = "retrolambdaJavacHacks", required = true)
public boolean javacHacks;

/**
* Reduces the amount of logging.
*
Expand Down Expand Up @@ -117,6 +125,7 @@ public void execute() throws MojoExecutionException {
config.setProperty(RetrolambdaApi.INPUT_DIR, getInputDir().getAbsolutePath());
config.setProperty(RetrolambdaApi.OUTPUT_DIR, getOutputDir().getAbsolutePath());
config.setProperty(RetrolambdaApi.CLASSPATH, getClasspath());
config.setProperty(RetrolambdaApi.JAVAC_HACKS, "" + javacHacks);

if (fork) {
processClassesInForkedProcess(config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package net.orfjackal.retrolambda;

import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;
import net.orfjackal.retrolambda.interfaces.*;
import net.orfjackal.retrolambda.lambdas.*;
import net.orfjackal.retrolambda.util.*;
Expand All @@ -21,11 +22,11 @@ public class ClassAnalyzer {
private final Map<MethodRef, MethodRef> relocatedMethods = new HashMap<>();
private final Map<MethodRef, MethodRef> renamedLambdaMethods = new HashMap<>();

public void analyze(byte[] bytecode) {
analyze(new ClassReader(bytecode));
public void analyze(byte[] bytecode, boolean isJavacHacksEnabled) {
analyze(new EnhancedClassReader(bytecode, isJavacHacksEnabled));
}

public void analyze(ClassReader cr) {
public void analyze(EnhancedClassReader cr) {
ClassInfo c = new ClassInfo(cr);
classes.put(c.type, c);

Expand All @@ -37,7 +38,7 @@ public void analyze(ClassReader cr) {
analyzeClassOrInterface(c, cr);
}

private void analyzeClass(ClassInfo c, ClassReader cr) {
private void analyzeClass(ClassInfo c, EnhancedClassReader cr) {
cr.accept(new ClassVisitor(ASM5) {
private String owner;

Expand All @@ -64,7 +65,7 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
}, ClassReader.SKIP_CODE);
}

private void analyzeInterface(ClassInfo c, ClassReader cr) {
private void analyzeInterface(ClassInfo c, EnhancedClassReader cr) {
cr.accept(new ClassVisitor(ASM5) {
private String owner;
private String companion;
Expand Down Expand Up @@ -100,7 +101,7 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
}, ClassReader.SKIP_CODE);
}

private void analyzeClassOrInterface(ClassInfo c, ClassReader cr) {
private void analyzeClassOrInterface(ClassInfo c, EnhancedClassReader cr) {
cr.accept(new ClassVisitor(ASM5) {
private String owner;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ public interface Config {

List<Path> getIncludedFiles();

boolean isJavacHacksEnabled();

boolean isQuiet();
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static boolean isAgentLoaded() {
return agentLoaded;
}

public static void setLambdaClassSaver(LambdaClassSaver lambdaClassSaver) {
agent.setLambdaClassSaver(lambdaClassSaver);
public static void setLambdaClassSaver(LambdaClassSaver lambdaClassSaver, boolean isJavacHacksEnabled) {
agent.setLambdaClassSaver(lambdaClassSaver, isJavacHacksEnabled);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static void run(Config config) throws Throwable {
Path outputDir = config.getOutputDir();
List<Path> classpath = config.getClasspath();
List<Path> includedFiles = config.getIncludedFiles();
boolean isJavacHacksEnabled = config.isJavacHacksEnabled();
if (config.isQuiet()) {
Log.WARN();
} else {
Expand All @@ -45,6 +46,9 @@ public static void run(Config config) throws Throwable {
Log.info("Included files: " + (includedFiles != null ? includedFiles.size() : "all"));
Log.info("JVM version: " + System.getProperty("java.version"));
Log.info("Agent enabled: " + PreMain.isAgentLoaded());
if (isJavacHacksEnabled) {
Log.info("javac hacks: " + isJavacHacksEnabled);
}

if (!Files.isDirectory(inputDir)) {
Log.info("Nothing to do; not a directory: " + inputDir);
Expand All @@ -56,19 +60,19 @@ public static void run(Config config) throws Throwable {
ClassAnalyzer analyzer = new ClassAnalyzer();
OutputDirectory outputDirectory = new OutputDirectory(outputDir);
Transformers transformers = new Transformers(bytecodeVersion, defaultMethodsEnabled, analyzer);
LambdaClassSaver lambdaClassSaver = new LambdaClassSaver(outputDirectory, transformers);
LambdaClassSaver lambdaClassSaver = new LambdaClassSaver(outputDirectory, transformers, isJavacHacksEnabled);

try (LambdaClassDumper dumper = new LambdaClassDumper(lambdaClassSaver)) {
if (PreMain.isAgentLoaded()) {
PreMain.setLambdaClassSaver(lambdaClassSaver);
PreMain.setLambdaClassSaver(lambdaClassSaver, isJavacHacksEnabled);
} else {
dumper.install();
}

visitFiles(inputDir, includedFiles, new ClasspathVisitor() {
@Override
protected void visitClass(byte[] bytecode) {
analyzer.analyze(bytecode);
analyzer.analyze(bytecode, isJavacHacksEnabled);
}

@Override
Expand All @@ -95,7 +99,7 @@ protected void visitResource(Path relativePath, byte[] content) throws IOExcepti
// We need to load some of the classes (for calling the lambda metafactory)
// so we need to take care not to modify any bytecode before loading them.
for (byte[] bytecode : transformed) {
outputDirectory.writeClass(bytecode);
outputDirectory.writeClass(bytecode, isJavacHacksEnabled);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,21 @@ public List<Path> getIncludedFiles() {
}


// useJavac8ReadLabelHack

static {
optionalParameterHelp(JAVAC_HACKS,
"Attempts to fix javac bugs (type-annotation emission for local variables).",
"Disabled by default. Enable by setting to \"true\"");

}

@Override
public boolean isJavacHacksEnabled() {
return Boolean.parseBoolean(p.getProperty(JAVAC_HACKS, "false"));
}


// quiet

static {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package net.orfjackal.retrolambda;

import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;
import net.orfjackal.retrolambda.interfaces.*;
import net.orfjackal.retrolambda.lambdas.*;
import net.orfjackal.retrolambda.requirenonnull.RequireNonNull;
Expand All @@ -26,7 +27,7 @@ public Transformers(int targetVersion, boolean defaultMethodsEnabled, ClassAnaly
this.analyzer = analyzer;
}

public byte[] backportLambdaClass(ClassReader reader) {
public byte[] backportLambdaClass(EnhancedClassReader reader) {
return transform(reader, (next) -> {
if (defaultMethodsEnabled) {
// Lambda classes are generated dynamically, so they were not
Expand All @@ -43,7 +44,7 @@ public byte[] backportLambdaClass(ClassReader reader) {
});
}

public byte[] backportClass(ClassReader reader) {
public byte[] backportClass(EnhancedClassReader reader) {
return transform(reader, (next) -> {
if (defaultMethodsEnabled) {
next = new UpdateRelocatedMethodInvocations(next, analyzer);
Expand All @@ -54,7 +55,7 @@ public byte[] backportClass(ClassReader reader) {
});
}

public List<byte[]> backportInterface(ClassReader reader) {
public List<byte[]> backportInterface(EnhancedClassReader reader) {
// The lambdas must be backported only once, because bad things will happen if a lambda
// is called by different class name in the interface and its companion class, and then
// the wrong one of them is written to disk last.
Expand Down Expand Up @@ -103,7 +104,7 @@ private byte[] transform(ClassNode node, ClassVisitorChain chain) {
return transform(node.name, node::accept, chain);
}

private byte[] transform(ClassReader reader, ClassVisitorChain chain) {
private byte[] transform(EnhancedClassReader reader, ClassVisitorChain chain) {
return transform(reader.getClassName(), cv -> reader.accept(cv, 0), chain);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright © 2013-2015 Esko Luontola <www.orfjackal.net>
// 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.ext.ow2asm;

import org.objectweb.asm.*;

public class EnhancedClassReader extends ClassReader {

private final boolean isJavacHacksEnabled;

public EnhancedClassReader(byte[] b, boolean isJavacHacksEnabled) {
super(b);
this.isJavacHacksEnabled = isJavacHacksEnabled;
}

@Override
protected Label readLabel(int offset, Label[] labels) {
if (!isJavacHacksEnabled) {
return super.readLabel(offset, labels);
}
// A workaround suggested by Evgeny Mandrikov. See more: https://gitlab.ow2.org/asm/asm/issues/317845
return super.readLabel(Math.min(offset, labels.length - 1), labels);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

package net.orfjackal.retrolambda.files;

import org.objectweb.asm.ClassReader;
import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;

import java.io.IOException;
import java.nio.file.*;
Expand All @@ -17,11 +17,11 @@ public OutputDirectory(Path outputDir) {
this.outputDir = outputDir;
}

public void writeClass(byte[] bytecode) throws IOException {
public void writeClass(byte[] bytecode, boolean isJavacHacksEnabled) throws IOException {
if (bytecode == null) {
return;
}
ClassReader cr = new ClassReader(bytecode);
EnhancedClassReader cr = new EnhancedClassReader(bytecode, isJavacHacksEnabled);
Path relativePath = outputDir.getFileSystem().getPath(cr.getClassName() + ".class");
writeFile(relativePath, bytecode);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

package net.orfjackal.retrolambda.interfaces;

import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;
import net.orfjackal.retrolambda.util.Flags;
import org.objectweb.asm.*;

import java.util.*;

public class ClassInfo {

public final ClassReader reader;
public final EnhancedClassReader reader;
private final int access;
public final Type type;
public final Type superclass;
Expand All @@ -26,7 +27,7 @@ public ClassInfo() {
this.superclass = null;
}

public ClassInfo(ClassReader cr) {
public ClassInfo(EnhancedClassReader cr) {
this.reader = cr;
this.access = cr.getAccess();
this.type = Type.getObjectType(cr.getClassName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@
import com.esotericsoftware.minlog.Log;
import net.orfjackal.retrolambda.Transformers;
import net.orfjackal.retrolambda.files.OutputDirectory;
import org.objectweb.asm.ClassReader;

import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;

import java.io.IOException;

public class LambdaClassSaver {

private final OutputDirectory saver;
private final Transformers transformers;
private final boolean isJavacHacksEnabled;

public LambdaClassSaver(OutputDirectory saver, Transformers transformers) {
public LambdaClassSaver(OutputDirectory saver, Transformers transformers, boolean isJavacHacksEnabled) {
this.saver = saver;
this.transformers = transformers;
this.isJavacHacksEnabled = isJavacHacksEnabled;
}

public void saveIfLambda(String className, byte[] bytecode) {
Expand All @@ -29,9 +32,9 @@ public void saveIfLambda(String className, byte[] bytecode) {

private void reifyLambdaClass(String className, byte[] bytecode) {
Log.info("Saving lambda class: " + className);
bytecode = transformers.backportLambdaClass(new ClassReader(bytecode));
bytecode = transformers.backportLambdaClass(new EnhancedClassReader(bytecode, isJavacHacksEnabled));
try {
saver.writeClass(bytecode);
saver.writeClass(bytecode, isJavacHacksEnabled);
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@

package net.orfjackal.retrolambda.lambdas;

import org.objectweb.asm.ClassReader;
import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;

import java.lang.instrument.*;
import java.security.ProtectionDomain;

public class LambdaClassSaverAgent implements ClassFileTransformer {

private LambdaClassSaver lambdaClassSaver;
private boolean isJavacHacksEnabled;

public void setLambdaClassSaver(LambdaClassSaver lambdaClassSaver) {
public void setLambdaClassSaver(LambdaClassSaver lambdaClassSaver, boolean isJavacHacksEnabled) {
this.lambdaClassSaver = lambdaClassSaver;
this.isJavacHacksEnabled = isJavacHacksEnabled;
}

@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (className == null) {
// Since JDK 8 build b121 or so, lambda classes have a null class name,
// but we can read it from the bytecode where the name still exists.
className = new ClassReader(classfileBuffer).getClassName();
className = new EnhancedClassReader(classfileBuffer, isJavacHacksEnabled).getClassName();
}
if (lambdaClassSaver != null) {
lambdaClassSaver.saveIfLambda(className, classfileBuffer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ private void analyze(Class<?>... classes) {
Collections.shuffle(inAnyOrder);
for (Class<?> clazz : inAnyOrder) {
byte[] bytecode = readBytecode(clazz);
analyzer.analyze(bytecode);
analyzer.analyze(bytecode, false);
}
}

Expand Down