/*
* 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.gen;
import com.google.common.collect.Lists;
import com.google.devtools.j2objc.GenerationTest;
import com.google.devtools.j2objc.ast.AbstractTypeDeclaration;
import com.google.devtools.j2objc.ast.CompilationUnit;
import com.google.devtools.j2objc.util.ElementUtil;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
/**
* Verifies generic signature generation. Signature strings used here were created by
* compiling each source using javac, then running "javap -h" to see what the generic
* signature attribute strings were created.
*
* @author Tom Ball
*/
public class SignatureGeneratorTest extends GenerationTest {
public void testClassSignatures() throws IOException {
CompilationUnit unit = translateType("A",
"import java.lang.ref.*; import java.util.concurrent.*; "
+ "class A {} "
+ "class B<X> {} "
+ "class C<X,Y,Z> {} "
+ "abstract class D extends java.util.AbstractList<String> {} "
+ "class E<C extends Comparable<? super C>> {}"
+ "class F extends WeakReference<ForkJoinTask<?>> { "
+ " F(ForkJoinTask<?> task, Throwable ex, F next) { super(task, null); }}");
SignatureGenerator signatureGenerator = unit.getEnv().signatureGenerator();
List<AbstractTypeDeclaration> decls = unit.getTypes();
assertEquals(6, decls.size());
// Verify A doesn't return a signature, since it isn't a generic type.
assertNull(signatureGenerator.createClassSignature(decls.get(0).getTypeElement()));
assertEquals("<X:Ljava/lang/Object;>Ljava/lang/Object;",
signatureGenerator.createClassSignature(decls.get(1).getTypeElement()));
assertEquals("<X:Ljava/lang/Object;Y:Ljava/lang/Object;Z:Ljava/lang/Object;>Ljava/lang/Object;",
signatureGenerator.createClassSignature(decls.get(2).getTypeElement()));
assertEquals("Ljava/util/AbstractList<Ljava/lang/String;>;",
signatureGenerator.createClassSignature(decls.get(3).getTypeElement()));
assertEquals("<C::Ljava/lang/Comparable<-TC;>;>Ljava/lang/Object;",
signatureGenerator.createClassSignature(decls.get(4).getTypeElement()));
assertEquals("Ljava/lang/ref/WeakReference<Ljava/util/concurrent/ForkJoinTask<*>;>;",
signatureGenerator.createClassSignature(decls.get(5).getTypeElement()));
}
public void testFieldSignatures() throws IOException {
CompilationUnit unit = translateType("A", "class A<X,Y,Z> { int a; double[] b; X c; Y[] d; "
+ "Class<?> e; java.util.List<X> f; Comparable<? super X> g; "
+ "A<? extends Number, ?, String> h; }");
SignatureGenerator signatureGenerator = unit.getEnv().signatureGenerator();
List<VariableElement> vars =
ElementUtil.getDeclaredFields(unit.getTypes().get(0).getTypeElement());
assertEquals(8, vars.size());
// Verify a and b don't return a signature, since they aren't generic types.
assertNull(signatureGenerator.createFieldTypeSignature(vars.get(0)));
assertNull(signatureGenerator.createFieldTypeSignature(vars.get(1)));
assertEquals("TX;", signatureGenerator.createFieldTypeSignature(vars.get(2)));
assertEquals("[TY;", signatureGenerator.createFieldTypeSignature(vars.get(3)));
assertEquals("Ljava/lang/Class<*>;", signatureGenerator.createFieldTypeSignature(vars.get(4)));
assertEquals("Ljava/util/List<TX;>;", signatureGenerator.createFieldTypeSignature(vars.get(5)));
assertEquals("Ljava/lang/Comparable<-TX;>;",
signatureGenerator.createFieldTypeSignature(vars.get(6)));
assertEquals("LA<+Ljava/lang/Number;*Ljava/lang/String;>;",
signatureGenerator.createFieldTypeSignature(vars.get(7)));
}
public void testMethodSignatures() throws IOException {
CompilationUnit unit = translateType("A", "class A<X, Y, Z extends Throwable> { "
+ "void a() {} "
+ "int b(boolean z) { return 0; } "
+ "void c(int i) throws IndexOutOfBoundsException {} "
+ "X d() { return null; } "
+ "void e(X x, Y y) {} "
+ "void f() throws Z {} "
+ "<T extends Throwable> void rethrow(Throwable t) {}"
+ "}");
SignatureGenerator signatureGenerator = unit.getEnv().signatureGenerator();
List<ExecutableElement> methods =
ElementUtil.getExecutables(unit.getTypes().get(0).getTypeElement());
assertEquals(8, methods.size()); // methods[0] is the default constructor.
// Verify a, b and c don't return a signature, since they aren't generic types.
assertNull(signatureGenerator.createMethodTypeSignature(methods.get(1)));
assertNull(signatureGenerator.createMethodTypeSignature(methods.get(2)));
assertNull(signatureGenerator.createMethodTypeSignature(methods.get(3)));
assertEquals("()TX;", signatureGenerator.createMethodTypeSignature(methods.get(4)));
assertEquals("(TX;TY;)V", signatureGenerator.createMethodTypeSignature(methods.get(5)));
assertEquals("()V^TZ;", signatureGenerator.createMethodTypeSignature(methods.get(6)));
assertEquals("<T:Ljava/lang/Throwable;>(Ljava/lang/Throwable;)V",
signatureGenerator.createMethodTypeSignature(methods.get(7)));
}
public void testMultipleBounds() throws IOException {
CompilationUnit unit = translateType("A",
"class A <T extends Number & java.io.Serializable >{}");
SignatureGenerator signatureGenerator = unit.getEnv().signatureGenerator();
List<AbstractTypeDeclaration> decls = unit.getTypes();
assertEquals(1, decls.size());
assertEquals("<T:Ljava/lang/Number;:Ljava/io/Serializable;>Ljava/lang/Object;",
signatureGenerator.createClassSignature(decls.get(0).getTypeElement()));
}
public void testGenericInterface() throws IOException {
CompilationUnit unit = translateType("A",
"interface A<E> extends java.util.Collection<E> {}");
SignatureGenerator signatureGenerator = unit.getEnv().signatureGenerator();
List<AbstractTypeDeclaration> decls = unit.getTypes();
assertEquals(1, decls.size());
assertEquals("<E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;",
signatureGenerator.createClassSignature(decls.get(0).getTypeElement()));
}
public void testJniSignatures() throws IOException {
CompilationUnit unit = translateType("D", "package foo.bar; class D {"
+ "native void foo(int i, float f, String s);"
+ "native void a_b$c();"
+ "native void 你好世界();"
+ "native void bar();"
+ "native void bar(String s);"
+ "native void bar(boolean b, String s);"
+ "native void bar(String[] s); "
+ "static class 测试 { native void mumble(); }}");
SignatureGenerator signatureGenerator = unit.getEnv().signatureGenerator();
List<AbstractTypeDeclaration> decls = unit.getTypes();
assertEquals(2, decls.size());
List<ExecutableElement> methods = Lists.newArrayList(
ElementUtil.getMethods(decls.get(0).getTypeElement()));
assertEquals(7, methods.size());
Set<String> signatures = new HashSet<>(methods.size());
for (ExecutableElement method : methods) {
signatures.add(signatureGenerator.createJniFunctionSignature(method));
}
// Expected JNI signatures were copied from javah output.
// Verify no parameters, since foo isn't overloaded.
assertTrue(signatures.contains("Java_foo_bar_D_foo"));
// Verify underscores and dollar signs in names are mangled.
assertTrue(signatures.contains("Java_foo_bar_D_a_1b_00024c"));
// Verify Unicode characters are mangled.
assertTrue(signatures.contains("Java_foo_bar_D__04f60_0597d_04e16_0754c"));
// Verify overloaded methods have parameter suffixes.
assertTrue(signatures.contains("Java_foo_bar_D_bar__"));
assertTrue(signatures.contains("Java_foo_bar_D_bar__Ljava_lang_String_2"));
assertTrue(signatures.contains("Java_foo_bar_D_bar__ZLjava_lang_String_2"));
assertTrue(signatures.contains("Java_foo_bar_D_bar___3Ljava_lang_String_2"));
// Check Unicode class name mangling.
methods = Lists.newArrayList(ElementUtil.getMethods(decls.get(1).getTypeElement()));
assertEquals(1, methods.size());
assertEquals("Java_foo_bar_D_00024_06d4b_08bd5_mumble",
signatureGenerator.createJniFunctionSignature(methods.get(0)));
}
public void testGenericTypeMetadata() throws IOException {
String translation = translateSourceFile(
"import java.util.*; class Test <T> { Set<T> set; "
+ " void test(Map<Long, List<T>> map) {} }",
"Test", "Test.m");
// Assert class metadata has generic signature.
assertTranslation(translation, "\"<T:Ljava/lang/Object;>Ljava/lang/Object;\"");
// Assert method metadata has generic signature. (in pointer table)
assertTranslation(translation, "\"(Ljava/util/Map<Ljava/lang/Long;Ljava/util/List<TT;>;>;)V\"");
// Assert field metadata has generic signature. (in pointer table)
assertTranslation(translation, "\"Ljava/util/Set<TT;>;\"");
}
public void testMethodParameterizedReturnTypeMetadata() throws IOException {
String translation = translateSourceFile(
"import java.util.*; class Test { List<String> getStringList() { return null; }}",
"Test", "Test.m");
// Assert method metadata has generic return signature.
assertTranslation(translation, "\"()Ljava/util/List<Ljava/lang/String;>;\"");
}
public void testGenericMethodWithConcreteTypeArgument() throws IOException {
CompilationUnit unit = translateType("MyList",
"abstract class MyList extends java.util.AbstractList<String> { "
+ "public boolean add(String s) { return true; }}");
SignatureGenerator signatureGenerator = unit.getEnv().signatureGenerator();
List<ExecutableElement> methods =
ElementUtil.getExecutables(unit.getTypes().get(0).getTypeElement());
assertEquals(2, methods.size()); // methods[0] is the default constructor.
// add(String) does not need a generic signature, even though it overrides a generic method.
assertNull(signatureGenerator.createMethodTypeSignature(methods.get(1)));
}
public void testGenericClassWithArrayTypeVariable() throws IOException {
String translation = translateSourceFile(
"abstract class Test extends java.util.AbstractList<String[]> {}", "Test", "Test.m");
// Assert array signature is valid in class signature.
assertTranslation(translation, "Ljava/util/AbstractList<[Ljava/lang/String;>;");
}
}