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

Default methods #30

Merged
merged 5 commits into from
Sep 5, 2014
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import org.junit.Test;

import java.util.Comparator;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

Expand Down Expand Up @@ -36,4 +38,272 @@ public interface Parent {
public interface Child extends Parent {
String foo(); // refined return type
}

public interface Parent2 {
default Object method() {
return "Parent";
}
}

public interface Child2 extends Parent2{
@Override
default String method() {
return "Child2";
}
}

@Test
public void will_return_right_string() {
boolean sameStrings = new Child2() {

}.method().equals("Child2");
assertThat("they are equal", sameStrings);
}

interface Primitives {
default int anInt() {
return 1;
}
default short aShort() {
return 2;
}
default long aLong() {
return 1L << 50;
}
default boolean aBoolean() {
return true;
}
default float aFloat() {
return 0f;
}
default double aDouble() {
return 0.0;
}
default void aVoid() {
}
}

@Test
public void primitives_run() {
Primitives p = new Primitives() {
};
assertThat(p.aBoolean(), is(true));
assertThat(p.anInt(), is(1));
assertThat(p.aShort(), is((short)2));
assertThat(p.aLong(), is(1L << 50));
assertThat(p.aFloat(), is(0f));
assertThat(p.aDouble(), is(0.0));
p.aVoid(); // would crash
}

interface Chaining {
default String myString() {
return "Interface";
}
default String join(Chaining other) {
return myString() + other.myString();
}
}

@Test
public void anonymous_instances() {
Chaining c1 = new Chaining() {

};
Chaining c2 = new Chaining() {

};
assertThat("Strings equals", c1.join(c2).equals("InterfaceInterface"));
Chaining anon = new Chaining() {
@Override
public String myString() {
return "Anon";
}
};
assertThat("Anonymous override equals", c1.join(anon).equals("InterfaceAnon"));
}

interface DeepParent {
default int level() {
return 1;
}
}
interface DeepChild extends DeepParent {
@Override
default int level() {
return DeepParent.super.level() + 1;
}
}

@Test
public void test_override_primitive() {
DeepChild d1 = new DeepChild() {

};
assertThat("override works", d1.level() == 2);
DeepChild d2 = new DeepChild() {
@Override
public int level() {
return 1 + DeepChild.super.level();
}
};
assertThat("super call interface works", d2.level() == 3);
}

interface Conflict1 {
default String confl() {
return "1";
}
}

interface Conflict2 {
default String confl() {
return "2";
}
}

@Test
public void will_handle_override_proprly() {
class C implements Conflict1, Conflict2 {
public String confl() {
return Conflict1.super.confl() + Conflict2.super.confl();
}
}
assertThat("Handles method conflict", new C().confl().equals("12"));
}

interface DeepParent2 {
int anInt();
default int method(DeepParent2 p1, DeepParent2 p2, DeepParent2 p3) {
return p1.anInt() + p2.anInt() + p3.anInt() + anInt();
}
}

@Test
public void will_handle_long_paramlist() {
DeepParent2 dp = new DeepParent2() {
@Override
public int anInt() {
return 2;
}
};
assertThat("Long call parameter list works", dp.method(dp, dp, dp) == 8);
}

@Test
public void will_handle_lambda() {
DeepParent2 dp = () -> 2;
assertThat("Long call parameter list with lambda works", dp.method(dp, dp, dp) == 8);
}

interface BridgeTest<T> {
default T max(T t1, T t2, Comparator<? super T> comparator) {
return comparator.compare(t1, t2) > 0 ? t1 : t2;
}
}

interface StringBridge extends BridgeTest<String> {
default boolean compare() {
return max("A", "B", String.CASE_INSENSITIVE_ORDER).equals("B");
}
}

@Test
public void handles_bridge_methods() {
StringBridge sb = new StringBridge() {
};
assertThat("returns true", sb.compare());
BridgeTest<String> sb2 = sb;
assertThat("still returns true", sb2.max("A", "B", String.CASE_INSENSITIVE_ORDER).equals("B"));
}

interface MiddleParent {
default int anInt() {
return 1;
}
}
interface Middle2Parent extends MiddleParent{
@Override
default int anInt() {
return 2;
}
}
interface Middle3aParent extends MiddleParent, Middle2Parent {

}
interface Middle3bParent extends Middle2Parent, MiddleParent {

}
@Test
public void right_method_chosen() {
assertThat(new Middle3aParent() {

}.anInt(), is(2));

assertThat(new Middle3bParent() {

}.anInt(), is(2));
}

interface Top<T> {
T anObject();
default int anInt() {
return 1;
}
}

interface SubTop<T extends CharSequence> extends Top<T> {
default int anInt() {
return Top.super.anInt() + 1;
}
}
interface SubSub extends SubTop<String> {
default int anInt() {
return SubTop.super.anInt() + 1;
}
default String anObject() {
return "0";
}
}
interface SubSub2 extends SubTop<String> {
default String anObject() {
return "1";
}
}

@Test
public void yet_another_deep_hiearchy_test_with_bridges() {
assertThat(new SubSub2() {

}.anInt(), is(2));

assertThat(new SubSub() {

}.anInt(), is(3));
SubSub sub = new SubSub() {

};
assertThat(sub.anInt(), is(3));
Top<?> top = sub;
assertThat("is instanceof string", top.anObject() instanceof String);
}

interface DefaultToStatic {
default int ifMeth() {
return staticMeth();
}

static int staticMeth() {
return 3;
}
}

@Test
public void call_static_methods_from_default() {
DefaultToStatic i = new DefaultToStatic() {
};
assertThat(i.ifMeth(), is(3));
assertThat(DefaultToStatic.staticMeth(), is(3));
}
//
}
2 changes: 1 addition & 1 deletion retrolambda/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
</execution>
</executions>
<configuration>
<minimizeJar>true</minimizeJar>
<!-- <minimizeJar>true</minimizeJar> --> <!-- http://jira.codehaus.org/browse/MSHADE-174 -->
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to re-enable minimizeJar after this is merged with master, because the latest Retrolambda backports itself to Java 6.

