Skip to content

Add initial support for remapping new API references (proof of concept) #147

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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 .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions .idea/encodings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ Configurable system properties:
Reduces the amount of logging.
Disabled by default. Enable by setting to "true"

retrolambda.apiMappings
Maps static field and method references to backported classes.
Disabled by default.
Provide a list of mapping files separated by ':' to enable.

If the Java agent is used, then Retrolambda will use it to capture the
lambda classes generated by Java. Otherwise Retrolambda will hook into
Java's internal lambda dumping API, which is more susceptible to suddenly
Expand Down Expand Up @@ -215,10 +220,34 @@ updated. This may cause weird error messages if static methods on
interfaces are accidentally used without enabling default method support.*


Backported APIs
---------------

Retrolambda can be configured to automatically replace references to
new Java APIs with backported versions.

You can use one of the prepackaged mappings, or write your own. For
details on the syntax of the mapping files, see the included
[java7-guava](retrolambda/src/main/resources/net/orfjackal/retrolambda/api/java7-guava) or
[streamsupport](retrolambda/src/main/resources/net/orfjackal/retrolambda/api/streamsupport).

Currently included mappings:

* **java6-guava**: Maps new Java 1.7 APIs to existing guava methods.
For example, from `java.nio.StandardCharsets` to `com.google.common.base.Charsets`

* **java7-guava**: Maps new Java 1.8 APIs to existing guava methods.
For example, from `java.lang.Integer.toUnsignedLong()` to `com.google.common.primitives.UnsignedInts.toLong()`

