Skip to content

Commit

Permalink
Emit static interface methods in companion classes
Browse files Browse the repository at this point in the history
	Change on 2016/03/10 by lukhnos <lukhnos@google.com>
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=116868937
  • Loading branch information
lukhnos authored and tomball committed Mar 11, 2016
1 parent 91d67c6 commit 8997de4
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 3 deletions.
Expand Up @@ -30,6 +30,7 @@
import com.google.devtools.j2objc.ast.Name;
import com.google.devtools.j2objc.ast.NativeDeclaration;
import com.google.devtools.j2objc.ast.PropertyAnnotation;
import com.google.devtools.j2objc.ast.TreeNode;
import com.google.devtools.j2objc.ast.TreeUtil;
import com.google.devtools.j2objc.ast.VariableDeclarationFragment;
import com.google.devtools.j2objc.util.BindingUtil;
Expand Down Expand Up @@ -201,6 +202,14 @@ private void printImplementedProtocols() {
}
}

protected void printStaticInterfaceMethods() {
for (BodyDeclaration declaration : getInnerDeclarations()) {
if (declaration.getKind().equals(TreeNode.Kind.METHOD_DECLARATION)) {
printMethodDeclaration((MethodDeclaration) declaration, true);
}
}
}

/**
* Prints the list of static variable and/or enum constant accessor methods.
*/
Expand Down Expand Up @@ -355,6 +364,7 @@ protected void printCompanionClassDeclaration() {
} else {
newline();
}
printStaticInterfaceMethods();
printStaticAccessors();
println("\n@end");
}
Expand Down Expand Up @@ -582,12 +592,29 @@ private void printAnnotationAccessors(List<AnnotationTypeMemberDeclaration> memb
}
}