<relocations>
<relocation>
<pattern>org.objectweb.asm</pattern>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

package net.orfjackal.retrolambda;

import net.orfjackal.retrolambda.defaultmethods.ClassModifier;
import org.objectweb.asm.*;

import java.util.Arrays;

import static org.objectweb.asm.Opcodes.*;

public class LambdaClassBackporter {
Expand All @@ -15,7 +18,8 @@ public class LambdaClassBackporter {

public static byte[] transform(byte[] bytecode, int targetVersion) {
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
new ClassReader(bytecode).accept(new LambdaClassVisitor(writer, targetVersion), 0);
ClassModifier stage2 = new ClassModifier(targetVersion, writer);
new ClassReader(bytecode).accept(new LambdaClassVisitor(stage2, targetVersion), 0);
return writer.toByteArray();
}

Expand All @@ -27,7 +31,7 @@ private static class LambdaClassVisitor extends ClassVisitor {
private Handle bridgeMethod;
private LambdaFactoryMethod factoryMethod;

public LambdaClassVisitor(ClassWriter cw, int targetVersion) {
public LambdaClassVisitor(ClassVisitor cw, int targetVersion) {
super(ASM5, cw);
this.targetVersion = targetVersion;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

package net.orfjackal.retrolambda;

import net.orfjackal.retrolambda.defaultmethods.ClassModifier;
import net.orfjackal.retrolambda.defaultmethods.Helpers;
import net.orfjackal.retrolambda.defaultmethods.InterfaceModifier;
import org.objectweb.asm.*;

import java.lang.reflect.Field;
Expand All @@ -16,9 +19,10 @@ public class LambdaUsageBackporter {

public static byte[] transform(byte[] bytecode, int targetVersion) {
resetLambdaClassSequenceNumber();

ClassWriter stage2 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
InvokeDynamicInsnConverter stage1 = new InvokeDynamicInsnConverter(stage2, targetVersion);
ClassModifier stage3 = new ClassModifier(targetVersion, stage2);
InterfaceModifier stage4 = new InterfaceModifier(stage3, targetVersion);
InvokeDynamicInsnConverter stage1 = new InvokeDynamicInsnConverter(stage4, targetVersion);
new ClassReader(bytecode).accept(stage1, 0);
return stage2.toByteArray();
}
Expand Down Expand Up @@ -64,22 +68,6 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
if (isBridgeMethodOnInterface(access)) {
return null; // remove the bridge method; Java 7 didn't use them
}
if (isNonAbstractMethodOnInterface(access)
&& !isClassInitializerMethod(name, desc, access)) {
// In case we have missed a case of Java 8 producing non-abstract methods
// on interfaces, we have this warning here to get a bug report sooner.
// Not allowed by Java 7:
// - default methods
// - static methods
// - bridge methods
// Allowed by Java 7:
// - class initializer methods (for initializing constants)
System.out.println("WARNING: Method '" + name + "' of interface '" + className + "' is non-abstract! " +
"This will probably fail to run on Java 7 and below. " +
"If you get this warning _without_ using Java 8's default methods, " +
"please report a bug at https://github.com/orfjackal/retrolambda/issues " +
"together with an SSCCE (http://www.sscce.org/)");
}
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return new InvokeDynamicInsnConvertingMethodVisitor(mv, this);
}
Expand Down Expand Up @@ -158,22 +146,13 @@ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object.
}

private void backportLambda(String invokedName, Type invokedType, Handle bsm, Object[] bsmArgs) {
Class<?> invoker = loadClass(context.className);
Class<?> invoker = Helpers.loadClass(context.className);
Handle implMethod = (Handle) bsmArgs[1];
Handle bridgeMethod = context.getLambdaBridgeMethod(implMethod);

LambdaFactoryMethod factory = LambdaReifier.reifyLambdaClass(implMethod, bridgeMethod,
invoker, invokedName, invokedType, bsm, bsmArgs);
super.visitMethodInsn(INVOKESTATIC, factory.getOwner(), factory.getName(), factory.getDesc(), false);
}

private static Class<?> loadClass(String className) {
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return cl.loadClass(className.replace('/', '.'));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}
}
Loading