Skip to content

Commit 8fae5c8

Browse files
authored
Merge pull request #143 from lyubomyr-shaydariv/javac-bug
Added retrolambda.javaHacks configuration option
2 parents 5afee55 + d605bfe commit 8fae5c8

File tree

15 files changed

+99
-29
lines changed

15 files changed

+99
-29
lines changed

Diff for: README.md

+4
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ Configurable system properties:
140140
Alternative to retrolambda.includedFiles for avoiding the command line
141141
length limit. The file must list one file per line with UTF-8 encoding.
142142
143+
retrolambda.javacHacks
144+
Attempts to fix javac bugs (type-annotation emission for local variables).
145+
Disabled by default. Enable by setting to "true"
146+
143147
retrolambda.quiet
144148
Reduces the amount of logging.
145149
Disabled by default. Enable by setting to "true"

Diff for: retrolambda-api/src/main/java/net/orfjackal/retrolambda/api/RetrolambdaApi.java

+1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ public class RetrolambdaApi {
1616
public static final String INPUT_DIR = PREFIX + "inputDir";
1717
public static final String DEFAULT_METHODS = PREFIX + "defaultMethods";
1818
public static final String BYTECODE_VERSION = PREFIX + "bytecodeVersion";
19+
public static final String JAVAC_HACKS = PREFIX + "javacHacks";
1920
}

Diff for: retrolambda-maven-plugin/src/main/java/net/orfjackal/retrolambda/maven/ProcessClassesMojo.java

