Skip to content

Commit

Permalink
Fix bad boxing in (instance) method references.
Browse files Browse the repository at this point in the history
Bug: #9340
Bug-Link: http://github.com/gwtproject/gwt/issues/9340
Change-Id: I4305181d680044bf8892d6acd0482df1f2e2eccb
  • Loading branch information
rluble committed May 23, 2016
1 parent f56956e commit 1da251c
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 11 deletions.
15 changes: 10 additions & 5 deletions dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
Expand Up @@ -1867,17 +1867,22 @@ public void endVisit(ReferenceExpression x, BlockScope blockScope) {
JExpression paramExpr = param.makeRef(info);
// params may need to be boxed or unboxed
TypeBinding destParam = null;
// The method declared in the functional interface might have more parameters than the
// method referred by the method reference. In the case of an instance method without
// an explicit qualifier (A::m vs instance::m) the method in the functional interface will
// have an additional parameter for the instance preceding all the method parameters.
TypeBinding samParameterBinding =
samBinding.parameters[paramNumber
+ (samBinding.parameters.length - referredMethodBinding.parameters.length)];
// if it is not the trailing param or varargs, or interface method is already varargs
if (varArgInitializers == null || !referredMethodBinding.isVarargs() || (paramNumber < varArg)) {
destParam = referredMethodBinding.parameters[paramNumber];
paramExpr = boxOrUnboxExpression(paramExpr, samBinding.parameters[paramNumber],
destParam);
paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam);
samCall.addArg(paramExpr);
} else if (!samBinding.parameters[paramNumber].isArrayType()) {
} else if (!samParameterBinding.isArrayType()) {
// else add trailing parameters to var-args initializer list for an array
destParam = referredMethodBinding.parameters[varArg].leafComponentType();
paramExpr = boxOrUnboxExpression(paramExpr, samBinding.parameters[paramNumber],
destParam);
paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam);
varArgInitializers.add(paramExpr);
}
paramNumber++;
Expand Down
Expand Up @@ -1441,19 +1441,19 @@ interface MyFunction2<T, U, V> {
V apply(T t, U u);
}

public void testMethodReferenceImplementedInSuperclass() {
public void testMethodReference_implementedInSuperclass() {
MyFunction1<StringBuilder, String> toString = StringBuilder::toString;
assertEquals("Hello", toString.apply(new StringBuilder("Hello")));
}

static MyFunction2<String, String, String> concat = (s,t) -> s + t;

public void testMethodReferenceWithGenericTypeParameters() {
testMethodReferencesWithGenericTypeParameters(
public void testMethodReference_genericTypeParameters() {
testMethodReference_genericTypeParameters(
new Some<String>("Hell", concat), "Hell", "o", concat);
}

private static <T> void testMethodReferencesWithGenericTypeParameters(
private static <T> void testMethodReference_genericTypeParameters(
Some<T> some, T t1, T t2, MyFunction2<T, T, T> combine) {
T t1t2 = combine.apply(t1, t2);

Expand All @@ -1471,4 +1471,87 @@ private static <T> void testMethodReferencesWithGenericTypeParameters(
assertEquals(t1t2,
((MyFunction2<T, MyFunction2<T, T, T>, Some<T>>) Some<T>::new).apply(t1t2, combine).m1());
}

static MyFunction2<Integer, Integer, Integer> addInteger = (s,t) -> s + t;

@FunctionalInterface
interface MyIntFunction1 {
int apply(int t);
}

@FunctionalInterface
interface MyIntFunction2 {
int apply(int t, int u);
}

@FunctionalInterface
interface MyIntFuncToSomeIntegeFunction2 {
SomeInteger apply(int t, MyFunction2<Integer, Integer, Integer> u);
}

@FunctionalInterface
interface MySomeIntegerFunction1 {
int apply(SomeInteger t);
}

@FunctionalInterface
interface MySomeIntegerIntFunction2 {
int apply(SomeInteger t, int u);
}

static MyIntFunction2 addint = (s,t) -> s + t;

static class SomeInteger {
int s;
MyFunction2<Integer, Integer ,Integer> combine;
SomeInteger(int s, MyFunction2<Integer, Integer, Integer> combine) {
this.s = s;
this.combine = combine;
}
public int m(int s2) {
return combine.apply(s, s2);
}
public int m1() {
return s;
}
}

public void testMethodReference_autoboxing() {
SomeInteger some = new SomeInteger(3, addInteger);

// Test all 4 flavours of methodReference autoboxing parameters.
// 1. Static method
assertEquals((Integer) 5, ((MyFunction1<Integer, Integer>) Java8Test::m).apply(5));
// 2. Qualified instance method
assertEquals((Integer) 5, ((MyFunction1<Integer, Integer>) some::m).apply(2));
// 3. Unqualified instance method
assertEquals((Integer) 3, ((MyFunction1<SomeInteger, Integer>) SomeInteger::m1).apply(some));
assertEquals((Integer) 5, ((MyFunction2<SomeInteger, Integer, Integer>)
SomeInteger::m).apply(some, 2));
assertEquals((Integer) 5,
((MyFunction1<SomeInteger, Integer>)
SomeInteger::m1).apply(new SomeInteger(5, addInteger)));
// 4. Constructor reference.
assertEquals(5,
((MyFunction2<Integer, MyFunction2<Integer, Integer, Integer>, SomeInteger>)
SomeInteger::new).apply(5, addInteger).m1());

// Test all 4 flavours of methodReference (interface unboxed)
// 1. Static method
assertEquals(5, ((MyIntFunction1) Java8Test::m).apply(5));
// 2. Qualified instance method
assertEquals(5, ((MyIntFunction1) some::m).apply(2));
// 3. Unqualified instance method
assertEquals(3, ((MySomeIntegerFunction1) SomeInteger::m1).apply(some));
// The next expression was the one that triggered bug #9346 where decisions on whether to
// box/unbox were decided incorrectly due to differring number of parameters in the method
// reference and the functional interface method.
assertEquals(5, ((MySomeIntegerIntFunction2) SomeInteger::m).apply(some, 2));
assertEquals(5,
((MySomeIntegerFunction1)
SomeInteger::m1).apply(new SomeInteger(5, addInteger)));
// 4. Constructor reference.
assertEquals(5,
((MyIntFuncToSomeIntegeFunction2) SomeInteger::new).apply(5, addInteger).m1());
}
}
8 changes: 6 additions & 2 deletions user/test/com/google/gwt/dev/jjs/test/Java8Test.java
Expand Up @@ -276,11 +276,15 @@ public void testJsVarargsLambda() {
assertFalse(isGwtSourceLevel8());
}

public void testMethodReferenceImplementedInSuperclass() {
public void testMethodReference_implementedInSuperclass() {
assertFalse(isGwtSourceLevel8());
}

public void testMethodReferenceWithGenericTypeParameters() {
public void testMethodReference_genericTypeParameters() {
assertFalse(isGwtSourceLevel8());
}

public void testMethodReference_autoboxing() {
assertFalse(isGwtSourceLevel8());
}

Expand Down

0 comments on commit 1da251c

Please sign in to comment.