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(
|
||||
|
||||
+14
-4
@@ -45,6 +45,19 @@ public final class JvmAnnotationNames {
|
||||
|
||||
public static class KotlinClass {
|
||||
public static final JvmClassName CLASS_NAME = JvmClassName.byInternalName("kotlin/jvm/internal/KotlinClass");
|
||||
public static final ClassId KIND_CLASS_ID =
|
||||
ClassId.topLevel(CLASS_NAME.getFqNameForClassNameWithoutDollars()).createNestedClassId(Name.identifier("Kind"));
|
||||
public static final String KIND_INTERNAL_NAME = JvmClassName.byClassId(KIND_CLASS_ID).getInternalName();
|
||||
|
||||
/**
|
||||
* This enum duplicates {@link kotlin.jvm.internal.KotlinClass.Kind}. Both places should be updated simultaneously.
|
||||
*/
|
||||
public enum Kind {
|
||||
CLASS,
|
||||
LOCAL_CLASS,
|
||||
ANONYMOUS_OBJECT,
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public static class KotlinSyntheticClass {
|
||||
@@ -54,8 +67,7 @@ public final class JvmAnnotationNames {
|
||||
public static final String KIND_INTERNAL_NAME = JvmClassName.byClassId(KIND_CLASS_ID).getInternalName();
|
||||
|
||||
/**
|
||||
* This enum duplicates {@link kotlin.jvm.internal.KotlinSyntheticClass.Kind}, because this code can't depend on "runtime.jvm".
|
||||
* Both places should be updated simultaneously
|
||||
* This enum duplicates {@link kotlin.jvm.internal.KotlinSyntheticClass.Kind}. Both places should be updated simultaneously.
|
||||
*/
|
||||
public enum Kind {
|
||||
PACKAGE_PART,
|
||||
@@ -66,8 +78,6 @@ public final class JvmAnnotationNames {
|
||||
CALLABLE_REFERENCE_WRAPPER,
|
||||
LOCAL_FUNCTION,
|
||||
ANONYMOUS_FUNCTION,
|
||||
LOCAL_CLASS,
|
||||
ANONYMOUS_OBJECT,
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
+18
-5
@@ -17,22 +17,29 @@
|
||||
package org.jetbrains.kotlin.load.kotlin.header
|
||||
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass
|
||||
import org.jetbrains.kotlin.load.java.AbiVersionUtil
|
||||
|
||||
public class KotlinClassHeader(
|
||||
public val kind: KotlinClassHeader.Kind,
|
||||
public val version: Int,
|
||||
public val annotationData: Array<String>?,
|
||||
public val classKind: KotlinClass.Kind?,
|
||||
public val syntheticClassKind: KotlinSyntheticClass.Kind?
|
||||
) {
|
||||
public val isCompatibleAbiVersion: Boolean get() = AbiVersionUtil.isAbiVersionCompatible(version);
|
||||
|
||||
{
|
||||
assert(!isCompatibleAbiVersion || (annotationData == null) == (kind != Kind.CLASS && kind != Kind.PACKAGE_FACADE)) {
|
||||
"Annotation data should be not null only for CLASS and PACKAGE_FACADE (kind=" + kind + ")"
|
||||
}
|
||||
assert(!isCompatibleAbiVersion || (syntheticClassKind == null) == (kind != Kind.SYNTHETIC_CLASS)) {
|
||||
"Synthetic class kind should be present for SYNTHETIC_CLASS (kind=" + kind + ")"
|
||||
if (isCompatibleAbiVersion) {
|
||||
assert((annotationData == null) == (kind != Kind.CLASS && kind != Kind.PACKAGE_FACADE)) {
|
||||
"Annotation data should be not null only for CLASS and PACKAGE_FACADE (kind=$kind)"
|
||||
}
|
||||
assert((syntheticClassKind == null) == (kind != Kind.SYNTHETIC_CLASS)) {
|
||||
"Synthetic class kind should be present for SYNTHETIC_CLASS (kind=$kind)"
|
||||
}
|
||||
assert((classKind == null) == (kind != Kind.CLASS)) {
|
||||
"Class kind should be present for CLASS (kind=$kind)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +48,12 @@ public class KotlinClassHeader(
|
||||
PACKAGE_FACADE
|
||||
SYNTHETIC_CLASS
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
"$kind " +
|
||||
(if (classKind != null) "$classKind " else "") +
|
||||
(if (syntheticClassKind != null) "$syntheticClassKind " else "") +
|
||||
"version=$version"
|
||||
}
|
||||
|
||||
public fun KotlinClassHeader.isCompatibleClassKind(): Boolean = isCompatibleAbiVersion && kind == KotlinClassHeader.Kind.CLASS
|
||||
|
||||
+18
-8
@@ -59,6 +59,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
||||
|
||||
private String[] annotationData = null;
|
||||
private KotlinClassHeader.Kind headerKind = null;
|
||||
private KotlinClass.Kind classKind = null;
|
||||
private KotlinSyntheticClass.Kind syntheticClassKind = null;
|
||||
|
||||
@Nullable
|
||||
@@ -68,7 +69,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
||||
}
|
||||
|
||||
if (!AbiVersionUtil.isAbiVersionCompatible(version)) {
|
||||
return new KotlinClassHeader(headerKind, version, null, syntheticClassKind);
|
||||
return new KotlinClassHeader(headerKind, version, null, classKind, syntheticClassKind);
|
||||
}
|
||||
|
||||
if ((headerKind == CLASS || headerKind == PACKAGE_FACADE) && annotationData == null) {
|
||||
@@ -77,7 +78,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
||||
return null;
|
||||
}
|
||||
|
||||
return new KotlinClassHeader(headerKind, version, annotationData, syntheticClassKind);
|
||||
return new KotlinClassHeader(headerKind, version, annotationData, classKind, syntheticClassKind);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -135,11 +136,6 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
|
||||
unexpectedEnumArgument(name, enumClassId, enumEntryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public AnnotationArrayArgumentVisitor visitArray(@NotNull Name name) {
|
||||
@@ -203,12 +199,26 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
||||
public ClassHeaderReader() {
|
||||
super(KotlinClass.CLASS_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
|
||||
if (KotlinClass.KIND_CLASS_ID.equals(enumClassId) && KIND_FIELD_NAME.equals(name.asString())) {
|
||||
classKind = valueOfOrNull(KotlinClass.Kind.class, enumEntryName.asString());
|
||||
if (classKind != null) return;
|
||||
}
|
||||
unexpectedEnumArgument(name, enumClassId, enumEntryName);
|
||||
}
|
||||
}
|
||||
|
||||
private class PackageHeaderReader extends HeaderAnnotationArgumentVisitor {
|
||||
public PackageHeaderReader() {
|
||||
super(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_PACKAGE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
|
||||
unexpectedEnumArgument(name, enumClassId, enumEntryName);
|
||||
}
|
||||
}
|
||||
|
||||
private class SyntheticClassHeaderReader extends HeaderAnnotationArgumentVisitor {
|
||||
@@ -218,7 +228,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
||||
|
||||
@Override
|
||||
public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
|
||||
if (enumClassId.equals(KotlinSyntheticClass.KIND_CLASS_ID) && name.asString().equals(KIND_FIELD_NAME)) {
|
||||
if (KotlinSyntheticClass.KIND_CLASS_ID.equals(enumClassId) && KIND_FIELD_NAME.equals(name.asString())) {
|
||||
syntheticClassKind = valueOfOrNull(KotlinSyntheticClass.Kind.class, enumEntryName.asString());
|
||||
if (syntheticClassKind != null) return;
|
||||
}
|
||||
|
||||
@@ -23,5 +23,18 @@ import java.lang.annotation.RetentionPolicy;
|
||||
public @interface KotlinClass {
|
||||
int abiVersion();
|
||||
|
||||
Kind kind();
|
||||
|
||||
String[] data();
|
||||
|
||||
enum Kind {
|
||||
CLASS,
|
||||
|
||||
/**
|
||||
* A class has this kind if and only if its first non-class container is not a package.
|
||||
*/
|
||||
LOCAL_CLASS,
|
||||
|
||||
ANONYMOUS_OBJECT,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ public @interface KotlinSyntheticClass {
|
||||
|
||||
Kind kind();
|
||||
|
||||
// Inner classes of local classes have kind LOCAL_CLASS
|
||||
public static enum Kind {
|
||||
enum Kind {
|
||||
PACKAGE_PART,
|
||||
TRAIT_IMPL,
|
||||
LOCAL_TRAIT_IMPL,
|
||||
@@ -35,7 +34,5 @@ public @interface KotlinSyntheticClass {
|
||||
CALLABLE_REFERENCE_WRAPPER,
|
||||
LOCAL_FUNCTION,
|
||||
ANONYMOUS_FUNCTION,
|
||||
LOCAL_CLASS,
|
||||
ANONYMOUS_OBJECT,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinConverterForWebDemoTest
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.AbstractDecompiledTextTest
|
||||
import org.jetbrains.kotlin.completion.AbstractMultiFileSmartCompletionTest
|
||||
import org.jetbrains.kotlin.completion.handlers.AbstractCompletionCharFilterTest
|
||||
import org.jetbrains.kotlin.serialization.AbstractLocalClassProtoTest
|
||||
import org.jetbrains.kotlin.idea.resolve.AbstractPartialBodyResolveTest
|
||||
import org.jetbrains.kotlin.checkers.AbstractJetDiagnosticsTestWithJsStdLib
|
||||
import org.jetbrains.kotlin.renderer.AbstractDescriptorRendererTest
|
||||
@@ -309,6 +310,10 @@ fun main(args: Array<String>) {
|
||||
model("lineNumber", recursive = false)
|
||||
model("lineNumber/custom", testMethod = "doTestCustom")
|
||||
}
|
||||
|
||||
testClass(javaClass<AbstractLocalClassProtoTest>()) {
|
||||
model("serialization/local")
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("idea/tests", "idea/testData") {
|
||||
|
||||
+8
-6
@@ -23,7 +23,6 @@ import com.intellij.psi.stubs.PsiFileStub
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.impl.compiled.ClassFileStubBuilder
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.LocalClassFinder
|
||||
@@ -32,6 +31,9 @@ import com.intellij.openapi.diagnostic.Logger
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
|
||||
import org.jetbrains.kotlin.idea.decompiler.isKotlinInternalCompiledFile
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.load.kotlin.header.isCompatiblePackageFacadeKind
|
||||
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
|
||||
public class KotlinClsStubBuilder : ClsStubBuilder() {
|
||||
override fun getStubVersion() = ClassFileStubBuilder.STUB_VERSION + 1
|
||||
@@ -62,19 +64,19 @@ public class KotlinClsStubBuilder : ClsStubBuilder() {
|
||||
LOG.error("Corrupted kotlin header for file ${file.getName()}")
|
||||
return null
|
||||
}
|
||||
return when (header.kind) {
|
||||
KotlinClassHeader.Kind.PACKAGE_FACADE -> {
|
||||
return when {
|
||||
header.isCompatiblePackageFacadeKind() -> {
|
||||
val packageData = JvmProtoBufUtil.readPackageDataFrom(annotationData)
|
||||
val context = components.createContext(packageData.getNameResolver(), packageFqName)
|
||||
createPackageFacadeFileStub(packageData.getPackageProto(), packageFqName, context)
|
||||
}
|
||||
|
||||
KotlinClassHeader.Kind.CLASS -> {
|
||||
header.isCompatibleClassKind() -> {
|
||||
if (header.classKind != JvmAnnotationNames.KotlinClass.Kind.CLASS) return null
|
||||
val classData = JvmProtoBufUtil.readClassDataFrom(annotationData)
|
||||
val context = components.createContext(classData.getNameResolver(), packageFqName)
|
||||
createTopLevelClassStub(classId, classData.getClassProto(), context)
|
||||
}
|
||||
else -> throw IllegalStateException("Should have processed " + file.getPath() + " with ${header.kind}")
|
||||
else -> throw IllegalStateException("Should have processed " + file.getPath() + " with header $header")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,6 @@ public class InternalCompiledClassesTest : AbstractInternalCompiledClassesTest()
|
||||
|
||||
fun testAnonymousFunctionIsInvisible() = doTestNoPsiFilesAreBuiltForSyntheticClass(ANONYMOUS_FUNCTION)
|
||||
|
||||
fun testLocalClassIsInvisible() = doTestNoPsiFilesAreBuiltForSyntheticClass(LOCAL_CLASS)
|
||||
|
||||
fun testAnonymousObjectIsInvisible() = doTestNoPsiFilesAreBuiltForSyntheticClass(ANONYMOUS_OBJECT)
|
||||
|
||||
fun testInnerClassIsInvisible() = doTestNoPsiFilesAreBuiltFor("inner or nested class") {
|
||||
ClassFileViewProvider.isInnerClass(this)
|
||||
}
|
||||
|
||||
@@ -30,11 +30,9 @@ import org.jetbrains.org.objectweb.asm.*
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.cache.IncrementalCache
|
||||
import java.util.HashMap
|
||||
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import java.security.MessageDigest
|
||||
import org.jetbrains.jps.incremental.storage.StorageOwner
|
||||
import org.jetbrains.jps.builders.storage.StorageProvider
|
||||
@@ -137,41 +135,34 @@ public class IncrementalCacheImpl(targetDataRoot: File) : StorageOwner, Incremen
|
||||
dirtyOutputClassesMap.notDirty(className.getInternalName())
|
||||
sourceFiles.forEach { sourceToClassesMap.addSourceToClass(it, className) }
|
||||
|
||||
val annotationDataEncoded = header.annotationData
|
||||
if (annotationDataEncoded != null) {
|
||||
val data = BitEncoding.decodeBytes(annotationDataEncoded)
|
||||
return when {
|
||||
header.isCompatiblePackageFacadeKind() ->
|
||||
getRecompilationDecision(
|
||||
protoChanged = protoMap.put(className, data),
|
||||
constantsChanged = false,
|
||||
inlinesChanged = false
|
||||
)
|
||||
header.isCompatibleClassKind() ->
|
||||
getRecompilationDecision(
|
||||
protoChanged = protoMap.put(className, data),
|
||||
constantsChanged = constantsMap.process(className, fileBytes),
|
||||
inlinesChanged = inlineFunctionsMap.process(className, fileBytes)
|
||||
)
|
||||
else -> {
|
||||
throw IllegalStateException("Unexpected kind with annotationData: ${header.kind}, isCompatible: ${header.isCompatibleAbiVersion}")
|
||||
}
|
||||
return when {
|
||||
header.isCompatiblePackageFacadeKind() ->
|
||||
getRecompilationDecision(
|
||||
protoChanged = protoMap.put(className, BitEncoding.decodeBytes(header.annotationData)),
|
||||
constantsChanged = false,
|
||||
inlinesChanged = false
|
||||
)
|
||||
header.isCompatibleClassKind() ->
|
||||
getRecompilationDecision(
|
||||
protoChanged = protoMap.put(className, BitEncoding.decodeBytes(header.annotationData)),
|
||||
constantsChanged = constantsMap.process(className, fileBytes),
|
||||
inlinesChanged = inlineFunctionsMap.process(className, fileBytes)
|
||||
)
|
||||
header.syntheticClassKind == JvmAnnotationNames.KotlinSyntheticClass.Kind.PACKAGE_PART -> {
|
||||
assert(sourceFiles.size() == 1) { "Package part from several source files: $sourceFiles" }
|
||||
|
||||
packagePartMap.addPackagePart(className)
|
||||
|
||||
getRecompilationDecision(
|
||||
protoChanged = false,
|
||||
constantsChanged = constantsMap.process(className, fileBytes),
|
||||
inlinesChanged = inlineFunctionsMap.process(className, fileBytes)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
DO_NOTHING
|
||||
}
|
||||
}
|
||||
|
||||
if (header.syntheticClassKind == JvmAnnotationNames.KotlinSyntheticClass.Kind.PACKAGE_PART) {
|
||||
assert(sourceFiles.size() == 1) { "Package part from several source files: $sourceFiles" }
|
||||
|
||||
packagePartMap.addPackagePart(className)
|
||||
|
||||
return getRecompilationDecision(
|
||||
protoChanged = false,
|
||||
constantsChanged = constantsMap.process(className, fileBytes),
|
||||
inlinesChanged = inlineFunctionsMap.process(className, fileBytes)
|
||||
)
|
||||
}
|
||||
|
||||
return DO_NOTHING
|
||||
}
|
||||
|
||||
public fun clearCacheForRemovedClasses(): RecompilationDecision {
|
||||
|
||||
@@ -135,6 +135,12 @@ public class IncrementalJpsTestGenerated extends AbstractIncrementalJpsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousObjectChanged")
|
||||
public void testAnonymousObjectChanged() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("jps-plugin/testData/incremental/pureKotlin/anonymousObjectChanged/");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("classInlineFunctionChanged")
|
||||
public void testClassInlineFunctionChanged() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("jps-plugin/testData/incremental/pureKotlin/classInlineFunctionChanged/");
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package a
|
||||
|
||||
fun foo() = object {
|
||||
fun bar() = ":("
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package a
|
||||
|
||||
// Signatures in the class "...$main$1" have changed, thus we need to recompile the whole module
|
||||
fun foo() = object {
|
||||
fun baz() = ":)"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
Cleaning output files:
|
||||
out/production/module/a/APackage$a$*$foo$1.class
|
||||
out/production/module/a/APackage$a$*.class
|
||||
out/production/module/a/APackage.class
|
||||
End of files
|
||||
Compiling files:
|
||||
src/a.kt
|
||||
End of files
|
||||
Cleaning output files:
|
||||
out/production/module/usage/UsagePackage$usage$*.class
|
||||
out/production/module/usage/UsagePackage.class
|
||||
End of files
|
||||
Compiling files:
|
||||
src/usage.kt
|
||||
End of files
|
||||
@@ -0,0 +1,5 @@
|
||||
package usage
|
||||
|
||||
import a.foo
|
||||
|
||||
fun bar() = foo()
|
||||
Reference in New Issue
Block a user