+9
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ abstract class ProcessClassesMojo extends AbstractMojo {
7979
@Parameter(defaultValue = "false", property = "retrolambdaDefaultMethods", required = true)
8080
public boolean defaultMethods;
8181

82+
/**
83+
* Whether to apply experimental javac issues workarounds.
84+
*
85+
* @since 2.5.5
86+
*/
87+
@Parameter(defaultValue = "false", property = "retrolambdaJavacHacks", required = true)
88+
public boolean javacHacks;
89+
8290
/**
8391
* Reduces the amount of logging.
8492
*
@@ -117,6 +125,7 @@ public void execute() throws MojoExecutionException {
117125
config.setProperty(RetrolambdaApi.INPUT_DIR, getInputDir().getAbsolutePath());
118126
config.setProperty(RetrolambdaApi.OUTPUT_DIR, getOutputDir().getAbsolutePath());
119127
config.setProperty(RetrolambdaApi.CLASSPATH, getClasspath());
128+
config.setProperty(RetrolambdaApi.JAVAC_HACKS, "" + javacHacks);
120129

121130
if (fork) {
122131
processClassesInForkedProcess(config);

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/ClassAnalyzer.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package net.orfjackal.retrolambda;
66

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

24-
public void analyze(byte[] bytecode) {
25-
analyze(new ClassReader(bytecode));
25+
public void analyze(byte[] bytecode, boolean isJavacHacksEnabled) {
26+
analyze(new EnhancedClassReader(bytecode, isJavacHacksEnabled));
2627
}
2728

28-
public void analyze(ClassReader cr) {
29+
public void analyze(EnhancedClassReader cr) {
2930
ClassInfo c = new ClassInfo(cr);
3031
classes.put(c.type, c);
3132

@@ -37,7 +38,7 @@ public void analyze(ClassReader cr) {
3738
analyzeClassOrInterface(c, cr);
3839
}
3940

40-
private void analyzeClass(ClassInfo c, ClassReader cr) {
41+
private void analyzeClass(ClassInfo c, EnhancedClassReader cr) {
4142
cr.accept(new ClassVisitor(ASM5) {
4243
private String owner;
4344

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

67-
private void analyzeInterface(ClassInfo c, ClassReader cr) {
68+
private void analyzeInterface(ClassInfo c, EnhancedClassReader cr) {
6869
cr.accept(new ClassVisitor(ASM5) {
6970
private String owner;
7071
private String companion;
@@ -100,7 +101,7 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
100101
}, ClassReader.SKIP_CODE);
101102
}
102103

103-
private void analyzeClassOrInterface(ClassInfo c, ClassReader cr) {
104+
private void analyzeClassOrInterface(ClassInfo c, EnhancedClassReader cr) {
104105
cr.accept(new ClassVisitor(ASM5) {
105106
private String owner;
106107

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/Config.java

+2
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,7 @@ public interface Config {
2121

2222
List<Path> getIncludedFiles();
2323

24+
boolean isJavacHacksEnabled();
25+
2426
boolean isQuiet();
2527
}

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/PreMain.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static boolean isAgentLoaded() {
2222
return agentLoaded;
2323
}
2424

25-
public static void setLambdaClassSaver(LambdaClassSaver lambdaClassSaver) {
26-
agent.setLambdaClassSaver(lambdaClassSaver);
25+
public static void setLambdaClassSaver(LambdaClassSaver lambdaClassSaver, boolean isJavacHacksEnabled) {
26+
agent.setLambdaClassSaver(lambdaClassSaver, isJavacHacksEnabled);
2727
}
2828
}

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/Retrolambda.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public static void run(Config config) throws Throwable {
3232
Path outputDir = config.getOutputDir();
3333
List<Path> classpath = config.getClasspath();
3434
List<Path> includedFiles = config.getIncludedFiles();
35+
boolean isJavacHacksEnabled = config.isJavacHacksEnabled();
3536
if (config.isQuiet()) {
3637
Log.WARN();
3738
} else {
@@ -45,6 +46,9 @@ public static void run(Config config) throws Throwable {
4546
Log.info("Included files: " + (includedFiles != null ? includedFiles.size() : "all"));
4647
Log.info("JVM version: " + System.getProperty("java.version"));
4748
Log.info("Agent enabled: " + PreMain.isAgentLoaded());
49+
if (isJavacHacksEnabled) {
50+
Log.info("javac hacks: " + isJavacHacksEnabled);
51+
}
4852

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

6165
try (LambdaClassDumper dumper = new LambdaClassDumper(lambdaClassSaver)) {
6266
if (PreMain.isAgentLoaded()) {
63-
PreMain.setLambdaClassSaver(lambdaClassSaver);
67+
PreMain.setLambdaClassSaver(lambdaClassSaver, isJavacHacksEnabled);
6468
} else {
6569
dumper.install();
6670
}
6771

6872
visitFiles(inputDir, includedFiles, new ClasspathVisitor() {
6973
@Override
7074
protected void visitClass(byte[] bytecode) {
71-
analyzer.analyze(bytecode);
75+
analyzer.analyze(bytecode, isJavacHacksEnabled);
7276
}
7377

7478
@Override
@@ -95,7 +99,7 @@ protected void visitResource(Path relativePath, byte[] content) throws IOExcepti
9599
// We need to load some of the classes (for calling the lambda metafactory)
96100
// so we need to take care not to modify any bytecode before loading them.
97101
for (byte[] bytecode : transformed) {
98-
outputDirectory.writeClass(bytecode);
102+
outputDirectory.writeClass(bytecode, isJavacHacksEnabled);
99103
}
100104
}
101105
}

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/SystemPropertiesConfig.java

+15
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,21 @@ public List<Path> getIncludedFiles() {
191191
}
192192

193193

194+
// useJavac8ReadLabelHack
195+
196+
static {
197+
optionalParameterHelp(JAVAC_HACKS,
198+
"Attempts to fix javac bugs (type-annotation emission for local variables).",
199+
"Disabled by default. Enable by setting to \"true\"");
200+
201+
}
202+
203+
@Override
204+
public boolean isJavacHacksEnabled() {
205+
return Boolean.parseBoolean(p.getProperty(JAVAC_HACKS, "false"));
206+
}
207+
208+
194209
// quiet
195210

196211
static {

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/Transformers.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package net.orfjackal.retrolambda;
66

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

29-
public byte[] backportLambdaClass(ClassReader reader) {
30+
public byte[] backportLambdaClass(EnhancedClassReader reader) {
3031
return transform(reader, (next) -> {
3132
if (defaultMethodsEnabled) {
3233
// Lambda classes are generated dynamically, so they were not
@@ -43,7 +44,7 @@ public byte[] backportLambdaClass(ClassReader reader) {
4344
});
4445
}
4546

46-
public byte[] backportClass(ClassReader reader) {
47+
public byte[] backportClass(EnhancedClassReader reader) {
4748
return transform(reader, (next) -> {
4849
if (defaultMethodsEnabled) {
4950
next = new UpdateRelocatedMethodInvocations(next, analyzer);
@@ -54,7 +55,7 @@ public byte[] backportClass(ClassReader reader) {
5455
});
5556
}
5657

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

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

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright © 2013-2015 Esko Luontola <www.orfjackal.net>
2+
// This software is released under the Apache License 2.0.
3+
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
4+
5+
package net.orfjackal.retrolambda.ext.ow2asm;
6+
7+
import org.objectweb.asm.*;
8+
9+
public class EnhancedClassReader extends ClassReader {
10+
11+
private final boolean isJavacHacksEnabled;
12+
13+
public EnhancedClassReader(byte[] b, boolean isJavacHacksEnabled) {
14+
super(b);
15+
this.isJavacHacksEnabled = isJavacHacksEnabled;
16+
}
17+
18+
@Override
19+
protected Label readLabel(int offset, Label[] labels) {
20+
if (!isJavacHacksEnabled) {
21+
return super.readLabel(offset, labels);
22+
}
23+
// A workaround suggested by Evgeny Mandrikov. See more: https://gitlab.ow2.org/asm/asm/issues/317845
24+
return super.readLabel(Math.min(offset, labels.length - 1), labels);
25+
}
26+
27+
}

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/files/OutputDirectory.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
package net.orfjackal.retrolambda.files;
66

7-
import org.objectweb.asm.ClassReader;
7+
import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;
88

99
import java.io.IOException;
1010
import java.nio.file.*;
@@ -17,11 +17,11 @@ public OutputDirectory(Path outputDir) {
1717
this.outputDir = outputDir;
1818
}
1919

20-
public void writeClass(byte[] bytecode) throws IOException {
20+
public void writeClass(byte[] bytecode, boolean isJavacHacksEnabled) throws IOException {
2121
if (bytecode == null) {
2222
return;
2323
}
24-
ClassReader cr = new ClassReader(bytecode);
24+
EnhancedClassReader cr = new EnhancedClassReader(bytecode, isJavacHacksEnabled);
2525
Path relativePath = outputDir.getFileSystem().getPath(cr.getClassName() + ".class");
2626
writeFile(relativePath, bytecode);
2727
}

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/interfaces/ClassInfo.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
package net.orfjackal.retrolambda.interfaces;
66

7+
import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;
78
import net.orfjackal.retrolambda.util.Flags;
89
import org.objectweb.asm.*;
910

1011
import java.util.*;
1112

1213
public class ClassInfo {
1314

14-
public final ClassReader reader;
15+
public final EnhancedClassReader reader;
1516
private final int access;
1617
public final Type type;
1718
public final Type superclass;
@@ -26,7 +27,7 @@ public ClassInfo() {
2627
this.superclass = null;
2728
}
2829

29-
public ClassInfo(ClassReader cr) {
30+
public ClassInfo(EnhancedClassReader cr) {
3031
this.reader = cr;
3132
this.access = cr.getAccess();
3233
this.type = Type.getObjectType(cr.getClassName());

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/LambdaClassSaver.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@
77
import com.esotericsoftware.minlog.Log;
88
import net.orfjackal.retrolambda.Transformers;
99
import net.orfjackal.retrolambda.files.OutputDirectory;
10-
import org.objectweb.asm.ClassReader;
10+
11+
import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;
1112

1213
import java.io.IOException;
1314

1415
public class LambdaClassSaver {
1516

1617
private final OutputDirectory saver;
1718
private final Transformers transformers;
19+
private final boolean isJavacHacksEnabled;
1820

19-
public LambdaClassSaver(OutputDirectory saver, Transformers transformers) {
21+
public LambdaClassSaver(OutputDirectory saver, Transformers transformers, boolean isJavacHacksEnabled) {
2022
this.saver = saver;
2123
this.transformers = transformers;
24+
this.isJavacHacksEnabled = isJavacHacksEnabled;
2225
}
2326

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

3033
private void reifyLambdaClass(String className, byte[] bytecode) {
3134
Log.info("Saving lambda class: " + className);
32-
bytecode = transformers.backportLambdaClass(new ClassReader(bytecode));
35+
bytecode = transformers.backportLambdaClass(new EnhancedClassReader(bytecode, isJavacHacksEnabled));
3336
try {
34-
saver.writeClass(bytecode);
37+
saver.writeClass(bytecode, isJavacHacksEnabled);
3538
} catch (IOException e) {
3639
throw new RuntimeException(e);
3740
}

Diff for: retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/LambdaClassSaverAgent.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,27 @@
44

55
package net.orfjackal.retrolambda.lambdas;
66

7-
import org.objectweb.asm.ClassReader;
7+
import net.orfjackal.retrolambda.ext.ow2asm.EnhancedClassReader;
88

99
import java.lang.instrument.*;
1010
import java.security.ProtectionDomain;
1111

1212
public class LambdaClassSaverAgent implements ClassFileTransformer {
1313

1414
private LambdaClassSaver lambdaClassSaver;
15+
private boolean isJavacHacksEnabled;
1516

16-
public void setLambdaClassSaver(LambdaClassSaver lambdaClassSaver) {
17+
public void setLambdaClassSaver(LambdaClassSaver lambdaClassSaver, boolean isJavacHacksEnabled) {
1718
this.lambdaClassSaver = lambdaClassSaver;
19+
this.isJavacHacksEnabled = isJavacHacksEnabled;
1820
}
1921

2022
@Override
2123
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
2224
if (className == null) {
2325
// Since JDK 8 build b121 or so, lambda classes have a null class name,
2426
// but we can read it from the bytecode where the name still exists.
25-
className = new ClassReader(classfileBuffer).getClassName();
27+
className = new EnhancedClassReader(classfileBuffer, isJavacHacksEnabled).getClassName();
2628
}
2729
if (lambdaClassSaver != null) {
2830
lambdaClassSaver.saveIfLambda(className, classfileBuffer);

Diff for: retrolambda/src/test/java/net/orfjackal/retrolambda/ClassAnalyzerTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ private void analyze(Class<?>... classes) {
513513
Collections.shuffle(inAnyOrder);
514514
for (Class<?> clazz : inAnyOrder) {
515515
byte[] bytecode = readBytecode(clazz);
516-
analyzer.analyze(bytecode);
516+
analyzer.analyze(bytecode, false);
517517
}
518518
}
519519

0 commit comments

Comments
 (0)