diff --git a/backend.native/build.gradle b/backend.native/build.gradle index bc134fada37..d67b359cb1c 100644 --- a/backend.native/build.gradle +++ b/backend.native/build.gradle @@ -13,6 +13,7 @@ buildscript { } +String protobufVersion = '2.6.1' apply plugin: "com.google.protobuf" apply plugin: 'java' apply plugin: 'kotlin' @@ -45,7 +46,10 @@ allprojects { sourceSets { compiler { proto.srcDir 'compiler/ir/backend.native/src/' - java.srcDirs 'compiler/ir/backend.native/src/', 'build/generated/source/proto/compiler/java/' + java { + srcDir 'compiler/ir/backend.native/src/' + srcDir 'build/renamed/source/proto/compiler/java' + } kotlin.srcDir 'compiler/ir/backend.native/src/' } cli_bc { @@ -58,12 +62,28 @@ sourceSets { } } -// The protobuf plugin specifies this dependency for java by itself, -// but not for Kotlin. compileCompilerKotlin { + dependsOn('renamePackage') + // The protobuf plugin specifies this dependency for java by itself, + // but not for Kotlin. dependsOn('generateCompilerProto') } +compileCompilerJava { + doFirst { + delete 'build/generated' + } + dependsOn('renamePackage') +} + +task renamePackage(type: Copy) { + dependsOn('generateCompilerProto') + from 'build/generated/source/proto/compiler/java' + into 'build/renamed/source/proto/compiler/java' + filter {line -> line.replaceAll("com.google.protobuf", "org.jetbrains.kotlin.protobuf")} + outputs.files('build/renamed') +} + List llvmLinkerOpts() { // TODO: cannot use llvm-config because it can be inaccessible during project configuration final List libs = [ @@ -145,7 +165,7 @@ configurations { dependencies { - compilerCompile "com.google.protobuf:protobuf-java:3.0.0" + compilerCompile "com.google.protobuf:protobuf-java:${protobufVersion}" compilerCompile kotlinCompilerModule compilerCompile kotlinNativeInterop['llvm'].configuration @@ -239,7 +259,7 @@ task jars(type: Jar) { task external_jars(type: Copy) { from configurations.compilerCompile { - include 'protobuf-java-3.0.0.jar' + include "protobuf-java-${protobufVersion}.jar" include 'kotlin-compiler*.jar' rename 'kotlin-compiler(.*).jar', 'kotlin-compiler.jar' @@ -250,7 +270,7 @@ task external_jars(type: Copy) { protobuf { protoc { - artifact = 'com.google.protobuf:protoc:3.0.0' + artifact = "com.google.protobuf:protoc:${protobufVersion}" } } diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 1ad31cc19a9..67efccb3a68 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -56,14 +56,16 @@ class K2Native : CLICompiler() { configuration.addKotlinSourceRoots(arguments.freeArgs) - configuration.put(CommonConfigurationKeys.MODULE_NAME, "main") + // This is a decision we could change + configuration.put(CommonConfigurationKeys.MODULE_NAME, arguments.outputFile) - val libraries = arguments.libraries.toNonNullList() configuration.put(KonanConfigKeys.LIBRARY_FILES, arguments.libraries.toNonNullList()) configuration.put(KonanConfigKeys.RUNTIME_FILE, arguments.runtimeFile) configuration.put(KonanConfigKeys.OUTPUT_FILE, arguments.outputFile) + configuration.put(KonanConfigKeys.ABI_VERSION, 1) + configuration.put(KonanConfigKeys.PRINT_IR, arguments.printIr) configuration.put(KonanConfigKeys.PRINT_DESCRIPTORS, arguments.printDescriptors) configuration.put(KonanConfigKeys.PRINT_BITCODE, arguments.printBitCode) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 44edae2f604..857557b188d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -1,20 +1,20 @@ package org.jetbrains.kotlin.backend.konan import llvm.* +import org.jetbrains.kotlin.backend.konan.ir.Ir +import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex import org.jetbrains.kotlin.backend.konan.llvm.Runtime import org.jetbrains.kotlin.backend.konan.llvm.getFunctionType import org.jetbrains.kotlin.backend.konan.llvm.StaticData import org.jetbrains.kotlin.backend.konan.llvm.Llvm import org.jetbrains.kotlin.backend.konan.KonanBackendContext import org.jetbrains.kotlin.backend.konan.KonanPhase -import org.jetbrains.kotlin.backend.konan.ModuleIndex import org.jetbrains.kotlin.backend.konan.KonanConfig import org.jetbrains.kotlin.backend.konan.KonanConfigKeys import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.backend.konan.DeepPrintVisitor -import org.jetbrains.kotlin.backend.konan.PrintVisitor +import org.jetbrains.kotlin.backend.konan.descriptors.deepPrint import org.jetbrains.kotlin.backend.konan.llvm.verifyModule import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor import java.lang.System.out @@ -32,10 +32,10 @@ internal final class Context(val config: KonanConfig, } field = module!! - moduleIndex = ModuleIndex(irModule!!) + ir = Ir(this, module) } - lateinit var moduleIndex: ModuleIndex + lateinit var ir: Ir var llvmModule: LLVMModuleRef? = null set(module: LLVMModuleRef?) { @@ -61,7 +61,7 @@ internal final class Context(val config: KonanConfig, fun printDescriptors() { separator("Descriptors after: ${phase?.description}") - moduleDescriptor!!.accept(DeepPrintVisitor(PrintVisitor()), 0) + moduleDescriptor!!.deepPrint() } fun verifyIr() { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 5c889b81600..0dbcffd5e88 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.backend.konan import com.intellij.openapi.project.Project import org.jetbrains.kotlin.backend.konan.KonanPlatform +import org.jetbrains.kotlin.backend.konan.llvm.loadMetadata import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl @@ -9,61 +10,41 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration -import org.jetbrains.kotlin.serialization.js.JsModuleDescriptor -import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil -import org.jetbrains.kotlin.serialization.js.ModuleKind -import org.jetbrains.kotlin.storage.LockBasedStorageManager - -import org.jetbrains.kotlin.backend.konan.llvm.KotlinKonanMetadata -import org.jetbrains.kotlin.backend.konan.llvm.KotlinKonanMetadataUtils +import java.io.File class KonanConfig(val project: Project, val configuration: CompilerConfiguration) { - private val storageManager = LockBasedStorageManager() - private val libraries = configuration.getList(KonanConfigKeys.LIBRARY_FILES) - private val metadata = KotlinKonanMetadataUtils.loadLibMetadata(libraries) + private val loadedDescriptors = loadLibMetadata(libraries) val moduleId: String get() = configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME) - val moduleKind: ModuleKind - get() = configuration.get(KonanConfigKeys.MODULE_KIND)!! + fun loadLibMetadata(libraries: List): List { - // We reuse JsModuleDescriptor for serialization for now, as we haven't got one for Konan yet. - internal val moduleDescriptors: MutableList> by lazy { + val allMetadata = mutableListOf() - val jsDescriptors = mutableListOf>() - val descriptors = mutableListOf() + for (path in libraries) { + val filePath = File(path) + if (!filePath.exists()) { + error("Path '" + path + "' does not exist"); + } - for (metadataEntry in metadata) { - val descriptor = createModuleDescriptor(metadataEntry) - jsDescriptors.add(descriptor) - descriptors.add(descriptor.data) + val moduleDescriptor = loadMetadata(configuration, filePath); + + allMetadata.add(moduleDescriptor); } - - for (module in jsDescriptors) { - setDependencies(module.data, descriptors) - } - - jsDescriptors + return allMetadata; } - private fun createModuleDescriptor(metadata: KotlinKonanMetadata): JsModuleDescriptor { + internal val moduleDescriptors: List by lazy { + for (module in loadedDescriptors) { + // Yes, just to all of them. + setDependencies(module, loadedDescriptors) + } - val moduleDescriptor = ModuleDescriptorImpl( - Name.special("<" + metadata.moduleName + ">"), storageManager, KonanPlatform.builtIns) - - val rawDescriptor = KotlinJavascriptSerializationUtil.readModule( - metadata.body, storageManager, moduleDescriptor, CompilerDeserializationConfiguration( - configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT))) - - val provider = rawDescriptor.data - moduleDescriptor.initialize(provider ?: PackageFragmentProvider.Empty) - - return rawDescriptor.copy(moduleDescriptor) + loadedDescriptors } companion object { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index e333096667e..784480ad96b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -11,6 +11,8 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("runtime file path"); val OUTPUT_FILE: CompilerConfigurationKey = CompilerConfigurationKey.create("output file path"); + val ABI_VERSION: CompilerConfigurationKey + = CompilerConfigurationKey.create("current abi version"); val SOURCE_MAP: CompilerConfigurationKey> = CompilerConfigurationKey.create("generate source map"); diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt index 395bce9ae35..676ac2c42d1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt @@ -40,7 +40,7 @@ object TopDownAnalyzerFacadeForKonan { // Make sure the compiler produced BuiltIns module comes in last context.setDependencies( listOf(context.module) + - config.moduleDescriptors.map { it.data } + + config.moduleDescriptors + listOf(compilerBuiltInsModule) ) return analyzeFilesWithGivenTrace(files, BindingTraceContext(), context, config) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DeepPrintVisitor.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepPrintVisitor.kt similarity index 98% rename from backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DeepPrintVisitor.kt rename to backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepPrintVisitor.kt index 49d1707e00f..d482bc8b591 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DeepPrintVisitor.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepPrintVisitor.kt @@ -1,4 +1,4 @@ -package org.jetbrains.kotlin.backend.konan +package org.jetbrains.kotlin.backend.konan.descriptors import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.renderer.* diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DeepVisitor.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepVisitor.kt similarity index 98% rename from backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DeepVisitor.kt rename to backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepVisitor.kt index 50561be0eec..e5c66d8d0f4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DeepVisitor.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepVisitor.kt @@ -1,4 +1,4 @@ -package org.jetbrains.kotlin.backend.konan +package org.jetbrains.kotlin.backend.konan.descriptors import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.name.FqName diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt index 788d54175bb..447323e42fd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt @@ -1,9 +1,11 @@ package org.jetbrains.kotlin.backend.konan.descriptors import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.KotlinType fun ClassDescriptor?.getter2Descriptor(methodName: Name) = this?.let { @@ -26,4 +28,18 @@ fun ClassDescriptor?.signature2Descriptor(methodName: Name, signature:Array() + + val moduleIndex = ModuleIndex(irModule!!) +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ModuleIndex.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt similarity index 96% rename from backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ModuleIndex.kt rename to backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt index b03dd36da08..4cd7f713cd1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ModuleIndex.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt @@ -1,4 +1,4 @@ -package org.jetbrains.kotlin.backend.konan +package org.jetbrains.kotlin.backend.konan.ir import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrClass diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryLinkdata.proto b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryLinkdata.proto deleted file mode 100644 index 158243a0676..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryLinkdata.proto +++ /dev/null @@ -1,17 +0,0 @@ - -syntax="proto2"; -package org.jetbrains.kotlin.backend.konan.llvm; - -option java_outer_classname = "BinaryLinkdata"; - -message TypeLinkdata { - required string name = 1; -} - -message FunLinkdata { - required string hash = 1; - required string name = 2; - required TypeLinkdata returnType = 3; - repeated TypeLinkdata arg = 4; -} - diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 88d80950bb4..8c885b6c77e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -5,11 +5,13 @@ import llvm.* import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.hash.GlobalHash import org.jetbrains.kotlin.backend.konan.KonanConfigKeys +import org.jetbrains.kotlin.backend.konan.descriptors.backingField import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.classId @@ -55,11 +57,35 @@ internal interface ContextUtils { */ val ClassDescriptor.declaredFields: List get() { - // TODO: do not use IR to get fields - val irClass = context.moduleIndex.classes[this.classId] ?: - throw IllegalArgumentException("Class ${this.fqNameSafe} is not found in current module") + // TODO: Here's what is going on here: + // The existence of a backing field for a property is only described in the IR, + // but not in the property descriptor. + // That works, while we process the IR, but not for deserialized descriptors. + // + // So to have something in deserialized descriptors, + // while we still see the IR, we mark the property with an annotation. + // + // We could apply the annotation during IR rewite, but we still are not + // that far in the rewriting infrastructure. So we postpone + // the annotation until the serializer. + // + // In this function we check the presence of the backing filed + // two ways: first we check IR, then we check the annotation. - return irClass.declarations.mapNotNull { (it as? IrProperty)?.backingField?.descriptor } + val irClass = context.ir.moduleIndex.classes[this.classId] + if (irClass != null) { + val irProperties = irClass.declarations + + return irProperties.mapNotNull { + (it as? IrProperty)?.backingField?.descriptor + } + } else { + val properties = this.unsubstitutedMemberScope. + getContributedDescriptors(). + filterIsInstance() + + return properties.mapNotNull{ it.backingField } + } } /** @@ -165,7 +191,6 @@ internal interface ContextUtils { get() = this.toString().localHash } - internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { private fun importFunction(name: String, otherModule: LLVMModuleRef): LLVMValueRef { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/HashUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/HashUtils.kt index 59f47e1b49c..8288d4e2abe 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/HashUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/HashUtils.kt @@ -21,15 +21,28 @@ internal fun globalHash(data: ByteArray, retValPlacement: NativePlacement): Glob return res } -internal fun base64Encode(data: ByteArray): String { +public fun base64Encode(data: ByteArray): String { memScoped { val resultSize = 4 * data.size / 3 + 3 + 1 val result = allocArray(resultSize) val bytes = allocArrayOf(data) - Base64Encode(bytes.ptr, data.size, result.ptr, resultSize) + EncodeBase64(bytes.ptr, data.size, result.ptr, resultSize) // TODO: any better way to do that without two copies? return CString.fromArray(result).toString() } } +public fun base64Decode(encoded: String): ByteArray { + memScoped { + val bufferSize: Int = 3 * encoded.length / 4 + val result = allocArray(bufferSize) + val resultSize = allocArray(1) + resultSize[0].value = bufferSize + val errorCode = DecodeBase64(encoded, encoded.length, result[0].ptr, resultSize[0].ptr) + if (errorCode != 0) throw Error("Non-zero exit code of DecodeBase64: ${errorCode}") + val realSize = resultSize[0].value!! + return ByteArray(realSize, {i -> result[i].value!!}) + } +} + internal class LocalHash(val value: Long) : ConstValue by Int64(value) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 17be172e9ba..a682fd233f3 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -103,9 +103,15 @@ internal class MetadatorVisitor(val context: Context) : IrElementVisitorVoid { element.acceptChildrenVoid(this) } + override fun visitProperty(declaration: IrProperty) { + declaration.acceptChildrenVoid(this) + metadator.property(declaration) + + } + override fun visitModuleFragment(module: IrModuleFragment) { module.acceptChildrenVoid(this) - metadator.endModule(module) + metadator.endModule(module) } } @@ -399,7 +405,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid LLVMSetInitializer(objectPtr, codegen.kNullObjHeaderPtr) } - val irOfCurrentClass = context.moduleIndex.classes[classDescriptor.classId] + val irOfCurrentClass = context.ir.moduleIndex.classes[classDescriptor.classId] irOfCurrentClass!!.acceptChildrenVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinKonanMetadataUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinKonanMetadataUtils.kt deleted file mode 100644 index 75bf3225ba5..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinKonanMetadataUtils.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - // this file has been adapted from the corresponding piece of Javascript compiler - -package org.jetbrains.kotlin.backend.konan.llvm - -import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion -import java.io.File -import javax.xml.bind.DatatypeConverter.parseBase64Binary -import javax.xml.bind.DatatypeConverter.printBase64Binary - - -class KotlinKonanMetadata(val abiVersion: Int, val moduleName: String, val body: ByteArray) { - val isAbiVersionCompatible: Boolean = KotlinKonanMetadataUtils.isAbiVersionCompatible(abiVersion) -} - -class JsBinaryVersion(vararg numbers: Int) : BinaryVersion(*numbers) { - override fun isCompatible() = this.isCompatibleTo(INSTANCE) - - companion object { - val INSTANCE = JsBinaryVersion(0, 5, 0) - - val INVALID_VERSION = JsBinaryVersion() - } -} - -object KotlinKonanMetadataUtils { - private val KOTLIN_JAVASCRIPT_METHOD_NAME = "kotlin_module_metadata" - private val KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN = "\\.kotlin_module_metadata\\(".toPattern() - - /** - * Matches string like .kotlin_module_metadata(, , ) - */ - private val METADATA_PATTERN = "(?m)\\w+\\.$KOTLIN_JAVASCRIPT_METHOD_NAME\\((\\d+),\\s*(['\"])([^'\"]*)\\2,\\s*(['\"])([^'\"]*)\\4\\)".toPattern() - - val ABI_VERSION: Int = JsBinaryVersion.INSTANCE.minor - - fun isAbiVersionCompatible(abiVersion: Int): Boolean = abiVersion == ABI_VERSION - - fun hasMetadata(text: String): Boolean = - KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN.matcher(text).find() && METADATA_PATTERN.matcher(text).find() - - fun formatMetadataAsString(moduleName: String, content: ByteArray): String = - "// Kotlin.$KOTLIN_JAVASCRIPT_METHOD_NAME($ABI_VERSION, \"$moduleName\", \"${printBase64Binary(content)}\");\n" - - fun loadMetadata(file: File): List { - assert(file.exists()) { "Library $file not found" } - val metadataList = arrayListOf() - val content = readModuleMetadata(file) - parseMetadata(content, metadataList) - - return metadataList - } - - fun loadMetadata(path: String): List = loadMetadata(File(path)) - - fun parseMetadata(text: String, metadataList: MutableList) { - // Check for literal pattern first in order to reduce time for large files without metadata - if (!KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN.matcher(text).find()) return - - val matcher = METADATA_PATTERN.matcher(text) - while (matcher.find()) { - val abiVersion = matcher.group(1).toInt() - val moduleName = matcher.group(3) - val data = matcher.group(5) - metadataList.add(KotlinKonanMetadata(abiVersion, moduleName, parseBase64Binary(data))) - } - } - - fun loadLibMetadata(libraries: List): List { - - val allMetadata = mutableListOf() - - for (path in libraries) { - val filePath = File(path) - if (!filePath.exists()) { - // TODO: should we throw here? - println("Path '" + path + "' does not exist"); - } - - val metadataList = loadMetadata(filePath); - - if (metadataList.isEmpty()) { - // TODO: should we throw here? - println("'" + path + "' is not a valid Kotlin Native library"); - } - - for (metadata in metadataList) { - if (!metadata.isAbiVersionCompatible) { - // TODO: should we throw here? - println("File '" + path + "' was compiled with an incompatible version of Kotlin. " + - "Its ABI version is " + metadata.abiVersion + - ", expected version is " + ABI_VERSION); - } - } - - allMetadata.addAll(metadataList); - } - return allMetadata; - } -} - diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/MetadataGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/MetadataGenerator.kt index 8e63f74c56b..c6a192832e0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/MetadataGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/MetadataGenerator.kt @@ -2,28 +2,52 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.backend.konan.ir.Ir import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.llvm.BinaryLinkdata.* -import org.jetbrains.kotlin.types.KotlinType - -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.backend.konan.serialization.* +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.backend.konan.KonanConfigKeys +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl -import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration -import org.jetbrains.kotlin.serialization.js.* -import org.jetbrains.kotlin.utils.* import java.io.* -fun readModuleMetadata(file: File): String { +fun loadMetadata(configuration: CompilerConfiguration, file: File): ModuleDescriptorImpl { val reader = MetadataReader(file) + var metadataAsString: String? = null + var moduleName: String? = null + val currentAbiVersion = configuration.get(KonanConfigKeys.ABI_VERSION) + reader.use { - val metadataNode = reader.namedMetadataNode("kmetadata", 0); - val stringNode = reader.metadataOperand(metadataNode, 0) - return reader.string(stringNode) + val (nodeCount, kmetadataNodeArg) = reader.namedMetadataNode("kmetadata") + + if (nodeCount != 1) { + throw Error("Unknown metadata format. The 'kmetadata' node has ${nodeCount} arguments. Don't know what to do.") + } + + val operands = reader.metadataNodeOperands(kmetadataNodeArg) + + val abiNode = operands[0] + val nameNode = operands[1] + val dataNode = operands[2] + + val abiVersion = reader.string(abiNode).toInt() + + if (abiVersion != currentAbiVersion) { + throw Error("Expected ABI version ${currentAbiVersion}, but the binary is ${abiVersion}") + } + moduleName = reader.string(nameNode) + + metadataAsString = reader.string(dataNode) } + + val moduleDescriptor = + deserializeModule(configuration, metadataAsString!!, moduleName!!) + + return moduleDescriptor } class MetadataReader(file: File) : Closeable { @@ -62,25 +86,25 @@ class MetadataReader(file: File) : Closeable { } } - fun namedMetadataNode(name: String, index: Int): LLVMValueRef { + fun namedMetadataNode(name: String): Pair { memScoped { - val nodeCount = LLVMGetNamedMetadataNumOperands(llvmModule, "kmetadata") + val nodeCount = LLVMGetNamedMetadataNumOperands(llvmModule, name) val nodeArray = allocArray(nodeCount) LLVMGetNamedMetadataOperands(llvmModule, "kmetadata", nodeArray[0].ptr) - return nodeArray[0].value!! + return Pair(nodeCount, nodeArray[0].value!!) } } - fun metadataOperand(metadataNode: LLVMValueRef, index: Int): LLVMValueRef { + fun metadataNodeOperands(metadataNode: LLVMValueRef): Array { memScoped { val operandCount = LLVMGetMDNodeNumOperands(metadataNode) val operandArray = allocArray(operandCount) LLVMGetMDNodeOperands(metadataNode, operandArray[0].ptr) - return operandArray[0].value!! + return Array(operandCount, {index -> operandArray[index].value!!}) } } @@ -90,13 +114,8 @@ class MetadataReader(file: File) : Closeable { } } - internal class MetadataGenerator(override val context: Context): ContextUtils { - private fun metadataString(str: String): LLVMValueRef { - return LLVMMDString(str, str.length)!! - } - private fun metadataNode(args: List): LLVMValueRef { memScoped { val references = allocArrayOf(args) @@ -110,92 +129,33 @@ internal class MetadataGenerator(override val context: Context): ContextUtils { return md } + internal fun property(declaration: IrProperty) { + if (declaration.backingField == null) return + assert(declaration.backingField!!.descriptor == declaration.descriptor) + + context.ir.propertiesWithBackingFields.add(declaration.descriptor) + } + private fun emitModuleMetadata(name: String, md: LLVMValueRef?) { LLVMAddNamedMetadataOperand(context.llvmModule, name, md) } - private fun protobufType(type:KotlinType): TypeLinkdata { - val builder = TypeLinkdata.newBuilder() - val name = type.toString() - val proto = builder - .setName(name) - .build() - - return proto - } - - private fun serializeFunSignature(declaration: IrFunction): String { - val func = declaration.descriptor - - val hash = "0x123456 some hash" - val name = func.name.asString() - - val kotlinType = func.getReturnType()!! - val returnType = protobufType(kotlinType) - - val parameters = func.getValueParameters() - val argumentTypes = parameters.map{ protobufType(it.getType()) } - - val proto = FunLinkdata.newBuilder() - .setHash(hash) - .setName(name) - .setReturnType(returnType) - .addAllArg(argumentTypes) - .build() - - // Convert it to ProtoBuf's TextFormat representation. - // Use TextFormat.merge(str, builder) to parse it back - val str = proto.toString() - - return str - } - - internal fun function(declaration: IrFunction) { - val fn = declaration.descriptor.llvmFunction!! - - val proto = serializeFunSignature(declaration) - - val md = metadataFun(fn, proto) - emitModuleMetadata("kfun", md); - } - - // Quick check consistency - private fun debug_blob(blob: String) { - val metadataList = mutableListOf() - KotlinKonanMetadataUtils.parseMetadata(blob, metadataList) - - val body = metadataList.first().body - val gzipInputStream = java.util.zip.GZIPInputStream(ByteArrayInputStream(body)) - val content = JsProtoBuf.Library.parseFrom(gzipInputStream) - gzipInputStream.close() - - println(content.toString()) - println(blob) - } - - private fun serializeModule(moduleDescriptor: ModuleDescriptor): String { - - val description = JsModuleDescriptor( - name = "foo", - kind = ModuleKind.PLAIN, - imported = listOf(), - data = moduleDescriptor - ) - - // TODO: eliminate this dependency on JavaScript compiler - val blobString = KotlinJavascriptSerializationUtil.metadataAsString( - context.bindingContext, description) - if (false) { - debug_blob(blobString) - } - return blobString - + private fun metadataString(str: String): LLVMValueRef { + return LLVMMDString(str, str.length)!! } internal fun endModule(module: IrModuleFragment) { - val moduleAsString = serializeModule(module.descriptor) - val stringNode = metadataString(moduleAsString) - emitModuleMetadata("kmetadata", stringNode) + val abiVersion = context.config.configuration.get(KonanConfigKeys.ABI_VERSION) + val abiNode = metadataString("$abiVersion") + + val moduleName = metadataString(module.descriptor.name.asString()) + + val serializer = KonanSerializationUtil(context) + val moduleAsString = serializer.serializeModule(module.descriptor) + val dataNode = metadataString(moduleAsString) + + val kmetadataArg = metadataNode(listOf(abiNode, moduleName, dataNode)) + emitModuleMetadata("kmetadata", kmetadataArg) } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanClassDataFinder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanClassDataFinder.kt new file mode 100644 index 00000000000..f1142ddfdb3 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanClassDataFinder.kt @@ -0,0 +1,39 @@ +package org.jetbrains.kotlin.backend.konan.serialization + +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.serialization.ClassData +import org.jetbrains.kotlin.serialization.ClassDataWithSource +import org.jetbrains.kotlin.serialization.DescriptorSerializer +import org.jetbrains.kotlin.serialization.KonanLinkData +import org.jetbrains.kotlin.serialization.StringTable +import org.jetbrains.kotlin.serialization.deserialization.NameResolver +import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder + +class KonanClassDataFinder( + private val fragment: KonanLinkData.PackageFragment, + private val nameResolver: NameResolver +) : ClassDataFinder { + override fun findClassData(classId: ClassId): ClassDataWithSource? { + val proto = fragment.classes + val classList = proto.getClassesList() + val nameList = proto.getClassNameList() + + val index = nameList.indexOfFirst({ + nameResolver.getClassId(it) == classId }) + if (index == -1) + error("Could not find serialized class ${classId}") + + val foundClass = proto.getClasses(index) + if (foundClass == null) + error("Could not find data for serialized class ${classId}") + + return ClassDataWithSource(ClassData(nameResolver, foundClass), SourceElement.NO_SOURCE) + } +} + + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanLinkData.proto b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanLinkData.proto new file mode 100644 index 00000000000..a2fe0f3fcb5 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanLinkData.proto @@ -0,0 +1,73 @@ + +syntax = "proto2"; +package org.jetbrains.kotlin.serialization; + +// This, an all the rest of the included proto files have ".proto1" extension. +// That allows us to construct the current file, +// but protoc will skip .class files for them. +// The issue here is that we need to co-exist with the big Kotlin, +// in the same namespace of renamed org.jetbrains.kotlin.protobuf packages. +// In case we merge to the main Kotlin workspace the scheme will be simplified. +import "org/jetbrains/kotlin/backend/konan/serialization/descriptors.proto1"; + +option java_outer_classname = "KonanLinkData"; +option optimize_for = LITE_RUNTIME; + +// Konan extensions to the "descriptors" protobuf. + +extend Class { + repeated Annotation class_annotation = 170; +} + +extend Constructor { + repeated Annotation constructor_annotation = 170; +} + +extend Function { + repeated Annotation function_annotation = 170; +} + +extend Property { + repeated Annotation property_annotation = 170; + optional Annotation.Argument.Value compile_time_value = 171; +} + +extend EnumEntry { + repeated Annotation enum_entry_annotation = 170; +} + +extend ValueParameter { + repeated Annotation parameter_annotation = 170; +} + +extend Type { + repeated Annotation type_annotation = 170; +} + +extend TypeParameter { + repeated Annotation type_parameter_annotation = 170; +} + +// Konan Binary Linkdata structures. + +message PackageFragment { + // Make if an index? + required string fq_name = 1; + required Package package = 4; + required Classes classes = 5; + + // To construct name resolver + required QualifiedNameTable name_table = 2; + required StringTable string_table = 3; +} + +message Classes { + // Index in the QualifiedNameIndex table. + repeated int32 class_name = 1; + repeated Class classes = 2; +} + +message Library { + repeated PackageFragment package_fragment = 2; +} + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanPackageFragment.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanPackageFragment.kt new file mode 100644 index 00000000000..0451545e0e4 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanPackageFragment.kt @@ -0,0 +1,43 @@ +package org.jetbrains.kotlin.backend.konan.serialization + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.serialization.KonanLinkData +import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment +import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope +import org.jetbrains.kotlin.storage.StorageManager + +class KonanPackageFragment( private val proto: KonanLinkData.PackageFragment, + storageManager: StorageManager, module: ModuleDescriptor) : + DeserializedPackageFragment(FqName(proto.getFqName()), + storageManager, module) { + + private val nameResolver = + NameResolverImpl(proto.getStringTable(), proto.getNameTable()) + + override val classDataFinder = KonanClassDataFinder(proto, nameResolver) + + override fun computeMemberScope(): DeserializedPackageMemberScope { + val packageProto = proto.getPackage() + + return DeserializedPackageMemberScope( this, packageProto, + nameResolver, /* containerSource = */ null, + components, {loadClassNames()} ) + } + + private fun loadClassNames(): Collection { + + val classNameList = proto.getClasses().getClassNameList() + + val names = classNameList.mapNotNull { + val classId = nameResolver.getClassId(it) + val shortName = classId.getShortClassName() + if (!classId.isNestedClass) shortName else null + } + + return names ?: listOf() + } +} + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt new file mode 100644 index 00000000000..b9a89dc8f35 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt @@ -0,0 +1,208 @@ +package org.jetbrains.kotlin.backend.konan.serialization + +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.KonanPlatform +import org.jetbrains.kotlin.backend.konan.llvm.base64Encode +import org.jetbrains.kotlin.backend.konan.llvm.base64Decode +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl; +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.serialization.AnnotationSerializer +import org.jetbrains.kotlin.serialization.DescriptorSerializer +import org.jetbrains.kotlin.serialization.ProtoBuf +import org.jetbrains.kotlin.serialization.StringTableImpl +import org.jetbrains.kotlin.serialization.deserialization.* +import org.jetbrains.kotlin.serialization.KonanLinkData +import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.SimpleType +import org.jetbrains.kotlin.utils.* +import org.jetbrains.kotlin.incremental.components.LookupTracker + +/* ------------ Deserializer part ------------------------------------------*/ + +object NullFlexibleTypeDeserializer : FlexibleTypeDeserializer { + override fun create(proto: ProtoBuf.Type, flexibleId: String, + lowerBound: SimpleType, upperBound: SimpleType): KotlinType { + error("Illegal use of flexible type deserializer.") + } +} + +fun createKonanPackageFragmentProvider( + fragments: List, + storageManager: StorageManager, module: ModuleDescriptor, + configuration: DeserializationConfiguration): PackageFragmentProvider { + + val packageFragments = fragments.map { + KonanPackageFragment(it, storageManager, module) + } + val provider = PackageFragmentProviderImpl(packageFragments) + + val notFoundClasses = NotFoundClasses(storageManager, module) + + val annotationAndConstantLoader = AnnotationAndConstantLoaderImpl(module, notFoundClasses, KonanSerializerProtocol) + + val components = DeserializationComponents( + storageManager, module, configuration, + DeserializedClassDataFinder(provider), + annotationAndConstantLoader, + provider, + LocalClassifierTypeSettings.Default, + ErrorReporter.DO_NOTHING, + LookupTracker.DO_NOTHING, NullFlexibleTypeDeserializer, + emptyList(), notFoundClasses) + + for (packageFragment in packageFragments) { + packageFragment.components = components + } + + return provider +} + +internal fun deserializeModule(configuration: CompilerConfiguration, + base64: String, moduleName: String): ModuleDescriptorImpl { + + val storageManager = LockBasedStorageManager() + val moduleDescriptor = ModuleDescriptorImpl( + Name.special(moduleName), storageManager, KonanPlatform.builtIns) + val deserialization_config = CompilerDeserializationConfiguration( + configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT)) + + + val libraryAsByteArray = base64Decode(base64) + val libraryProto = KonanLinkData.Library.parseFrom( + libraryAsByteArray, KonanSerializerProtocol.extensionRegistry) + + val provider = createKonanPackageFragmentProvider( + libraryProto.packageFragmentList, storageManager, + moduleDescriptor, deserialization_config) + + moduleDescriptor.initialize(provider ?: PackageFragmentProvider.Empty) + + return moduleDescriptor +} + + +/* ------------ Serializer part ------------------------------------------*/ + +internal class KonanSerializationUtil(val context: Context) { + + val serializerExtension = KonanSerializerExtension(context) + val serializer = DescriptorSerializer.createTopLevel(serializerExtension) + + fun serializeClass(packageName: FqName, + builder: KonanLinkData.Classes.Builder, + classDescriptor: ClassDescriptor, + skip: (DeclarationDescriptor) -> Boolean) { + + if (skip(classDescriptor)) return + + val classProto = serializer.classProto(classDescriptor).build() + ?: error("Class not serialized: $classDescriptor") + + builder.addClasses(classProto) + val index = serializer.stringTable.getFqNameIndex(classDescriptor) + builder.addClassName(index) + + serializeClasses(packageName, builder, + classDescriptor.unsubstitutedInnerClassesScope + .getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS), skip) + } + + fun serializeClasses(packageName: FqName, + builder: KonanLinkData.Classes.Builder, + descriptors: Collection, + skip: (DeclarationDescriptor) -> Boolean) { + + for (descriptor in descriptors) { + if (descriptor is ClassDescriptor) { + serializeClass(packageName, builder, descriptor, skip) + } + } + } + + fun serializePackage(fqName: FqName, module: ModuleDescriptor) : + KonanLinkData.PackageFragment { + + val packageView = module.getPackage(fqName) + + // TODO: ModuleDescriptor should be able to return + // the package only with the contents of that module, without dependencies + val skip: (DeclarationDescriptor) -> Boolean = + { DescriptorUtils.getContainingModule(it) != module } + + val classifierDescriptors = DescriptorSerializer.sort(packageView.memberScope.getContributedDescriptors( + DescriptorKindFilter.CLASSIFIERS)) + + val classesBuilder = KonanLinkData.Classes.newBuilder() + + serializeClasses(fqName, classesBuilder, classifierDescriptors, skip) + val classesProto = classesBuilder.build() + + val fragments = packageView.fragments + val members = fragments + .flatMap { fragment -> DescriptorUtils.getAllDescriptors(fragment.getMemberScope()) } + .filterNot(skip) + val packageProto = serializer.packagePartProto(members).build() + ?: error("Package fragments not serialized: $fragments") + + + val strings = serializerExtension.stringTable + val (stringTableProto, nameTableProto) = strings.buildProto() + + val fragmentBuilder = KonanLinkData.PackageFragment.newBuilder() + + val fragmentProto = fragmentBuilder + .setPackage(packageProto) + .setFqName(fqName.asString()) + .setClasses(classesProto) + .setStringTable(stringTableProto) + .setNameTable(nameTableProto) + .build() + + return fragmentProto + } + + private fun getPackagesFqNames(module: ModuleDescriptor): Set { + val fqNames = mutableSetOf(FqName.ROOT) + getSubPackagesFqNames(module.getPackage(FqName.ROOT), fqNames) + return fqNames + } + private fun getSubPackagesFqNames(packageView: PackageViewDescriptor, result: MutableSet) { + val fqName = packageView.fqName + if (!fqName.isRoot) { + result.add(fqName) + } + + for (descriptor in packageView.memberScope.getContributedDescriptors( + DescriptorKindFilter.PACKAGES, MemberScope.ALL_NAME_FILTER)) { + if (descriptor is PackageViewDescriptor) { + getSubPackagesFqNames(descriptor, result) + } + } + } + + internal fun serializeModule(moduleDescriptor: ModuleDescriptor): String { + val library = KonanLinkData.Library.newBuilder() + + getPackagesFqNames(moduleDescriptor).forEach { + val packageProto = serializePackage(it, moduleDescriptor) + library.addPackageFragment(packageProto) + } + + val libraryAsByteArray = library.build().toByteArray() + val base64 = base64Encode(libraryAsByteArray) + return base64 + } +} + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializerExtension.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializerExtension.kt new file mode 100644 index 00000000000..b64b7c4d78a --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializerExtension.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.konan.serialization + +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.getKonanInternalClass +import org.jetbrains.kotlin.serialization.Flags +import org.jetbrains.kotlin.serialization.KonanLinkData +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.* +import org.jetbrains.kotlin.backend.konan.descriptors.* +import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.source.PsiSourceFile +import org.jetbrains.kotlin.serialization.KotlinSerializerExtensionBase +import org.jetbrains.kotlin.serialization.ProtoBuf +import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol +import org.jetbrains.kotlin.types.FlexibleType + +internal class KonanSerializerExtension(val context: Context) : + KotlinSerializerExtensionBase(KonanSerializerProtocol) { + + private val backingFieldClass = + context.builtIns.getKonanInternalClass("HasBackingField").getDefaultType() + + private val backingFieldAnnotation = AnnotationDescriptorImpl( + backingFieldClass, emptyMap(), SourceElement.NO_SOURCE) + + override fun serializeProperty(descriptor: PropertyDescriptor, proto: ProtoBuf.Property.Builder) { + super.serializeProperty(descriptor, proto) + + if (context.ir.propertiesWithBackingFields.contains(descriptor)) { + proto.addExtension(KonanLinkData.propertyAnnotation, + annotationSerializer.serializeAnnotation(backingFieldAnnotation)) + + proto.flags = proto.flags or Flags.HAS_ANNOTATIONS.toFlags(true) + } + } +} + +object KonanSerializerProtocol : SerializerExtensionProtocol( + ExtensionRegistryLite.newInstance().apply { + KonanLinkData.registerAllExtensions(this) + }, + KonanLinkData.constructorAnnotation, + KonanLinkData.classAnnotation, + KonanLinkData.functionAnnotation, + KonanLinkData.propertyAnnotation, + KonanLinkData.enumEntryAnnotation, + KonanLinkData.compileTimeValue, + KonanLinkData.parameterAnnotation, + KonanLinkData.typeAnnotation, + KonanLinkData.typeParameterAnnotation +) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/descriptors.proto1 b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/descriptors.proto1 new file mode 100644 index 00000000000..115e4c6d72f --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/descriptors.proto1 @@ -0,0 +1,387 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package org.jetbrains.kotlin.serialization; + +import "org/jetbrains/kotlin/backend/konan/serialization/ext_options.proto1"; + +option java_outer_classname = "ProtoBuf"; +option optimize_for = LITE_RUNTIME; + +message StringTable { + repeated string string = 1; +} + +message QualifiedNameTable { + message QualifiedName { + optional int32 parent_qualified_name = 1 [default = -1]; + // id in the StringTable + required int32 short_name = 2; + optional Kind kind = 3 [default = PACKAGE]; + + enum Kind { + CLASS = 0; + PACKAGE = 1; + LOCAL = 2; + } + } + + repeated QualifiedName qualified_name = 1; +} + +message Annotation { + message Argument { + message Value { + enum Type { + BYTE = 0; + CHAR = 1; + SHORT = 2; + INT = 3; + LONG = 4; + FLOAT = 5; + DOUBLE = 6; + BOOLEAN = 7; + + STRING = 8; + CLASS = 9; + ENUM = 10; + ANNOTATION = 11; + ARRAY = 12; + } + + // Note: a *Value* has a Type, not an Argument! This is done for future language features which may involve using arrays + // of elements of different types. Such entries are allowed in the constant pool of JVM class files. + // However, to save space, this field is optional: in case of homogeneous arrays, only the type of the first element is required + optional Type type = 1; + + // Only one of the following values should be present + + optional sint64 int_value = 2; + optional float float_value = 3; + optional double double_value = 4; + + optional int32 string_value = 5 [(string_id_in_table) = true]; + + // If type = CLASS, FQ name of the referenced class; if type = ENUM, FQ name of the enum class + optional int32 class_id = 6 [(fq_name_id_in_table) = true]; + + optional int32 enum_value_id = 7 [(name_id_in_table) = true]; + + optional Annotation annotation = 8; + + repeated Value array_element = 9; + } + + required int32 name_id = 1 [(name_id_in_table) = true]; + required Value value = 2; + } + + required int32 id = 1 [(fq_name_id_in_table) = true]; + + repeated Argument argument = 2; +} + +message Type { + message Argument { + enum Projection { + IN = 0; + OUT = 1; + INV = 2; + STAR = 3; + } + + optional Projection projection = 1 [default = INV]; + + // When projection is STAR, no type is written, otherwise type must be specified + optional Type type = 2; + optional int32 type_id = 3; + } + + repeated Argument argument = 2; + + optional bool nullable = 3 [default = false]; + + // If this field is set, the type is flexible. + // All the other fields and extensions represent its lower bound, and flexible_upper_bound must be set and represents its upper bound. + optional int32 flexible_type_capabilities_id = 4 [(string_id_in_table) = true]; + + optional Type flexible_upper_bound = 5; + optional int32 flexible_upper_bound_id = 8; + + // Only one of the following values should be present + + optional int32 class_name = 6 [(fq_name_id_in_table) = true]; + optional int32 type_parameter = 7; // id of the type parameter + // Name of the type parameter in the immediate owner + optional int32 type_parameter_name = 9 [(name_id_in_table) = true]; + + // Note that this may be present only for abbreviated_type + // Top level types are always fully expanded + optional int32 type_alias_name = 12 [(fq_name_id_in_table) = true]; + + optional Type outer_type = 10; + optional int32 outer_type_id = 11; + + optional Type abbreviated_type = 13; + optional int32 abbreviated_type_id = 14; + + extensions 100 to 199; +} + +message TypeParameter { + required int32 id = 1; + + required int32 name = 2 [(name_id_in_table) = true]; + + optional bool reified = 3 [default = false]; + + enum Variance { + IN = 0; + OUT = 1; + INV = 2; + } + optional Variance variance = 4 [default = INV]; + + repeated Type upper_bound = 5; + repeated int32 upper_bound_id = 6; + + extensions 100 to 999; +} + +message Class { + enum Kind { + // 3 bits + CLASS = 0; + INTERFACE = 1; + ENUM_CLASS = 2; + ENUM_ENTRY = 3; + ANNOTATION_CLASS = 4; + OBJECT = 5; + COMPANION_OBJECT = 6; + } + + /* + hasAnnotations + Visibility + Modality + ClassKind + isInner + isData + */ + optional int32 flags = 1 [default = 6 /* public final class, no annotations */]; + + required int32 fq_name = 3 [(fq_name_id_in_table) = true]; + + optional int32 companion_object_name = 4 [(name_id_in_table) = true]; + + repeated TypeParameter type_parameter = 5; + + repeated Type supertype = 6; + repeated int32 supertype_id = 2 [packed = true]; + + repeated int32 nested_class_name = 7 [packed = true, (name_id_in_table) = true]; + + repeated Constructor constructor = 8; + repeated Function function = 9; + repeated Property property = 10; + repeated TypeAlias type_alias = 11; + + repeated EnumEntry enum_entry = 13; + + optional TypeTable type_table = 30; + + extensions 100 to 199; +} + +message Package { + repeated Function function = 3; + repeated Property property = 4; + repeated TypeAlias type_alias = 5; + + optional TypeTable type_table = 30; + + extensions 100 to 199; +} + +message TypeTable { + repeated Type type = 1; + + // Index starting from which all types are nullable, or nothing if all types in this table are non-null. + // Note that the 'nullable' field of Type messages is ignored and shouldn't be written because it wastes too much space + optional int32 first_nullable = 2 [default = -1]; +} + +message Constructor { + /* + hasAnnotations + Visibility + isSecondary + */ + optional int32 flags = 1 [default = 6 /* public constructor, no annotations */]; + + repeated ValueParameter value_parameter = 2; + + extensions 100 to 199; +} + +message Function { + /* + hasAnnotations + Visibility + Modality + MemberKind + isOperator + isInfix + isInline + isTailrec + isExternal + */ + optional int32 flags = 9 [default = 6 /* public final function, no annotations */]; + optional int32 old_flags = 1 [default = 6]; + + required int32 name = 2 [(name_id_in_table) = true]; + + optional Type return_type = 3; + optional int32 return_type_id = 7; + + repeated TypeParameter type_parameter = 4; + + optional Type receiver_type = 5; + optional int32 receiver_type_id = 8; + + repeated ValueParameter value_parameter = 6; + + optional TypeTable type_table = 30; + + extensions 100 to 199; +} + +message Property { + /* + hasAnnotations + Visibility + Modality + MemberKind + isVar + hasGetter + hasSetter + isConst + lateinit + hasConstant + */ + optional int32 flags = 11 [default = 518 /* public (6) final property with getter (512) */]; + optional int32 old_flags = 1 [default = 2054]; + + required int32 name = 2 [(name_id_in_table) = true]; + + optional Type return_type = 3; + optional int32 return_type_id = 9; + + repeated TypeParameter type_parameter = 4; + + optional Type receiver_type = 5; + optional int32 receiver_type_id = 10; + + optional ValueParameter setter_value_parameter = 6; + + /* + hasAnnotations + Visibility + Modality + isNotDefault + isExternal + isInline + */ + optional int32 getter_flags = 7 /* absent => same as property */; + optional int32 setter_flags = 8 /* absent => same as property */; + + extensions 100 to 199; +} + +message ValueParameter { + /* + declaresDefault + hasAnnotations + isCrossinline + isNoinline + */ + optional int32 flags = 1 [default = 0]; + + required int32 name = 2 [(name_id_in_table) = true]; + + optional Type type = 3; + optional int32 type_id = 5; + + optional Type vararg_element_type = 4; + optional int32 vararg_element_type_id = 6; + + extensions 100 to 199; +} + +message TypeAlias { + /* + hasAnnotations + Visibility + */ + optional int32 flags = 1 [default = 6 /* public, no annotations */]; + + required int32 name = 2 [(name_id_in_table) = true]; + + repeated TypeParameter type_parameter = 3; + + optional Type underlying_type = 4; + optional int32 underlying_type_id = 5; + + optional Type expanded_type = 6; + optional int32 expanded_type_id = 7; + + repeated Annotation annotation = 8; + + extensions 100 to 199; +} + +message EnumEntry { + optional int32 name = 1 [(name_id_in_table) = true]; + + extensions 100 to 199; +} + +enum Modality { + // 2 bits + FINAL = 0; + OPEN = 1; + ABSTRACT = 2; + SEALED = 3; +} + +enum Visibility { + // 3 bits + INTERNAL = 0; + PRIVATE = 1; + PROTECTED = 2; + PUBLIC = 3; + PRIVATE_TO_THIS = 4; + LOCAL = 5; +} + +enum MemberKind { + // 2 bits + DECLARATION = 0; + FAKE_OVERRIDE = 1; + DELEGATION = 2; + SYNTHESIZED = 3; +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/ext_options.proto1 b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/ext_options.proto1 new file mode 100644 index 00000000000..c6a865ecd4c --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/ext_options.proto1 @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package org.jetbrains.kotlin.serialization; + +import "org/jetbrains/kotlin/backend/konan/serialization/google_descriptor.proto1"; + +option java_outer_classname = "ExtOptionsProtoBuf"; + +extend google.protobuf.FieldOptions { + optional bool skip_in_comparison = 50000; + optional bool name_id_in_table = 50001; + optional bool fq_name_id_in_table = 50002; + optional bool string_id_in_table = 50003; +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/google_descriptor.proto1 b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/google_descriptor.proto1 new file mode 100644 index 00000000000..8e10c6e69c1 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/google_descriptor.proto1 @@ -0,0 +1,621 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// The messages in this file describe the definitions found in .proto files. +// A valid .proto file can be translated directly to a FileDescriptorProto +// without any other information (e.g. without reading its imports). + + + +syntax = "proto2"; +package google.protobuf; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DescriptorProtos"; + +// descriptor.proto must be optimized for speed because reflection-based +// algorithms don't work during bootstrapping. +option optimize_for = SPEED; + +// The protocol compiler can output a FileDescriptorSet containing the .proto +// files it parses. +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; +} + +// Describes a complete .proto file. +message FileDescriptorProto { + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. + + // Names of files imported by this file. + repeated string dependency = 3; + // Indexes of the public imported files in the dependency list above. + repeated int32 public_dependency = 10; + // Indexes of the weak imported files in the dependency list. + // For Google-internal migration only. Do not use. + repeated int32 weak_dependency = 11; + + // All top-level definitions in this file. + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + + optional FileOptions options = 8; + + // This field contains optional information about the original source code. + // You may safely remove this entire field whithout harming runtime + // functionality of the descriptors -- the information is needed only by + // development tools. + optional SourceCodeInfo source_code_info = 9; +} + +// Describes a message type. +message DescriptorProto { + optional string name = 1; + + repeated FieldDescriptorProto field = 2; + repeated FieldDescriptorProto extension = 6; + + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + } + repeated ExtensionRange extension_range = 5; + + optional MessageOptions options = 7; +} + +// Describes a field within a message. +message FieldDescriptorProto { + enum Type { + // 0 is reserved for errors. + // Order is weird for historical reasons. + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if + // negative values are likely. + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if + // negative values are likely. + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; // Tag-delimited aggregate. + TYPE_MESSAGE = 11; // Length-delimited aggregate. + + // New in version 2. + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + }; + + enum Label { + // 0 is reserved for errors + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + // TODO(sanjay): Should we add LABEL_MAP? + }; + + optional string name = 1; + optional int32 number = 3; + optional Label label = 4; + + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be either TYPE_ENUM or TYPE_MESSAGE. + optional Type type = 5; + + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + optional string type_name = 6; + + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + optional string extendee = 2; + + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + // TODO(kenton): Base-64 encode? + optional string default_value = 7; + + optional FieldOptions options = 8; +} + +// Describes an enum type. +message EnumDescriptorProto { + optional string name = 1; + + repeated EnumValueDescriptorProto value = 2; + + optional EnumOptions options = 3; +} + +// Describes a value within an enum. +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + + optional EnumValueOptions options = 3; +} + +// Describes a service. +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + + optional ServiceOptions options = 3; +} + +// Describes a method of a service. +message MethodDescriptorProto { + optional string name = 1; + + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + optional string input_type = 2; + optional string output_type = 3; + + optional MethodOptions options = 4; +} + + +// =================================================================== +// Options + +// Each of the definitions above may have "options" attached. These are +// just annotations which may cause code to be generated slightly differently +// or may contain hints for code that manipulates protocol messages. +// +// Clients may define custom options as extensions of the *Options messages. +// These extensions may not yet be known at parsing time, so the parser cannot +// store the values in them. Instead it stores them in a field in the *Options +// message called uninterpreted_option. This field must have the same name +// across all *Options messages. We then use this field to populate the +// extensions when we build a descriptor, at which point all protos have been +// parsed and so all extensions are known. +// +// Extension numbers for custom options may be chosen as follows: +// * For options which will only be used within a single application or +// organization, or for experimental options, use field numbers 50000 +// through 99999. It is up to you to ensure that you do not use the +// same number for multiple options. +// * For options which will be published and used publicly by multiple +// independent entities, e-mail protobuf-global-extension-registry@google.com +// to reserve extension numbers. Simply provide your project name (e.g. +// Object-C plugin) and your porject website (if available) -- there's no need +// to explain how you intend to use them. Usually you only need one extension +// number. You can declare multiple options with only one extension number by +// putting them in a sub-message. See the Custom Options section of the docs +// for examples: +// http://code.google.com/apis/protocolbuffers/docs/proto.html#options +// If this turns out to be popular, a web service will be set up +// to automatically assign option numbers. + + +message FileOptions { + + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + optional string java_package = 1; + + + // If set, all the classes from the .proto file are wrapped in a single + // outer class with the given name. This applies to both Proto1 + // (equivalent to the old "--one_java_file" option) and Proto2 (where + // a .proto always translates to a single class, but you may want to + // explicitly choose the class name). + optional string java_outer_classname = 8; + + // If set true, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the outer class + // named by java_outer_classname. However, the outer class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + optional bool java_multiple_files = 10 [default=false]; + + // If set true, then the Java code generator will generate equals() and + // hashCode() methods for all messages defined in the .proto file. This is + // purely a speed optimization, as the AbstractMessage base class includes + // reflection-based implementations of these methods. + optional bool java_generate_equals_and_hash = 20 [default=false]; + + // Generated classes can be optimized for speed or code size. + enum OptimizeMode { + SPEED = 1; // Generate complete code for parsing, serialization, + // etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. + } + optional OptimizeMode optimize_for = 9 [default=SPEED]; + + // Sets the Go package where structs generated from this .proto will be + // placed. There is no default. + optional string go_package = 11; + + + + // Should generic services be generated in each language? "Generic" services + // are not specific to any particular RPC system. They are generated by the + // main code generators in each language (without additional plugins). + // Generic services were the only kind of service generation supported by + // early versions of proto2. + // + // Generic services are now considered deprecated in favor of using plugins + // that generate code specific to your particular RPC system. Therefore, + // these default to false. Old code which depends on generic services should + // explicitly set them to true. + optional bool cc_generic_services = 16 [default=false]; + optional bool java_generic_services = 17 [default=false]; + optional bool py_generic_services = 18 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message MessageOptions { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + optional bool message_set_wire_format = 1 [default=false]; + + // Disables the generation of the standard "descriptor()" accessor, which can + // conflict with a field of the same name. This is meant to make migration + // from proto1 easier; new code should avoid fields named "descriptor". + optional bool no_standard_descriptor_accessor = 2 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message FieldOptions { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + optional CType ctype = 1 [default = STRING]; + enum CType { + // Default mode. + STRING = 0; + + CORD = 1; + + STRING_PIECE = 2; + } + // The packed option can be enabled for repeated primitive fields to enable + // a more efficient representation on the wire. Rather than repeatedly + // writing the tag and type for each element, the entire array is encoded as + // a single length-delimited blob. + optional bool packed = 2; + + + + // Should this field be parsed lazily? Lazy applies only to message-type + // fields. It means that when the outer message is initially parsed, the + // inner message's contents will not be parsed but instead stored in encoded + // form. The inner message will actually be parsed when it is first accessed. + // + // This is only a hint. Implementations are free to choose whether to use + // eager or lazy parsing regardless of the value of this option. However, + // setting this option true suggests that the protocol author believes that + // using lazy parsing on this field is worth the additional bookkeeping + // overhead typically needed to implement it. + // + // This option does not affect the public interface of any generated code; + // all method signatures remain the same. Furthermore, thread-safety of the + // interface is not affected by this option; const methods remain safe to + // call from multiple threads concurrently, while non-const methods continue + // to require exclusive access. + // + // + // Note that implementations may choose not to check required fields within + // a lazy sub-message. That is, calling IsInitialized() on the outher message + // may return true even if the inner message has missing required fields. + // This is necessary because otherwise the inner message would have to be + // parsed in order to perform the check, defeating the purpose of lazy + // parsing. An implementation which chooses not to check required fields + // must be consistent about it. That is, for any particular sub-message, the + // implementation must either *always* check its required fields, or *never* + // check its required fields, regardless of whether or not the message has + // been parsed. + optional bool lazy = 5 [default=false]; + + // Is this field deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for accessors, or it will be completely ignored; in the very least, this + // is a formalization for deprecating fields. + optional bool deprecated = 3 [default=false]; + + // EXPERIMENTAL. DO NOT USE. + // For "map" fields, the name of the field in the enclosed type that + // is the key for this map. For example, suppose we have: + // message Item { + // required string name = 1; + // required string value = 2; + // } + // message Config { + // repeated Item items = 1 [experimental_map_key="name"]; + // } + // In this situation, the map key for Item will be set to "name". + // TODO: Fully-implement this, then remove the "experimental_" prefix. + optional string experimental_map_key = 9; + + // For Google-internal migration only. Do not use. + optional bool weak = 10 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumOptions { + + // Set this option to false to disallow mapping different tag names to a same + // value. + optional bool allow_alias = 2 [default=true]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumValueOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message ServiceOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message MethodOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + + +// A message representing a option the parser does not recognize. This only +// appears in options protos created by the compiler::Parser class. +// DescriptorPool resolves these when building Descriptor objects. Therefore, +// options protos in descriptor objects (e.g. returned by Descriptor::options(), +// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions +// in them. +message UninterpretedOption { + // The name of the uninterpreted option. Each string represents a segment in + // a dot-separated name. is_extension is true iff a segment represents an + // extension (denoted with parentheses in options specs in .proto files). + // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents + // "foo.(bar.baz).qux". + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } + repeated NamePart name = 2; + + // The value of the uninterpreted option, in whatever type the tokenizer + // identified it as during parsing. Exactly one of these should be set. + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; +} + +// =================================================================== +// Optional source code info + +// Encapsulates information about the original source file from which a +// FileDescriptorProto was generated. +message SourceCodeInfo { + // A Location identifies a piece of source code in a .proto file which + // corresponds to a particular definition. This information is intended + // to be useful to IDEs, code indexers, documentation generators, and similar + // tools. + // + // For example, say we have a file like: + // message Foo { + // optional string foo = 1; + // } + // Let's look at just the field definition: + // optional string foo = 1; + // ^ ^^ ^^ ^ ^^^ + // a bc de f ghi + // We have the following locations: + // span path represents + // [a,i) [ 4, 0, 2, 0 ] The whole field definition. + // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). + // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). + // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). + // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). + // + // Notes: + // - A location may refer to a repeated field itself (i.e. not to any + // particular index within it). This is used whenever a set of elements are + // logically enclosed in a single code segment. For example, an entire + // extend block (possibly containing multiple extension definitions) will + // have an outer location whose path refers to the "extensions" repeated + // field without an index. + // - Multiple locations may have the same path. This happens when a single + // logical declaration is spread out across multiple places. The most + // obvious example is the "extend" block again -- there may be multiple + // extend blocks in the same scope, each of which will have the same path. + // - A location's span is not always a subset of its parent's span. For + // example, the "extendee" of an extension declaration appears at the + // beginning of the "extend" block and is shared by all extensions within + // the block. + // - Just because a location's span is a subset of some other location's span + // does not mean that it is a descendent. For example, a "group" defines + // both a type and a field in a single declaration. Thus, the locations + // corresponding to the type and field and their components will overlap. + // - Code which tries to interpret locations should probably be designed to + // ignore those that it doesn't understand, as more types of locations could + // be recorded in the future. + repeated Location location = 1; + message Location { + // Identifies which part of the FileDescriptorProto was defined at this + // location. + // + // Each element is a field number or an index. They form a path from + // the root FileDescriptorProto to the place where the definition. For + // example, this path: + // [ 4, 3, 2, 7, 1 ] + // refers to: + // file.message_type(3) // 4, 3 + // .field(7) // 2, 7 + // .name() // 1 + // This is because FileDescriptorProto.message_type has field number 4: + // repeated DescriptorProto message_type = 4; + // and DescriptorProto.field has field number 2: + // repeated FieldDescriptorProto field = 2; + // and FieldDescriptorProto.name has field number 1: + // optional string name = 1; + // + // Thus, the above path gives the location of a field name. If we removed + // the last element: + // [ 4, 3, 2, 7 ] + // this path refers to the whole field declaration (from the beginning + // of the label to the terminating semicolon). + repeated int32 path = 1 [packed=true]; + + // Always has exactly three or four elements: start line, start column, + // end line (optional, otherwise assumed same as start line), end column. + // These are packed into a single field for efficiency. Note that line + // and column numbers are zero-based -- typically you will want to add + // 1 to each before displaying to a user. + repeated int32 span = 2 [packed=true]; + + // If this SourceCodeInfo represents a complete declaration, these are any + // comments appearing before and after the declaration which appear to be + // attached to the declaration. + // + // A series of line comments appearing on consecutive lines, with no other + // tokens appearing on those lines, will be treated as a single comment. + // + // Only the comment content is provided; comment markers (e.g. //) are + // stripped out. For block comments, leading whitespace and an asterisk + // will be stripped from the beginning of each line other than the first. + // Newlines are included in the output. + // + // Examples: + // + // optional int32 foo = 1; // Comment attached to foo. + // // Comment attached to bar. + // optional int32 bar = 2; + // + // optional string baz = 3; + // // Comment attached to baz. + // // Another line attached to baz. + // + // // Comment attached to qux. + // // + // // Another line attached to qux. + // optional double qux = 4; + // + // optional string corge = 5; + // /* Block comment attached + // * to corge. Leading asterisks + // * will be removed. */ + // /* Block comment attached to + // * grault. */ + // optional int32 grault = 6; + optional string leading_comments = 3; + optional string trailing_comments = 4; + } +} diff --git a/build.gradle b/build.gradle index 4cfab210213..edfc3c2ef5e 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,7 @@ allprojects { } } + convention.plugins.clangFlags = new ClangFlags(new PlatformInfo(), "$llvmDir") ext.clangPath = ["$llvmDir/bin"] ext.clangArgs = [] diff --git a/common/src/hash/cpp/Base64.cpp b/common/src/hash/cpp/Base64.cpp index e99dda97d49..8cd26731389 100644 --- a/common/src/hash/cpp/Base64.cpp +++ b/common/src/hash/cpp/Base64.cpp @@ -35,7 +35,7 @@ static const unsigned char kDecode[] = { extern "C" { #endif -int Base64Encode( +int EncodeBase64( const void* dataBuf, uint32_t dataLength, void* resultBuf, uint32_t resultSize) { char *result = reinterpret_cast(resultBuf); const uint8_t *data = reinterpret_cast(dataBuf); @@ -113,7 +113,7 @@ int Base64Encode( return 0; /* indicate success */ } -int Base64Decode( +int DecodeBase64( const char *in, uint32_t inLen, void* outBuf, uint32_t* outLen) { uint8_t* out = reinterpret_cast(outBuf); const char* end = in + inLen; diff --git a/common/src/hash/cpp/Names.cpp b/common/src/hash/cpp/Names.cpp index 5cff17c0b9d..ee9f93c5fbc 100644 --- a/common/src/hash/cpp/Names.cpp +++ b/common/src/hash/cpp/Names.cpp @@ -26,7 +26,7 @@ constexpr uint32_t PrintableBase64Size(uint32_t input_length) { } void PrintableBase64(const uint8_t* data, uint32_t data_length, char* base64) { - int rv = Base64Encode(data, data_length, base64, PrintableBase64Size(data_length)); + int rv = EncodeBase64(data, data_length, base64, PrintableBase64Size(data_length)); assert(rv == 0); } diff --git a/common/src/hash/headers/Base64.h b/common/src/hash/headers/Base64.h index 70d5df36691..a8d7a7c5403 100644 --- a/common/src/hash/headers/Base64.h +++ b/common/src/hash/headers/Base64.h @@ -7,10 +7,10 @@ extern "C" { #endif -int Base64Encode( +int EncodeBase64( const void* input, uint32_t inputLen, void* output, uint32_t outputLen); -int Base64Decode( +int DecodeBase64( const char* input, uint32_t inputLen, void* output, uint32_t* outputLen); #ifdef __cplusplus diff --git a/runtime/src/main/kotlin/konan/internal/Annotations.kt b/runtime/src/main/kotlin/konan/internal/Annotations.kt index 992e0c75df2..ddf1b96c5c4 100644 --- a/runtime/src/main/kotlin/konan/internal/Annotations.kt +++ b/runtime/src/main/kotlin/konan/internal/Annotations.kt @@ -10,4 +10,9 @@ package konan.internal */ //@Target(AnnotationTarget.FUNCTION) //@Retention(AnnotationRetention.SOURCE) -annotation class ExportForCppRuntime(val name: String = "") \ No newline at end of file +annotation class ExportForCppRuntime(val name: String = "") + +// This one is used internally to mark the presence of a backing field +// in the absence of IR. +annotation class HasBackingField +