JVM_IR: reconstruct fake overrides after IR deserialization.
We need to keep track of local signatures for deserialized symbols.
This commit is contained in:
committed by
TeamCityServer
parent
a63cc95a2a
commit
7bbc04b6a2
+2
-8
@@ -125,10 +125,7 @@ open class JvmGeneratorExtensionsImpl(
|
|||||||
allowErrorNodes: Boolean
|
allowErrorNodes: Boolean
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val serializedIr = (irClass.source as? KotlinJvmBinarySourceElement)?.binaryClass?.classHeader?.serializedIr ?: return false
|
val serializedIr = (irClass.source as? KotlinJvmBinarySourceElement)?.binaryClass?.classHeader?.serializedIr ?: return false
|
||||||
deserializeClassFromByteArray(
|
deserializeClassFromByteArray(serializedIr, stubGenerator, irClass, allowErrorNodes)
|
||||||
serializedIr, stubGenerator.moduleDescriptor, stubGenerator.irBuiltIns, stubGenerator.symbolTable, parent, allowErrorNodes
|
|
||||||
)
|
|
||||||
ExternalDependenciesGenerator(stubGenerator.symbolTable, listOf(stubGenerator)).generateUnboundSymbolsAsDependencies()
|
|
||||||
val cachedFields = getCachedFields(stubGenerator.irBuiltIns.irFactory, stubGenerator.irBuiltIns.languageVersionSettings)
|
val cachedFields = getCachedFields(stubGenerator.irBuiltIns.irFactory, stubGenerator.irBuiltIns.languageVersionSettings)
|
||||||
irClass.transform(SingletonObjectJvmStaticTransformer(stubGenerator.irBuiltIns, cachedFields), null)
|
irClass.transform(SingletonObjectJvmStaticTransformer(stubGenerator.irBuiltIns, cachedFields), null)
|
||||||
return true
|
return true
|
||||||
@@ -141,10 +138,7 @@ open class JvmGeneratorExtensionsImpl(
|
|||||||
allowErrorNodes: Boolean
|
allowErrorNodes: Boolean
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val serializedIr = (irClass.source as? JvmPackagePartSource)?.knownJvmBinaryClass?.classHeader?.serializedIr ?: return false
|
val serializedIr = (irClass.source as? JvmPackagePartSource)?.knownJvmBinaryClass?.classHeader?.serializedIr ?: return false
|
||||||
deserializeIrFileFromByteArray(
|
deserializeIrFileFromByteArray(serializedIr, stubGenerator, irClass, allowErrorNodes)
|
||||||
serializedIr, stubGenerator.moduleDescriptor, stubGenerator.irBuiltIns, stubGenerator.symbolTable, irClass, allowErrorNodes
|
|
||||||
)
|
|
||||||
ExternalDependenciesGenerator(stubGenerator.symbolTable, listOf(stubGenerator)).generateUnboundSymbolsAsDependencies()
|
|
||||||
val cachedFields = getCachedFields(stubGenerator.irBuiltIns.irFactory, stubGenerator.irBuiltIns.languageVersionSettings)
|
val cachedFields = getCachedFields(stubGenerator.irBuiltIns.irFactory, stubGenerator.irBuiltIns.languageVersionSettings)
|
||||||
irClass.transform(SingletonObjectJvmStaticTransformer(stubGenerator.irBuiltIns, cachedFields), null)
|
irClass.transform(SingletonObjectJvmStaticTransformer(stubGenerator.irBuiltIns, cachedFields), null)
|
||||||
return true
|
return true
|
||||||
|
|||||||
+5
-6
@@ -42,7 +42,7 @@ class FakeOverrideGlobalDeclarationTable(
|
|||||||
fun clear() = table.clear()
|
fun clear() = table.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeOverrideDeclarationTable(
|
open class FakeOverrideDeclarationTable(
|
||||||
mangler: KotlinMangler.IrMangler,
|
mangler: KotlinMangler.IrMangler,
|
||||||
globalTable: FakeOverrideGlobalDeclarationTable = FakeOverrideGlobalDeclarationTable(mangler)
|
globalTable: FakeOverrideGlobalDeclarationTable = FakeOverrideGlobalDeclarationTable(mangler)
|
||||||
) : DeclarationTable(globalTable) {
|
) : DeclarationTable(globalTable) {
|
||||||
@@ -71,15 +71,14 @@ class FakeOverrideBuilder(
|
|||||||
val symbolTable: SymbolTable,
|
val symbolTable: SymbolTable,
|
||||||
mangler: KotlinMangler.IrMangler,
|
mangler: KotlinMangler.IrMangler,
|
||||||
typeSystem: IrTypeSystemContext,
|
typeSystem: IrTypeSystemContext,
|
||||||
val platformSpecificClassFilter: FakeOverrideClassFilter = DefaultFakeOverrideClassFilter
|
val platformSpecificClassFilter: FakeOverrideClassFilter = DefaultFakeOverrideClassFilter,
|
||||||
|
// TODO: The declaration table is needed for the signaturer.
|
||||||
|
private val fakeOverrideDeclarationTable: DeclarationTable = FakeOverrideDeclarationTable(mangler),
|
||||||
) : FakeOverrideBuilderStrategy() {
|
) : FakeOverrideBuilderStrategy() {
|
||||||
private val haveFakeOverrides = mutableSetOf<IrClass>()
|
private val haveFakeOverrides = mutableSetOf<IrClass>()
|
||||||
|
|
||||||
private val irOverridingUtil = IrOverridingUtil(typeSystem, this)
|
private val irOverridingUtil = IrOverridingUtil(typeSystem, this)
|
||||||
|
|
||||||
// TODO: The declaration table is needed for the signaturer.
|
|
||||||
private val fakeOverrideDeclarationTable = FakeOverrideDeclarationTable(mangler)
|
|
||||||
|
|
||||||
// private class CompatibilityMode(val oldSignatures: Boolean)
|
// private class CompatibilityMode(val oldSignatures: Boolean)
|
||||||
|
|
||||||
private val fakeOverrideCandidates = mutableMapOf<IrClass, CompatibilityMode>()
|
private val fakeOverrideCandidates = mutableMapOf<IrClass, CompatibilityMode>()
|
||||||
@@ -169,7 +168,7 @@ class FakeOverrideBuilder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun provideFakeOverrides(klass: IrClass, compatibleMode: CompatibilityMode) {
|
fun provideFakeOverrides(klass: IrClass, compatibleMode: CompatibilityMode) {
|
||||||
buildFakeOverrideChainsForClass(klass, compatibleMode)
|
buildFakeOverrideChainsForClass(klass, compatibleMode)
|
||||||
irOverridingUtil.clear()
|
irOverridingUtil.clear()
|
||||||
haveFakeOverrides.add(klass)
|
haveFakeOverrides.add(klass)
|
||||||
|
|||||||
-3
@@ -26,9 +26,6 @@ class JvmIrSerializerSession(
|
|||||||
|
|
||||||
// Usage protocol: construct an instance, call only one of `serializeIrFile()` and `serializeTopLevelClass()` only once.
|
// Usage protocol: construct an instance, call only one of `serializeIrFile()` and `serializeTopLevelClass()` only once.
|
||||||
|
|
||||||
// We do not reconstruct fake overrides upon deserialization (yet).
|
|
||||||
override fun backendSpecificSerializeAllMembers(irClass: IrClass): Boolean = true
|
|
||||||
|
|
||||||
fun serializeJvmIrFile(irFile: IrFile): JvmIr.JvmIrFile {
|
fun serializeJvmIrFile(irFile: IrFile): JvmIr.JvmIrFile {
|
||||||
val proto = JvmIr.JvmIrFile.newBuilder()
|
val proto = JvmIr.JvmIrFile.newBuilder()
|
||||||
|
|
||||||
|
|||||||
+80
-17
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.jvm.serialization
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.overrides.DefaultFakeOverrideClassFilter
|
import org.jetbrains.kotlin.backend.common.overrides.DefaultFakeOverrideClassFilter
|
||||||
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
|
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideDeclarationTable
|
||||||
import org.jetbrains.kotlin.backend.common.overrides.FileLocalAwareLinker
|
import org.jetbrains.kotlin.backend.common.overrides.FileLocalAwareLinker
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinder
|
import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinder
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.IrDeclarationDeserializer
|
import org.jetbrains.kotlin.backend.common.serialization.IrDeclarationDeserializer
|
||||||
@@ -16,24 +17,29 @@ import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolD
|
|||||||
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
|
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
|
||||||
import org.jetbrains.kotlin.backend.jvm.serialization.proto.JvmIr
|
import org.jetbrains.kotlin.backend.jvm.serialization.proto.JvmIr
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
|
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
|
||||||
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler
|
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.lazy.LazyIrFactory
|
import org.jetbrains.kotlin.ir.declarations.lazy.LazyIrFactory
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.symbols.*
|
import org.jetbrains.kotlin.ir.symbols.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
|
||||||
|
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
|
||||||
import org.jetbrains.kotlin.ir.util.IdSignature
|
import org.jetbrains.kotlin.ir.util.IdSignature
|
||||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.protobuf.ByteString
|
import org.jetbrains.kotlin.protobuf.ByteString
|
||||||
|
|
||||||
fun deserializeClassFromByteArray(
|
fun deserializeClassFromByteArray(
|
||||||
byteArray: ByteArray,
|
byteArray: ByteArray,
|
||||||
moduleDescriptor: ModuleDescriptor,
|
stubGenerator: DeclarationStubGenerator,
|
||||||
irBuiltIns: IrBuiltIns,
|
irClass: IrClass,
|
||||||
symbolTable: SymbolTable,
|
|
||||||
parent: IrDeclarationParent,
|
|
||||||
allowErrorNodes: Boolean,
|
allowErrorNodes: Boolean,
|
||||||
) {
|
) {
|
||||||
|
val irBuiltIns = stubGenerator.irBuiltIns
|
||||||
|
val symbolTable = stubGenerator.symbolTable
|
||||||
val irProto = JvmIr.JvmIrClass.parseFrom(byteArray)
|
val irProto = JvmIr.JvmIrClass.parseFrom(byteArray)
|
||||||
val irLibraryFile = IrLibraryFileFromAnnotation(
|
val irLibraryFile = IrLibraryFileFromAnnotation(
|
||||||
irProto.auxTables.typeList,
|
irProto.auxTables.typeList,
|
||||||
@@ -42,7 +48,11 @@ fun deserializeClassFromByteArray(
|
|||||||
irProto.auxTables.bodyList
|
irProto.auxTables.bodyList
|
||||||
)
|
)
|
||||||
val descriptorFinder =
|
val descriptorFinder =
|
||||||
DescriptorByIdSignatureFinder(moduleDescriptor, JvmDescriptorMangler(null), DescriptorByIdSignatureFinder.LookupMode.MODULE_WITH_DEPENDENCIES)
|
DescriptorByIdSignatureFinder(
|
||||||
|
stubGenerator.moduleDescriptor,
|
||||||
|
JvmDescriptorMangler(null),
|
||||||
|
DescriptorByIdSignatureFinder.LookupMode.MODULE_WITH_DEPENDENCIES
|
||||||
|
)
|
||||||
val symbolDeserializer = IrSymbolDeserializer(
|
val symbolDeserializer = IrSymbolDeserializer(
|
||||||
symbolTable,
|
symbolTable,
|
||||||
irLibraryFile,
|
irLibraryFile,
|
||||||
@@ -57,26 +67,29 @@ fun deserializeClassFromByteArray(
|
|||||||
val lazyIrFactory = LazyIrFactory(irBuiltIns.irFactory)
|
val lazyIrFactory = LazyIrFactory(irBuiltIns.irFactory)
|
||||||
|
|
||||||
val deserializer = IrDeclarationDeserializer(
|
val deserializer = IrDeclarationDeserializer(
|
||||||
irBuiltIns, symbolTable, lazyIrFactory, irLibraryFile, parent,
|
irBuiltIns, symbolTable, lazyIrFactory, irLibraryFile, irClass.parent,
|
||||||
allowErrorNodes = allowErrorNodes,
|
allowErrorNodes = allowErrorNodes,
|
||||||
deserializeInlineFunctions = true,
|
deserializeInlineFunctions = true,
|
||||||
deserializeBodies = true,
|
deserializeBodies = true,
|
||||||
symbolDeserializer,
|
symbolDeserializer,
|
||||||
DefaultFakeOverrideClassFilter,
|
DefaultFakeOverrideClassFilter,
|
||||||
makeEmptyFakeOverrideBuilder(symbolTable, irBuiltIns)
|
makeSimpleFakeOverrideBuilder(symbolTable, irBuiltIns, symbolDeserializer)
|
||||||
)
|
)
|
||||||
|
|
||||||
deserializer.deserializeIrClass(irProto.irClass)
|
deserializer.deserializeIrClass(irProto.irClass)
|
||||||
|
|
||||||
|
ExternalDependenciesGenerator(stubGenerator.symbolTable, listOf(stubGenerator)).generateUnboundSymbolsAsDependencies()
|
||||||
|
buildFakeOverridesForLocalClasses(stubGenerator.symbolTable, stubGenerator.irBuiltIns, symbolDeserializer, irClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deserializeIrFileFromByteArray(
|
fun deserializeIrFileFromByteArray(
|
||||||
byteArray: ByteArray,
|
byteArray: ByteArray,
|
||||||
moduleDescriptor: ModuleDescriptor,
|
stubGenerator: DeclarationStubGenerator,
|
||||||
irBuiltIns: IrBuiltIns,
|
|
||||||
symbolTable: SymbolTable,
|
|
||||||
facadeClass: IrClass,
|
facadeClass: IrClass,
|
||||||
allowErrorNodes: Boolean,
|
allowErrorNodes: Boolean,
|
||||||
) {
|
) {
|
||||||
|
val irBuiltIns = stubGenerator.irBuiltIns
|
||||||
|
val symbolTable = stubGenerator.symbolTable
|
||||||
val irProto = JvmIr.JvmIrFile.parseFrom(byteArray)
|
val irProto = JvmIr.JvmIrFile.parseFrom(byteArray)
|
||||||
val irLibraryFile = IrLibraryFileFromAnnotation(
|
val irLibraryFile = IrLibraryFileFromAnnotation(
|
||||||
irProto.auxTables.typeList,
|
irProto.auxTables.typeList,
|
||||||
@@ -85,7 +98,11 @@ fun deserializeIrFileFromByteArray(
|
|||||||
irProto.auxTables.bodyList
|
irProto.auxTables.bodyList
|
||||||
)
|
)
|
||||||
val descriptorFinder =
|
val descriptorFinder =
|
||||||
DescriptorByIdSignatureFinder(moduleDescriptor, JvmDescriptorMangler(null), DescriptorByIdSignatureFinder.LookupMode.MODULE_WITH_DEPENDENCIES)
|
DescriptorByIdSignatureFinder(
|
||||||
|
stubGenerator.moduleDescriptor,
|
||||||
|
JvmDescriptorMangler(null),
|
||||||
|
DescriptorByIdSignatureFinder.LookupMode.MODULE_WITH_DEPENDENCIES
|
||||||
|
)
|
||||||
val symbolDeserializer = IrSymbolDeserializer(
|
val symbolDeserializer = IrSymbolDeserializer(
|
||||||
symbolTable,
|
symbolTable,
|
||||||
irLibraryFile,
|
irLibraryFile,
|
||||||
@@ -99,6 +116,8 @@ fun deserializeIrFileFromByteArray(
|
|||||||
|
|
||||||
val lazyIrFactory = LazyIrFactory(irBuiltIns.irFactory)
|
val lazyIrFactory = LazyIrFactory(irBuiltIns.irFactory)
|
||||||
|
|
||||||
|
val fakeOverrideBuilder = makeSimpleFakeOverrideBuilder(symbolTable, irBuiltIns, symbolDeserializer)
|
||||||
|
|
||||||
val deserializer = IrDeclarationDeserializer(
|
val deserializer = IrDeclarationDeserializer(
|
||||||
irBuiltIns, symbolTable, lazyIrFactory, irLibraryFile, facadeClass,
|
irBuiltIns, symbolTable, lazyIrFactory, irLibraryFile, facadeClass,
|
||||||
allowErrorNodes = allowErrorNodes,
|
allowErrorNodes = allowErrorNodes,
|
||||||
@@ -106,11 +125,14 @@ fun deserializeIrFileFromByteArray(
|
|||||||
deserializeBodies = true,
|
deserializeBodies = true,
|
||||||
symbolDeserializer,
|
symbolDeserializer,
|
||||||
DefaultFakeOverrideClassFilter,
|
DefaultFakeOverrideClassFilter,
|
||||||
makeEmptyFakeOverrideBuilder(symbolTable, irBuiltIns)
|
fakeOverrideBuilder
|
||||||
)
|
)
|
||||||
for (declarationProto in irProto.declarationList) {
|
for (declarationProto in irProto.declarationList) {
|
||||||
deserializer.deserializeDeclaration(declarationProto)
|
deserializer.deserializeDeclaration(declarationProto)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExternalDependenciesGenerator(stubGenerator.symbolTable, listOf(stubGenerator)).generateUnboundSymbolsAsDependencies()
|
||||||
|
buildFakeOverridesForLocalClasses(stubGenerator.symbolTable, stubGenerator.irBuiltIns, symbolDeserializer, facadeClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class IrLibraryFileFromAnnotation(
|
private class IrLibraryFileFromAnnotation(
|
||||||
@@ -164,17 +186,58 @@ private fun referencePublicSymbol(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement properly
|
// TODO: implement properly
|
||||||
private fun makeEmptyFakeOverrideBuilder(symbolTable: SymbolTable, irBuiltIns: IrBuiltIns): FakeOverrideBuilder {
|
fun makeSimpleFakeOverrideBuilder(
|
||||||
|
symbolTable: SymbolTable,
|
||||||
|
irBuiltIns: IrBuiltIns,
|
||||||
|
symbolDeserializer: IrSymbolDeserializer
|
||||||
|
): FakeOverrideBuilder {
|
||||||
|
val signatureSerializer = IdSignatureSerializer(JvmIrMangler)
|
||||||
return FakeOverrideBuilder(
|
return FakeOverrideBuilder(
|
||||||
object : FileLocalAwareLinker {
|
object : FileLocalAwareLinker {
|
||||||
override fun tryReferencingPropertyByLocalSignature(parent: IrDeclaration, idSignature: IdSignature): IrPropertySymbol? = null
|
override fun tryReferencingPropertyByLocalSignature(parent: IrDeclaration, idSignature: IdSignature): IrPropertySymbol =
|
||||||
|
symbolDeserializer.referencePropertyByLocalSignature(idSignature)
|
||||||
|
|
||||||
override fun tryReferencingSimpleFunctionByLocalSignature(
|
override fun tryReferencingSimpleFunctionByLocalSignature(
|
||||||
parent: IrDeclaration, idSignature: IdSignature
|
parent: IrDeclaration, idSignature: IdSignature
|
||||||
): IrSimpleFunctionSymbol? = null
|
): IrSimpleFunctionSymbol =
|
||||||
|
symbolDeserializer.referenceSimpleFunctionByLocalSignature(idSignature)
|
||||||
},
|
},
|
||||||
symbolTable,
|
symbolTable,
|
||||||
IdSignatureSerializer(JvmIrMangler),
|
signatureSerializer,
|
||||||
irBuiltIns
|
irBuiltIns,
|
||||||
|
fakeOverrideDeclarationTable = PrePopulatedDeclarationTable(symbolDeserializer.deserializedSymbols, signatureSerializer)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildFakeOverridesForLocalClasses(
|
||||||
|
symbolTable: SymbolTable,
|
||||||
|
irBuiltIns: IrBuiltIns,
|
||||||
|
symbolDeserializer: IrSymbolDeserializer,
|
||||||
|
toplevel: IrClass
|
||||||
|
) {
|
||||||
|
val builder = makeSimpleFakeOverrideBuilder(symbolTable, irBuiltIns, symbolDeserializer)
|
||||||
|
toplevel.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitClass(declaration: IrClass) {
|
||||||
|
if (declaration.visibility == DescriptorVisibilities.LOCAL) {
|
||||||
|
builder.provideFakeOverrides(declaration)
|
||||||
|
}
|
||||||
|
super.visitClass(declaration)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
class PrePopulatedDeclarationTable(
|
||||||
|
sig2symbol: Map<IdSignature, IrSymbol>,
|
||||||
|
signatureSerializer: IdSignatureSerializer
|
||||||
|
) : FakeOverrideDeclarationTable(signatureSerializer) {
|
||||||
|
private val symbol2Sig = sig2symbol.entries.associate { (x, y) -> y to x }
|
||||||
|
|
||||||
|
override fun tryComputeBackendSpecificSignature(declaration: IrDeclaration): IdSignature? {
|
||||||
|
symbol2Sig[declaration.symbol]?.let { return it }
|
||||||
|
return super.tryComputeBackendSpecificSignature(declaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
|
|
||||||
open class C {
|
open class C {
|
||||||
fun f() = "OK"
|
fun o() = "O"
|
||||||
|
val k = "K"
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun inlineFun(): String {
|
inline fun inlineFun(): String {
|
||||||
val cc = object : C() {}
|
val cc = object : C() {}
|
||||||
return cc.f()
|
return cc.o() + cc.k
|
||||||
}
|
}
|
||||||
|
|
||||||
// FILE: 2.kt
|
// FILE: 2.kt
|
||||||
|
|||||||
Reference in New Issue
Block a user