KT-11410: Class hierarchy for parts/facade of multi-file class.
Preserve static initialization semantics for parts by introducing a special "clinit trigger" class.
Insert "static initialization trigger" call to every method of a part class, remove this call on inline.
Always mangle names for private functions in multifile class parts to avoid resolution clashes on inheritance.
NB in codegen tests initializers for all non-const vals are wrapped in 'run { ... }',
so that the initializer is not a constant expression, and some static initialization code should be generated.
This commit is contained in:
@@ -374,6 +374,9 @@ public class FunctionCodegen {
|
|||||||
if (!KotlinTypeMapper.isAccessor(functionDescriptor)) {
|
if (!KotlinTypeMapper.isAccessor(functionDescriptor)) {
|
||||||
genNotNullAssertionsForParameters(new InstructionAdapter(mv), parentCodegen.state, functionDescriptor, frameMap);
|
genNotNullAssertionsForParameters(new InstructionAdapter(mv), parentCodegen.state, functionDescriptor, frameMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parentCodegen.beforeMethodBody(mv);
|
||||||
|
|
||||||
methodEnd = new Label();
|
methodEnd = new Label();
|
||||||
context.setMethodEndLabel(methodEnd);
|
context.setMethodEndLabel(methodEnd);
|
||||||
strategy.generateBody(mv, frameMap, signature, context, parentCodegen);
|
strategy.generateBody(mv, frameMap, signature, context, parentCodegen);
|
||||||
|
|||||||
@@ -336,23 +336,32 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected ExpressionCodegen createOrGetClInitCodegen() {
|
protected final ExpressionCodegen createOrGetClInitCodegen() {
|
||||||
DeclarationDescriptor descriptor = context.getContextDescriptor();
|
|
||||||
if (clInit == null) {
|
if (clInit == null) {
|
||||||
MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), ACC_STATIC, "<clinit>", "()V", null, null);
|
DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
|
||||||
SimpleFunctionDescriptorImpl clInit =
|
SimpleFunctionDescriptorImpl clInitDescriptor = createClInitFunctionDescriptor(contextDescriptor);
|
||||||
SimpleFunctionDescriptorImpl.create(descriptor, Annotations.Companion.getEMPTY(), Name.special("<clinit>"), SYNTHESIZED,
|
MethodVisitor mv = createClInitMethodVisitor(contextDescriptor);
|
||||||
KotlinSourceElementKt.toSourceElement(element));
|
clInit = new ExpressionCodegen(mv, new FrameMap(), Type.VOID_TYPE, context.intoFunction(clInitDescriptor), state, this);
|
||||||
clInit.initialize(null, null, Collections.<TypeParameterDescriptor>emptyList(),
|
|
||||||
Collections.<ValueParameterDescriptor>emptyList(),
|
|
||||||
DescriptorUtilsKt.getModule(descriptor).getBuiltIns().getUnitType(),
|
|
||||||
null, Visibilities.PRIVATE);
|
|
||||||
|
|
||||||
this.clInit = new ExpressionCodegen(mv, new FrameMap(), Type.VOID_TYPE, context.intoFunction(clInit), state, this);
|
|
||||||
}
|
}
|
||||||
return clInit;
|
return clInit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected MethodVisitor createClInitMethodVisitor(@NotNull DeclarationDescriptor contextDescriptor) {
|
||||||
|
return v.newMethod(JvmDeclarationOriginKt.OtherOrigin(contextDescriptor), ACC_STATIC, "<clinit>", "()V", null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected SimpleFunctionDescriptorImpl createClInitFunctionDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
||||||
|
SimpleFunctionDescriptorImpl clInit = SimpleFunctionDescriptorImpl.create(descriptor, Annotations.Companion.getEMPTY(),
|
||||||
|
Name.special("<clinit>"), SYNTHESIZED, KotlinSourceElementKt.toSourceElement(element));
|
||||||
|
clInit.initialize(null, null, Collections.<TypeParameterDescriptor>emptyList(),
|
||||||
|
Collections.<ValueParameterDescriptor>emptyList(),
|
||||||
|
DescriptorUtilsKt.getModule(descriptor).getBuiltIns().getUnitType(),
|
||||||
|
null, Visibilities.PRIVATE);
|
||||||
|
return clInit;
|
||||||
|
}
|
||||||
|
|
||||||
protected void generateInitializers(@NotNull Function0<ExpressionCodegen> createCodegen) {
|
protected void generateInitializers(@NotNull Function0<ExpressionCodegen> createCodegen) {
|
||||||
NotNullLazyValue<ExpressionCodegen> codegen = LockBasedStorageManager.NO_LOCKS.createLazyValue(createCodegen);
|
NotNullLazyValue<ExpressionCodegen> codegen = LockBasedStorageManager.NO_LOCKS.createLazyValue(createCodegen);
|
||||||
for (KtDeclaration declaration : ((KtDeclarationContainer) element).getDeclarations()) {
|
for (KtDeclaration declaration : ((KtDeclarationContainer) element).getDeclarations()) {
|
||||||
@@ -370,6 +379,9 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void beforeMethodBody(@NotNull MethodVisitor mv) {
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeProperty(@NotNull ExpressionCodegen codegen, @NotNull KtProperty property) {
|
private void initializeProperty(@NotNull ExpressionCodegen codegen, @NotNull KtProperty property) {
|
||||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(VARIABLE, property);
|
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(VARIABLE, property);
|
||||||
assert propertyDescriptor != null;
|
assert propertyDescriptor != null;
|
||||||
@@ -382,7 +394,7 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
|
|||||||
propValue.store(codegen.gen(initializer), codegen.v);
|
propValue.store(codegen.gen(initializer), codegen.v);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldInitializeProperty(@NotNull KtProperty property) {
|
protected boolean shouldInitializeProperty(@NotNull KtProperty property) {
|
||||||
if (!property.hasDelegateExpressionOrInitializer()) return false;
|
if (!property.hasDelegateExpressionOrInitializer()) return false;
|
||||||
|
|
||||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(VARIABLE, property);
|
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(VARIABLE, property);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
|
|||||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
||||||
@@ -37,7 +38,7 @@ import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStat
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.MemberComparator
|
import org.jetbrains.kotlin.resolve.MemberComparator
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClassPart
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClassPart
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||||
@@ -50,11 +51,10 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
|
|||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class MultifileClassCodegen(
|
class MultifileClassCodegen(
|
||||||
private val state: GenerationState,
|
private val state: GenerationState,
|
||||||
val files: Collection<KtFile>,
|
private val files: Collection<KtFile>,
|
||||||
private val facadeFqName: FqName,
|
private val facadeFqName: FqName,
|
||||||
private val packagePartRegistry: PackagePartRegistry
|
private val packagePartRegistry: PackagePartRegistry
|
||||||
) {
|
) {
|
||||||
@@ -71,55 +71,105 @@ class MultifileClassCodegen(
|
|||||||
getDeserializedCallables(compiledPackageFragment)
|
getDeserializedCallables(compiledPackageFragment)
|
||||||
|
|
||||||
private fun getDeserializedCallables(compiledPackageFragment: PackageFragmentDescriptor) =
|
private fun getDeserializedCallables(compiledPackageFragment: PackageFragmentDescriptor) =
|
||||||
compiledPackageFragment.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CALLABLES, MemberScope.ALL_NAME_FILTER).filterIsInstance<DeserializedCallableMemberDescriptor>()
|
compiledPackageFragment.getMemberScope()
|
||||||
|
.getContributedDescriptors(DescriptorKindFilter.CALLABLES, MemberScope.ALL_NAME_FILTER)
|
||||||
|
.filterIsInstance<DeserializedCallableMemberDescriptor>()
|
||||||
|
|
||||||
|
private fun KtFile.getFileClassFqName() =
|
||||||
|
state.fileClassesProvider.getFileClassInfo(this).fileClassFqName
|
||||||
|
|
||||||
|
private val shouldGeneratePartHierarchy =
|
||||||
|
state.inheritMultifileParts // TODO support incremental compilation
|
||||||
|
|
||||||
|
private val partInternalNamesSorted = run {
|
||||||
|
val partInternalNamesSet = hashSetOf<String>()
|
||||||
|
for (file in files) {
|
||||||
|
if (file.hasDeclarationsForPartClass()) {
|
||||||
|
partInternalNamesSet.add(file.getFileClassFqName().toInternalName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compiledPackageFragment?.let {
|
||||||
|
partInternalNamesSet.addAll(it.partsNames)
|
||||||
|
}
|
||||||
|
partInternalNamesSet.sorted()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val superClassForInheritedPart = run {
|
||||||
|
val result = hashMapOf<String, String>()
|
||||||
|
for (i in 1 ..partInternalNamesSorted.size - 1) {
|
||||||
|
result[partInternalNamesSorted[i]] = partInternalNamesSorted[i - 1]
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
private val delegateGenerationTasks = hashMapOf<CallableMemberDescriptor, () -> Unit>()
|
||||||
|
|
||||||
|
private fun getSuperClassForPart(partInternalName: String) =
|
||||||
|
if (shouldGeneratePartHierarchy)
|
||||||
|
superClassForInheritedPart[partInternalName] ?: J_L_OBJECT
|
||||||
|
else
|
||||||
|
J_L_OBJECT
|
||||||
|
|
||||||
private val classBuilder = ClassBuilderOnDemand {
|
private val classBuilder = ClassBuilderOnDemand {
|
||||||
val originFile = files.firstOrNull()
|
val originFile = files.firstOrNull()
|
||||||
val actualPackageFragment = packageFragment ?:
|
|
||||||
compiledPackageFragment ?:
|
|
||||||
throw AssertionError("No package fragment for multifile facade $facadeFqName; files: $files")
|
|
||||||
val declarationOrigin = MultifileClass(originFile, actualPackageFragment, facadeFqName)
|
|
||||||
val classBuilder = state.factory.newVisitor(declarationOrigin, facadeClassType, files)
|
|
||||||
|
|
||||||
val filesWithCallables = files.filter { it.declarations.any { it is KtNamedFunction || it is KtProperty } }
|
val actualPackageFragment = packageFragment
|
||||||
|
?: compiledPackageFragment
|
||||||
|
?: throw AssertionError("No package fragment for multifile facade $facadeFqName; files: $files")
|
||||||
|
|
||||||
val singleSourceFile = if (previouslyCompiledCallables.isNotEmpty()) null else filesWithCallables.singleOrNull()
|
val declarationOrigin = MultifileClass(originFile, actualPackageFragment)
|
||||||
|
|
||||||
classBuilder.defineClass(singleSourceFile, Opcodes.V1_6,
|
val singleSourceFile =
|
||||||
FACADE_CLASS_ATTRIBUTES,
|
if (previouslyCompiledCallables.isEmpty())
|
||||||
facadeClassType.internalName,
|
files.singleOrNull { it.hasDeclarationsForPartClass() }
|
||||||
null, "java/lang/Object", ArrayUtil.EMPTY_STRING_ARRAY)
|
else
|
||||||
if (singleSourceFile != null) {
|
null
|
||||||
classBuilder.visitSource(singleSourceFile.name, null)
|
|
||||||
|
val superClassForFacade =
|
||||||
|
if (shouldGeneratePartHierarchy)
|
||||||
|
partInternalNamesSorted.last()
|
||||||
|
else
|
||||||
|
J_L_OBJECT
|
||||||
|
|
||||||
|
state.factory.newVisitor(declarationOrigin, facadeClassType, files).apply {
|
||||||
|
defineClass(singleSourceFile, Opcodes.V1_6, FACADE_CLASS_ATTRIBUTES,
|
||||||
|
facadeClassType.internalName, null, superClassForFacade, ArrayUtil.EMPTY_STRING_ARRAY)
|
||||||
|
if (singleSourceFile != null) {
|
||||||
|
visitSource(singleSourceFile.name, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldGeneratePartHierarchy) {
|
||||||
|
newMethod(OtherOrigin(actualPackageFragment), Opcodes.ACC_PRIVATE, "<init>", "()V", null, null).apply {
|
||||||
|
visitCode()
|
||||||
|
visitVarInsn(Opcodes.ALOAD, 0)
|
||||||
|
visitMethodInsn(Opcodes.INVOKESPECIAL, superClassForFacade, "<init>", "()V", false)
|
||||||
|
visitInsn(Opcodes.RETURN)
|
||||||
|
visitMaxs(1, 1)
|
||||||
|
visitEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
classBuilder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generate(errorHandler: CompilationErrorHandler) {
|
fun generate(errorHandler: CompilationErrorHandler) {
|
||||||
val generateCallableMemberTasks = HashMap<CallableMemberDescriptor, () -> Unit>()
|
assert(delegateGenerationTasks.isEmpty()) { "generate() is called twice for facade class $facadeFqName" }
|
||||||
val partFqNames = arrayListOf<FqName>()
|
|
||||||
|
|
||||||
generateCodeForSourceFiles(errorHandler, generateCallableMemberTasks, partFqNames)
|
generateCodeForSourceFiles(errorHandler)
|
||||||
|
|
||||||
generateDelegatesToPreviouslyCompiledParts(generateCallableMemberTasks, partFqNames)
|
generateDelegatesToPreviouslyCompiledParts()
|
||||||
|
|
||||||
if (!partFqNames.isEmpty()) {
|
if (!partInternalNamesSorted.isEmpty()) {
|
||||||
generateMultifileFacadeClass(generateCallableMemberTasks, partFqNames)
|
generateMultifileFacadeClass()
|
||||||
}
|
}
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateCodeForSourceFiles(
|
private fun generateCodeForSourceFiles(errorHandler: CompilationErrorHandler) {
|
||||||
errorHandler: CompilationErrorHandler,
|
|
||||||
generateCallableMemberTasks: MutableMap<CallableMemberDescriptor, () -> Unit>,
|
|
||||||
partFqNames: MutableList<FqName>
|
|
||||||
) {
|
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||||
try {
|
try {
|
||||||
generatePart(file, generateCallableMemberTasks, partFqNames)
|
generatePart(file)
|
||||||
state.afterIndependentPart()
|
state.afterIndependentPart()
|
||||||
}
|
}
|
||||||
catch (e: ProcessCanceledException) {
|
catch (e: ProcessCanceledException) {
|
||||||
@@ -137,87 +187,103 @@ class MultifileClassCodegen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateMultifileFacadeClass(
|
private fun generateMultifileFacadeClass() {
|
||||||
tasks: Map<CallableMemberDescriptor, () -> Unit>,
|
for (member in delegateGenerationTasks.keys.sortedWith(MemberComparator.INSTANCE)) {
|
||||||
partFqNames: List<FqName>
|
delegateGenerationTasks[member]!!()
|
||||||
) {
|
|
||||||
for (member in tasks.keys.sortedWith(MemberComparator.INSTANCE)) {
|
|
||||||
tasks[member]!!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writeKotlinMultifileFacadeAnnotationIfNeeded(partFqNames)
|
writeKotlinMultifileFacadeAnnotationIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateClassOrObject(classOrObject: KtClassOrObject, packagePartContext: FieldOwnerContext<PackageFragmentDescriptor>) {
|
fun generateClassOrObject(classOrObject: KtClassOrObject, packagePartContext: FieldOwnerContext<PackageFragmentDescriptor>) {
|
||||||
MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null)
|
MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generatePart(
|
private fun generatePart(file: KtFile) {
|
||||||
file: KtFile,
|
val packageFragment = this.packageFragment
|
||||||
generateCallableMemberTasks: MutableMap<CallableMemberDescriptor, () -> Unit>,
|
?: throw AssertionError("File part $file of $facadeFqName: no package fragment")
|
||||||
partFqNames: MutableList<FqName>
|
|
||||||
) {
|
|
||||||
val packageFragment = this.packageFragment ?:
|
|
||||||
throw AssertionError("File part $file of $facadeFqName: no package fragment")
|
|
||||||
|
|
||||||
var generatePart = false
|
val partClassFqName = file.getFileClassFqName()
|
||||||
val partClassInfo = state.fileClassesProvider.getFileClassInfo(file)
|
val partInitializerClassFqName = FqName(partClassFqName.asString() + "__Init")
|
||||||
val partType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(partClassInfo.fileClassFqName)
|
val partType = partClassFqName.toAsmType()
|
||||||
val partContext = state.rootContext.intoMultifileClassPart(packageFragment, facadeClassType, partType, file)
|
val partInitializerType = partInitializerClassFqName.toAsmType()
|
||||||
|
val partContext = state.rootContext.intoMultifileClassPart(packageFragment, facadeClassType, partType, partInitializerType, file)
|
||||||
|
|
||||||
|
generateNonPartClassDeclarations(file, partContext)
|
||||||
|
|
||||||
|
if (!state.generateDeclaredClassFilter.shouldGeneratePackagePart(file) || !file.hasDeclarationsForPartClass()) return
|
||||||
|
|
||||||
|
val partInternalName = partType.internalName
|
||||||
|
packagePartRegistry.addPart(partInternalName.substring(partInternalName.lastIndexOf('/') + 1))
|
||||||
|
|
||||||
|
val builder = state.factory.newVisitor(MultifileClassPart(file, packageFragment), partType, file)
|
||||||
|
|
||||||
|
MultifileClassPartCodegen(
|
||||||
|
builder, file, packageFragment,
|
||||||
|
getSuperClassForPart(partInternalName),
|
||||||
|
shouldGeneratePartHierarchy,
|
||||||
|
partContext, state
|
||||||
|
).generate()
|
||||||
|
|
||||||
|
addDelegateGenerationTasksForDeclarationsInFile(file, packageFragment, partType)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateNonPartClassDeclarations(file: KtFile, partContext: FieldOwnerContext<PackageFragmentDescriptor>) {
|
||||||
for (declaration in file.declarations) {
|
for (declaration in file.declarations) {
|
||||||
when (declaration) {
|
when (declaration) {
|
||||||
is KtProperty, is KtNamedFunction -> {
|
is KtClassOrObject ->
|
||||||
generatePart = true
|
if (state.generateDeclaredClassFilter.shouldGenerateClass(declaration)) {
|
||||||
}
|
generateClassOrObject(declaration, partContext)
|
||||||
is KtClassOrObject -> if (state.generateDeclaredClassFilter.shouldGenerateClass(declaration)) {
|
}
|
||||||
generateClassOrObject(declaration, partContext)
|
is KtScript ->
|
||||||
}
|
if (state.generateDeclaredClassFilter.shouldGenerateScript(declaration)) {
|
||||||
is KtScript -> if (state.generateDeclaredClassFilter.shouldGenerateScript(declaration)) {
|
ScriptCodegen.createScriptCodegen(declaration, state, partContext).generate()
|
||||||
ScriptCodegen.createScriptCodegen(declaration, state, partContext).generate()
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!generatePart || !state.generateDeclaredClassFilter.shouldGeneratePackagePart(file)) return
|
|
||||||
|
|
||||||
partFqNames.add(partClassInfo.fileClassFqName)
|
|
||||||
|
|
||||||
val name = partType.internalName
|
|
||||||
packagePartRegistry.addPart(name.substring(name.lastIndexOf('/') + 1))
|
|
||||||
|
|
||||||
val builder = state.factory.newVisitor(MultifileClassPart(file, packageFragment, facadeFqName), partType, file)
|
|
||||||
|
|
||||||
MultifileClassPartCodegen(builder, file, partType, facadeClassType, partContext, state).generate()
|
|
||||||
|
|
||||||
val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType)
|
|
||||||
val memberCodegen = createCodegenForPartOfMultifileFacade(facadeContext)
|
|
||||||
for (declaration in file.declarations) {
|
|
||||||
if (declaration is KtNamedFunction || declaration is KtProperty) {
|
|
||||||
val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)
|
|
||||||
assert(descriptor is CallableMemberDescriptor) { "Expected callable member, was " + descriptor + " for " + declaration.text }
|
|
||||||
if (!Visibilities.isPrivate((descriptor as CallableMemberDescriptor).visibility)
|
|
||||||
&& AsmUtil.getVisibilityAccessFlag(descriptor) != Opcodes.ACC_PRIVATE) {
|
|
||||||
generateCallableMemberTasks.put(descriptor, { memberCodegen.genFunctionOrProperty(declaration) })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateDelegatesToPreviouslyCompiledParts(
|
private fun addDelegateGenerationTasksForDeclarationsInFile(file: KtFile, packageFragment: PackageFragmentDescriptor, partType: Type) {
|
||||||
generateCallableMemberTasks: MutableMap<CallableMemberDescriptor, () -> Unit>,
|
val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType)
|
||||||
partFqNames: MutableList<FqName>
|
val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext)
|
||||||
) {
|
for (declaration in file.declarations) {
|
||||||
if (compiledPackageFragment == null) return
|
if (declaration is KtNamedFunction || declaration is KtProperty) {
|
||||||
|
val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)
|
||||||
|
if (descriptor !is CallableMemberDescriptor) {
|
||||||
|
throw AssertionError("Expected callable member, was " + descriptor + " for " + declaration.text)
|
||||||
|
}
|
||||||
|
addDelegateGenerationTaskIfNeeded(descriptor, { memberCodegen.genFunctionOrProperty(declaration) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
partFqNames.addAll(compiledPackageFragment.partsNames.map { JvmClassName.byInternalName(it).fqNameForClassNameWithoutDollars })
|
private fun shouldGenerateInFacade(descriptor: CallableMemberDescriptor): Boolean {
|
||||||
|
if (Visibilities.isPrivate(descriptor.visibility)) return false
|
||||||
|
if (AsmUtil.getVisibilityAccessFlag(descriptor) == Opcodes.ACC_PRIVATE) return false
|
||||||
|
|
||||||
|
if (state.classBuilderMode == ClassBuilderMode.LIGHT_CLASSES) return true
|
||||||
|
|
||||||
|
if (shouldGeneratePartHierarchy) {
|
||||||
|
if (descriptor !is PropertyDescriptor || !descriptor.isConst) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addDelegateGenerationTaskIfNeeded(callable: CallableMemberDescriptor, task: () -> Unit) {
|
||||||
|
if (shouldGenerateInFacade(callable)) {
|
||||||
|
delegateGenerationTasks[callable] = task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateDelegatesToPreviouslyCompiledParts() {
|
||||||
|
if (compiledPackageFragment == null) return
|
||||||
|
|
||||||
for (callable in previouslyCompiledCallables) {
|
for (callable in previouslyCompiledCallables) {
|
||||||
val partFqName = JvmFileClassUtil.getPartFqNameForDeserializedCallable(callable)
|
val partFqName = JvmFileClassUtil.getPartFqNameForDeserializedCallable(callable)
|
||||||
val partType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(partFqName)
|
val partType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(partFqName)
|
||||||
|
|
||||||
generateCallableMemberTasks[callable] = { generateDelegateToCompiledMember(callable, compiledPackageFragment, partType) }
|
addDelegateGenerationTaskIfNeeded(callable, { generateDelegateToCompiledMember(callable, compiledPackageFragment, partType) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +294,7 @@ class MultifileClassCodegen(
|
|||||||
) {
|
) {
|
||||||
val context = state.rootContext.intoMultifileClass(compiledPackageFragment, facadeClassType, partType)
|
val context = state.rootContext.intoMultifileClass(compiledPackageFragment, facadeClassType, partType)
|
||||||
|
|
||||||
val memberCodegen = createCodegenForPartOfMultifileFacade(context)
|
val memberCodegen = createCodegenForDelegatesInMultifileFacade(context)
|
||||||
|
|
||||||
when (member) {
|
when (member) {
|
||||||
is DeserializedSimpleFunctionDescriptor -> {
|
is DeserializedSimpleFunctionDescriptor -> {
|
||||||
@@ -254,20 +320,20 @@ class MultifileClassCodegen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeKotlinMultifileFacadeAnnotationIfNeeded(partFqNames: List<FqName>) {
|
private fun writeKotlinMultifileFacadeAnnotationIfNeeded() {
|
||||||
if (state.classBuilderMode != ClassBuilderMode.FULL) return
|
if (state.classBuilderMode != ClassBuilderMode.FULL) return
|
||||||
if (files.any { it.isScript }) return
|
if (files.any { it.isScript }) return
|
||||||
|
|
||||||
writeKotlinMetadata(classBuilder, KotlinClassHeader.Kind.MULTIFILE_CLASS) { av ->
|
writeKotlinMetadata(classBuilder, KotlinClassHeader.Kind.MULTIFILE_CLASS) { av ->
|
||||||
val arv = av.visitArray(JvmAnnotationNames.METADATA_DATA_FIELD_NAME)
|
val arv = av.visitArray(JvmAnnotationNames.METADATA_DATA_FIELD_NAME)
|
||||||
for (internalName in partFqNames.map(AsmUtil::internalNameByFqNameWithoutInnerClasses).sorted()) {
|
for (internalName in partInternalNamesSorted) {
|
||||||
arv.visit(null, internalName)
|
arv.visit(null, internalName)
|
||||||
}
|
}
|
||||||
arv.visitEnd()
|
arv.visitEnd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createCodegenForPartOfMultifileFacade(facadeContext: FieldOwnerContext<*>): MemberCodegen<KtFile> =
|
private fun createCodegenForDelegatesInMultifileFacade(facadeContext: FieldOwnerContext<*>): MemberCodegen<KtFile> =
|
||||||
object : MemberCodegen<KtFile>(state, null, facadeContext, null, classBuilder) {
|
object : MemberCodegen<KtFile>(state, null, facadeContext, null, classBuilder) {
|
||||||
override fun generateDeclaration() = throw UnsupportedOperationException()
|
override fun generateDeclaration() = throw UnsupportedOperationException()
|
||||||
override fun generateBody() = throw UnsupportedOperationException()
|
override fun generateBody() = throw UnsupportedOperationException()
|
||||||
@@ -282,15 +348,16 @@ class MultifileClassCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val J_L_OBJECT = AsmTypes.OBJECT_TYPE.internalName
|
||||||
private val FACADE_CLASS_ATTRIBUTES = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER
|
private val FACADE_CLASS_ATTRIBUTES = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER
|
||||||
|
|
||||||
private fun getOnlyPackageFragment(packageFqName: FqName, files: Collection<KtFile>, bindingContext: BindingContext): PackageFragmentDescriptor? {
|
private fun getOnlyPackageFragment(packageFqName: FqName, files: Collection<KtFile>, bindingContext: BindingContext): PackageFragmentDescriptor? {
|
||||||
val fragments = SmartList<PackageFragmentDescriptor>()
|
val fragments = SmartList<PackageFragmentDescriptor>()
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
val fragment = bindingContext.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file)
|
val fragment = bindingContext.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file)
|
||||||
assert(fragment != null) { "package fragment is null for " + file + "\n" + file.text }
|
?: throw AssertionError("package fragment is null for " + file + "\n" + file.text)
|
||||||
|
|
||||||
assert(packageFqName == fragment!!.fqName) { "expected package fq name: " + packageFqName + ", actual: " + fragment.fqName }
|
assert(packageFqName == fragment.fqName) { "expected package fq name: " + packageFqName + ", actual: " + fragment.fqName }
|
||||||
|
|
||||||
if (!fragments.contains(fragment)) {
|
if (!fragments.contains(fragment)) {
|
||||||
fragments.add(fragment)
|
fragments.add(fragment)
|
||||||
@@ -302,6 +369,15 @@ class MultifileClassCodegen(
|
|||||||
return fragments.firstOrNull()
|
return fragments.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KtFile.hasDeclarationsForPartClass() =
|
||||||
|
declarations.any { it is KtProperty || it is KtFunction }
|
||||||
|
|
||||||
|
private fun FqName.toInternalName() =
|
||||||
|
AsmUtil.internalNameByFqNameWithoutInnerClasses(this)
|
||||||
|
|
||||||
|
private fun FqName.toAsmType() =
|
||||||
|
AsmUtil.asmTypeByFqNameWithoutInnerClasses(this)
|
||||||
|
|
||||||
private fun getCompiledPackageFragment(facadeFqName: FqName, state: GenerationState):
|
private fun getCompiledPackageFragment(facadeFqName: FqName, state: GenerationState):
|
||||||
IncrementalPackageFragmentProvider.IncrementalPackageFragment.IncrementalMultifileClassPackageFragment? {
|
IncrementalPackageFragmentProvider.IncrementalPackageFragment.IncrementalMultifileClassPackageFragment? {
|
||||||
if (!IncrementalCompilation.isEnabled()) return null
|
if (!IncrementalCompilation.isEnabled()) return null
|
||||||
|
|||||||
@@ -17,44 +17,104 @@
|
|||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
import com.intellij.util.ArrayUtil
|
import com.intellij.util.ArrayUtil
|
||||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext
|
||||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
|
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
import org.jetbrains.kotlin.psi.KtProperty
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||||
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class MultifileClassPartCodegen(
|
class MultifileClassPartCodegen(
|
||||||
v: ClassBuilder,
|
v: ClassBuilder,
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
private val filePartType: Type,
|
private val packageFragment: PackageFragmentDescriptor,
|
||||||
private val multifileClassType: Type,
|
private val superClassInternalName: String,
|
||||||
partContext: FieldOwnerContext<*>,
|
private val shouldGeneratePartHierarchy: Boolean,
|
||||||
|
private val partContext: MultifileClassPartContext,
|
||||||
state: GenerationState
|
state: GenerationState
|
||||||
) : MemberCodegen<KtFile>(state, null, partContext, file, v) {
|
) : MemberCodegen<KtFile>(state, null, partContext, file, v) {
|
||||||
|
private val partType = partContext.filePartType
|
||||||
|
private val facadeClassType = partContext.multifileClassType
|
||||||
|
private val staticInitClassType = Type.getObjectType(partType.internalName + STATIC_INIT_CLASS_SUFFIX)
|
||||||
|
|
||||||
|
private val partClassAttributes =
|
||||||
|
if (shouldGeneratePartHierarchy)
|
||||||
|
OPEN_PART_CLASS_ATTRIBUTES
|
||||||
|
else
|
||||||
|
FINAL_PART_CLASS_ATTRIBUTES
|
||||||
|
|
||||||
|
private fun ClassBuilder.newSpecialMethod(originDescriptor: DeclarationDescriptor, name: String) =
|
||||||
|
newMethod(OtherOrigin(originDescriptor), Opcodes.ACC_STATIC or Opcodes.ACC_SUPER, name, "()V", null, null)
|
||||||
|
|
||||||
|
private val staticInitClassBuilder = ClassBuilderOnDemand {
|
||||||
|
state.factory.newVisitor(MultifileClass(file, packageFragment), staticInitClassType, file).apply {
|
||||||
|
defineClass(file, Opcodes.V1_6, STATE_INITIALIZER_CLASS_ATTRIBUTES,
|
||||||
|
staticInitClassType.internalName, null, "java/lang/Object", ArrayUtil.EMPTY_STRING_ARRAY)
|
||||||
|
|
||||||
|
visitSource(file.name, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val requiresDeferredStaticInitialization =
|
||||||
|
shouldGeneratePartHierarchy && file.declarations.any {
|
||||||
|
it is KtProperty && shouldInitializeProperty(it)
|
||||||
|
}
|
||||||
|
|
||||||
override fun generate() {
|
override fun generate() {
|
||||||
if (state.classBuilderMode == ClassBuilderMode.LIGHT_CLASSES) return
|
if (state.classBuilderMode == ClassBuilderMode.LIGHT_CLASSES) return
|
||||||
|
|
||||||
super.generate()
|
super.generate()
|
||||||
|
|
||||||
|
if (shouldGeneratePartHierarchy) {
|
||||||
|
v.newMethod(OtherOrigin(packageFragment), Opcodes.ACC_PUBLIC, "<init>", "()V", null, null).apply {
|
||||||
|
visitCode()
|
||||||
|
visitVarInsn(Opcodes.ALOAD, 0)
|
||||||
|
visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "<init>", "()V", false)
|
||||||
|
visitInsn(Opcodes.RETURN)
|
||||||
|
visitMaxs(1, 1)
|
||||||
|
visitEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requiresDeferredStaticInitialization) {
|
||||||
|
staticInitClassBuilder.apply {
|
||||||
|
newSpecialMethod(packageFragment, CLINIT_TRIGGER_NAME).apply {
|
||||||
|
visitCode()
|
||||||
|
visitInsn(Opcodes.RETURN)
|
||||||
|
visitMaxs(0, 0)
|
||||||
|
visitEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
newSpecialMethod(packageFragment, "<clinit>").apply {
|
||||||
|
visitCode()
|
||||||
|
visitMethodInsn(Opcodes.INVOKESTATIC, partType.internalName, DEFERRED_PART_CLINIT_NAME, "()V", false)
|
||||||
|
visitInsn(Opcodes.RETURN)
|
||||||
|
visitMaxs(0, 0)
|
||||||
|
visitEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateDeclaration() {
|
override fun generateDeclaration() {
|
||||||
v.defineClass(element, Opcodes.V1_6,
|
v.defineClass(element, Opcodes.V1_6, partClassAttributes, partType.internalName, null, superClassInternalName, ArrayUtil.EMPTY_STRING_ARRAY)
|
||||||
Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_SUPER,
|
|
||||||
filePartType.internalName,
|
|
||||||
null,
|
|
||||||
"java/lang/Object",
|
|
||||||
ArrayUtil.EMPTY_STRING_ARRAY)
|
|
||||||
v.visitSource(element.name, null)
|
v.visitSource(element.name, null)
|
||||||
|
|
||||||
generatePropertyMetadataArrayFieldIfNeeded(filePartType)
|
generatePropertyMetadataArrayFieldIfNeeded(partType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateBody() {
|
override fun generateBody() {
|
||||||
@@ -69,6 +129,20 @@ class MultifileClassPartCodegen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createClInitMethodVisitor(contextDescriptor: DeclarationDescriptor): MethodVisitor =
|
||||||
|
if (requiresDeferredStaticInitialization)
|
||||||
|
v.newSpecialMethod(contextDescriptor, DEFERRED_PART_CLINIT_NAME)
|
||||||
|
else
|
||||||
|
super.createClInitMethodVisitor(contextDescriptor)
|
||||||
|
|
||||||
|
override fun done() {
|
||||||
|
super.done()
|
||||||
|
|
||||||
|
if (staticInitClassBuilder.isComputed) {
|
||||||
|
staticInitClassBuilder.done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun generateKotlinMetadataAnnotation() {
|
override fun generateKotlinMetadataAnnotation() {
|
||||||
val members = ArrayList<DeclarationDescriptor>()
|
val members = ArrayList<DeclarationDescriptor>()
|
||||||
for (declaration in element.declarations) {
|
for (declaration in element.declarations) {
|
||||||
@@ -89,11 +163,34 @@ class MultifileClassPartCodegen(
|
|||||||
|
|
||||||
writeKotlinMetadata(v, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART) { av ->
|
writeKotlinMetadata(v, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART) { av ->
|
||||||
AsmUtil.writeAnnotationData(av, serializer, packageProto)
|
AsmUtil.writeAnnotationData(av, serializer, packageProto)
|
||||||
av.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, multifileClassType.internalName)
|
av.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassType.internalName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateSyntheticParts() {
|
override fun generateSyntheticParts() {
|
||||||
generateSyntheticAccessors()
|
generateSyntheticAccessors()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun beforeMethodBody(mv: MethodVisitor) {
|
||||||
|
if (requiresDeferredStaticInitialization) {
|
||||||
|
mv.visitMethodInsn(Opcodes.INVOKESTATIC, staticInitClassType.internalName, CLINIT_TRIGGER_NAME, "()V", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val OPEN_PART_CLASS_ATTRIBUTES = Opcodes.ACC_SUPER
|
||||||
|
private val FINAL_PART_CLASS_ATTRIBUTES = Opcodes.ACC_SYNTHETIC or Opcodes.ACC_SUPER or Opcodes.ACC_FINAL
|
||||||
|
private val STATE_INITIALIZER_CLASS_ATTRIBUTES = Opcodes.ACC_SYNTHETIC or Opcodes.ACC_SUPER or Opcodes.ACC_FINAL
|
||||||
|
|
||||||
|
private val STATIC_INIT_CLASS_SUFFIX = "__Clinit"
|
||||||
|
private val CLINIT_TRIGGER_NAME = "\$\$clinitTrigger"
|
||||||
|
private val DEFERRED_PART_CLINIT_NAME = "\$\$clinit"
|
||||||
|
|
||||||
|
@JvmStatic fun isStaticInitTrigger(insn: AbstractInsnNode) =
|
||||||
|
insn.opcode == Opcodes.INVOKESTATIC
|
||||||
|
&& insn is MethodInsnNode
|
||||||
|
&& insn.owner.endsWith(STATIC_INIT_CLASS_SUFFIX)
|
||||||
|
&& insn.name == CLINIT_TRIGGER_NAME
|
||||||
|
&& insn.desc == "()V"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
|||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationSplitter;
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationSplitter;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||||
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtilKt;
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||||
@@ -184,6 +185,9 @@ public class PropertyCodegen {
|
|||||||
// Companion object properties always should have accessors, because their backing fields are moved/copied to the outer class
|
// Companion object properties always should have accessors, because their backing fields are moved/copied to the outer class
|
||||||
if (isCompanionObject(descriptor.getContainingDeclaration())) return true;
|
if (isCompanionObject(descriptor.getContainingDeclaration())) return true;
|
||||||
|
|
||||||
|
// Non-const properties from multifile classes have accessors regardless of visibility
|
||||||
|
if (!descriptor.isConst() && JvmFileClassUtilKt.isInsideJvmMultifileClassFile(declaration)) return true;
|
||||||
|
|
||||||
// Private class properties have accessors only in cases when those accessors are non-trivial
|
// Private class properties have accessors only in cases when those accessors are non-trivial
|
||||||
if (Visibilities.isPrivate(descriptor.getVisibility())) {
|
if (Visibilities.isPrivate(descriptor.getVisibility())) {
|
||||||
return !isDefaultAccessor;
|
return !isDefaultAccessor;
|
||||||
|
|||||||
@@ -250,13 +250,14 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public FieldOwnerContext<PackageFragmentDescriptor> intoMultifileClassPart(
|
public MultifileClassPartContext intoMultifileClassPart(
|
||||||
@NotNull PackageFragmentDescriptor descriptor,
|
@NotNull PackageFragmentDescriptor descriptor,
|
||||||
@NotNull Type multifileClassType,
|
@NotNull Type multifileClassType,
|
||||||
@NotNull Type filePartType,
|
@NotNull Type filePartType,
|
||||||
|
@NotNull Type filePartInitializerType,
|
||||||
@NotNull KtFile sourceFile
|
@NotNull KtFile sourceFile
|
||||||
) {
|
) {
|
||||||
return new MultifileClassPartContext(descriptor, this, multifileClassType, filePartType, sourceFile);
|
return new MultifileClassPartContext(descriptor, this, multifileClassType, filePartType, filePartInitializerType, sourceFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
+8
@@ -24,16 +24,19 @@ import org.jetbrains.org.objectweb.asm.Type;
|
|||||||
|
|
||||||
public class MultifileClassPartContext extends MultifileClassContextBase implements DelegatingToPartContext, FacadePartWithSourceFile {
|
public class MultifileClassPartContext extends MultifileClassContextBase implements DelegatingToPartContext, FacadePartWithSourceFile {
|
||||||
private final KtFile sourceFile;
|
private final KtFile sourceFile;
|
||||||
|
private final Type partInitializerType;
|
||||||
|
|
||||||
public MultifileClassPartContext(
|
public MultifileClassPartContext(
|
||||||
PackageFragmentDescriptor descriptor,
|
PackageFragmentDescriptor descriptor,
|
||||||
CodegenContext parent,
|
CodegenContext parent,
|
||||||
Type multifileClassType,
|
Type multifileClassType,
|
||||||
Type filePartType,
|
Type filePartType,
|
||||||
|
Type partInitializerType,
|
||||||
@NotNull KtFile sourceFile
|
@NotNull KtFile sourceFile
|
||||||
) {
|
) {
|
||||||
super(descriptor, parent, multifileClassType, filePartType);
|
super(descriptor, parent, multifileClassType, filePartType);
|
||||||
this.sourceFile = sourceFile;
|
this.sourceFile = sourceFile;
|
||||||
|
this.partInitializerType = partInitializerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -42,6 +45,11 @@ public class MultifileClassPartContext extends MultifileClassContextBase impleme
|
|||||||
return getFilePartType();
|
return getFilePartType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Type getPartInitializerType() {
|
||||||
|
return partInitializerType;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public KtFile getSourceFile() {
|
public KtFile getSourceFile() {
|
||||||
|
|||||||
@@ -66,9 +66,7 @@ import java.util.*;
|
|||||||
|
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
|
||||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.API;
|
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*;
|
||||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker;
|
|
||||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getConstant;
|
|
||||||
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
|
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
|
||||||
|
|
||||||
public class InlineCodegen extends CallGenerator {
|
public class InlineCodegen extends CallGenerator {
|
||||||
@@ -395,6 +393,7 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
|
|
||||||
List<MethodInliner.PointForExternalFinallyBlocks> infos = MethodInliner.processReturns(adapter, labelOwner, true, null);
|
List<MethodInliner.PointForExternalFinallyBlocks> infos = MethodInliner.processReturns(adapter, labelOwner, true, null);
|
||||||
generateAndInsertFinallyBlocks(adapter, infos, ((StackValue.Local)remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index);
|
generateAndInsertFinallyBlocks(adapter, infos, ((StackValue.Local)remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index);
|
||||||
|
removeStaticInitializationTrigger(adapter);
|
||||||
removeFinallyMarkers(adapter);
|
removeFinallyMarkers(adapter);
|
||||||
|
|
||||||
adapter.accept(new MethodBodyVisitor(codegen.v));
|
adapter.accept(new MethodBodyVisitor(codegen.v));
|
||||||
@@ -404,6 +403,21 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void removeStaticInitializationTrigger(MethodNode methodNode) {
|
||||||
|
InsnList insnList = methodNode.instructions;
|
||||||
|
AbstractInsnNode insn = insnList.getFirst();
|
||||||
|
while (insn != null) {
|
||||||
|
if (MultifileClassPartCodegen.isStaticInitTrigger(insn)) {
|
||||||
|
AbstractInsnNode clinitTriggerCall = insn;
|
||||||
|
insn = insn.getNext();
|
||||||
|
insnList.remove(clinitTriggerCall);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
insn = insn.getNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private InlineCallSiteInfo getInlineCallSiteInfo() {
|
private InlineCallSiteInfo getInlineCallSiteInfo() {
|
||||||
MethodContext context = codegen.getContext();
|
MethodContext context = codegen.getContext();
|
||||||
MemberCodegen<?> parentCodegen = codegen.getParentCodegen();
|
MemberCodegen<?> parentCodegen = codegen.getParentCodegen();
|
||||||
|
|||||||
@@ -973,13 +973,20 @@ public class KotlinTypeMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String updateMemberNameIfInternal(@NotNull String name, @NotNull CallableMemberDescriptor descriptor) {
|
private String updateMemberNameIfInternal(@NotNull String name, @NotNull CallableMemberDescriptor descriptor) {
|
||||||
if (descriptor.getContainingDeclaration() instanceof ScriptDescriptor) {
|
if (descriptor.getContainingDeclaration() instanceof ScriptDescriptor) {
|
||||||
//script properties should be public
|
//script properties should be public
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
||||||
|
if (Visibilities.isPrivate(descriptor.getVisibility()) && !(descriptor instanceof ConstructorDescriptor) && !"<clinit>".equals(name)) {
|
||||||
|
KtFile containingFile = DescriptorToSourceUtils.getContainingFile(descriptor);
|
||||||
|
assert containingFile != null : "Private descriptor accessed outside of corresponding file scope: " + descriptor;
|
||||||
|
if (JvmFileClassUtil.isFromMultifileClass(containingFile, descriptor)) {
|
||||||
|
return name + "$" + JvmAbi.sanitizeAsJavaIdentifier(containingFile.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,15 @@ package org.jetbrains.kotlin.fileClasses
|
|||||||
|
|
||||||
import com.intellij.psi.util.CachedValueProvider
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.getImplClassNameForDeserialized
|
import org.jetbrains.kotlin.load.java.descriptors.getImplClassNameForDeserialized
|
||||||
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
|
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
|
||||||
|
|
||||||
object JvmFileClassUtil {
|
object JvmFileClassUtil {
|
||||||
@@ -84,14 +87,21 @@ object JvmFileClassUtil {
|
|||||||
it.calleeExpression?.constructorReferenceExpression?.getReferencedName() == shortName
|
it.calleeExpression?.constructorReferenceExpression?.getReferencedName() == shortName
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic private fun getLiteralStringFromRestrictedConstExpression(argumentExpression: KtExpression?): String? {
|
||||||
private fun getLiteralStringFromRestrictedConstExpression(argumentExpression: KtExpression?): String? {
|
|
||||||
val stringTemplate = argumentExpression as? KtStringTemplateExpression ?: return null
|
val stringTemplate = argumentExpression as? KtStringTemplateExpression ?: return null
|
||||||
val stringTemplateEntries = stringTemplate.entries
|
val stringTemplateEntries = stringTemplate.entries
|
||||||
if (stringTemplateEntries.size != 1) return null
|
if (stringTemplateEntries.size != 1) return null
|
||||||
val singleEntry = stringTemplateEntries[0] as? KtLiteralStringTemplateEntry ?: return null
|
val singleEntry = stringTemplateEntries[0] as? KtLiteralStringTemplateEntry ?: return null
|
||||||
return singleEntry.text
|
return singleEntry.text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic fun isFromMultifileClass(declarationElement: KtElement, descriptor: DeclarationDescriptor): Boolean {
|
||||||
|
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
||||||
|
val fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(declarationElement.getContainingKtFile())
|
||||||
|
return fileClassInfo.withJvmMultifileClass
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ParsedJvmFileClassAnnotations(val name: String, val multipleFiles: Boolean)
|
internal class ParsedJvmFileClassAnnotations(val name: String, val multipleFiles: Boolean)
|
||||||
|
|||||||
+2
-3
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.resolve.jvm.diagnostics
|
|||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
@@ -68,9 +67,9 @@ fun PackagePart(file: KtFile, descriptor: PackageFragmentDescriptor): JvmDeclara
|
|||||||
/**
|
/**
|
||||||
* @param representativeFile one of the files representing this multifile class (will be used for diagnostics)
|
* @param representativeFile one of the files representing this multifile class (will be used for diagnostics)
|
||||||
*/
|
*/
|
||||||
fun MultifileClass(representativeFile: KtFile?, descriptor: PackageFragmentDescriptor, multifileClassFqName: FqName): JvmDeclarationOrigin =
|
fun MultifileClass(representativeFile: KtFile?, descriptor: PackageFragmentDescriptor): JvmDeclarationOrigin =
|
||||||
JvmDeclarationOrigin(MULTIFILE_CLASS, representativeFile, descriptor)
|
JvmDeclarationOrigin(MULTIFILE_CLASS, representativeFile, descriptor)
|
||||||
fun MultifileClassPart(file: KtFile, descriptor: PackageFragmentDescriptor, multifileClassFqName: FqName): JvmDeclarationOrigin =
|
fun MultifileClassPart(file: KtFile, descriptor: PackageFragmentDescriptor): JvmDeclarationOrigin =
|
||||||
JvmDeclarationOrigin(MULTIFILE_CLASS_PART, file, descriptor)
|
JvmDeclarationOrigin(MULTIFILE_CLASS_PART, file, descriptor)
|
||||||
|
|
||||||
fun TraitImpl(element: KtClassOrObject, descriptor: ClassDescriptor): JvmDeclarationOrigin = JvmDeclarationOrigin(INTERFACE_DEFAULT_IMPL, element, descriptor)
|
fun TraitImpl(element: KtClassOrObject, descriptor: ClassDescriptor): JvmDeclarationOrigin = JvmDeclarationOrigin(INTERFACE_DEFAULT_IMPL, element, descriptor)
|
||||||
|
|||||||
+8
@@ -25,3 +25,11 @@ val K: String = "K"
|
|||||||
package a
|
package a
|
||||||
|
|
||||||
val OK: String = O + K
|
val OK: String = O + K
|
||||||
|
|
||||||
|
// FILE: irrelevant.kt
|
||||||
|
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val X: Nothing = throw AssertionError("X should not be initialized")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = (::ok)()
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
fun ok() = "OK"
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = okInline()
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
internal val ok = run { "OK" }
|
||||||
|
|
||||||
|
internal inline fun okInline() =
|
||||||
|
::ok.get()
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = OK.okRef.get()
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
private val ok = run { "OK" }
|
||||||
|
|
||||||
|
object OK {
|
||||||
|
val okRef = ::ok
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = ::OK.get()
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val OK = run { "OK" }
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: Baz.java
|
||||||
|
|
||||||
|
public class Baz {
|
||||||
|
public static String baz() {
|
||||||
|
return Util.foo() + Util.bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: bar.kt
|
||||||
|
|
||||||
|
@file:JvmName("Util")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
public fun bar(): String = barx()
|
||||||
|
|
||||||
|
public fun foox(): String = "O"
|
||||||
|
|
||||||
|
// FILE: foo.kt
|
||||||
|
|
||||||
|
@file:JvmName("Util")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
public fun foo(): String = foox()
|
||||||
|
|
||||||
|
public fun barx(): String = "K"
|
||||||
|
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun box(): String = Baz.baz()
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = OK
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val O = run { "O" }
|
||||||
|
|
||||||
|
// FILE: part2.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
const val K = "K"
|
||||||
|
|
||||||
|
// FILE: part3.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val OK: String = run { O + K }
|
||||||
|
|
||||||
|
// FILE: irrelevantPart.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val X1: Nothing =
|
||||||
|
throw AssertionError("X1 should not be initialized")
|
||||||
|
|
||||||
|
// FILE: reallyIrrelevantPart.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val X2: Nothing =
|
||||||
|
throw AssertionError("X2 should not be initialized")
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = OK
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val OK: String by lazy { "OK" }
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = ok()
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
private val OK = run { "OK" }
|
||||||
|
|
||||||
|
fun ok() = OK
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = OK
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
public val OK = run { "OK" }
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = ok()
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
private fun overlapping() = "oops #1"
|
||||||
|
|
||||||
|
// FILE: part2.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
fun overlapping() = "OK"
|
||||||
|
|
||||||
|
fun ok() = overlapping()
|
||||||
|
|
||||||
|
// FILE: part3.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
private fun overlapping() = "oops #2"
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = overlapping
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
private val overlapping = run { "oops #1" }
|
||||||
|
|
||||||
|
// FILE: part2.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val overlapping = run { "OK" }
|
||||||
|
|
||||||
|
// FILE: part3.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
private val overlapping = run { "oops #2" }
|
||||||
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = J.ok()
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MC") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val O = run { "O" }
|
||||||
|
const val K = "K"
|
||||||
|
|
||||||
|
inline fun ok(): String {
|
||||||
|
return O + K
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: J.java
|
||||||
|
import a.MC;
|
||||||
|
|
||||||
|
public class J {
|
||||||
|
public static String ok() {
|
||||||
|
return MC.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = ok {}
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
val O = run { "O" }
|
||||||
|
const val K = "K"
|
||||||
|
|
||||||
|
inline fun ok(block: () -> Unit): String {
|
||||||
|
block()
|
||||||
|
return O + K
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: box.kt
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun box(): String = OK().ok
|
||||||
|
|
||||||
|
// FILE: part1.kt
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
private val reallyOk = run { "OK" }
|
||||||
|
|
||||||
|
class OK() {
|
||||||
|
val ok = reallyOk
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: 1.kt
|
||||||
|
|
||||||
|
@file:[JvmName("MultifileClass") JvmMultifileClass]
|
||||||
|
package a
|
||||||
|
|
||||||
|
inline fun foo(body: () -> String): String = bar(body())
|
||||||
|
|
||||||
|
public fun bar(x: String): String = x
|
||||||
|
|
||||||
|
inline fun <reified T> inlineOnly(x: Any?): Boolean = x is T
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
|
||||||
|
import a.foo
|
||||||
|
import a.inlineOnly
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
if (!a.inlineOnly<String>("OK")) return "fail 1"
|
||||||
|
return foo { "OK" }
|
||||||
|
}
|
||||||
@@ -3,5 +3,5 @@ public final class test/Foo
|
|||||||
|
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
synthetic final class test/Foo__EmptyMultifileFacadeKt {
|
synthetic final class test/Foo__EmptyMultifileFacadeKt {
|
||||||
private final static method privateOnly(): void
|
private final static method privateOnly$emptyMultifileFacade_kt(): void
|
||||||
}
|
}
|
||||||
Vendored
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS
|
||||||
|
// FILE: bar.kt
|
||||||
|
|
||||||
|
@file:JvmName("Util")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
public fun bar(): String = barx()
|
||||||
|
|
||||||
|
public fun foox(): String = "O"
|
||||||
|
|
||||||
|
// FILE: foo.kt
|
||||||
|
|
||||||
|
@file:JvmName("Util")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
public fun foo(): String = foox()
|
||||||
|
|
||||||
|
public fun barx(): String = "K"
|
||||||
|
|
||||||
|
// @Util.class:
|
||||||
|
// 1 public final class Util extends Util__FooKt
|
||||||
|
// 0 public final static foo\(\)
|
||||||
|
// 0 public final static foox\(\)
|
||||||
|
// 0 public final static bar\(\)
|
||||||
|
// 0 public final static barx\(\)
|
||||||
@@ -8469,6 +8469,99 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/samePartNameDifferentFacades.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/samePartNameDifferentFacades.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/multifileClasses/optimized")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Optimized extends AbstractBlackBoxCodegenTest {
|
||||||
|
public void testAllFilesPresentInOptimized() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/multifileClasses/optimized"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callableRefToFun.kt")
|
||||||
|
public void testCallableRefToFun() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/callableRefToFun.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callableRefToInternalValInline.kt")
|
||||||
|
public void testCallableRefToInternalValInline() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/callableRefToInternalValInline.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callableRefToPrivateVal.kt")
|
||||||
|
public void testCallableRefToPrivateVal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/callableRefToPrivateVal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callableRefToVal.kt")
|
||||||
|
public void testCallableRefToVal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/callableRefToVal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("calls.kt")
|
||||||
|
public void testCalls() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/calls.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("deferredStaticInitialization.kt")
|
||||||
|
public void testDeferredStaticInitialization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/deferredStaticInitialization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("delegatedVal.kt")
|
||||||
|
public void testDelegatedVal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/delegatedVal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("initializePrivateVal.kt")
|
||||||
|
public void testInitializePrivateVal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/initializePrivateVal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("initializePublicVal.kt")
|
||||||
|
public void testInitializePublicVal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/initializePublicVal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("overlappingFuns.kt")
|
||||||
|
public void testOverlappingFuns() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/overlappingFuns.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("overlappingVals.kt")
|
||||||
|
public void testOverlappingVals() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/overlappingVals.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("valAccessFromInlineFunCalledFromJava.kt")
|
||||||
|
public void testValAccessFromInlineFunCalledFromJava() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlineFunCalledFromJava.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("valAccessFromInlinedToDifferentPackage.kt")
|
||||||
|
public void testValAccessFromInlinedToDifferentPackage() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlinedToDifferentPackage.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("valWithAccessor.kt")
|
||||||
|
public void testValWithAccessor() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/valWithAccessor.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/nonLocalReturns")
|
@TestMetadata("compiler/testData/codegen/box/nonLocalReturns")
|
||||||
|
|||||||
@@ -916,6 +916,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFromOptimizedMultifileClass.kt")
|
||||||
|
public void testInlineFromOptimizedMultifileClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOptimizedMultifileClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inlineFromOtherPackage.kt")
|
@TestMetadata("inlineFromOtherPackage.kt")
|
||||||
public void testInlineFromOtherPackage() throws Exception {
|
public void testInlineFromOtherPackage() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOtherPackage.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOtherPackage.kt");
|
||||||
|
|||||||
@@ -1007,6 +1007,21 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/bytecodeText/multifileClasses")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class MultifileClasses extends AbstractBytecodeTextTest {
|
||||||
|
public void testAllFilesPresentInMultifileClasses() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("optimizedMultifileClassFacadeMethods.kt")
|
||||||
|
public void testOptimizedMultifileClassFacadeMethods() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/multifileClasses/optimizedMultifileClassFacadeMethods.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeText/signature")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/signature")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+6
@@ -916,6 +916,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFromOptimizedMultifileClass.kt")
|
||||||
|
public void testInlineFromOptimizedMultifileClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOptimizedMultifileClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inlineFromOtherPackage.kt")
|
@TestMetadata("inlineFromOtherPackage.kt")
|
||||||
public void testInlineFromOtherPackage() throws Exception {
|
public void testInlineFromOtherPackage() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOtherPackage.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOtherPackage.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user