* **streamsupport**: Maps java.util.function.* interfaces to the
java8.util.function replacements in the [streamsupport](http://sourceforge.net/projects/streamsupport/) library.



Known Limitations
-----------------

Does not backport Java 8 APIs.
Does not backport all Java 8 APIs.

Backporting default methods and static methods on interfaces requires all
backported interfaces and all classes which implement them or call their
Expand Down
10 changes: 10 additions & 0 deletions end-to-end-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@
<systemPath>${basedir}/src/test/lib/java-lang-dummies.jar</systemPath>
</dependency>

<dependency>
<groupId>net.sourceforge.streamsupport</groupId>
<artifactId>streamsupport</artifactId>
<version>1.6.3</version>
</dependency>

</dependencies>

<build>
Expand Down Expand Up @@ -119,6 +125,10 @@
<defaultMethods>${testDefaultMethods}</defaultMethods>
<fork>${testFork}</fork>
<javacHacks>true</javacHacks>
<apiMappings>
<mapping>java7-guava</mapping>
<mapping>streamsupport</mapping>
</apiMappings>
</configuration>
</plugin>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright © 2013-2018 Esko Luontola and other Retrolambda contributors
// 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.test;

import org.junit.Test;

import java.nio.charset.*;
import java.util.*;
import java.util.function.Predicate;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;

public class ApiMapperTest {

@Test
public void toUnsignedLong() {

assertThat(Integer.toUnsignedLong(0xffffffff), equalTo(4294967295L));
}

@Test
public void predicates() {
assertThat(filter(Arrays.asList("Hello World", "Goodbye World"), x -> x.startsWith("Hello")), hasSize(1));
}

private List<String> filter(List<String> strings, Predicate<String> predicate) {
List<String> result = new ArrayList<>();
for (String string : strings) {
if(predicate.test(string)) {
result.add(string);
}
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright © 2013-2018 Esko Luontola and other Retrolambda contributors
// 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.test;

public class BackportedApis {
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ public class RetrolambdaApi {
public static final String DEFAULT_METHODS = PREFIX + "defaultMethods";
public static final String BYTECODE_VERSION = PREFIX + "bytecodeVersion";
public static final String JAVAC_HACKS = PREFIX + "javacHacks";
public static final String API_MAPPINGS = PREFIX + "apiMappings";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.google.common.base.*;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import net.orfjackal.retrolambda.Retrolambda;
import net.orfjackal.retrolambda.api.RetrolambdaApi;
import org.apache.commons.lang3.*;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
Expand Down Expand Up @@ -107,6 +108,9 @@ abstract class ProcessClassesMojo extends AbstractMojo {
@Parameter(defaultValue = "false")
public boolean fork;

@Parameter
public List<String> apiMappings;

protected abstract File getInputDir();

protected abstract File getOutputDir();
Expand All @@ -126,6 +130,7 @@ public void execute() throws MojoExecutionException {
config.setProperty(RetrolambdaApi.OUTPUT_DIR, getOutputDir().getAbsolutePath());
config.setProperty(RetrolambdaApi.CLASSPATH, getClasspath());
config.setProperty(RetrolambdaApi.JAVAC_HACKS, "" + javacHacks);
config.setProperty(RetrolambdaApi.API_MAPPINGS, apiMappingsList());

if (fork) {
processClassesInForkedProcess(config);
Expand All @@ -134,6 +139,19 @@ public void execute() throws MojoExecutionException {
}
}

private String apiMappingsList() {
StringBuilder list = new StringBuilder();
if(apiMappings != null) {
for (String apiMapping : apiMappings) {
if(list.length() > 0) {
list.append(File.pathSeparator);
}
list.append(apiMapping);
}
}
return list.toString();
}

private void validateTarget() throws MojoExecutionException {
if (!targetBytecodeVersions.containsKey(target)) {
String possibleValues = Joiner.on(", ").join(new TreeSet<String>(targetBytecodeVersions.keySet()));
Expand Down
1 change: 1 addition & 0 deletions retrolambda/retrolambda.iml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public interface Config {

List<Path> getIncludedFiles();

List<String> getApiMappings();

boolean isJavacHacksEnabled();

boolean isQuiet();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package net.orfjackal.retrolambda;

import com.esotericsoftware.minlog.Log;
import net.orfjackal.retrolambda.api.ApiMappingSet;
import net.orfjackal.retrolambda.files.*;
import net.orfjackal.retrolambda.interfaces.ClassInfo;
import net.orfjackal.retrolambda.lambdas.*;
Expand Down Expand Up @@ -33,6 +34,7 @@ public static void run(Config config) throws Throwable {
List<Path> classpath = config.getClasspath();
List<Path> includedFiles = config.getIncludedFiles();
boolean isJavacHacksEnabled = config.isJavacHacksEnabled();
List<String> apiMappings = config.getApiMappings();
if (config.isQuiet()) {
Log.WARN();
} else {
Expand All @@ -57,7 +59,8 @@ public static void run(Config config) throws Throwable {

ClassAnalyzer analyzer = new ClassAnalyzer();
OutputDirectory outputDirectory = new OutputDirectory(outputDir);
Transformers transformers = new Transformers(bytecodeVersion, defaultMethodsEnabled, analyzer);
ApiMappingSet apiMappingSet = new ApiMappingSet(config.getApiMappings());
Transformers transformers = new Transformers(bytecodeVersion, defaultMethodsEnabled, analyzer, apiMappingSet);
LambdaClassSaver lambdaClassSaver = new LambdaClassSaver(outputDirectory, transformers, isJavacHacksEnabled);

try (LambdaClassDumper dumper = new LambdaClassDumper(lambdaClassSaver)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ public List<Path> getIncludedFiles() {

}



@Override
public boolean isJavacHacksEnabled() {
return Boolean.parseBoolean(p.getProperty(JAVAC_HACKS, "false"));
Expand All @@ -221,6 +223,27 @@ public boolean isQuiet() {
}



// API Mappings

static {
optionalParameterHelp(API_MAPPINGS,
"Maps static field and method references to backported classes.",
"Disabled by default.",
"Provide a list of mapping files separated by '" + File.pathSeparator + "' to enable.");
}


@Override
public List<String> getApiMappings() {
String list = p.getProperty(API_MAPPINGS, "");
if(list.isEmpty()) {
return Collections.emptyList();
} else {
return Arrays.asList(list.split(File.pathSeparator));
}
}

// help

public String getHelp() {
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.api.*;
import net.orfjackal.retrolambda.interfaces.*;
import net.orfjackal.retrolambda.lambdas.*;
import net.orfjackal.retrolambda.requirenonnull.RequireNonNull;
Expand All @@ -19,11 +20,13 @@ public class Transformers {
private final int targetVersion;
private final boolean defaultMethodsEnabled;
private final ClassAnalyzer analyzer;
private final ApiMappingSet mapping;

public Transformers(int targetVersion, boolean defaultMethodsEnabled, ClassAnalyzer analyzer) {
public Transformers(int targetVersion, boolean defaultMethodsEnabled, ClassAnalyzer analyzer, ApiMappingSet mapping) {
this.targetVersion = targetVersion;
this.defaultMethodsEnabled = defaultMethodsEnabled;
this.analyzer = analyzer;
this.mapping = mapping;
}

public byte[] backportLambdaClass(ClassReader reader) {
Expand All @@ -50,6 +53,9 @@ public byte[] backportClass(ClassReader reader) {
next = new AddMethodDefaultImplementations(next, analyzer);
}
next = new BackportLambdaInvocations(next, analyzer);
if (mapping.isEnabled()) {
next = new RewriteApiReferences(next, mapping);
}
return next;
});
}
Expand Down
Loading