validating android annotations

This commit is contained in:
Ilya Klyuchnikov
2014-02-28 17:03:22 +04:00
committed by Andrey Breslav
parent 1ee64f8186
commit ea40389e34
6 changed files with 274 additions and 135 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -267,6 +267,10 @@ public class JetTestUtils {
return new File(JetTestCaseBuilder.getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/rt.jar");
}
public static File findAndroidApiJar() {
return new File(JetTestCaseBuilder.getHomeDirectory(), "dependencies/android.jar");
}
public static File getAnnotationsJar() {
return new File(JetTestCaseBuilder.getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/annotations.jar");
}
@@ -280,6 +284,15 @@ public class JetTestUtils {
return jdkAnnotations;
}
@NotNull
public static File getAndroidSdkAnnotationsJar() {
File androidSdkAnnotations = new File("dependencies/annotations/kotlin-android-sdk-annotations.jar");
if (!androidSdkAnnotations.exists()) {
throw new RuntimeException("Kotlin Android SDK annotations jar not found; please run 'ant dist' to build it");
}
return androidSdkAnnotations;
}
public static void mkdirs(File file) throws IOException {
if (file.isDirectory()) {
return;
@@ -369,6 +382,9 @@ public class JetTestUtils {
if (jdkKind == TestJdkKind.MOCK_JDK) {
configuration.add(CLASSPATH_KEY, findMockJdkRtJar());
}
else if (jdkKind == TestJdkKind.ANDROID_API) {
configuration.add(CLASSPATH_KEY, findAndroidApiJar());
}
else {
configuration.addAll(CLASSPATH_KEY, PathUtil.getJdkClassesRoots());
}
@@ -378,7 +394,11 @@ public class JetTestUtils {
configuration.addAll(CLASSPATH_KEY, extraClasspath);
if (configurationKind == ALL || configurationKind == JDK_AND_ANNOTATIONS) {
configuration.add(ANNOTATIONS_PATH_KEY, getJdkAnnotationsJar());
if (jdkKind == TestJdkKind.ANDROID_API) {
configuration.add(ANNOTATIONS_PATH_KEY, getAndroidSdkAnnotationsJar());
} else {
configuration.add(ANNOTATIONS_PATH_KEY, getJdkAnnotationsJar());
}
}
return configuration;
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,4 +19,5 @@ package org.jetbrains.jet;
public enum TestJdkKind {
MOCK_JDK,
FULL_JDK,
ANDROID_API,
}
@@ -0,0 +1,164 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* 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 org.jetbrains.jet.jvm.compiler;
import com.google.common.collect.Maps;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.Disposer;
import com.intellij.testFramework.UsefulTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.jet.di.InjectorForJavaDescriptorResolver;
import org.jetbrains.jet.di.InjectorForJavaDescriptorResolverUtil;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.BindingTraceContext;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.JavaBindingContext;
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
import org.jetbrains.jet.lang.resolve.java.kotlinSignature.TypeTransformingVisitor;
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public abstract class AbstractSdkAnnotationsValidityTest extends UsefulTestCase {
private static final int CLASSES_IN_CHUNK = 100;
protected abstract JetCoreEnvironment createEnvironment(Disposable parentDisposable);
protected abstract List<FqName> getClassesToValidate() throws IOException;
@Override
protected void setUp() throws Exception {
super.setUp();
TypeTransformingVisitor.setStrictMode(true);
}
@Override
protected void tearDown() throws Exception {
TypeTransformingVisitor.setStrictMode(false);
super.tearDown();
}
public void testNoErrorsInAlternativeSignatures() throws IOException {
List<FqName> affectedClasses = getClassesToValidate();
Map<String, List<String>> errors = Maps.newLinkedHashMap();
for (int chunkIndex = 0; chunkIndex < affectedClasses.size() / CLASSES_IN_CHUNK + 1; chunkIndex++) {
Disposable parentDisposable = Disposer.newDisposable();
try {
JetCoreEnvironment commonEnvironment = createEnvironment(parentDisposable);
BindingTrace trace = new BindingTraceContext();
InjectorForJavaDescriptorResolver injector =
InjectorForJavaDescriptorResolverUtil.create(commonEnvironment.getProject(), trace);
BindingContext bindingContext = trace.getBindingContext();
JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver();
AlternativeSignatureErrorFindingVisitor visitor = new AlternativeSignatureErrorFindingVisitor(bindingContext, errors);
int chunkStart = chunkIndex * CLASSES_IN_CHUNK;
for (FqName javaClass : affectedClasses.subList(chunkStart, Math.min(chunkStart + CLASSES_IN_CHUNK, affectedClasses.size()))) {
ClassDescriptor topLevelClass = javaDescriptorResolver.resolveClass(javaClass);
PackageViewDescriptor topLevelPackage = injector.getModule().getPackage(javaClass);
if (topLevelClass == null) {
continue;
}
topLevelClass.acceptVoid(visitor);
if (topLevelPackage != null) {
topLevelPackage.acceptVoid(visitor);
}
}
}
finally {
Disposer.dispose(parentDisposable);
}
}
if (!errors.isEmpty()) {
StringBuilder sb = new StringBuilder("Error(s) in SDK alternative signatures: \n");
for (Map.Entry<String, List<String>> entry : errors.entrySet()) {
sb.append(entry.getKey()).append(" : ").append(entry.getValue()).append("\n");
}
fail(sb.toString());
}
}
private static class AlternativeSignatureErrorFindingVisitor extends DeclarationDescriptorVisitorEmptyBodies<Void, Void> {
private final BindingContext bindingContext;
private final Map<String, List<String>> errors;
public AlternativeSignatureErrorFindingVisitor(BindingContext bindingContext, Map<String, List<String>> errors) {
this.bindingContext = bindingContext;
this.errors = errors;
}
@Override
public Void visitPackageViewDescriptor(PackageViewDescriptor descriptor, Void data) {
return visitDeclarationRecursively(descriptor, descriptor.getMemberScope());
}
@Override
public Void visitClassDescriptor(ClassDescriptor descriptor, Void data) {
// skip java.util.Collection, etc.
if (!JavaToKotlinClassMap.getInstance().mapPlatformClass(DescriptorUtils.getFqNameSafe(descriptor)).isEmpty()) {
return null;
}
return visitDeclarationRecursively(descriptor, descriptor.getDefaultType().getMemberScope());
}
@Override
public Void visitFunctionDescriptor(FunctionDescriptor descriptor, Void data) {
return visitDeclaration(descriptor);
}
@Override
public Void visitPropertyDescriptor(PropertyDescriptor descriptor, Void data) {
return visitDeclaration(descriptor);
}
private Void visitDeclaration(@NotNull DeclarationDescriptor descriptor) {
List<String> errors = bindingContext.get(JavaBindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, descriptor);
if (errors != null) {
this.errors.put(DescriptorRenderer.TEXT.render(descriptor), errors);
}
return null;
}
private Void visitDeclarationRecursively(@NotNull DeclarationDescriptor descriptor, @NotNull JetScope memberScope) {
for (DeclarationDescriptor member : memberScope.getAllDescriptors()) {
member.acceptVoid(this);
}
return visitDeclaration(descriptor);
}
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* 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 org.jetbrains.jet.jvm.compiler;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.openapi.Disposable;
import org.jetbrains.jet.ConfigurationKind;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.TestJdkKind;
import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.jet.config.CompilerConfiguration;
import org.jetbrains.jet.lang.resolve.name.FqName;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class AndroidSdkAnnotationsValidityTest extends AbstractSdkAnnotationsValidityTest {
@Override
protected JetCoreEnvironment createEnvironment(Disposable parentDisposable) {
CompilerConfiguration configuration = JetTestUtils.compilerConfigurationForTests(
ConfigurationKind.JDK_AND_ANNOTATIONS, TestJdkKind.ANDROID_API, JetTestUtils.getAnnotationsJar());
return JetCoreEnvironment.createForTests(parentDisposable, configuration);
}
@Override
protected List<FqName> getClassesToValidate() throws IOException {
JarFile jar = new JarFile(JetTestUtils.findAndroidApiJar());
try {
Enumeration<JarEntry> entries = jar.entries();
Set<FqName> result = Sets.newLinkedHashSet();
while (entries.hasMoreElements()){
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
if (!entry.isDirectory() && entryName.endsWith(".class")) {
String className = entryName.substring(0, entryName.length() - ".class".length()).replace("/", ".").replace("$", ".");
result.add(new FqName(className));
}
}
return Lists.newArrayList(result);
} finally {
jar.close();
}
}
}
@@ -17,7 +17,6 @@
package org.jetbrains.jet.jvm.compiler;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.openapi.Disposable;
@@ -27,7 +26,6 @@ import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.testFramework.UsefulTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.ConfigurationKind;
import org.jetbrains.jet.JetTestUtils;
@@ -35,31 +33,18 @@ import org.jetbrains.jet.TestJdkKind;
import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys;
import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.jet.config.CompilerConfiguration;
import org.jetbrains.jet.di.InjectorForJavaDescriptorResolver;
import org.jetbrains.jet.di.InjectorForJavaDescriptorResolverUtil;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.BindingTraceContext;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.JavaBindingContext;
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
import org.jetbrains.jet.lang.resolve.java.kotlinSignature.TypeTransformingVisitor;
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JdkAnnotationsValidityTest extends UsefulTestCase {
private static final int CLASSES_IN_CHUNK = 500;
public class JdkAnnotationsValidityTest extends AbstractSdkAnnotationsValidityTest {
// KT-4359 Alternative signature checking problem: Set<?> is incompatible with Set<Object>
//
@@ -81,7 +66,7 @@ public class JdkAnnotationsValidityTest extends UsefulTestCase {
// [Incompatible types in superclasses: [Any?, Any, Any], Incompatible projection kinds in type arguments of super methods' return types: [out Any?, Any, Any]]
private static final Set<String> classesToIgnore = new HashSet<String>(Arrays.asList("javax.management.openmbean.TabularDataSupport"));
private static JetCoreEnvironment createEnvironment(Disposable parentDisposable) {
private static JetCoreEnvironment createFullJdkEnvironment(Disposable parentDisposable) {
CompilerConfiguration configuration = JetTestUtils.compilerConfigurationForTests(
ConfigurationKind.JDK_AND_ANNOTATIONS, TestJdkKind.FULL_JDK, JetTestUtils.getAnnotationsJar());
configuration.add(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY, new File("ideaSDK/lib/jdkAnnotations.jar"));
@@ -89,72 +74,20 @@ public class JdkAnnotationsValidityTest extends UsefulTestCase {
}
@Override
protected void setUp() throws Exception {
super.setUp();
TypeTransformingVisitor.setStrictMode(true);
protected JetCoreEnvironment createEnvironment(Disposable parentDisposable) {
return createFullJdkEnvironment(parentDisposable);
}
@Override
protected void tearDown() throws Exception {
TypeTransformingVisitor.setStrictMode(false);
super.tearDown();
}
public void testNoErrorsInAlternativeSignatures() {
List<FqName> affectedClasses = getAffectedClasses("jar://dependencies/annotations/kotlin-jdk-annotations.jar!/");
Map<String, List<String>> errors = Maps.newLinkedHashMap();
for (int chunkIndex = 0; chunkIndex < affectedClasses.size() / CLASSES_IN_CHUNK + 1; chunkIndex++) {
Disposable parentDisposable = Disposer.newDisposable();
try {
JetCoreEnvironment commonEnvironment = createEnvironment(parentDisposable);
BindingTrace trace = new BindingTraceContext();
InjectorForJavaDescriptorResolver injector =
InjectorForJavaDescriptorResolverUtil.create(commonEnvironment.getProject(), trace);
BindingContext bindingContext = trace.getBindingContext();
JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver();
AlternativeSignatureErrorFindingVisitor visitor = new AlternativeSignatureErrorFindingVisitor(bindingContext, errors);
int chunkStart = chunkIndex * CLASSES_IN_CHUNK;
for (FqName javaClass : affectedClasses.subList(chunkStart, Math.min(chunkStart + CLASSES_IN_CHUNK, affectedClasses.size()))) {
ClassDescriptor topLevelClass = javaDescriptorResolver.resolveClass(javaClass);
PackageViewDescriptor topLevelPackage = injector.getModule().getPackage(javaClass);
if (topLevelClass == null) {
continue;
}
topLevelClass.acceptVoid(visitor);
if (topLevelPackage != null) {
topLevelPackage.acceptVoid(visitor);
}
}
}
finally {
Disposer.dispose(parentDisposable);
}
}
if (!errors.isEmpty()) {
StringBuilder sb = new StringBuilder("Error(s) in JDK alternative signatures: \n");
for (Map.Entry<String, List<String>> entry : errors.entrySet()) {
sb.append(entry.getKey()).append(" : ").append(entry.getValue()).append("\n");
}
fail(sb.toString());
}
protected List<FqName> getClassesToValidate() throws IOException {
return getAffectedClasses("jar://dependencies/annotations/kotlin-jdk-annotations.jar!/");
}
static List<FqName> getAffectedClasses(String rootUrl) {
Disposable myDisposable = Disposer.newDisposable();
try {
createEnvironment(myDisposable);
createFullJdkEnvironment(myDisposable);
VirtualFile root = VirtualFileManager.getInstance().findFileByUrl(rootUrl);
assert root != null;
@@ -189,58 +122,4 @@ public class JdkAnnotationsValidityTest extends UsefulTestCase {
}
}
private static class AlternativeSignatureErrorFindingVisitor extends DeclarationDescriptorVisitorEmptyBodies<Void, Void> {
private final BindingContext bindingContext;
private final Map<String, List<String>> errors;
public AlternativeSignatureErrorFindingVisitor(BindingContext bindingContext, Map<String, List<String>> errors) {
this.bindingContext = bindingContext;
this.errors = errors;
}
@Override
public Void visitPackageViewDescriptor(PackageViewDescriptor descriptor, Void data) {
return visitDeclarationRecursively(descriptor, descriptor.getMemberScope());
}
@Override
public Void visitClassDescriptor(ClassDescriptor descriptor, Void data) {
// skip java.util.Collection, etc.
if (!JavaToKotlinClassMap.getInstance().mapPlatformClass(DescriptorUtils.getFqNameSafe(descriptor)).isEmpty()) {
return null;
}
return visitDeclarationRecursively(descriptor, descriptor.getDefaultType().getMemberScope());
}
@Override
public Void visitFunctionDescriptor(FunctionDescriptor descriptor, Void data) {
return visitDeclaration(descriptor);
}
@Override
public Void visitPropertyDescriptor(PropertyDescriptor descriptor, Void data) {
return visitDeclaration(descriptor);
}
private Void visitDeclaration(@NotNull DeclarationDescriptor descriptor) {
List<String> errors = bindingContext.get(JavaBindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, descriptor);
if (errors != null) {
this.errors.put(DescriptorRenderer.TEXT.render(descriptor), errors);
}
return null;
}
private Void visitDeclarationRecursively(@NotNull DeclarationDescriptor descriptor, @NotNull JetScope memberScope) {
for (DeclarationDescriptor member : memberScope.getAllDescriptors()) {
if (member instanceof DeclarationDescriptorWithVisibility
&& ((DeclarationDescriptorWithVisibility) member).getVisibility().isPublicAPI()) {
member.acceptVoid(this);
}
}
return visitDeclaration(descriptor);
}
}
}
+10
View File
@@ -187,6 +187,16 @@
<mapper type="merge" to="closure-compiler.jar"/>
</unzip>
<delete file="dependencies/android.jar" failonerror="false"/>
<get src="http://dl-ssl.google.com/android/repository/android-19_r02.zip"
dest="dependencies/download/android-sdk.zip" usetimestamp="true"/>
<unzip src="dependencies/download/android-sdk.zip" dest="dependencies">
<patternset>
<include name="**/android.jar"/>
</patternset>
<mapper type="flatten"/>
</unzip>
<!-- Bootstrap compiler -->
<get src="http://teamcity.jetbrains.com/guestAuth/repository/download/bt345/bootstrap.tcbuildtag/kotlin-plugin-{build.number}.zip"
dest="dependencies/download/bootstrap-compiler.zip" usetimestamp="true"/>