Serialize descriptors for local/anonymous classes on JVM
Reflection needs this information to work for local classes and anonymous objects
This commit is contained in:
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
@@ -72,7 +73,6 @@ import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.isLocalNamedFun;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.classDescriptorToDeclaration;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
|
||||
@@ -218,19 +218,20 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
@Override
|
||||
protected void generateKotlinAnnotation() {
|
||||
if (isAnonymousObject(descriptor)) {
|
||||
writeKotlinSyntheticClassAnnotation(v, KotlinSyntheticClass.Kind.ANONYMOUS_OBJECT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isTopLevelOrInnerClass(descriptor)) {
|
||||
// LOCAL_CLASS is also written to inner classes of local classes
|
||||
writeKotlinSyntheticClassAnnotation(v, KotlinSyntheticClass.Kind.LOCAL_CLASS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
|
||||
|
||||
KotlinClass.Kind kind;
|
||||
if (isAnonymousObject(descriptor)) {
|
||||
kind = KotlinClass.Kind.ANONYMOUS_OBJECT;
|
||||
}
|
||||
else if (isTopLevelOrInnerClass(descriptor)) {
|
||||
kind = KotlinClass.Kind.CLASS;
|
||||
}
|
||||
else {
|
||||
// LOCAL_CLASS is also written to inner classes of local classes
|
||||
kind = KotlinClass.Kind.LOCAL_CLASS;
|
||||
}
|
||||
|
||||
DescriptorSerializer serializer =
|
||||
DescriptorSerializer.create(descriptor, new JvmSerializerExtension(v.getSerializationBindings(), typeMapper));
|
||||
|
||||
@@ -239,8 +240,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
ClassData data = new ClassData(createNameResolver(serializer.getStringTable()), classProto);
|
||||
|
||||
AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CLASS), true);
|
||||
//noinspection ConstantConditions
|
||||
av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
|
||||
av.visitEnum(
|
||||
JvmAnnotationNames.KIND_FIELD_NAME,
|
||||
Type.getObjectType(KotlinClass.KIND_INTERNAL_NAME).getDescriptor(),
|
||||
kind.toString()
|
||||
);
|
||||
AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
|
||||
for (String string : BitEncoding.encodeBytes(data.toBytes())) {
|
||||
array.visit(null, string);
|
||||
|
||||
+4
-1
@@ -260,10 +260,13 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
return ClassId.topLevel(new FqName(name.replace('/', '.')));
|
||||
}
|
||||
|
||||
// TODO: this is a hack which can be dropped once JVM back-end begins to write InnerClasses attribute for all referenced classes
|
||||
if (name.equals(JvmAnnotationNames.KotlinSyntheticClass.KIND_INTERNAL_NAME)) {
|
||||
// TODO: this is a hack which can be dropped once JVM back-end begins to write InnerClasses attribute for all referenced classes
|
||||
return JvmAnnotationNames.KotlinSyntheticClass.KIND_CLASS_ID;
|
||||
}
|
||||
else if (name.equals(JvmAnnotationNames.KotlinClass.KIND_INTERNAL_NAME)) {
|
||||
return JvmAnnotationNames.KotlinClass.KIND_CLASS_ID;
|
||||
}
|
||||
|
||||
List<String> classes = new ArrayList<String>(1);
|
||||
boolean local = false;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// CLASS_NAME_SUFFIX: A$foo$Local
|
||||
|
||||
class A {
|
||||
annotation class Ann(val info: String)
|
||||
|
||||
fun foo() {
|
||||
[Ann("class")] class Local {
|
||||
Ann("fun") fun foo(): Local = this
|
||||
Ann("val") val x = foo()
|
||||
|
||||
Ann("inner") inner class Inner
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
A.Ann(info = "class": kotlin.String) local final class `A$foo$Local`
|
||||
|
||||
public constructor `A$foo$Local`()
|
||||
A.Ann(info = "val": kotlin.String) internal final val x: `A$foo$Local`
|
||||
A.Ann(info = "fun": kotlin.String) internal final fun foo(): `A$foo$Local`
|
||||
|
||||
A.Ann(info = "inner": kotlin.String) local final inner class Inner {
|
||||
public constructor Inner()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// CLASS_NAME_SUFFIX: $main$1
|
||||
|
||||
fun main() = object : Runnable {
|
||||
override fun run() { }
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
local final class `_DefaultPackage$anonymousObject$*$main$1` : java.lang.Runnable
|
||||
|
||||
public constructor `_DefaultPackage$anonymousObject$*$main$1`()
|
||||
public open override /*1*/ fun run(): kotlin.Unit
|
||||
@@ -0,0 +1,22 @@
|
||||
// CLASS_NAME_SUFFIX: Deepest
|
||||
|
||||
fun main() {
|
||||
class Local {
|
||||
inner class Inner {
|
||||
val prop = object {
|
||||
fun foo() {
|
||||
fun bar() {
|
||||
class DeepLocal {
|
||||
inner class Deepest {
|
||||
fun local(): Local = Local()
|
||||
fun inner(): Inner = Inner()
|
||||
fun deep(): DeepLocal = DeepLocal()
|
||||
fun deepest(): Deepest? = Deepest()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
local final inner class Deepest
|
||||
|
||||
public constructor Deepest()
|
||||
internal final fun deep(): `_DefaultPackage$deepInnerChain$*$main$Local$Inner$prop$1$foo$1$DeepLocal`
|
||||
internal final fun deepest(): `_DefaultPackage$deepInnerChain$*$main$Local$Inner$prop$1$foo$1$DeepLocal`.Deepest?
|
||||
internal final fun inner(): `_DefaultPackage$deepInnerChain$*$main$Local`.Inner
|
||||
internal final fun local(): `_DefaultPackage$deepInnerChain$*$main$Local`
|
||||
@@ -0,0 +1,10 @@
|
||||
// CLASS_NAME_SUFFIX: main$Local$Inner
|
||||
|
||||
fun main() {
|
||||
class Local {
|
||||
inner class Inner {
|
||||
fun local(l: Local) {}
|
||||
fun inner(i: Inner) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
local final inner class Inner
|
||||
|
||||
public constructor Inner()
|
||||
internal final fun inner(/*0*/ i: `_DefaultPackage$innerOfLocal$*$main$Local`.Inner): kotlin.Unit
|
||||
internal final fun local(/*0*/ l: `_DefaultPackage$innerOfLocal$*$main$Local`): kotlin.Unit
|
||||
@@ -0,0 +1,13 @@
|
||||
// CLASS_NAME_SUFFIX: $main$Local
|
||||
|
||||
fun main() {
|
||||
open class Local {
|
||||
fun param(l: Local) {}
|
||||
|
||||
val returnType: Local = this
|
||||
|
||||
fun Local.receiver() = this
|
||||
|
||||
fun <T : Local, U : T> generic(t: T): U = null!!
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
local open class `_DefaultPackage$localClassInSignature$*$main$Local`
|
||||
|
||||
public constructor `_DefaultPackage$localClassInSignature$*$main$Local`()
|
||||
internal final val returnType: `_DefaultPackage$localClassInSignature$*$main$Local`
|
||||
internal final fun </*0*/ T : `_DefaultPackage$localClassInSignature$*$main$Local`, /*1*/ U : T> generic(/*0*/ t: T): U
|
||||
internal final fun param(/*0*/ l: `_DefaultPackage$localClassInSignature$*$main$Local`): kotlin.Unit
|
||||
internal final fun `_DefaultPackage$localClassInSignature$*$main$Local`.receiver(): `_DefaultPackage$localClassInSignature$*$main$Local`
|
||||
@@ -0,0 +1,7 @@
|
||||
// CLASS_NAME_SUFFIX: A$foo$Local
|
||||
|
||||
class A {
|
||||
fun foo() {
|
||||
class Local
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
local final class `A$foo$Local`
|
||||
|
||||
public constructor `A$foo$Local`()
|
||||
@@ -0,0 +1,5 @@
|
||||
// CLASS_NAME_SUFFIX: $main$Local
|
||||
|
||||
fun main() {
|
||||
class Local
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
local final class `_DefaultPackage$simpleInTopLevelFunction$*$main$Local`
|
||||
|
||||
public constructor `_DefaultPackage$simpleInTopLevelFunction$*$main$Local`()
|
||||
+96
-45
@@ -22,14 +22,20 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.load.java.AbiVersionUtil;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass;
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.*;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KIND_FIELD_NAME;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass.Kind.ANONYMOUS_OBJECT;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass.Kind.LOCAL_CLASS;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass.Kind.*;
|
||||
|
||||
public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
|
||||
@@ -42,81 +48,122 @@ public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
|
||||
}
|
||||
|
||||
public void testPackagePart() {
|
||||
doTest("fun foo() = 42",
|
||||
"$",
|
||||
PACKAGE_PART);
|
||||
doTestKotlinSyntheticClass(
|
||||
"fun foo() = 42",
|
||||
"$",
|
||||
PACKAGE_PART
|
||||
);
|
||||
}
|
||||
|
||||
public void testTraitImpl() {
|
||||
doTest("trait A { fun foo() = 42 }",
|
||||
JvmAbi.TRAIT_IMPL_SUFFIX,
|
||||
TRAIT_IMPL);
|
||||
doTestKotlinSyntheticClass(
|
||||
"trait A { fun foo() = 42 }",
|
||||
JvmAbi.TRAIT_IMPL_SUFFIX,
|
||||
TRAIT_IMPL
|
||||
);
|
||||
}
|
||||
|
||||
public void testSamWrapper() {
|
||||
doTest("val f = {}\nval foo = Thread(f)",
|
||||
"$sam",
|
||||
SAM_WRAPPER);
|
||||
doTestKotlinSyntheticClass(
|
||||
"val f = {}\nval foo = Thread(f)",
|
||||
"$sam",
|
||||
SAM_WRAPPER
|
||||
);
|
||||
}
|
||||
|
||||
public void testSamLambda() {
|
||||
doTest("val foo = Thread { }",
|
||||
"$1",
|
||||
SAM_LAMBDA);
|
||||
doTestKotlinSyntheticClass(
|
||||
"val foo = Thread { }",
|
||||
"$1",
|
||||
SAM_LAMBDA
|
||||
);
|
||||
}
|
||||
|
||||
public void testCallableReferenceWrapper() {
|
||||
doTest("val f = String::get",
|
||||
"$1",
|
||||
CALLABLE_REFERENCE_WRAPPER);
|
||||
doTestKotlinSyntheticClass(
|
||||
"val f = String::get",
|
||||
"$1",
|
||||
CALLABLE_REFERENCE_WRAPPER
|
||||
);
|
||||
}
|
||||
|
||||
public void testLocalFunction() {
|
||||
doTest("fun foo() { fun bar() {} }",
|
||||
"$1",
|
||||
LOCAL_FUNCTION);
|
||||
doTestKotlinSyntheticClass(
|
||||
"fun foo() { fun bar() {} }",
|
||||
"$1",
|
||||
LOCAL_FUNCTION
|
||||
);
|
||||
}
|
||||
|
||||
public void testAnonymousFunction() {
|
||||
doTest("val f = {}",
|
||||
"$1",
|
||||
ANONYMOUS_FUNCTION);
|
||||
doTestKotlinSyntheticClass(
|
||||
"val f = {}",
|
||||
"$1",
|
||||
ANONYMOUS_FUNCTION
|
||||
);
|
||||
}
|
||||
|
||||
public void testLocalClass() {
|
||||
doTest("fun foo() { class Local }",
|
||||
"Local",
|
||||
LOCAL_CLASS);
|
||||
doTestKotlinClass(
|
||||
"fun foo() { class Local }",
|
||||
"Local",
|
||||
LOCAL_CLASS
|
||||
);
|
||||
}
|
||||
|
||||
public void testLocalTraitImpl() {
|
||||
doTest("fun foo() { trait Local { fun bar() = 42 } }",
|
||||
"Local$$TImpl.class",
|
||||
LOCAL_TRAIT_IMPL);
|
||||
doTestKotlinSyntheticClass(
|
||||
"fun foo() { trait Local { fun bar() = 42 } }",
|
||||
"Local$$TImpl.class",
|
||||
LOCAL_TRAIT_IMPL
|
||||
);
|
||||
}
|
||||
|
||||
public void testLocalTraitInterface() {
|
||||
doTest("fun foo() { trait Local { fun bar() = 42 } }",
|
||||
"Local.class",
|
||||
LOCAL_CLASS);
|
||||
doTestKotlinClass(
|
||||
"fun foo() { trait Local { fun bar() = 42 } }",
|
||||
"Local.class",
|
||||
LOCAL_CLASS
|
||||
);
|
||||
}
|
||||
|
||||
public void testInnerClassOfLocalClass() {
|
||||
doTest("fun foo() { class Local { inner class Inner } }",
|
||||
"Inner",
|
||||
LOCAL_CLASS);
|
||||
doTestKotlinClass(
|
||||
"fun foo() { class Local { inner class Inner } }",
|
||||
"Inner",
|
||||
LOCAL_CLASS
|
||||
);
|
||||
}
|
||||
|
||||
public void testAnonymousObject() {
|
||||
doTest("val o = object {}",
|
||||
"$1",
|
||||
ANONYMOUS_OBJECT);
|
||||
doTestKotlinClass(
|
||||
"val o = object {}",
|
||||
"$1",
|
||||
ANONYMOUS_OBJECT
|
||||
);
|
||||
}
|
||||
|
||||
private void doTestKotlinSyntheticClass(
|
||||
@NotNull String code,
|
||||
@NotNull String classFilePart,
|
||||
@NotNull KotlinSyntheticClass.Kind expectedKind
|
||||
) {
|
||||
doTest(code, classFilePart, KotlinSyntheticClass.CLASS_NAME, expectedKind.toString());
|
||||
}
|
||||
|
||||
private void doTestKotlinClass(
|
||||
@NotNull String code,
|
||||
@NotNull String classFilePart,
|
||||
@NotNull KotlinClass.Kind expectedKind
|
||||
) {
|
||||
doTest(code, classFilePart, KotlinClass.CLASS_NAME, expectedKind.toString());
|
||||
}
|
||||
|
||||
private void doTest(
|
||||
@NotNull String code,
|
||||
@NotNull final String classFilePart,
|
||||
@NotNull KotlinSyntheticClass.Kind expectedKind
|
||||
@NotNull JvmClassName annotationName,
|
||||
@NotNull String expectedKind
|
||||
) {
|
||||
loadText("package " + PACKAGE_NAME + "\n\n" + code);
|
||||
List<OutputFile> output = generateClassesInFile().asList();
|
||||
@@ -132,22 +179,26 @@ public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
|
||||
String path = files.iterator().next().getRelativePath();
|
||||
String fqName = path.substring(0, path.length() - ".class".length()).replace('/', '.');
|
||||
Class<?> aClass = generateClass(fqName);
|
||||
assertAnnotatedWithKind(aClass, expectedKind);
|
||||
assertAnnotatedWithKind(aClass, annotationName.getFqNameForClassNameWithoutDollars().asString(), expectedKind);
|
||||
}
|
||||
|
||||
private void assertAnnotatedWithKind(@NotNull Class<?> aClass, @NotNull KotlinSyntheticClass.Kind expectedKind) {
|
||||
Class<? extends Annotation> annotationClass = loadAnnotationClassQuietly(
|
||||
KotlinSyntheticClass.CLASS_NAME.getFqNameForClassNameWithoutDollars().asString());
|
||||
assertTrue("No KotlinSyntheticClass annotation found", aClass.isAnnotationPresent(annotationClass));
|
||||
private void assertAnnotatedWithKind(
|
||||
@NotNull Class<?> aClass,
|
||||
@NotNull String annotationFqName,
|
||||
@NotNull String expectedKind
|
||||
) {
|
||||
Class<? extends Annotation> annotationClass = loadAnnotationClassQuietly(annotationFqName);
|
||||
assertTrue("No annotation " + annotationFqName + " found in " + aClass, aClass.isAnnotationPresent(annotationClass));
|
||||
|
||||
Annotation annotation = aClass.getAnnotation(annotationClass);
|
||||
|
||||
Integer version = (Integer) CodegenTestUtil.getAnnotationAttribute(annotation, ABI_VERSION_FIELD_NAME);
|
||||
assertNotNull(version);
|
||||
assertTrue("KotlinSyntheticClass annotation is written with an unsupported format", AbiVersionUtil.isAbiVersionCompatible(version));
|
||||
assertTrue("Annotation " + annotationFqName + " is written with an unsupported format",
|
||||
AbiVersionUtil.isAbiVersionCompatible(version));
|
||||
|
||||
Object actualKind = CodegenTestUtil.getAnnotationAttribute(annotation, KIND_FIELD_NAME);
|
||||
assertNotNull(actualKind);
|
||||
assertEquals("KotlinSyntheticClass annotation has the wrong kind", expectedKind.toString(), actualKind.toString());
|
||||
assertEquals("Annotation " + annotationFqName + " has the wrong kind", expectedKind, actualKind.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.kotlin.serialization
|
||||
|
||||
import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil
|
||||
import org.jetbrains.kotlin.test.TestCaseWithTmpdir
|
||||
import java.io.File
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import java.net.URLClassLoader
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.test.JetTestUtils
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JetCoreEnvironment
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.di.InjectorForTopDownAnalyzerForJvm
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.context.GlobalContext
|
||||
import org.jetbrains.kotlin.resolve.TopDownAnalysisParameters
|
||||
import com.google.common.base.Predicates
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
|
||||
public abstract class AbstractLocalClassProtoTest : TestCaseWithTmpdir() {
|
||||
protected fun doTest(filename: String) {
|
||||
val source = File(filename)
|
||||
LoadDescriptorUtil.compileKotlinToDirAndGetAnalysisResult(listOf(source), tmpdir, getTestRootDisposable(), ConfigurationKind.ALL)
|
||||
|
||||
val classNameSuffix = InTextDirectivesUtils.findStringWithPrefixes(source.readText(), "// CLASS_NAME_SUFFIX: ")
|
||||
?: error("CLASS_NAME_SUFFIX directive not found in test data")
|
||||
|
||||
val classLoader = URLClassLoader(array(tmpdir.toURI().toURL(), ForTestCompileRuntime.runtimeJarForTests().toURI().toURL()), null)
|
||||
|
||||
val classFile = tmpdir.listFiles().singleOrNull { it.getPath().endsWith("$classNameSuffix.class") }
|
||||
?: error("Local class with suffix `$classNameSuffix` is not found in: ${tmpdir.listFiles().toList()}")
|
||||
val clazz = classLoader.loadClass(classFile.name.substringBeforeLast(".class"))
|
||||
assertHasAnnotationData(clazz)
|
||||
|
||||
val environment = JetCoreEnvironment.createForTests(
|
||||
getTestRootDisposable(),
|
||||
JetTestUtils.compilerConfigurationForTests(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, tmpdir),
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
)
|
||||
val module = TopDownAnalyzerFacadeForJVM.createSealedJavaModule()
|
||||
val globalContext = GlobalContext()
|
||||
val params = TopDownAnalysisParameters.create(
|
||||
globalContext.storageManager, globalContext.exceptionTracker, Predicates.alwaysTrue(), false, false
|
||||
)
|
||||
val providerFactory = FileBasedDeclarationProviderFactory(globalContext.storageManager, emptyList())
|
||||
|
||||
val injector = InjectorForTopDownAnalyzerForJvm(
|
||||
environment.getProject(), params, CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(), module,
|
||||
providerFactory, GlobalSearchScope.allScope(environment.getProject())
|
||||
)
|
||||
module.initialize(injector.getJavaDescriptorResolver().packageFragmentProvider)
|
||||
|
||||
val components = injector.getDeserializationComponentsForJava().components
|
||||
|
||||
val classDescriptor = components.classDeserializer.deserializeClass(clazz.classId)
|
||||
?: error("Class is not resolved: $clazz (classId = ${clazz.classId})")
|
||||
|
||||
RecursiveDescriptorComparator.validateAndCompareDescriptorWithFile(
|
||||
classDescriptor,
|
||||
RecursiveDescriptorComparator.DONT_INCLUDE_METHODS_OF_OBJECT,
|
||||
JetTestUtils.replaceExtension(source, "txt")
|
||||
)
|
||||
}
|
||||
|
||||
private val Class<*>.classId: ClassId
|
||||
get() {
|
||||
if (getEnclosingMethod() != null || getEnclosingConstructor() != null) {
|
||||
return ClassId(FqName.ROOT, FqNameUnsafe(getName()), /* local = */ true)
|
||||
}
|
||||
return getDeclaringClass()?.classId?.createNestedClassId(Name.identifier(getSimpleName()))
|
||||
?: ClassId.topLevel(FqName(getName()))
|
||||
}
|
||||
|
||||
private fun assertHasAnnotationData(clazz: Class<*>) {
|
||||
[suppress("UNCHECKED_CAST")]
|
||||
val annotation = clazz.getAnnotation(
|
||||
clazz.getClassLoader().loadClass(JvmAnnotationNames.KOTLIN_CLASS.asString()) as Class<Annotation>
|
||||
)
|
||||
assert(annotation != null) { "KotlinClass annotation is not found for class $clazz" }
|
||||
|
||||
val kindMethod = annotation.annotationType().getDeclaredMethod("kind")
|
||||
val kind = kindMethod(annotation)
|
||||
assert(kind.toString() != JvmAnnotationNames.KotlinClass.Kind.CLASS.toString()) {
|
||||
"'kind' should not be CLASS: $clazz (was $kind)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.kotlin.serialization;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.InnerTestClasses;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.JetTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("compiler/testData/serialization/local")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class LocalClassProtoTestGenerated extends AbstractLocalClassProtoTest {
|
||||
public void testAllFilesPresentInLocal() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/serialization/local"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("annotationsInLocalClass.kt")
|
||||
public void testAnnotationsInLocalClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/serialization/local/annotationsInLocalClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousObject.kt")
|
||||
public void testAnonymousObject() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/serialization/local/anonymousObject.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("deepInnerChain.kt")
|
||||
public void testDeepInnerChain() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/serialization/local/deepInnerChain.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("innerOfLocal.kt")
|
||||
public void testInnerOfLocal() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/serialization/local/innerOfLocal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localClassInSignature.kt")
|
||||
public void testLocalClassInSignature() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/serialization/local/localClassInSignature.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleInMemberFunction.kt")
|
||||
public void testSimpleInMemberFunction() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/serialization/local/simpleInMemberFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleInTopLevelFunction.kt")
|
||||
public void testSimpleInTopLevelFunction() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/serialization/local/simpleInTopLevelFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
@@ -85,8 +85,9 @@ public class RecursiveDescriptorComparator {
|
||||
|
||||
public String serializeRecursively(@NotNull DeclarationDescriptor declarationDescriptor) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
appendDeclarationRecursively(declarationDescriptor, DescriptorUtils.getContainingModule(declarationDescriptor), new Printer(result, 1), true);
|
||||
return result.toString();
|
||||
appendDeclarationRecursively(declarationDescriptor, DescriptorUtils.getContainingModule(declarationDescriptor),
|
||||
new Printer(result, 1), true);
|
||||
return JetTestUtils.replaceHashWithStar(result.toString());
|
||||
}
|
||||
|
||||
private void appendDeclarationRecursively(
|
||||
|
||||
Reference in New Issue
Block a user