/* * Copyright 2011 Google Inc. All Rights Reserved. * * 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.translate; import com.google.devtools.j2objc.GenerationTest; import com.google.devtools.j2objc.Options.MemoryManagementOption; import com.google.devtools.j2objc.ast.AbstractTypeDeclaration; import com.google.devtools.j2objc.ast.CompilationUnit; import java.io.IOException; import java.util.List; /** * Unit tests for {@link InnerClassExtractor}. * * @author Tom Ball */ public class InnerClassExtractorTest extends GenerationTest { @Override protected void setUp() throws IOException { super.setUp(); // Reference counting by default, change for ARC-specific tests. options.setMemoryManagementOption(MemoryManagementOption.REFERENCE_COUNTING); } protected List<AbstractTypeDeclaration> translateClassBody(String testSource) { String source = "public class Test { " + testSource + " }"; CompilationUnit unit = translateType("Test", source); return unit.getTypes(); } public void testSimpleInnerClass() throws IOException { String source = "public class A { class B { int test() { return o.hashCode(); }} Object o; }"; String translation = translateSourceFile(source, "A", "A.h"); assertTranslation(translation, "- (instancetype)initWithA:(A *)outer$;"); translation = getTranslatedFile("A.m"); assertTranslation(translation, "A *this$0_;"); assertTranslation(translation, "[nil_chk(this$0_->o_) hash]"); assertTranslation(translation, "JreStrongAssign(&self->this$0_, outer$);"); } public void testWeakSimpleInnerClass() throws IOException { String source = "import com.google.j2objc.annotations.WeakOuter; " + "public class A { @WeakOuter class B { int test() { return o.hashCode(); }} Object o; }"; String translation = translateSourceFile(source, "A", "A.m"); assertTranslation(translation, "__unsafe_unretained A *this$0_;"); assertTranslation(translation, "this$0_ = outer$;"); } public void testWeakArcSimpleInnerClass() throws IOException { options.setMemoryManagementOption(MemoryManagementOption.ARC); String source = "import com.google.j2objc.annotations.WeakOuter; " + "public class A { Object o;" + " @WeakOuter class B { int test() { return o.hashCode(); } Object o2; }}"; String translation = translateSourceFile(source, "A", "A.h"); assertTranslation(translation, "id o_;"); assertTranslation(translation, "id o2_;"); translation = getTranslatedFile("A.m"); assertTranslation(translation, "__unsafe_unretained A *this$0_;"); } public void testInnerInnerClass() throws IOException { String source = "public class A { class B { " + "class C {int test() { return o.hashCode(); }}} Object o; }"; String translation = translateSourceFile(source, "A", "A.h"); assertTranslation(translation, "- (instancetype)initWithA:(A *)outer$;"); assertTranslation(translation, "- (instancetype)initWithA_B:(A_B *)outer$;"); translation = getTranslatedFile("A.m"); assertTranslation(translation, "A *this$0_;"); assertTranslation(translation, "A_B *this$0_;"); assertTranslation(translation, "[nil_chk(this$0_->this$0_->o_) hash]"); } public void testWeakInnerInnerClass() throws IOException { String source = "public class A { class B { " + "@com.google.j2objc.annotations.WeakOuter class C {" + " int test() { return o.hashCode(); }}} Object o; }"; String translation = translateSourceFile(source, "A", "A.m"); assertTranslation(translation, "A *this$0_;"); assertTranslation(translation, "__unsafe_unretained A_B *this$0_;"); assertTranslation(translation, "[nil_chk(this$0_->this$0_->o_) hash]"); } public void testInnerMethodAnonymousClass() throws IOException { String source = "public class A {" + " abstract class C { public abstract void foo(); }" + " class B { " + " public void foo(final int j) {" + " C r = new C() {" + " public void foo() { int hash = j + o.hashCode(); }" + " };" + " }" + " }" + " Object o;" + "}"; String translation = translateSourceFile(source, "A", "A.h"); assertTranslation(translation, "- (instancetype)initWithA:(A *)outer$;"); translation = getTranslatedFile("A.m"); assertTranslatedLines(translation, "- (instancetype)initWithA_B:(A_B *)outer$", "withInt:(jint)capture$0;"); assertTranslation(translation, "A *this$0_;"); assertTranslation(translation, "A_B *this$1_;"); assertTranslation(translation, "jint val$j_;"); assertTranslatedLines(translation, "void A_B_1_initWithA_B_withInt_(A_B_1 *self, A_B *outer$, jint capture$0) {", " JreStrongAssign(&self->this$1_, outer$);", " self->val$j_ = capture$0;", " A_C_initWithA_(self, outer$->this$0_);", "}"); assertTranslation(translation, "[nil_chk(this$1_->this$0_->o_) hash]"); } /** * Verify that a static inner class is extracted. */ public void testStaticInnerClass() throws IOException { String translation = translateSourceFile( "class Test { static class Foo { int i; Foo() { this(0); } Foo(int i) { this.i = i; } } }", "Test", "Test.h"); assertTranslatedLines(translation, "@interface Test_Foo : NSObject {", "@public", "jint i_;", "}"); translation = getTranslatedFile("Test.m"); assertTranslatedLines(translation, "void Test_Foo_init(Test_Foo *self) {", " Test_Foo_initWithInt_(self, 0);", "}"); assertTranslatedLines(translation, "void Test_Foo_initWithInt_(Test_Foo *self, jint i) {", " NSObject_init(self);", " self->i_ = i;", "}"); } /** * Verify that an inner class is moved to the compilation unit's types list. */ public void testInnerClassExtracted() { List<AbstractTypeDeclaration> types = translateClassBody("class Foo { }"); assertEquals(2, types.size()); assertEquals("Test", types.get(0).getName().getIdentifier()); assertEquals("Foo", types.get(1).getName().getIdentifier()); } /** * Regression test: verify that references to class members of a type with * an inner class aren't disturbed. */ public void testStaticMethodInvokingStaticMethodWithInnerClass() throws IOException { String translation = translateSourceFile( "class Test { public static int test(Object object) { return 0; }" + "public static int test(Object object, Object foo) {" + " if (foo == null) { return Test.test(object); } return 1; } " + "private class Inner {} }", "Test", "Test.m"); assertTranslation(translation, "return Test_testWithId_(object);"); } public void testInnerClassInvokingExplicitOuterMethod() throws IOException { String translation = translateSourceFile( "class Test { public int size() { return 0; } " + "class Inner { int size() { return Test.this.size(); } } }", "Test", "Test.m"); assertTranslation(translation, "JreStrongAssign(&self->this$0_, outer$);"); assertTranslation(translation, "return [this$0_ size];"); } public void testInnerClassInvokingOuterMethod() throws IOException { String translation = translateSourceFile( "class Test { public int size() { return 0; } " + "class Inner { int getCount() { return size(); } } }", "Test", "Test.m"); assertTranslation(translation, "return [this$0_ size];"); } public void testInnerSubclassInvokingOuterMethod() throws IOException { String translation = translateSourceFile( "class Test { public int size() { return 0; } public void add(int n) {} class Inner {} " + "class Innermost { void test() { Test.this.add(size()); } } }", "Test", "Test.m"); assertTranslation(translation, "JreStrongAssign(&self->this$0_, outer$);"); assertTranslation(translation, "[this$0_ addWithInt:[this$0_ size]];"); } public void testInnerClassDefaultInitialization() throws IOException { String translation = translateSourceFile( "class Test { Inner inner = new Inner(true); public int size() { return 0; }" + "class Inner { Inner(boolean b) {} int size() { return Test.this.size(); } } }", "Test", "Test.m"); assertTranslation(translation, "JreStrongAssignAndConsume(&self->inner_, " + "new_Test_Inner_initWithTest_withBoolean_(self, true));"); assertTranslation(translation, "JreStrongAssign(&self->this$0_, outer$);"); } public void testOuterClassAccessOuterVars() throws IOException { String translation = translateSourceFile( "class Test { int elementCount;" + "public Test() { elementCount = 0; }" + "private class Iterator { public void remove() { elementCount--; } } }", "Test", "Test.m"); assertTranslatedLines(translation, "elementCount_ = 0;"); assertTranslatedLines(translation, "- (void)remove {", "this$0_->elementCount_--;"); } public void testOuterInterfaceMethodReference() throws IOException { String source = "class Test { " + " interface Foo { void foo(); } " + " abstract class Bar implements Foo { " + " class Inner { Inner() { foo(); } } " + " class Inner2 extends Inner { void bar() { foo(); } } " + " Inner makeInner() { return new Inner(); } }" + " public void test() { " + " Bar bar = new Bar() { public void foo() { } };" + " Bar.Inner inner = bar.new Inner(); } }"; String translation = translateSourceFile(source, "Test", "Test.m"); assertTranslation(translation, "- (void)bar {\n [this$1_ foo]"); assertTranslation(translation, "- (Test_Bar_Inner *)makeInner {\n" + " return create_Test_Bar_Inner_initWithTest_Bar_(self);"); assertTranslation(translation, "create_Test_Bar_Inner_initWithTest_Bar_(bar);"); } public void testMultipleThisReferences() throws IOException { String source = "class A { private int x = 0; " + " interface Foo { void doSomething(); } " + " class Inner { private int x = 1; " + " public void blah() { " + " new Foo() { public void doSomething() { " + " Inner.this.x = 2; A.this.x = 3; }}; }}}"; CompilationUnit unit = translateType("A", source); List<AbstractTypeDeclaration> types = unit.getTypes(); assertEquals(4, types.size()); String translation = translateSourceFile(source, "A", "A.m"); // Anonymous class constructor in Inner.blah() assertTranslation(translation, "create_A_Inner_1_initWithA_Inner_(self)"); // A.Inner.x referred to in anonymous Foo assertTranslation(translation, "this$0_->x_ = 2"); // A.x referred to in anonymous Foo assertTranslation(translation, "this$0_->this$0_->x_ = 3"); // A.Inner init in anonymous Foo's constructor assertTranslation(translation, "JreStrongAssign(&self->this$0_, outer$)"); } /** * This test differs from the last one only in the addition of another * 'this' reference before the anonymous class creation. */ public void testMultipleThisReferencesWithPreviousReference() throws IOException { String source = "class A { private int x = 0; " + " interface Foo { void doSomething(); } " + " class Inner { private int x = 1; " + " public void blah() { " + " A.this.x = 2; " + " new Foo() { public void doSomething() { " + " Inner.this.x = 3; A.this.x = 4; }}; }}}"; CompilationUnit unit = translateType("A", source); List<AbstractTypeDeclaration> types = unit.getTypes(); assertEquals(4, types.size()); String translation = translateSourceFile(source, "A", "A.m"); // Anonymous class constructor in Inner.blah() assertTranslation(translation, "create_A_Inner_1_initWithA_Inner_(self)"); // A.x referred to in A.Inner. assertTranslation(translation, "this$0_->x_ = 2"); // A.Inner.x referred to in anonymous Foo. assertTranslation(translation, "this$0_->x_ = 3"); // A.x referred to in anonymous Foo assertTranslation(translation, "this$0_->this$0_->x_ = 4"); // A.Inner init in anonymous Foo's constructor assertTranslation(translation, "JreStrongAssign(&self->this$0_, outer$)"); } public void testOuterMethodReference() throws IOException { String source = "class Test { " + " interface Foo { void foo(); } " + " class Inner { " + " void bar() { " + " final int x = 0; final int y = 0; " + " Foo foo = new Foo() { " + " public void foo() { if (x ==0) mumble(y); } }; } }" + " private void mumble(int y) { } }"; String translation = translateSourceFile(source, "Test", "Test.m"); assertTranslation(translation, "Test_mumbleWithInt_(this$0_->this$0_, 0)"); } public void testInnerSubClassOfGenericClassInner() throws IOException { String source = "class Test { " + "class A<E extends A<E>.Inner> { public class Inner { } } " + "class B extends A<B.BInner> { public class BInner extends A<B.BInner>.Inner { } } }"; String translation = translateSourceFile(source, "Test", "Test.h"); assertTranslation(translation, "@interface Test_B_BInner : Test_A_Inner"); } public void testGenericInnerSubClassOfGenericClassGenericInner() throws IOException { String source = "class Test<E> { " + "class A<E> { } class B<E> extends A<E> { B(int i) { } } }"; String translation = translateSourceFile(source, "Test", "Test.m"); assertTranslation(translation, "- (instancetype)initWithTest:(Test *)outer$\n" + " withInt:(jint)i"); } public void testInnerSubClassOfOtherInnerWithOuterRefs() throws IOException { String source = "class Test { " + "class A { " + " public void foo() { } " + " public class Inner { void test() { foo(); } } } " + "class B extends A { " + " public class BInner extends A.Inner { void test() { foo(); } } } " + " public static void main(String[] args) { B b = new Test().new B(); }}"; String translation = translateSourceFile(source, "Test", "Test.m"); // Check that outer fields are added to A.Inner and B.BInner. assertTranslation(translation, "@interface Test_A_Inner () {\n @public\n Test_A *this$0_;"); assertTranslation(translation, "@interface Test_B_BInner () {\n @public\n Test_B *this$1_;"); // Check that B has a constructor that correctly calls constructor of A // with right outer. assertTranslatedLines(translation, "void Test_B_initWithTest_(Test_B *self, Test *outer$) {", " Test_A_initWithTest_(self, outer$);", "}"); } public void testInnerClassQualifiedAndUnqualfiedOuterReferences() throws IOException { String source = "class Test { " + " public int i = 0; " + " class Inner { " + " void foo(int i) { Test.this.i = i; } " + " void bar() { int j = i; } } }"; String translation = translateSourceFile(source, "Test", "Test.m"); assertTranslation(translation, "- (void)fooWithInt:(jint)i {\n this$0_->i_ ="); assertTranslation(translation, "- (void)bar {\n jint j = this$0_->i_"); } public void testInnerClassExtendsAnotherInner() throws IOException { String translation = translateSourceFile( "class Test { " + " Integer i = 1; " + " class Inner1 { } " + " class Inner2 extends Inner1 { " + " int j = 1; " + " public int foo() { return i + j; } } }", "Test", "Test.m"); assertTranslation(translation, "Test *this$1"); // Inner2's outer reference. assertTranslation(translation, "[((JavaLangInteger *) nil_chk(this$1_->i_)) intValue] + j_"); } public void testInnerClassInstantiatesAnotherInner() throws IOException { String translation = translateSourceFile( "class Test { " + " Integer i = 1; " + " class Inner1 { public int foo() { return i + 1; } } " + " class Inner2 { Inner1 inner1 = new Inner1(); } }", "Test", "Test.m"); assertTranslation(translation, "new_Test_Inner1_initWithTest_(outer$)"); translation = getTranslatedFile("Test.h"); assertTranslation(translation, "@interface Test_Inner2 : NSObject {\n" + " @public\n" + " Test_Inner1 *inner1_;"); } public void testInnerClassWithInnerSuperClass() throws IOException { String translation = translateSourceFile( "class Test { " + " class Inner1 { public Inner1(int n) { } } " + " class Inner2 extends Inner1 { public Inner2(int n, long l) { super(n); } } }", "Test", "Test.m"); assertTranslation(translation, "Test_Inner1_initWithTest_withInt_(self, outer$, n);"); } public void testInnerSubClassOfOtherInnerWithOuterRefsExtraction() throws IOException { String source = "public class Test { " + "int i; " + "class A { " + " private void foo() { i++; } " + " public class Inner { Inner() { foo(); } } } " + "class B extends A { " + " public class BInner extends A.Inner { } } " + "public static void main(String[] args) { B b = new Test().new B(); }}"; String translation = translateSourceFile(source, "Test", "Test.m"); // Verify that B's translation has the Test field declared. assertTranslation(translation, "Test *this$0_;"); // Verify that A has a Test field (this$0). assertTranslatedLines(translation, "@interface Test_A () {", "@public", "Test *this$0_;", "}"); // Verify that B does not have a Test field. assertNotInTranslation(translation, "@interface Test_B ()"); // Verify that main method creates a new instanceof B associated with // a new instance of Test. assertTranslatedLines(translation, "void Test_mainWithNSStringArray_(IOSObjectArray *args) {", "Test_initialize();", "Test_B *b = create_Test_B_initWithTest_(create_Test_init());"); // Verify that BInner's constructor takes a B instance and correctly calls // the super constructor. assertTranslatedLines(translation, "void Test_B_BInner_initWithTest_B_(Test_B_BInner *self, Test_B *outer$) {", " Test_A_Inner_initWithTest_A_(self, outer$);", "}"); } // Identical sample code to above test, except the order of B and A is switched. public void testInnerSubClassOfOtherInnerWithOuterRefsExtraction2() throws IOException { String source = "public class Test { " + "int i; " + "class B extends A { " + " public class BInner extends A.Inner { } } " + "class A { " + " private void foo() { i++; } " + " public class Inner { Inner() { foo(); } } } " + "public static void main(String[] args) { B b = new Test().new B(); }}"; // Verify that B's translation has the Test field declared. String translation = translateSourceFile(source, "Test", "Test.m"); assertTranslation(translation, "Test *this$0_;"); // Verify that A has a Test field (this$0). assertTranslatedLines(translation, "@interface Test_A () {", "@public", "Test *this$0_;", "}"); // Verify that B does not have a Test field. assertNotInTranslation(translation, "@interface Test_B () {"); // Verify that main method creates a new instanceof B associated with // a new instance of Test. assertTranslatedLines(translation, "void Test_mainWithNSStringArray_(IOSObjectArray *args) {", "Test_initialize();", "Test_B *b = create_Test_B_initWithTest_(create_Test_init());"); // Verify that BInner's constructor takes a B instance and correctly calls // the super constructor. assertTranslatedLines(translation, "void Test_B_BInner_initWithTest_B_(Test_B_BInner *self, Test_B *outer$) {", " Test_A_Inner_initWithTest_A_(self, outer$);", "}"); } // Identical sample code to above test, except A is a generic class. public void testInnerSubClassOfOtherInnerWithOuterRefsWithGenerics() throws IOException { String source = "public class Test { " + "class B extends A<B.BInner> { " + " public class BInner extends A<B.BInner>.Inner { BInner() { super(null); } } } " + "class A<T extends A<T>.Inner> { " + " private void foo() { } " + " public class Inner { Inner(T t) { foo(); } } } " + "public static void main(String[] args) { B b = new Test().new B(); }}"; String translation = translateSourceFile(source, "Test", "Test.m"); // Make sure that the call to super(null) in B.BInner's constructor // is translated with the right keyword for the generic second parameter. assertTranslation(translation, "Test_A_Inner_initWithTest_A_withTest_A_Inner_(self, outer$, nil);"); } public void testStaticImportReferenceInInnerClass() throws IOException { String translation = translateSourceFile( "import static java.lang.Character.isDigit; public class Test { class Inner { " + " public void foo() { boolean b = isDigit('c'); } } }", "Test", "Test.m"); assertTranslation(translation, "JavaLangCharacter_isDigitWithChar_('c')"); } public void testStaticReferenceInInnerClass() throws IOException { String translation = translateSourceFile( "public class Test { public static void foo() { } class Inner { " + " public void bar() { foo(); } } }", "Test", "Test.m"); assertTranslation(translation, "Test_foo()"); } public void testMethodInnerClass() throws IOException { String source = "public class A { void foo() { class MyRunnable implements Runnable {" + "public void run() {} }}}"; String translation = translateSourceFile(source, "A", "A.m"); assertTranslation(translation, "@interface A_1MyRunnable : NSObject < JavaLangRunnable >"); assertNotInTranslation(translation, "A *this"); } public void testInnerClassConstructor() throws IOException { String source = "public class A { class B { Object test() { return new B(); }}}"; String translation = translateSourceFile(source, "A", "A.m"); assertTranslation(translation, "return create_A_B_initWithA_(this$0_);"); } public void testMethodInnerClassWithSameName() throws IOException { String source = "public class A { class MyClass {} void foo() { class MyClass {}}}"; String translation = translateSourceFile(source, "A", "A.h"); assertTranslation(translation, "@interface A_MyClass"); translation = getTranslatedFile("A.m"); assertTranslation(translation, "@interface A_1MyClass"); } public void testOuterThisReferenceInInner() throws IOException { String translation = translateSourceFile( "class Test { " + " class Inner { Inner(int i) { } Inner foo() { return new Inner(1); } } " + " public Inner bar() { return new Inner(2); } }", "Test", "Test.m"); assertTranslation(translation, "create_Test_Inner_initWithTest_withInt_(this$0_, 1)"); assertTranslation(translation, "create_Test_Inner_initWithTest_withInt_(self, 2)"); } public void testInnerThisReferenceInInnerAsFieldAccess() throws IOException { String translation = translateSourceFile( "class Test { " + " class Inner { int i = 0; Inner() { Inner.this.i = 1; } } }", "Test", "Test.m"); assertTranslation(translation, "self->i_ = 1"); } public void testInnerThisReferenceInInnerAsThisExpression() throws IOException { String translation = translateSourceFile( "class Test { " + " static void foo(Inner i) { } " + " class Inner { Inner() { foo(Inner.this); } } }", "Test", "Test.m"); assertTranslation(translation, "Test_fooWithTest_Inner_(self)"); } // Verify that an anonymous class in a static initializer does not reference // instance. public void testNoOuterInStaticInitializer() throws IOException { String source = "import java.util.*; " + "public class A { static { foo(new Enumeration() { " + " public boolean hasMoreElements() { return false; }" + " public Object nextElement() { return null; }}); }" + " public static void foo(Object o) { } }"; String translation = translateSourceFile(source, "A", "A.h"); assertNotInTranslation(translation, "this$0_"); translation = getTranslatedFile("A.m"); assertNotInTranslation(translation, "this$0_"); assertTranslation(translation, "A_fooWithId_(create_A_1_init())"); } // Verify that an anonymous class assigned to a static field does not // reference instance. public void testNoOuterWhenAssignedToStaticField() throws IOException { String source = "import java.util.*; " + "public class A { static Enumeration test = new Enumeration() { " + " public boolean hasMoreElements() { return false; }" + " public Object nextElement() { return null; }}; }"; String translation = translateSourceFile(source, "A", "A.h"); assertFalse(translation.contains("this$0_")); translation = getTranslatedFile("A.m"); assertFalse(translation.contains("this$0_")); assertTranslation(translation, "JreStrongAssignAndConsume(&A_test, new_A_1_init());"); } // Verify that an anonymous class in a static method does not reference // instance. public void testNoOuterWhenInStaticMethod() throws IOException { String source = "import java.util.*; " + "public class A { static Enumeration test(Collection collection) { " + " final Collection c = collection; " + " return new Enumeration() { " + " Iterator it = c.iterator(); " + " public boolean hasMoreElements() { return it.hasNext(); }" + " public Object nextElement() { return it.next(); }}; }}"; String translation = translateSourceFile(source, "A", "A.m"); assertNotInTranslation(translation, "this$0_"); assertTranslation(translation, "- (instancetype)initWithJavaUtilCollection:(id<JavaUtilCollection>)capture$0;"); assertTranslation(translation, "[((id<JavaUtilCollection>) nil_chk(capture$0)) iterator]"); assertTranslation(translation, "return create_A_1_initWithJavaUtilCollection_(c);"); assertTranslation(translation, "- (instancetype)initWithJavaUtilCollection:(id<JavaUtilCollection>)capture$0 {"); } public void testInnerAccessingOuterArrayLength() throws IOException { String source = "public class A<E> { transient E[] elements; " + "private class B implements java.util.Iterator<E> { " + "public boolean hasNext() { return elements.length > 0; } " + "public E next() { return null; }" + "public void remove() {} }}"; String translation = translateSourceFile(source, "A", "A.m"); assertTranslation(translation, "- (instancetype)initWithA:(A *)outer$;"); assertTranslation(translation, "A *this$0_;"); assertTranslation(translation, "((IOSObjectArray *) nil_chk(this$0_->elements_))->size_"); } public void testCreateInnerClassOfSuperclass() throws IOException { String source = "class B {\n" + " class C {}\n" + "}\n" + "class A extends B {\n" + " void foo() { new C(); }\n" + "}\n"; String translation = translateSourceFile(source, "A", "A.m"); assertTranslation(translation, "create_B_C_initWithB_(self)"); } public void testCallInnerConstructorOfParameterizedOuterClass() throws IOException { String outerSource = "abstract class Outer<T> { class Inner { public Inner(T t) {} }}"; String callerSource = "class A extends Outer<String> { public void foo() { new Inner(\"test\"); } }"; addSourceFile(outerSource, "Outer.java"); addSourceFile(callerSource, "A.java"); String translation = translateSourceFile("A", "A.m"); assertTranslation(translation, "create_Outer_Inner_initWithOuter_withId_(self, @\"test\");"); } public void testNoOuterFieldAssignmentWhenCallingOtherConstructor() throws IOException { String source = "class Outer { class Inner { Inner(int i) {} Inner() { this(42); } } }"; String translation = translateSourceFile(source, "Outer", "Outer.m"); assertTranslatedLines(translation, "void Outer_Inner_initWithOuter_(Outer_Inner *self, Outer *outer$) {", " Outer_Inner_initWithOuter_withInt_(self, outer$, 42);", "}"); } public void testListArgsInEnumConstantDeclaration() throws IOException { String source = "class Outer { " + " enum Inner { " + " A(new String[] { \"1\", \"2\", \"3\" }), " + " B(new String[] { \"4\", \"5\", \"6\" }); " + " Inner(String[] values) {} " + " } " + "}"; String translation = translateSourceFile(source, "Outer", "Outer.m"); assertTranslation(translation, "[IOSObjectArray arrayWithObjects:(id[]){ " + "@\"1\", @\"2\", @\"3\" } count:3 type:NSString_class_()]"); assertTranslation(translation, "[IOSObjectArray arrayWithObjects:(id[]){ " + "@\"4\", @\"5\", @\"6\" } count:3 type:NSString_class_()]"); } public void testInnerClassVarargsConstructor() throws IOException { String translation = translateSourceFile( "class Test { class Inner { Inner(int... i) {} } void test() { new Inner(1, 2, 3); } }", "Test", "Test.m"); assertTranslation(translation, "create_Test_Inner_initWithTest_withIntArray_(self, " + "[IOSIntArray arrayWithInts:(jint[]){ 1, 2, 3 } count:3])"); } public void testInnerClassConstructedInSuperConstructorInvocation() throws IOException { String translation = translateSourceFile( "class Outer { " + " class Inner1 { } " + " class Inner2Super { Inner2Super(Inner1 i) { } } " + " class Inner2 extends Inner2Super { " + " Inner2() { " + " super(new Inner1()); " + " } " + " } " + "}", "Outer", "Outer.m"); assertTranslation(translation, "create_Outer_Inner1_initWithOuter_(outer$)"); } public void testOuterReferenceInSuperConstructorInvocation() throws IOException { String translation = translateSourceFile( "class Outer { " + " int foo; " + " class Inner1 { Inner1(int i) { } } " + " class Inner2 extends Inner1 { " + " Inner2() { " + " super(foo); " + " } " + " } " + "}", "Outer", "Outer.m"); assertTranslation(translation, "Outer_Inner1_initWithOuter_withInt_(self, outer$, outer$->foo_);"); } public void testOuterThisReferenceInSuperConstructorInvocation() throws IOException { String translation = translateSourceFile( "class Outer { " + " int foo; " + " class Outer1 { " + " int foo; " + " class Inner1 { Inner1(int i) { } } " + " class Inner2 extends Inner1 { " + " Inner2() { " + " super(Outer.this.foo); " + " } " + " } " + " } " + "}", "Outer", "Outer.m"); assertTranslation(translation, "Outer_Outer1_Inner1_initWithOuter_Outer1_withInt_(self, outer$, outer$->this$0_->foo_);"); } public void testAnonymousClassWithinTypeDeclarationStatement() throws IOException { String translation = translateSourceFile( "class Test { Runnable foo() { class MyRunnable implements Runnable { " + "public void run() { Runnable r = new Runnable() { public void run() {} }; } } " + "return new MyRunnable(); } }", "Test", "Test.m"); assertOccurrences(translation, "@interface Test_1MyRunnable_1", 1); } public void testOuterInitializedBeforeSuperInit() throws IOException { String translation = translateSourceFile( "class Test { int i; class Inner { void test() { i++; } } }", "Test", "Test.m"); assertTranslatedLines(translation, "void Test_Inner_initWithTest_(Test_Inner *self, Test *outer$) {", " JreStrongAssign(&self->this$0_, outer$);", " NSObject_init(self);", "}"); } public void testInnerClassOuterStackReference() throws IOException { String translation = translateSourceFile( "public class A { " + " void test() { " + " final Object obj = new Object(); " + " class TestThread extends Thread { " + " public void run() { " + " System.out.println(obj); }}}}", "A", "A.m"); assertTranslation(translation, "printlnWithId:val$obj_"); assertTranslation(translation, "id val$obj_;"); } public void testLocalClassWithCaptureVariables() throws IOException { String translation = translateSourceFile( "class Test { void test(final String s) { " + " class Inner { " + " Inner() { this(0); } " + " Inner(int i) { } " + " void foo() { s.toString(); } " + " } " + " new Inner(); } }", "Test", "Test.m"); assertTranslation(translation, "create_Test_1Inner_initWithNSString_(s)"); assertTranslatedLines(translation, "void Test_1Inner_initWithNSString_(Test_1Inner *self, NSString *capture$0) {", " Test_1Inner_initWithNSString_withInt_(self, capture$0, 0);", "}"); assertTranslatedLines(translation, "void Test_1Inner_initWithNSString_withInt_(" + "Test_1Inner *self, NSString *capture$0, jint i) {", " JreStrongAssign(&self->val$s_, capture$0);", " NSObject_init(self);", "}"); } public void testWeakStaticClass() throws IOException { String source = "import com.google.j2objc.annotations.WeakOuter; " + "public class A { @WeakOuter static class B {}}"; String translation = translateSourceFile(source, "A", "A.h"); assertWarningCount(1); assertErrorCount(0); assertNotInTranslation(translation, "__unsafe_unretained"); } public void testInnerClassWithVarargsAndCaptureVariables() throws IOException { String translation = translateSourceFile( "class Test { int test(final int i, Object o) { class Inner { Inner(Object... o) {} " + "int foo() { return i; } } return new Inner(o).foo(); } }", "Test", "Test.m"); assertTranslation(translation, "create_Test_1Inner_initWithInt_withNSObjectArray_(i, " + "[IOSObjectArray arrayWithObjects:(id[]){ o } count:1 type:NSObject_class_()])"); assertTranslation(translation, "void Test_1Inner_initWithInt_withNSObjectArray_(" + "Test_1Inner *self, jint capture$0, IOSObjectArray *o)"); } }