@Override
protected void printMethodDeclaration(MethodDeclaration m) {
/**
* Emit method declaration.
*
* @param m The method.
* @param isCompanionClass If true, emit only if m is a static interface method.
*/
private void printMethodDeclaration(MethodDeclaration m, boolean isCompanionClass) {
IMethodBinding methodBinding = m.getMethodBinding();
ITypeBinding typeBinding = methodBinding.getDeclaringClass();

if (typeBinding.isInterface()) {
// isCompanion and isStatic must be both false (i.e. this prints a non-static method decl
// in @protocol) or must both be true (i.e. this prints a static method decl in the
// companion class' @interface).
if (isCompanionClass != BindingUtil.isStatic(methodBinding)) {
return;
}
}

newline();
JavadocGenerator.printDocComment(getBuilder(), m.getJavadoc());
print(getMethodSignature(m));
String methodName = nameTable.getMethodSelector(m.getMethodBinding());
String methodName = nameTable.getMethodSelector(methodBinding);
if (!m.isConstructor() && !BindingUtil.isSynthetic(m.getModifiers())
&& needsObjcMethodFamilyNoneAttribute(methodName)) {
// Getting around a clang warning.
Expand All @@ -607,6 +634,11 @@ && needsObjcMethodFamilyNoneAttribute(methodName)) {
println(";");
}

@Override
protected void printMethodDeclaration(MethodDeclaration m) {
printMethodDeclaration(m, false);
}

private boolean needsObjcMethodFamilyNoneAttribute(String name) {
return FAMILY_METHOD_REGEX.matcher(name).matches();
}
Expand Down
Expand Up @@ -109,6 +109,7 @@ private List<BodyDeclaration> filterDeclarations(Iterable<BodyDeclaration> decla

private static final Predicate<VariableDeclarationFragment> IS_STATIC_FIELD =
new Predicate<VariableDeclarationFragment>() {
@Override
public boolean apply(VariableDeclarationFragment frag) {
// isGlobalVar includes non-static but final primitives, which are treated
// like static fields in J2ObjC.
Expand All @@ -118,12 +119,14 @@ public boolean apply(VariableDeclarationFragment frag) {

private static final Predicate<VariableDeclarationFragment> IS_INSTANCE_FIELD =
new Predicate<VariableDeclarationFragment>() {
@Override
public boolean apply(VariableDeclarationFragment frag) {
return BindingUtil.isInstanceVar(frag.getVariableBinding());
}
};

private static final Predicate<BodyDeclaration> IS_OUTER_DECL = new Predicate<BodyDeclaration>() {
@Override
public boolean apply(BodyDeclaration decl) {
switch (decl.getKind()) {
case FUNCTION_DECLARATION:
Expand All @@ -137,6 +140,7 @@ public boolean apply(BodyDeclaration decl) {
};

private static final Predicate<BodyDeclaration> IS_INNER_DECL = new Predicate<BodyDeclaration>() {
@Override
public boolean apply(BodyDeclaration decl) {
switch (decl.getKind()) {
case METHOD_DECLARATION:
Expand Down Expand Up @@ -233,6 +237,7 @@ private boolean hasStaticAccessorMethods() {

protected boolean needsPublicCompanionClass() {
return BindingUtil.hasDefaultMethodsInFamily(typeBinding)
|| BindingUtil.hasStaticInterfaceMethods(typeBinding)
|| (!typeNode.hasPrivateDeclaration()
&& (hasInitializeMethod() || BindingUtil.isRuntimeAnnotation(typeBinding)
|| hasStaticAccessorMethods()));
Expand Down
Expand Up @@ -652,4 +652,19 @@ public static boolean hasDefaultMethodsInFamily(ITypeBinding type) {

return false;
}

/**
* Returns true if any of the declared methods in the interface is static.
*/
public static boolean hasStaticInterfaceMethods(ITypeBinding type) {
assert type.isInterface();

for (IMethodBinding method : type.getDeclaredMethods()) {
if (isStatic(method)) {
return true;
}
}

return false;
}
}
Expand Up @@ -19,6 +19,7 @@
import com.google.common.collect.Lists;
import com.google.devtools.j2objc.GenerationTest;
import com.google.devtools.j2objc.Options;
import com.google.devtools.j2objc.util.FileUtil;
import com.google.devtools.j2objc.util.HeaderMap;

import java.io.File;
Expand All @@ -32,6 +33,19 @@
*/
public class ObjectiveCHeaderGeneratorTest extends GenerationTest {

@Override
protected void setUp() throws IOException {
tempDir = FileUtil.createTempDir("testout");
Options.load(new String[]{
"-d", tempDir.getAbsolutePath(),
"-sourcepath", tempDir.getAbsolutePath(),
"-q", // Suppress console output.
"-encoding", "UTF-8", // Translate strings correctly when encodings are nonstandard.
"-source", "8" // Treat as Java 8 source.
});
parser = initializeParser(tempDir);
}

public void testInnerEnumWithPackage() throws IOException {
String translation = translateSourceFile(
"package mypackage;"
Expand Down Expand Up @@ -845,4 +859,19 @@ public void testConstructorsDisallowed() throws IOException {
"withBoolean:(jboolean)arg2",
"withBoolean:(jboolean)arg3 NS_UNAVAILABLE;");
}

public void testStaticInterfaceMethodDeclaredInCompanionClass() throws IOException {
String source = "interface Foo { static void f() {} }"
+ "class Bar implements Foo { void g() { Foo.f(); } }";
String header = translateSourceFile(source, "Test", "Test.h");
String impl = getTranslatedFile("Test.m");

assertTranslation(header, "@protocol Foo < NSObject, JavaObject >");
assertTranslatedSegments(header, "@interface Foo : NSObject", "+ (void)f;", "@end");
// Should only have one occurrence from the companion class.
assertOccurrences(header, "+ (void)f;", 1);

// The companion class of Foo still has the class method +[Foo f].
assertTranslatedLines(impl, "+ (void)f {", "Foo_f();", "}");
}
}

0 comments on commit 8997de4

Please sign in to comment.