/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.devtools.j2objc.ast;
import com.google.devtools.j2objc.GenerationTest;
import java.io.IOException;
/**
* Unit tests for {@link LambdaExpression}.
*
* @author Seth Kirby
*/
public class LambdaExpressionTest extends GenerationTest {
private String functionHeader = "interface Function<T, R> { R apply(T t); }";
private String fourToOneHeader = "interface FourToOne<F, G, H, I, R> {"
+ " R apply(F f, G g, H h, I i); }";
public void testCaptureDetection() throws IOException {
translateSourceFile(functionHeader + "interface A { boolean r(boolean a);"
+ "default int ret1() { return 2; }} interface B { default int ret() { return 1; } }"
+ "class Test { void f() { ((A & B)(a) -> a).ret(); } }", "Test", "Test.m");
translateSourceFile(functionHeader + "interface A2 { boolean r();"
+ "default int ret1() { return 2; }} interface B { default int ret() { return 1; } }"
+ "class Test { void f() { ((A2 & B)() -> true).ret(); } }", "Test", "Test.m");
String nonCaptureTranslation = translateSourceFile(
functionHeader + "class Test { Function f = x -> x;}", "Test", "Test.m");
String captureTranslationOuter = translateSourceFile(
functionHeader + "class Test { int y; Function f = x -> y;}", "Test", "Test.m");
String captureTranslation = translateSourceFile(
functionHeader + "class Test { Function<Function, Function> f = y -> x -> y;}", "Test",
"Test.m");
assertTranslation(nonCaptureTranslation, "Test_$Lambda$1_get_instance()");
assertTranslatedLines(captureTranslationOuter,
"@interface Test_$Lambda$1 : NSObject < Function > {",
" @public",
" Test *this$0_;",
"}");
assertTranslation(captureTranslation, "Test_$Lambda$1_get_instance()");
assertTranslatedLines(captureTranslation,
"@interface Test_$Lambda$2 : NSObject < Function > {",
" @public",
" id<Function> val$y_;",
"}");
}
public void testTypeInference() throws IOException {
String quadObjectTranslation = translateSourceFile(
fourToOneHeader + "class Test { FourToOne f = (a, b, c, d) -> 1;}", "Test", "Test.m");
assertTranslatedLines(quadObjectTranslation,
"- (id)applyWithId:(id)a",
" withId:(id)b",
" withId:(id)c",
" withId:(id)d {",
" return JavaLangInteger_valueOfWithInt_(1);",
"}");
String mixedObjectTranslation = translateSourceFile(fourToOneHeader
+ "class Test { FourToOne<String, Double, Integer, Boolean, String> f = "
+ "(a, b, c, d) -> \"1\";}", "Test", "Test.m");
assertTranslatedLines(mixedObjectTranslation,
"- (id)applyWithId:(NSString *)a",
" withId:(JavaLangDouble *)b",
" withId:(JavaLangInteger *)c",
" withId:(JavaLangBoolean *)d {",
" return @\"1\";",
"}");
}
public void testOuterFunctions() throws IOException {
String translation = translateSourceFile(
functionHeader + "class Test { Function outerF = (x) -> x;}", "Test", "Test.m");
assertTranslation(translation,
"JreStrongAssign(&self->outerF_, JreLoadStatic(Test_$Lambda$1, instance));");
}
public void testStaticFunctions() throws IOException {
String translation = translateSourceFile(
functionHeader + "class Test { static Function staticF = (x) -> x;}", "Test", "Test.m");
assertTranslatedSegments(translation,
"id<Function> Test_staticF;",
"if (self == [Test class]) {",
"JreStrongAssign(&Test_staticF, JreLoadStatic(Test_$Lambda$1, instance))");
}
public void testNestedLambdas() throws IOException {
String outerCapture = translateSourceFile(functionHeader
+ "class Test { Function<String, Function<String, String>> f = x -> y -> x;}", "Test",
"Test.m");
assertTranslation(outerCapture, "Test_$Lambda$1_get_instance()");
assertTranslatedLines(outerCapture,
"- (id)applyWithId:(NSString *)x {",
" return create_Test_$Lambda$2_initWithNSString_(x);",
"}");
assertTranslatedLines(outerCapture,
"@interface Test_$Lambda$2 : NSObject < Function > {",
" @public",
" NSString *val$x_;",
"}");
assertTranslatedLines(outerCapture,
"- (id)applyWithId:(NSString *)y {",
" return val$x_;",
"}");
String noCapture = translateSourceFile(functionHeader
+ "class Test { Function<String, Function<String, String>> f = x -> y -> y;}", "Test",
"Test.m");
assertTranslation(noCapture, "Test_$Lambda$1_get_instance()");
assertTranslation(noCapture, "Test_$Lambda$2_get_instance()");
assertTranslatedLines(noCapture,
"- (id)applyWithId:(NSString *)x {",
" return JreLoadStatic(Test_$Lambda$2, instance);",
"}");
assertTranslatedLines(noCapture,
"- (id)applyWithId:(NSString *)y {",
" return y;",
"}");
}
// There's no need for a cast_check call on a lambda whose type matches the assigned type.
public void testNoCastCheck() throws IOException {
String translation = translateSourceFile(
functionHeader + "class Test { Function f = (Function) (x) -> x;}", "Test", "Test.m");
assertNotInTranslation(translation, "cast_check");
}
// Test that we aren't trying to import lambda types.
public void testImportExclusion() throws IOException {
String translation = translateSourceFile(
functionHeader + "class Test { Function f = (Function) (x) -> x;}", "Test", "Test.m");
assertNotInTranslation(translation, "lambda$0.h");
}
// Check that lambdas are uniquely named.
public void testLambdaUniquify() throws IOException {
String translation = translateSourceFile(functionHeader
+ "class Test { class Foo{ class Bar { Function f = x -> x; }}\n"
+ "Function f = x -> x;}",
"Test", "Test.m");
assertTranslation(translation, "@interface Test_Foo_Bar_$Lambda$1 : NSObject < Function >");
assertTranslation(translation, "@interface Test_$Lambda$1 : NSObject < Function >");
}
// Check that lambda captures respect reserved words.
public void testLambdaCloseOverReservedWord() throws IOException {
String translation = translateSourceFile(functionHeader
+ "class Test { void f(int operator) { Function l = (a) -> operator; } }",
"Test", "Test.m");
assertTranslation(translation, "val$operator_");
}
public void testLargeArgumentCount() throws IOException {
String interfaceHeader = "interface TooManyArgs<T> { T f(T a, T b, T c, T d, T e, T f, T g,"
+ " T h, T i, T j, T k, T l, T m, T n, T o, T p, T q, T r, T s, T t, T u, T v, T w, T x,"
+ " T y, T z, T aa, T ab, T ac, T ad, T ae, T af, T ag, T ah, T ai, T aj, T ak, T al,"
+ " T am, T an, T ao, T ap, T aq, T ar, T as, T at, T au, T av, T aw, T ax, T ay, T az,"
+ " T ba, T bb, T bc, T bd, T be, T bf, T bg, T bh, T bi, T bj, T bk, T bl, T bm, T bn,"
+ " T bo, T bp, T bq, T br, T bs, T bt, T bu, T bv, T bw, T bx, T by, T bz, T foo);}";
String translation = translateSourceFile(interfaceHeader + "class Test { void a() {"
+ "Object foo = \"Foo\";"
+ "TooManyArgs fun = (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w,"
+ " x, y, z, aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, an, ao, ap, aq, ar, as,"
+ " at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm, bn,"
+ " bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, bar) -> foo;}}",
"Test", "Test.m");
assertTranslatedLines(translation,
"- (id)fWithId:(id)a",
" withId:(id)b",
" withId:(id)c",
" withId:(id)d",
" withId:(id)e");
assertTranslatedLines(translation,
" withId:(id)bw",
" withId:(id)bx",
" withId:(id)by",
" withId:(id)bz",
" withId:(id)bar {",
" return val$foo_;",
"}");
}
public void testCapturingBasicTypeReturn() throws IOException {
String header = "interface I { int foo(); }";
String translation = translateSourceFile(
header + "class Test { int f = 1234; void foo() { I i = () -> f; } }", "Test",
"Test.m");
assertTranslatedLines(translation,
"- (jint)foo {",
" return this$0_->f_;",
"}");
}
// Verify that an #include is generated for the lambda's functionalType.
public void testLambdaFunctionalTypeImport() throws IOException {
String translation = translateSourceFile("import java.util.*;"
+ "class Test { "
+ " public void test(List<String> names) { "
+ " Collections.sort(names, (p1, p2) -> p1.compareTo(p2)); "
+ " }"
+ "}", "Test", "Test.m");
assertTranslation(translation, "#include \"java/util/Comparator.h\"");
}
public void testClassLiteralNamesInLambda() throws IOException {
String translation = translateSourceFile("package foo.bar; import java.io.Serializable; "
+ "interface Comparator<T> {"
+ " int compare(T o1, T o2);"
+ " default Comparator<T> thenComparing(Comparator<? super T> other) {"
+ " return (Comparator<T> & Serializable) (c1, c2) -> {"
+ " int res = compare(c1, c2);"
+ " return (res != 0) ? res : other.compare(c1, c2);"
+ " }; "
+ " }"
+ "}", "Comparator", "foo/bar/Comparator.m");
assertTranslatedLines(translation,
"@interface FooBarComparator_$Lambda$1 : "
+ "NSObject < FooBarComparator, JavaIoSerializable > {",
" @public",
" id<FooBarComparator> this$0_;",
" id<FooBarComparator> val$other_;",
"}");
assertTranslatedLines(translation,
"- (jint)compareWithId:(id)c1",
" withId:(id)c2 {",
" jint res = [this$0_ compareWithId:c1 withId:c2];",
" return (res != 0) ? res : [((id<FooBarComparator>) nil_chk(val$other_)) "
+ "compareWithId:c1 withId:c2];",
"}");
}
}