IR: move serialization to serialization.common

This commit is contained in:
Anton Bannykh
2019-03-20 15:30:47 +03:00
parent 59c5d8bdb8
commit 42f576f033
22 changed files with 47 additions and 26 deletions
@@ -0,0 +1,19 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(project(":compiler:util"))
compile(project(":compiler:frontend"))
compile(project(":compiler:backend-common"))
compile(project(":compiler:ir.backend.common"))
compile(project(":compiler:ir.tree"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
}
sourceSets {
"main" { projectDefault() }
"test" {}
}
@@ -0,0 +1,710 @@
syntax = "proto2";
package org.jetbrains.kotlin.backend.common.serialization;
option java_outer_classname = "KotlinIr";
option optimize_for = LITE_RUNTIME;
message DescriptorReference {
required String package_fq_name = 1;
required String class_fq_name = 2;
required String name = 3;
optional UniqId uniq_id = 4;
optional bool is_getter = 5 [default = false];
optional bool is_setter = 6 [default = false];
optional bool is_backing_field = 7 [default = false];
optional bool is_fake_override = 8 [default = false];
optional bool is_default_constructor = 9 [default = false];
optional bool is_enum_entry = 10 [default = false];
optional bool is_enum_special = 11 [default = false];
optional bool is_type_parameter = 12 [default = false];
}
message UniqId {
required uint64 index = 1;
required bool isLocal = 2;
}
message Coordinates {
required int32 start_offset = 1;
required int32 end_offset = 2;
}
message Visibility {
required String name = 1;
}
message IrStatementOrigin {
required String name = 1;
}
enum KnownOrigin {
CUSTOM = 1;
DEFINED = 2;
FAKE_OVERRIDE = 3;
FOR_LOOP_ITERATOR = 4;
FOR_LOOP_VARIABLE = 5;
FOR_LOOP_IMPLICIT_VARIABLE = 6;
PROPERTY_BACKING_FIELD = 7;
DEFAULT_PROPERTY_ACCESSOR = 8;
DELEGATE = 9;
DELEGATED_PROPERTY_ACCESSOR = 10;
DELEGATED_MEMBER = 11;
ENUM_CLASS_SPECIAL_MEMBER = 12;
FUNCTION_FOR_DEFAULT_PARAMETER = 13;
FILE_CLASS = 14;
GENERATED_DATA_CLASS_MEMBER = 15;
GENERATED_INLINE_CLASS_MEMBER = 16;
LOCAL_FUNCTION_FOR_LAMBDA = 17;
CATCH_PARAMETER = 19;
INSTANCE_RECEIVER = 20;
PRIMARY_CONSTRUCTOR_PARAMETER = 21;
IR_TEMPORARY_VARIABLE = 22;
IR_EXTERNAL_DECLARATION_STUB = 23;
IR_EXTERNAL_JAVA_DECLARATION_STUB = 24;
IR_BUILTINS_STUB = 25;
BRIDGE = 26;
FIELD_FOR_ENUM_ENTRY = 27;
FIELD_FOR_ENUM_VALUES = 28;
FIELD_FOR_OBJECT_INSTANCE = 29;
}
message IrDeclarationOrigin {
oneof either {
KnownOrigin origin = 1;
String custom = 2;
}
}
/* ------ Top Level---------------------------------------------- */
message IrDeclarationContainer {
repeated IrDeclaration declaration = 1;
}
message FileEntry {
required String name = 1;
repeated int32 line_start_offsets = 2;
}
message IrFile {
repeated UniqId declaration_id = 1;
required FileEntry file_entry = 2;
required String fq_name = 3;
required Annotations annotations = 4;
repeated IrSymbol explicitly_exported_to_compiler = 5;
}
message IrModule {
required String name = 1;
repeated IrFile file = 2;
required IrSymbolTable symbol_table = 3;
required IrTypeTable type_table = 4;
required StringTable string_table = 5;
}
/* ------ String Table ------------------------------------------ */
message String {
required int32 index = 1;
}
message StringTable {
repeated string strings = 1;
}
/* ------ IrSymbols --------------------------------------------- */
enum IrSymbolKind {
FUNCTION_SYMBOL = 1;
CONSTRUCTOR_SYMBOL = 2;
ENUM_ENTRY_SYMBOL = 3;
FIELD_SYMBOL = 4;
VALUE_PARAMETER_SYMBOL = 5;
RETURNABLE_BLOCK_SYMBOL = 6;
CLASS_SYMBOL = 7;
TYPE_PARAMETER_SYMBOL = 8;
VARIABLE_SYMBOL = 9;
ANONYMOUS_INIT_SYMBOL = 10;
STANDALONE_FIELD_SYMBOL = 11; // For fields without properties. WrappedFieldDescriptor, rather than WrappedPropertyDescriptor.
RECEIVER_PARAMETER_SYMBOL = 12; // ReceiverParameterDescriptor rather than ValueParameterDescriptor.
}
message IrSymbolData {
required IrSymbolKind kind = 1;
required UniqId uniq_id = 2;
required UniqId top_level_uniq_id = 3;
optional String fqname = 4;
optional DescriptorReference descriptor_reference = 5;
}
message IrSymbol {
required int32 index = 1;
}
message IrSymbolTable {
repeated IrSymbolData symbols = 1;
}
/* ------ IrTypes --------------------------------------------- */
enum IrTypeVariance { // Should we import metadata variance, or better stay separate?
IN = 0;
OUT = 1;
INV = 2;
}
message Annotations {
repeated IrCall annotation = 1;
}
message TypeArguments {
repeated IrTypeIndex type_argument = 1;
}
message IrStarProjection {
optional bool void = 1;
}
message IrTypeProjection {
required IrTypeVariance variance = 1;
required IrTypeIndex type = 2;
}
message IrTypeArgument {
oneof kind {
IrStarProjection star = 1;
IrTypeProjection type = 2;
}
}
message IrSimpleType {
required Annotations annotations = 1;
required IrSymbol classifier = 2;
required bool has_question_mark = 3;
repeated IrTypeArgument argument = 4;
}
message IrDynamicType {
required Annotations annotations = 1;
}
message IrErrorType {
required Annotations annotations = 1;
}
message IrType {
oneof kind {
IrSimpleType simple = 1;
IrDynamicType dynamic = 2;
IrErrorType error = 3;
}
}
message IrTypeTable {
repeated IrType types = 1;
}
message IrTypeIndex {
required int32 index = 1;
}
/* ------ IrExpressions --------------------------------------------- */
message IrBreak {
required int32 loop_id = 1;
optional String label = 2;
}
message IrBlock {
required bool is_lambda_origin = 1;
repeated IrStatement statement = 2;
}
message MemberAccessCommon {
optional IrExpression dispatch_receiver = 1;
optional IrExpression extension_receiver = 2;
repeated NullableIrExpression value_argument = 3;
required TypeArguments type_arguments = 4;
}
message IrCall {
enum Primitive {
NOT_PRIMITIVE = 1;
NULLARY = 2;
BINARY = 4;
}
required Primitive kind = 1;
required IrSymbol symbol = 2;
required MemberAccessCommon member_access = 3;
optional IrSymbol super = 4;
}
message IrFunctionReference {
required IrSymbol symbol = 1;
optional IrStatementOrigin origin = 2;
required MemberAccessCommon member_access = 3;
}
message IrPropertyReference {
optional IrSymbol field = 1;
optional IrSymbol getter = 2;
optional IrSymbol setter = 3;
optional IrStatementOrigin origin = 4;
required MemberAccessCommon member_access = 5;
optional DescriptorReference descriptor_reference = 6; // IrProperty doesn't have a symbol at all. Preserve this rudiment for now.
}
message IrComposite {
repeated IrStatement statement = 1;
}
message IrClassReference {
required IrSymbol class_symbol = 1;
required IrTypeIndex class_type = 2;
}
message IrConst {
oneof value {
bool null = 1;
bool boolean = 2;
int32 char = 3;
int32 byte = 4;
int32 short = 5;
int32 int = 6;
int64 long = 7;
float float = 8;
double double = 9;
String string = 10;
}
}
message IrContinue {
required int32 loop_id = 1;
optional String label = 2;
}
message IrDelegatingConstructorCall {
required IrSymbol symbol = 1;
required MemberAccessCommon member_access = 2;
}
message IrDoWhile {
required Loop loop = 1;
}
message IrEnumConstructorCall {
required IrSymbol symbol = 1;
required MemberAccessCommon member_access = 2;
}
message IrGetClass {
required IrExpression argument = 1;
}
message IrGetEnumValue {
required IrSymbol symbol = 2;
}
message FieldAccessCommon {
required IrSymbol symbol = 1;
optional IrSymbol super = 2;
optional IrExpression receiver = 3;
}
message IrGetField {
required FieldAccessCommon field_access = 1;
}
message IrGetValue {
required IrSymbol symbol = 1;
}
message IrGetObject {
required IrSymbol symbol = 1;
}
message IrInstanceInitializerCall {
required IrSymbol symbol = 1;
}
message Loop {
required int32 loop_id = 1;
required IrExpression condition = 2;
optional String label = 3;
optional IrExpression body = 4;
}
message IrReturn {
required IrSymbol return_target = 1;
required IrExpression value = 2;
}
message IrSetField {
required FieldAccessCommon field_access = 1;
required IrExpression value = 2;
}
message IrSetVariable {
required IrSymbol symbol = 1;
required IrExpression value = 2;
}
message IrSpreadElement {
required IrExpression expression = 1;
required Coordinates coordinates = 2;
}
message IrStringConcat {
repeated IrExpression argument = 1;
}
message IrThrow {
required IrExpression value = 1;
}
message IrTry {
required IrExpression result = 1;
repeated IrStatement catch = 2;
optional IrExpression finally = 3;
}
message IrTypeOp {
required IrTypeOperator operator = 1;
required IrTypeIndex operand = 2;
required IrExpression argument = 3;
}
message IrVararg {
required IrTypeIndex element_type = 1;
repeated IrVarargElement element = 2;
}
message IrVarargElement {
oneof vararg_element {
IrExpression expression = 1;
IrSpreadElement spread_element = 2;
}
}
message IrWhen {
repeated IrStatement branch = 1;
}
message IrWhile {
required Loop loop = 1;
}
/* ------ Dynamic expression --------------------------------------------- */
message IrDynamicMemberExpression {
required String memberName = 1;
required IrExpression receiver = 2;
}
message IrDynamicOperatorExpression {
enum IrDynamicOperator {
UNARY_PLUS = 1;
UNARY_MINUS = 2;
EXCL = 3;
PREFIX_INCREMENT = 4;
POSTFIX_INCREMENT = 5;
PREFIX_DECREMENT = 6;
POSTFIX_DECREMENT = 7;
BINARY_PLUS = 8;
BINARY_MINUS = 9;
MUL = 10;
DIV = 11;
MOD = 12;
GT = 13;
LT = 14;
GE = 15;
LE = 16;
EQEQ = 17;
EXCLEQ = 18;
EQEQEQ = 19;
EXCLEQEQ = 20;
ANDAND = 21;
OROR = 22;
EQ = 23;
PLUSEQ = 24;
MINUSEQ = 25;
MULEQ = 26;
DIVEQ = 27;
MODEQ = 28;
ARRAY_ACCESS = 29;
INVOKE = 30;
}
required IrDynamicOperator operator = 1;
required IrExpression receiver = 2;
repeated IrExpression argument = 3;
}
// TODO: we need an extension mechanism to accomodate new
// IR operators in upcoming releases.
message IrOperation {
oneof operation {
IrBlock block = 1;
IrBreak break = 2;
IrCall call = 3;
IrClassReference class_reference = 4;
IrComposite composite = 5;
IrConst const = 6;
IrContinue continue = 7;
IrDelegatingConstructorCall delegating_constructor_call = 8;
IrDoWhile do_while = 9;
IrEnumConstructorCall enum_constructor_call = 10;
IrFunctionReference function_reference = 11;
IrGetClass get_class = 12;
IrGetEnumValue get_enum_value = 13;
IrGetField get_field = 14;
IrGetObject get_object = 15;
IrGetValue get_value = 16;
IrInstanceInitializerCall instance_initializer_call = 17;
IrPropertyReference property_reference = 18;
IrReturn return = 19;
IrSetField set_field = 20;
IrSetVariable set_variable = 21;
IrStringConcat string_concat = 22;
IrThrow throw = 23;
IrTry try = 24;
IrTypeOp type_op = 25;
IrVararg vararg = 26;
IrWhen when = 27;
IrWhile while = 28;
IrDynamicMemberExpression dynamic_member = 29;
IrDynamicOperatorExpression dynamic_operator = 30;
}
}
enum IrTypeOperator {
CAST = 1;
IMPLICIT_CAST = 2;
IMPLICIT_NOTNULL = 3;
IMPLICIT_COERCION_TO_UNIT = 4;
IMPLICIT_INTEGER_COERCION = 5;
SAFE_CAST = 6;
INSTANCEOF = 7;
NOT_INSTANCEOF = 8;
SAM_CONVERSION = 9;
}
message IrExpression {
required IrOperation operation = 1;
required IrTypeIndex type = 2;
required Coordinates coordinates = 3;
}
message NullableIrExpression {
optional IrExpression expression = 1;
}
/* ------ Declarations --------------------------------------------- */
message IrTypeAlias {
// Nothing for now.
}
message IrFunction {
required IrSymbol symbol = 1;
required IrFunctionBase base = 2;
required ModalityKind modality = 3;
required bool is_tailrec = 4;
required bool is_suspend = 5;
repeated IrSymbol overridden = 6;
//optional UniqId corresponding_property = 7;
}
message IrFunctionBase {
required String name = 1;
required Visibility visibility = 2;
required bool is_inline = 3;
required bool is_external = 4;
required IrTypeParameterContainer type_parameters = 5;
optional IrDeclaration dispatch_receiver = 6;
optional IrDeclaration extension_receiver = 7;
repeated IrDeclaration value_parameter = 8;
optional IrStatement body = 9;
required IrTypeIndex return_type = 10;
}
message IrConstructor {
required IrSymbol symbol = 1;
required IrFunctionBase base = 2;
required bool is_primary = 3;
}
message IrField {
required IrSymbol symbol = 1;
optional IrExpression initializer = 2;
required String name = 3;
required Visibility visibility = 4;
required bool is_final = 5;
required bool is_external = 6;
required bool is_static = 7;
required IrTypeIndex type = 8;
}
message IrProperty {
optional DescriptorReference descriptor_reference = 1; // IrProperty doesn't have a symbol at all. Preserve this rudiment for now.
required String name = 2;
required Visibility visibility = 3;
required ModalityKind modality = 4;
required bool is_var = 5;
required bool is_const = 6;
required bool is_lateinit = 7;
required bool is_delegated = 8;
required bool is_external = 9;
optional IrField backing_field = 10;
optional IrFunction getter = 11;
optional IrFunction setter = 12;
}
message IrVariable {
required String name = 1;
required IrSymbol symbol = 2;
required IrTypeIndex type = 3;
required bool is_var = 4;
required bool is_const = 5;
required bool is_lateinit = 6;
optional IrExpression initializer = 7;
}
enum ClassKind {
CLASS = 1;
INTERFACE = 2;
ENUM_CLASS = 3;
ENUM_ENTRY = 4;
ANNOTATION_CLASS = 5;
OBJECT = 6;
}
enum ModalityKind { // It is ModalityKind to not clash with Modality in descriptor metadata.
FINAL_MODALITY = 1;
SEALED_MODALITY = 2;
OPEN_MODALITY = 3;
ABSTRACT_MODALITY = 4;
}
message IrValueParameter {
required IrSymbol symbol = 1;
required String name = 2;
required int32 index = 3;
required IrTypeIndex type = 4;
optional IrTypeIndex vararg_element_type = 5;
required bool is_crossinline = 6;
required bool is_noinline = 7;
optional IrExpression default_value = 8;
}
message IrTypeParameter {
required IrSymbol symbol = 1;
required String name = 2;
required int32 index = 3;
required IrTypeVariance variance = 4;
repeated IrTypeIndex super_type = 5;
required bool is_reified = 6;
}
message IrTypeParameterContainer {
repeated IrDeclaration type_parameter = 1;
}
message IrClass {
required IrSymbol symbol = 1;
required String name = 2;
required ClassKind kind = 3;
required Visibility visibility = 4;
required ModalityKind modality = 5;
// TODO: consider using flags for the booleans.
required bool is_companion = 6;
required bool is_inner = 7;
required bool is_data = 8;
required bool is_external = 9;
required bool is_inline = 10;
optional IrDeclaration this_receiver = 11;
required IrTypeParameterContainer type_parameters = 12;
required IrDeclarationContainer declaration_container = 13;
repeated IrTypeIndex super_type = 14;
}
message IrEnumEntry {
required IrSymbol symbol = 1;
optional IrExpression initializer = 2;
optional IrDeclaration corresponding_class = 3;
required String name = 4;
}
message IrAnonymousInit {
required IrSymbol symbol = 1;
required IrStatement body = 2;
}
// TODO: we need an extension mechanism to accomodate new
// IR operators in upcoming releases.
message IrDeclarator {
oneof declarator {
IrAnonymousInit ir_anonymous_init = 1;
IrClass ir_class = 2;
IrConstructor ir_constructor = 3;
IrEnumEntry ir_enum_entry = 4;
IrField ir_field = 5;
IrFunction ir_function = 6;
IrProperty ir_property = 7;
IrTypeAlias ir_type_alias = 8;
IrTypeParameter ir_type_parameter = 9;
IrVariable ir_variable = 10;
IrValueParameter ir_value_parameter = 11;
}
}
message IrDeclaration {
required IrDeclarationOrigin origin = 1;
required Coordinates coordinates = 2;
required Annotations annotations = 3;
required IrDeclarator declarator = 4;
}
/* ------- IrStatements --------------------------------------------- */
message IrBranch {
required IrExpression condition = 1;
required IrExpression result = 2;
}
message IrBlockBody {
repeated IrStatement statement = 1;
}
message IrCatch {
required IrDeclaration catch_parameter = 1;
required IrExpression result = 2;
}
enum IrSyntheticBodyKind {
ENUM_VALUES = 1;
ENUM_VALUEOF = 2;
}
message IrSyntheticBody {
required IrSyntheticBodyKind kind = 1;
}
// Let's try to map IrElement as well as IrStatement to IrStatement.
message IrStatement {
required Coordinates coordinates = 1;
oneof statement {
IrDeclaration declaration = 2;
IrExpression expression = 3;
IrBlockBody block_body = 4;
IrBranch branch = 5;
IrCatch catch = 6;
IrSyntheticBody synthetic_body = 7;
}
}
@@ -0,0 +1,108 @@
/**
* Copyright 2010-2019 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.common.library
import java.io.File
import java.io.RandomAccessFile
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
import java.nio.file.Files
data class DeclarationId(val id: Long, val isLocal: Boolean)
fun File.map(mode: FileChannel.MapMode = FileChannel.MapMode.READ_ONLY,
start: Long = 0, size: Long = -1): MappedByteBuffer {
val file = RandomAccessFile(path,
if (mode == FileChannel.MapMode.READ_ONLY) "r" else "rw")
val fileSize = if (mode == FileChannel.MapMode.READ_ONLY)
file.length() else size.also { assert(size != -1L) }
val channel = file.channel
return channel.map(mode, start, fileSize)
.also { channel.close() } // Channel close closes the file also.
}
class CombinedIrFileReader(file: File) {
private val buffer = file.map(FileChannel.MapMode.READ_ONLY)
private val declarationToOffsetSize = mutableMapOf<DeclarationId, Pair<Int, Int>>()
init {
val declarationsCount = buffer.int
for (i in 0 until declarationsCount) {
val id = buffer.long
val isLocal = buffer.int != 0
val offset = buffer.int
val size = buffer.int
declarationToOffsetSize[DeclarationId(id, isLocal)] = offset to size
}
}
fun declarationBytes(id: DeclarationId): ByteArray {
val offsetSize = declarationToOffsetSize[id] ?: throw Error("No declaration with $id here")
val result = ByteArray(offsetSize.second)
buffer.position(offsetSize.first)
buffer.get(result, 0, offsetSize.second)
return result
}
}
private const val SINGLE_INDEX_RECORD_SIZE = 20 // sizeof(Long) + 3 * sizeof(Int).
private const val INDEX_HEADER_SIZE = 4 // sizeof(Int).
class CombinedIrFileWriter(val declarationCount: Int) {
private var currentDeclaration = 0
private var currentPosition = 0
private val file = Files.createTempFile("ir", "").toFile()
private val randomAccessFile = RandomAccessFile(file.path, "rw")
init {
randomAccessFile.writeInt(declarationCount)
assert(randomAccessFile.filePointer.toInt() == INDEX_HEADER_SIZE)
for (i in 0 until declarationCount) {
randomAccessFile.writeLong(-1) // id
randomAccessFile.writeInt(-1) // isLocal
randomAccessFile.writeInt(-1) // offset
randomAccessFile.writeInt(-1) // size
}
currentPosition = randomAccessFile.filePointer.toInt()
assert(currentPosition == INDEX_HEADER_SIZE + SINGLE_INDEX_RECORD_SIZE * declarationCount)
}
fun skipDeclaration() {
currentDeclaration++
}
fun addDeclaration(id: DeclarationId, bytes: ByteArray) {
randomAccessFile.seek((currentDeclaration * SINGLE_INDEX_RECORD_SIZE + INDEX_HEADER_SIZE).toLong())
randomAccessFile.writeLong(id.id)
randomAccessFile.writeInt(if (id.isLocal) 1 else 0)
randomAccessFile.writeInt(currentPosition)
randomAccessFile.writeInt(bytes.size)
randomAccessFile.seek(currentPosition.toLong())
randomAccessFile.write(bytes)
assert(randomAccessFile.filePointer < Int.MAX_VALUE.toLong())
currentPosition = randomAccessFile.filePointer.toInt()
currentDeclaration++
}
fun finishWriting(): File {
assert(currentDeclaration == declarationCount)
randomAccessFile.close()
return file
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrAnonymousInitializerImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrSymbol
class DescriptorTable {
private val descriptors = mutableMapOf<DeclarationDescriptor, Long>()
fun put(descriptor: DeclarationDescriptor, uniqId: UniqId) {
descriptors.getOrPut(descriptor) { uniqId.index }
}
fun get(descriptor: DeclarationDescriptor) = descriptors[descriptor]
}
// TODO: We don't manage id clashes anyhow now.
abstract class DeclarationTable(val builtIns: IrBuiltIns, val descriptorTable: DescriptorTable, mangler: KotlinMangler): KotlinMangler by mangler {
private val builtInsTable = mutableMapOf<IrSymbol, UniqId>()
private val table = mutableMapOf<IrDeclaration, UniqId>()
val descriptors = descriptorTable
protected abstract var currentIndex: Long
open fun loadKnownBuiltins(): Long {
builtIns.knownBuiltins.forEach {
builtInsTable[it] = UniqId(currentIndex++, false)
}
return currentIndex
}
fun uniqIdByDeclaration(value: IrDeclaration) = (value as? IrSymbolOwner)?.let { builtInsTable[it.symbol] } ?: table.getOrPut(value) {
computeUniqIdByDeclaration(value)
}
open protected fun computeUniqIdByDeclaration(value: IrDeclaration): UniqId {
val index = if (value.origin == IrDeclarationOrigin.FAKE_OVERRIDE ||
!value.isExported()
|| value is IrVariable
|| (value is IrTypeParameter && value.parent !is IrClass)
|| value is IrValueParameter
|| value is IrAnonymousInitializerImpl
) {
UniqId(currentIndex++, true)
} else {
UniqId(value.hashedMangle, false)
}
return index
}
}
// This is what we pre-populate tables with
val IrBuiltIns.knownBuiltins
get() = irBuiltInsSymbols
@@ -0,0 +1,161 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.backend.common.serialization.UniqIdKey
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.backend.common.serialization.resolveFakeOverrideMaybeAbstract
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.resolve.scopes.MemberScope
abstract class DescriptorReferenceDeserializer(
val currentModule: ModuleDescriptor,
val resolvedForwardDeclarations: MutableMap<UniqIdKey, UniqIdKey>
) : DescriptorUniqIdAware {
protected abstract fun resolveSpecialDescriptor(fqn: FqName): DeclarationDescriptor
protected abstract fun checkIfSpecialDescriptorId(id: Long): Boolean
protected abstract fun getDescriptorIdOrNull(descriptor: DeclarationDescriptor): Long?
protected fun getContributedDescriptors(packageFqNameString: String, name: String): Collection<DeclarationDescriptor> {
val packageFqName = packageFqNameString.let {
if (it == "<root>") FqName.ROOT else FqName(it)
}// TODO: would we store an empty string in the protobuf?
val memberScope = currentModule.getPackage(packageFqName).memberScope
return getContributedDescriptors(memberScope, name)
}
protected fun getContributedDescriptors(memberScope: MemberScope, name: String): Collection<DeclarationDescriptor> {
val contributedNameString = if (name.startsWith("<get-") || name.startsWith("<set-")) {
name.substring(5, name.length - 1) // FIXME: rework serialization format.
} else {
name
}
val contributedName = Name.identifier(contributedNameString)
return memberScope.getContributedFunctions(contributedName, NoLookupLocation.FROM_BACKEND) +
memberScope.getContributedVariables(contributedName, NoLookupLocation.FROM_BACKEND) +
listOfNotNull(memberScope.getContributedClassifier(contributedName, NoLookupLocation.FROM_BACKEND))
}
protected class ClassMembers(val defaultConstructor: ClassConstructorDescriptor?,
val members: Map<Long, DeclarationDescriptor>,
val realMembers: Map<Long, DeclarationDescriptor>)
private fun computeUniqIdIndex(descriptor: DeclarationDescriptor) = descriptor.getUniqId() ?: getDescriptorIdOrNull(descriptor)
protected fun getMembers(members: Collection<DeclarationDescriptor>): ClassMembers {
val allMembersMap = mutableMapOf<Long, DeclarationDescriptor>()
val realMembersMap = mutableMapOf<Long, DeclarationDescriptor>()
var classConstructorDescriptor: ClassConstructorDescriptor? = null
members.forEach { member ->
if (member is ClassConstructorDescriptor)
classConstructorDescriptor = member
val realMembers =
if (member is CallableMemberDescriptor && member.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE)
member.resolveFakeOverrideMaybeAbstract()
else
setOf(member)
computeUniqIdIndex(member)?.let { allMembersMap[it] = member }
realMembers.mapNotNull { computeUniqIdIndex(it) }.forEach { realMembersMap[it] = member }
}
return ClassMembers(classConstructorDescriptor, allMembersMap, realMembersMap)
}
open fun deserializeDescriptorReference(
packageFqNameString: String,
classFqNameString: String,
name: String,
index: Long?,
isEnumEntry: Boolean = false,
isEnumSpecial: Boolean = false,
isDefaultConstructor: Boolean = false,
isFakeOverride: Boolean = false,
isGetter: Boolean = false,
isSetter: Boolean = false,
isTypeParameter: Boolean = false
): DeclarationDescriptor {
val packageFqName = packageFqNameString.let {
if (it == "<root>") FqName.ROOT else FqName(it)
}// TODO: whould we store an empty string in the protobuf?
val classFqName = if (classFqNameString == "<root>") FqName.ROOT else FqName(classFqNameString)
val protoIndex = index
val (clazz, members) = if (classFqNameString == "" || classFqNameString == "<root>") {
Pair(null, getContributedDescriptors(packageFqNameString, name))
} else {
val clazz = currentModule.findClassAcrossModuleDependencies(ClassId(packageFqName, classFqName, false))!!
Pair(clazz, getContributedDescriptors(clazz.unsubstitutedMemberScope, name) + clazz.getConstructors())
}
// TODO: This is still native specific. Eliminate.
if (packageFqNameString.startsWith("cnames.") || packageFqNameString.startsWith("objcnames.")) {
val descriptor =
currentModule.findClassAcrossModuleDependencies(ClassId(packageFqName, FqName(name), false))!!
if (!descriptor.fqNameUnsafe.asString().startsWith("cnames") && !descriptor.fqNameUnsafe.asString().startsWith(
"objcnames"
)
) {
if (descriptor is DeserializedClassDescriptor) {
val uniqId = UniqId(descriptor.getUniqId()!!, false)
val newKey = UniqIdKey(null, uniqId)
val oldKey = UniqIdKey(null, UniqId(protoIndex!!, false))
resolvedForwardDeclarations.put(oldKey, newKey)
} else {
/* ??? */
}
}
return descriptor
}
if (isEnumEntry) {
val memberScope = (clazz as DeserializedClassDescriptor).getUnsubstitutedMemberScope()
return memberScope.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BACKEND)!!
}
if (isEnumSpecial) {
return clazz!!.getStaticScope()
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).single()
}
if (isTypeParameter) {
return clazz!!.declaredTypeParameters.first { it.name.asString() == name }
}
if (protoIndex?.let { checkIfSpecialDescriptorId(it) } == true) {
return resolveSpecialDescriptor(packageFqName.child(Name.identifier(name)))
}
val membersWithIndices = getMembers(members)
return when {
isDefaultConstructor -> membersWithIndices.defaultConstructor
else -> {
val map = if (isFakeOverride) membersWithIndices.realMembers else membersWithIndices.members
map[protoIndex]?.let { member ->
when {
member is PropertyDescriptor && isSetter -> member.setter!!
member is PropertyDescriptor && isGetter -> member.getter!!
else -> member
}
}
}
} ?:
error("Could not find serialized descriptor for index: ${index} ${packageFqName},${classFqName},${name}")
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.isReal
import org.jetbrains.kotlin.ir.util.original
/**
* Implementation of given method.
*
* TODO: this method is actually a part of resolve and probably duplicates another one
*/
fun IrSimpleFunction.resolveFakeOverride(allowAbstract: Boolean = false): IrSimpleFunction {
if (this.isReal) {
return this
}
val visited = mutableSetOf<IrSimpleFunction>()
val realSupers = mutableSetOf<IrSimpleFunction>()
fun findRealSupers(function: IrSimpleFunction) {
if (function in visited) return
visited += function
if (function.isReal) {
realSupers += function
} else {
function.overriddenSymbols.forEach { findRealSupers(it.owner) }
}
}
findRealSupers(this)
if (realSupers.size > 1) {
visited.clear()
fun excludeOverridden(function: IrSimpleFunction) {
if (function in visited) return
visited += function
function.overriddenSymbols.forEach {
realSupers.remove(it.owner)
excludeOverridden(it.owner)
}
}
realSupers.toList().forEach { excludeOverridden(it) }
}
return realSupers.first { allowAbstract || it.modality != Modality.ABSTRACT }
}
/**
* Implementation of given method.
*
* TODO: this method is actually a part of resolve and probably duplicates another one
*/
internal fun IrSimpleFunction.resolveFakeOverrideMaybeAbstract() = this.resolveFakeOverride(allowAbstract = true)
internal fun IrProperty.resolveFakeOverrideMaybeAbstract() = this.getter!!.resolveFakeOverrideMaybeAbstract().correspondingProperty!!
internal fun IrField.resolveFakeOverrideMaybeAbstract() = this.correspondingProperty!!.getter!!.resolveFakeOverrideMaybeAbstract().correspondingProperty!!.backingField
val IrSimpleFunction.target: IrSimpleFunction
get() = (if (modality == Modality.ABSTRACT) this else resolveFakeOverride()).original
val IrFunction.target: IrFunction get() = when (this) {
is IrSimpleFunction -> this.target
is IrConstructor -> this
else -> error(this)
}
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
class SerializedIr (
val module: ByteArray,
val combinedDeclarationFilePath: String
)
@@ -0,0 +1,445 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.descriptors.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.impl.IrLoopBase
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrFileSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.IrDeserializer
import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite.newInstance
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
abstract class KotlinIrLinker(
val logger: LoggingContext,
val builtIns: IrBuiltIns,
val symbolTable: SymbolTable,
val exportedDependencies: List<ModuleDescriptor>,
private val forwardModuleDescriptor: ModuleDescriptor?,
private val firstKnownBuiltinsIndex: Long
) : DescriptorUniqIdAware, IrDeserializer {
protected val deserializedSymbols = mutableMapOf<UniqIdKey, IrSymbol>()
private val reachableTopLevels = mutableSetOf<UniqIdKey>()
private val deserializedTopLevels = mutableSetOf<UniqIdKey>()
//TODO: This is Native specific. Eliminate me.
private val forwardDeclarations = mutableSetOf<IrSymbol>()
val resolvedForwardDeclarations = mutableMapOf<UniqIdKey, UniqIdKey>()
protected val deserializersForModules = mutableMapOf<ModuleDescriptor, IrDeserializerForModule>()
val fileAnnotations = mutableMapOf<IrFile, KotlinIr.Annotations>()
inner class IrDeserializerForModule(
private val moduleDescriptor: ModuleDescriptor,
private val moduleProto: KotlinIr.IrModule,
private val deserializationStrategy: DeserializationStrategy
) : IrModuleDeserializer(logger, builtIns, symbolTable) {
private var moduleLoops = mutableMapOf<Int, IrLoopBase>()
// This is a heavy initializer
val module = deserializeIrModuleHeader(moduleProto)
private fun referenceDeserializedSymbol(
proto: KotlinIr.IrSymbolData,
descriptor: DeclarationDescriptor?
): IrSymbol = when (proto.kind) {
KotlinIr.IrSymbolKind.ANONYMOUS_INIT_SYMBOL ->
IrAnonymousInitializerSymbolImpl(
descriptor as ClassDescriptor?
?: WrappedClassDescriptor()
)
KotlinIr.IrSymbolKind.CLASS_SYMBOL ->
symbolTable.referenceClass(
descriptor as ClassDescriptor?
?: WrappedClassDescriptor()
)
KotlinIr.IrSymbolKind.CONSTRUCTOR_SYMBOL ->
symbolTable.referenceConstructor(
descriptor as ClassConstructorDescriptor?
?: WrappedClassConstructorDescriptor()
)
KotlinIr.IrSymbolKind.TYPE_PARAMETER_SYMBOL ->
symbolTable.referenceTypeParameter(
descriptor as TypeParameterDescriptor?
?: WrappedTypeParameterDescriptor()
)
KotlinIr.IrSymbolKind.ENUM_ENTRY_SYMBOL ->
symbolTable.referenceEnumEntry(
descriptor as ClassDescriptor?
?: WrappedEnumEntryDescriptor()
)
KotlinIr.IrSymbolKind.STANDALONE_FIELD_SYMBOL ->
symbolTable.referenceField(WrappedFieldDescriptor())
KotlinIr.IrSymbolKind.FIELD_SYMBOL ->
symbolTable.referenceField(
descriptor as PropertyDescriptor?
?: WrappedPropertyDescriptor()
)
KotlinIr.IrSymbolKind.FUNCTION_SYMBOL ->
symbolTable.referenceSimpleFunction(
descriptor as FunctionDescriptor?
?: WrappedSimpleFunctionDescriptor()
)
KotlinIr.IrSymbolKind.VARIABLE_SYMBOL ->
IrVariableSymbolImpl(
descriptor as VariableDescriptor?
?: WrappedVariableDescriptor()
)
KotlinIr.IrSymbolKind.VALUE_PARAMETER_SYMBOL ->
IrValueParameterSymbolImpl(
descriptor as ParameterDescriptor?
?: WrappedValueParameterDescriptor()
)
KotlinIr.IrSymbolKind.RECEIVER_PARAMETER_SYMBOL ->
IrValueParameterSymbolImpl(
descriptor as ParameterDescriptor? ?: WrappedReceiverParameterDescriptor()
)
else -> TODO("Unexpected classifier symbol kind: ${proto.kind}")
}
override fun deserializeIrSymbol(proto: KotlinIr.IrSymbol): IrSymbol {
val symbolData = moduleProto.symbolTable.getSymbols(proto.index)
return deserializeIrSymbolData(symbolData)
}
override fun deserializeIrType(proto: KotlinIr.IrTypeIndex): IrType {
val typeData = moduleProto.typeTable.getTypes(proto.index)
return deserializeIrTypeData(typeData)
}
override fun deserializeString(proto: KotlinIr.String): String =
moduleProto.stringTable.getStrings(proto.index)
override fun deserializeLoopHeader(loopIndex: Int, loopBuilder: () -> IrLoopBase) =
moduleLoops.getOrPut(loopIndex, loopBuilder)
private fun deserializeIrSymbolData(proto: KotlinIr.IrSymbolData): IrSymbol {
val key = proto.uniqId.uniqIdKey(moduleDescriptor)
val topLevelKey = proto.topLevelUniqId.uniqIdKey(moduleDescriptor)
if (!deserializedTopLevels.contains(topLevelKey)) reachableTopLevels.add(topLevelKey)
val symbol = deserializedSymbols.getOrPut(key) {
val descriptor = if (proto.hasDescriptorReference()) {
deserializeDescriptorReference(proto.descriptorReference)
} else {
null
}
resolvedForwardDeclarations[key]?.let {
if (!deserializedTopLevels.contains(it)) reachableTopLevels.add(it) // Assuming forward declarations are always top levels.
}
descriptor?.module?.let {
if (!deserializersForModules.containsKey(it) && !it.isForwardDeclarationModule) {
deserializeIrModuleHeader(it)!!
}
}
referenceDeserializedSymbol(proto, descriptor)
}
if (symbol.descriptor is ClassDescriptor &&
symbol.descriptor !is WrappedDeclarationDescriptor<*> &&
symbol.descriptor.module.isForwardDeclarationModule
) {
forwardDeclarations.add(symbol)
}
return symbol
}
override fun deserializeDescriptorReference(proto: KotlinIr.DescriptorReference) =
descriptorReferenceDeserializer.deserializeDescriptorReference(
deserializeString(proto.packageFqName),
deserializeString(proto.classFqName),
deserializeString(proto.name),
if (proto.hasUniqId()) proto.uniqId.index else null,
isEnumEntry = proto.isEnumEntry,
isEnumSpecial = proto.isEnumSpecial,
isDefaultConstructor = proto.isDefaultConstructor,
isFakeOverride = proto.isFakeOverride,
isGetter = proto.isGetter,
isSetter = proto.isSetter,
isTypeParameter = proto.isTypeParameter
)
// TODO: this is JS specific. Eliminate me.
override fun getPrimitiveTypeOrNull(symbol: IrClassifierSymbol, hasQuestionMark: Boolean) =
this@KotlinIrLinker.getPrimitiveTypeOrNull(symbol, hasQuestionMark)
fun deserializeIrFile(fileProto: KotlinIr.IrFile): IrFile {
val fileEntry = NaiveSourceBasedFileEntryImpl(
this.deserializeString(fileProto.fileEntry.name),
fileProto.fileEntry.lineStartOffsetsList.toIntArray()
)
// TODO: we need to store "" in protobuf, I suppose. Or better yet, reuse fqname storage from metadata.
val fqName = this.deserializeString(fileProto.fqName)
.let { if (it == "<root>") FqName.ROOT else FqName(it) }
val packageFragmentDescriptor = EmptyPackageFragmentDescriptor(moduleDescriptor, fqName)
val symbol = IrFileSymbolImpl(packageFragmentDescriptor)
val file = IrFileImpl(fileEntry, symbol, fqName)
// We deserialize file annotations on first file use.
fileAnnotations.put(file, fileProto.annotations)
fileProto.declarationIdList.forEach {
val uniqIdKey = it.uniqIdKey(moduleDescriptor)
reversedFileIndex.put(uniqIdKey, file)
}
when (deserializationStrategy) {
DeserializationStrategy.EXPLICITLY_EXPORTED -> {
fileProto.explicitlyExportedToCompilerList.forEach {
val symbolProto = moduleProto.symbolTable.getSymbols(it.index)
reachableTopLevels.add(symbolProto.topLevelUniqId.uniqIdKey(moduleDescriptor))
}
}
DeserializationStrategy.ALL -> {
fileProto.declarationIdList.forEach {
val uniqIdKey = it.uniqIdKey(moduleDescriptor)
reachableTopLevels.add(uniqIdKey)
}
}
else -> error("Unixpected deserialization strategy")
}
return file
}
fun deserializeIrModuleHeader(
proto: KotlinIr.IrModule
): IrModuleFragment {
val files = proto.fileList.map {
deserializeIrFile(it)
}
val module = IrModuleFragmentImpl(moduleDescriptor, builtIns, files)
module.patchDeclarationParents(null)
return module
}
}
// TODO: this is JS specific. Eliminate me.
protected open fun getPrimitiveTypeOrNull(symbol: IrClassifierSymbol, hasQuestionMark: Boolean): IrSimpleType? = null
protected abstract val descriptorReferenceDeserializer: DescriptorReferenceDeserializer
protected val indexAfterKnownBuiltins = loadKnownBuiltinSymbols()
private fun loadKnownBuiltinSymbols(): Long {
var currentIndex = firstKnownBuiltinsIndex
builtIns.knownBuiltins.forEach {
deserializedSymbols[UniqIdKey(null, UniqId(currentIndex, isLocal = false))] = it
assert(symbolTable.referenceSimpleFunction(it.descriptor) == it)
currentIndex++
}
return currentIndex
}
private val ByteArray.codedInputStream: org.jetbrains.kotlin.protobuf.CodedInputStream
get() {
val codedInputStream = org.jetbrains.kotlin.protobuf.CodedInputStream.newInstance(this)
codedInputStream.setRecursionLimit(65535) // The default 64 is blatantly not enough for IR.
return codedInputStream
}
private val reversedFileIndex = mutableMapOf<UniqIdKey, IrFile>()
private val UniqIdKey.moduleOfOrigin
get() =
this.moduleDescriptor ?: reversedFileIndex[this]?.packageFragmentDescriptor?.containingDeclaration
private fun deserializeTopLevelDeclaration(uniqIdKey: UniqIdKey): IrDeclaration {
val proto = loadTopLevelDeclarationProto(uniqIdKey)
return deserializersForModules[uniqIdKey.moduleOfOrigin]!!
.deserializeDeclaration(proto, reversedFileIndex[uniqIdKey]!!)
}
protected abstract fun reader(moduleDescriptor: ModuleDescriptor, uniqId: UniqId): ByteArray
private fun loadTopLevelDeclarationProto(uniqIdKey: UniqIdKey): KotlinIr.IrDeclaration {
val stream = reader(uniqIdKey.moduleOfOrigin!!, uniqIdKey.uniqId).codedInputStream
return KotlinIr.IrDeclaration.parseFrom(stream, newInstance())
}
private fun deserializeFileAnnotationsIfFirstUse(module: ModuleDescriptor, file: IrFile) {
val annotations = fileAnnotations[file] ?: return
file.annotations.addAll(deserializersForModules[module]!!.deserializeAnnotations(annotations))
fileAnnotations.remove(file)
}
private fun deserializeAllReachableTopLevels() {
do {
val key = reachableTopLevels.first()
val moduleOfOrigin = key.moduleOfOrigin
if (deserializedSymbols[key]?.isBound == true ||
// The key.moduleOrigin is null for uniqIds that we haven't seen in any of the library headers.
// Just skip it for now and handle it elsewhere.
moduleOfOrigin == null
) {
reachableTopLevels.remove(key)
deserializedTopLevels.add(key)
continue
}
val reachable = deserializeTopLevelDeclaration(key)
val file = reversedFileIndex[key]!!
file.declarations.add(reachable)
reachable.patchDeclarationParents(file)
deserializeFileAnnotationsIfFirstUse(moduleOfOrigin, file)
reachableTopLevels.remove(key)
deserializedTopLevels.add(key)
} while (reachableTopLevels.isNotEmpty())
}
private fun findDeserializedDeclarationForDescriptor(descriptor: DeclarationDescriptor): DeclarationDescriptor? {
val topLevelDescriptor = descriptor.findTopLevelDescriptor()
// This is Native specific. Try to eliminate.
if (topLevelDescriptor.module.isForwardDeclarationModule) return null
if (topLevelDescriptor !is DeserializedClassDescriptor && topLevelDescriptor !is DeserializedCallableMemberDescriptor) {
return null
}
val descriptorUniqId = topLevelDescriptor.getUniqId()
?: error("could not get descriptor uniq id for $topLevelDescriptor")
val uniqId = UniqId(descriptorUniqId, isLocal = false)
val topLevelKey = UniqIdKey(topLevelDescriptor.module, uniqId)
// This top level descriptor doesn't have a serialized IR declaration.
if (topLevelKey.moduleOfOrigin == null) return null
reachableTopLevels.add(topLevelKey)
deserializeAllReachableTopLevels()
return topLevelDescriptor
}
override fun findDeserializedDeclaration(symbol: IrSymbol): IrDeclaration? {
if (!symbol.isBound) {
findDeserializedDeclarationForDescriptor(symbol.descriptor) ?: return null
}
assert(symbol.isBound) {
"findDeserializedDeclaration: symbol ${symbol} is unbound, descriptor = ${symbol.descriptor}, hash = ${symbol.descriptor.hashCode()}"
}
return symbol.owner as IrDeclaration
}
override fun findDeserializedDeclaration(propertyDescriptor: PropertyDescriptor): IrProperty? {
val topLevelDesecriptor = findDeserializedDeclarationForDescriptor(propertyDescriptor)
if (topLevelDesecriptor == null) return null
return symbolTable.propertyTable[propertyDescriptor]
?: error("findDeserializedDeclaration: property descriptor $propertyDescriptor} is not present in propertyTable after deserialization}")
}
// TODO: This is Native specific. Eliminate me.
override fun declareForwardDeclarations() {
if (forwardModuleDescriptor == null) return
val packageFragments = forwardDeclarations.map { it.descriptor.findPackage() }.distinct()
// We don't bother making a real IR module here, as we have no need in it any later.
// All we need is just to declare forward declarations in the symbol table
// In case you need a full fledged module, turn the forEach into a map and collect
// produced files into an IrModuleFragment.
packageFragments.forEach { packageFragment ->
val symbol = IrFileSymbolImpl(packageFragment)
val file = IrFileImpl(NaiveSourceBasedFileEntryImpl("forward declarations pseudo-file"), symbol)
val symbols = forwardDeclarations
.filter { !it.isBound }
.filter { it.descriptor.findPackage() == packageFragment }
val declarations = symbols.map {
val classDescriptor = it.descriptor as ClassDescriptor
val declaration = symbolTable.declareClass(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, irrelevantOrigin,
classDescriptor,
classDescriptor.modality
) { symbol: IrClassSymbol -> IrClassImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irrelevantOrigin, symbol) }
.also {
it.parent = file
}
declaration
}
file.declarations.addAll(declarations)
}
}
fun deserializeIrModuleHeader(
moduleDescriptor: ModuleDescriptor,
byteArray: ByteArray,
deserializationStrategy: DeserializationStrategy = DeserializationStrategy.ONLY_REFERENCED
): IrModuleFragment {
val deserializerForModule = deserializersForModules.getOrPut(moduleDescriptor) {
val proto = KotlinIr.IrModule.parseFrom(byteArray.codedInputStream, newInstance())
IrDeserializerForModule(moduleDescriptor, proto, deserializationStrategy)
}
// The IrModule and its IrFiles have been created during module initialization.
return deserializerForModule.module
}
abstract val ModuleDescriptor.irHeader: ByteArray?
fun deserializeIrModuleHeader(moduleDescriptor: ModuleDescriptor): IrModuleFragment? =
// TODO: do we really allow libraries without any IR?
moduleDescriptor.irHeader?.let { header ->
// TODO: consider skip deserializing explicitly exported declarations for libraries.
// Now it's not valid because of all dependencies that must be computed.
val deserializationStrategy =
if (exportedDependencies.contains(moduleDescriptor)) {
DeserializationStrategy.ALL
} else {
DeserializationStrategy.EXPLICITLY_EXPORTED
}
deserializeIrModuleHeader(moduleDescriptor, header, deserializationStrategy)
}
}
enum class DeserializationStrategy {
ONLY_REFERENCED,
ALL,
EXPLICITLY_EXPORTED
}
@@ -0,0 +1,271 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
interface KotlinMangler {
val String.hashMangle: Long
val IrDeclaration.hashedMangle: Long
fun IrDeclaration.isExported(): Boolean
val IrFunction.functionName: String
}
abstract class KotlinManglerImpl: KotlinMangler {
override val IrDeclaration.hashedMangle: Long
get() = this.uniqSymbolName().hashMangle
// We can't call "with (super) { this.isExported() }" in children.
// So provide a hook.
protected open fun IrDeclaration.isPlatformSpecificExported(): Boolean = false
/**
* Defines whether the declaration is exported, i.e. visible from other modules.
*
* Exported declarations must have predictable and stable ABI
* that doesn't depend on any internal transformations (e.g. IR lowering),
* and so should be computable from the descriptor itself without checking a backend state.
*/
override tailrec fun IrDeclaration.isExported(): Boolean {
// TODO: revise
val descriptorAnnotations = this.descriptor.annotations
if (this.isPlatformSpecificExported()) return true
if (descriptorAnnotations.hasAnnotation(publishedApiAnnotation)) {
return true
}
if (this.isAnonymousObject)
return false
if (this is IrConstructor && constructedClass.kind.isSingleton) {
// Currently code generator can access the constructor of the singleton,
// so ignore visibility of the constructor itself.
return constructedClass.isExported()
}
if (this is IrFunction) {
val descriptor = this.descriptor
// TODO: this code is required because accessor doesn't have a reference to property.
if (descriptor is PropertyAccessorDescriptor) {
val property = descriptor.correspondingProperty
if (property.annotations.hasAnnotation(publishedApiAnnotation)) return true
}
}
val visibility = when (this) {
is IrClass -> this.visibility
is IrFunction -> this.visibility
is IrProperty -> this.visibility
is IrField -> this.visibility
else -> null
}
/**
* note: about INTERNAL - with support of friend modules we let frontend to deal with internal declarations.
*/
if (visibility != null && !visibility.isPublicAPI && visibility != Visibilities.INTERNAL) {
// If the declaration is explicitly marked as non-public,
// then it must not be accessible from other modules.
return false
}
val parent = this.parent
if (parent is IrDeclaration) {
return parent.isExported()
}
return true
}
private val publishedApiAnnotation = FqName("kotlin.PublishedApi")
protected fun acyclicTypeMangler(visited: MutableSet<IrTypeParameter>, type: IrType): String {
val descriptor = (type.classifierOrNull as? IrTypeParameterSymbol)?.owner
if (descriptor != null) {
val upperBounds = if (visited.contains(descriptor)) "" else {
visited.add(descriptor)
descriptor.superTypes.map {
val bound = acyclicTypeMangler(visited, it)
if (bound == "kotlin.Any?") "" else "_$bound"
}.joinToString("")
}
return "#GENERIC${if (type.isMarkedNullable()) "?" else ""}$upperBounds"
}
var hashString = type.getClass()?.run { fqNameSafe.asString() } ?: "<dynamic>"
when (type) {
is IrSimpleType -> {
if (!type.arguments.isEmpty()) {
hashString += "<${type.arguments.map {
when (it) {
is IrStarProjection -> "#STAR"
is IrTypeProjection -> {
val variance = it.variance.label
val projection = if (variance == "") "" else "${variance}_"
projection + acyclicTypeMangler(visited, it.type)
}
else -> error(it)
}
}.joinToString(",")}>"
}
if (type.hasQuestionMark) hashString += "?"
}
!is IrDynamicType -> {
error(type)
}
}
return hashString
}
protected fun typeToHashString(type: IrType) = acyclicTypeMangler(mutableSetOf(), type)
val IrValueParameter.extensionReceiverNamePart: String
get() = "@${typeToHashString(this.type)}."
open val IrFunction.argsPart get() = this.valueParameters.map {
"${typeToHashString(it.type)}${if (it.isVararg) "_VarArg" else ""}"
}.joinToString(";")
open val IrFunction.signature: String
get() {
val extensionReceiverPart = this.extensionReceiverParameter?.extensionReceiverNamePart ?: ""
val argsPart = this.argsPart
// Distinguish value types and references - it's needed for calling virtual methods through bridges.
// Also is function has type arguments - frontend allows exactly matching overrides.
val signatureSuffix =
when {
this.typeParameters.isNotEmpty() -> "Generic"
returnType.isInlined() -> "ValueType"
!returnType.isUnitOrNullableUnit() -> typeToHashString(returnType)
else -> ""
}
return "$extensionReceiverPart($argsPart)$signatureSuffix"
}
open val IrFunction.platformSpecificFunctionName: String? get() = null
// TODO: rename to indicate that it has signature included
override val IrFunction.functionName: String
get() {
// TODO: Again. We can't call super in children, so provide a hook for now.
this.platformSpecificFunctionName ?. let { return it }
val name = this.name.mangleIfInternal(this.module, this.visibility)
return "$name$signature"
}
fun Name.mangleIfInternal(moduleDescriptor: ModuleDescriptor, visibility: Visibility): String =
if (visibility != Visibilities.INTERNAL) {
this.asString()
} else {
val moduleName = moduleDescriptor.name.asString()
.let { it.substring(1, it.lastIndex) } // Remove < and >.
"$this\$$moduleName"
}
val IrField.symbolName: String
get() {
val containingDeclarationPart = parent.fqNameSafe.let {
if (it.isRoot) "" else "$it."
}
return "kfield:$containingDeclarationPart$name"
}
val IrClass.typeInfoSymbolName: String
get() {
assert(this.isExported())
return "ktype:" + this.fqNameSafe.toString()
}
val IrTypeParameter.symbolName: String
get() {
val containingDeclarationPart = parent.fqNameSafe
return "ktypeparam:$containingDeclarationPart$name"
}
// This is a little extension over what's used in real mangling
// since some declarations never appear in the bitcode symbols.
internal fun IrDeclaration.uniqSymbolName(): String = when (this) {
is IrFunction
-> this.uniqFunctionName
is IrProperty
-> this.symbolName
is IrClass
-> this.typeInfoSymbolName
is IrField
-> this.symbolName
is IrEnumEntry
-> this.symbolName
is IrTypeParameter
-> this.symbolName
else -> error("Unexpected exported declaration: $this")
}
private val IrDeclarationParent.fqNameUnique: FqName
get() = when (this) {
is IrPackageFragment -> this.fqName
is IrDeclaration -> this.parent.fqNameUnique.child(this.uniqName)
else -> error(this)
}
private val IrDeclaration.uniqName: Name
get() = when (this) {
is IrSimpleFunction -> Name.special("<${this.uniqFunctionName}>")
else -> this.name
}
private val IrProperty.symbolName: String
get() {
val extensionReceiver: String = getter!!.extensionReceiverParameter?.extensionReceiverNamePart ?: ""
val containingDeclarationPart = parent.fqNameSafe.let {
if (it.isRoot) "" else "$it."
}
return "kprop:$containingDeclarationPart$extensionReceiver$name"
}
private val IrEnumEntry.symbolName: String
get() {
val containingDeclarationPart = parent.fqNameSafe.let {
if (it.isRoot) "" else "$it."
}
return "kenumentry:$containingDeclarationPart$name"
}
// This is basicly the same as .symbolName, but disambiguates external functions with the same C name.
// In addition functions appearing in fq sequence appear as <full signature>.
private val IrFunction.uniqFunctionName: String
get() {
val parent = this.parent
val containingDeclarationPart = parent.fqNameUnique.let {
if (it.isRoot) "" else "$it."
}
return "kfun:$containingDeclarationPart#$functionName"
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.backend.common.descriptors.propertyIfAccessor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil
internal val DeclarationDescriptor.isExpectMember: Boolean
get() = this is MemberDescriptor && this.isExpect
internal val DeclarationDescriptor.isSerializableExpectClass: Boolean
get() = this is ClassDescriptor && ExpectedActualDeclarationChecker.shouldGenerateExpectClass(this)
internal tailrec fun DeclarationDescriptor.findPackage(): PackageFragmentDescriptor {
return if (this is PackageFragmentDescriptor) this
else this.containingDeclaration!!.findPackage()
}
fun DeclarationDescriptor.findTopLevelDescriptor(): DeclarationDescriptor {
return if (this.containingDeclaration is org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor) this.propertyIfAccessor
else this.containingDeclaration!!.findTopLevelDescriptor()
}
/**
* TODO: this method is actually a part of resolve and probably duplicates another one
*/
internal fun <T : CallableMemberDescriptor> T.resolveFakeOverrideMaybeAbstract(): Set<T> {
if (this.kind.isReal) {
return setOf(this)
} else {
val overridden = OverridingUtil.getOverriddenDeclarations(this)
val filtered = OverridingUtil.filterOutOverridden(overridden)
// TODO: is it correct to take first?
@Suppress("UNCHECKED_CAST")
return filtered as Set<T>
}
}
// This is Native specific. Try to eliminate.
val ModuleDescriptor.isForwardDeclarationModule get() =
name == Name.special("<forward declarations>")
@@ -0,0 +1,121 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.isAccessor
import org.jetbrains.kotlin.ir.util.isGetter
import org.jetbrains.kotlin.ir.util.isSetter
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
open class DescriptorReferenceSerializer(
val declarationTable: DeclarationTable,
val serializeString: (String) -> KotlinIr.String,
mangler: KotlinMangler): KotlinMangler by mangler {
// Not all exported descriptors are deserialized, some a synthesized anew during metadata deserialization.
// Those created descriptors can't carry the uniqIdIndex, since it is available only for deserialized descriptors.
// So we record the uniq id of some other "discoverable" descriptor for which we know for sure that it will be
// available as deserialized descriptor, plus the path to find the needed descriptor from that one.
fun serializeDescriptorReference(declaration: IrDeclaration): KotlinIr.DescriptorReference? {
val descriptor = declaration.descriptor
if (!declaration.isExported() &&
!((declaration as? IrDeclarationWithVisibility)?.visibility == Visibilities.INVISIBLE_FAKE)) {
return null
}
if (declaration is IrAnonymousInitializer) return null
if (descriptor is ParameterDescriptor ||
(descriptor is VariableDescriptor && descriptor !is PropertyDescriptor)
|| (declaration is IrTypeParameter && declaration.parent !is IrClass)
) return null
val containingDeclaration = descriptor.containingDeclaration!!
val (packageFqName, classFqName) = when (containingDeclaration) {
is ClassDescriptor -> {
val classId = containingDeclaration.classId ?: return null
Pair(classId.packageFqName.toString(), classId.relativeClassName.toString())
}
is PackageFragmentDescriptor -> Pair(containingDeclaration.fqName.toString(), "")
else -> return null
}
val isAccessor = declaration.isAccessor
val isBackingField = declaration is IrField && declaration.correspondingProperty != null
val isFakeOverride = declaration.origin == IrDeclarationOrigin.FAKE_OVERRIDE
val isDefaultConstructor =
descriptor is ClassConstructorDescriptor && containingDeclaration is ClassDescriptor && containingDeclaration.kind == ClassKind.OBJECT
val isEnumEntry = descriptor is ClassDescriptor && descriptor.kind == ClassKind.ENUM_ENTRY
val isEnumSpecial = declaration.origin == IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER
val isTypeParameter = declaration is IrTypeParameter && declaration.parent is IrClass
val realDeclaration = if (isFakeOverride) {
when (declaration) {
is IrSimpleFunction -> declaration.resolveFakeOverrideMaybeAbstract()
is IrField -> declaration.resolveFakeOverrideMaybeAbstract()
is IrProperty -> declaration.resolveFakeOverrideMaybeAbstract()
else -> error("Unexpected fake override declaration")
}
} else {
declaration
}
val discoverableDescriptorsDeclaration: IrDeclaration? = if (isAccessor) {
(realDeclaration as IrSimpleFunction).correspondingProperty!!
} else if (isBackingField) {
(realDeclaration as IrField).correspondingProperty!!
} else if (isDefaultConstructor || isEnumEntry) {
null
} else {
realDeclaration
}
val uniqId = discoverableDescriptorsDeclaration?.let { declarationTable.uniqIdByDeclaration(it) }
uniqId?.let { declarationTable.descriptors.put(discoverableDescriptorsDeclaration.descriptor, it) }
val proto = KotlinIr.DescriptorReference.newBuilder()
.setPackageFqName(serializeString(packageFqName))
.setClassFqName(serializeString(classFqName))
.setName(serializeString(descriptor.name.toString()))
if (uniqId != null) proto.setUniqId(protoUniqId(uniqId))
if (isFakeOverride) {
proto.setIsFakeOverride(true)
}
if (isBackingField) {
proto.setIsBackingField(true)
}
if (isAccessor) {
if (declaration.isGetter)
proto.setIsGetter(true)
else if (declaration.isSetter)
proto.setIsSetter(true)
else
error("A property accessor which is neither a getter, nor a setter: $descriptor")
} else if (isDefaultConstructor) {
proto.setIsDefaultConstructor(true)
} else if (isEnumEntry) {
proto.setIsEnumEntry(true)
} else if (isEnumSpecial) {
proto.setIsEnumSpecial(true)
} else if (isTypeParameter) {
proto.setIsTypeParameter(true)
}
return proto.build()
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.descriptors.*
//import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataProtoBuf
import org.jetbrains.kotlin.protobuf.GeneratedMessageLite
// This is an abstract uniqIdIndex any serialized IR declarations gets.
// It is either isLocal and then just gets and ordinary number within its module.
// Or is visible across modules and then gets a hash of mangled name as its index.
data class UniqId (
val index: Long,
val isLocal: Boolean
)
// isLocal=true in UniqId is good while we dealing with a single current module.
// To disambiguate module local declarations of different modules we use UniqIdKey.
// It has moduleDescriptor specified for isLocal=true uniqIds.
// TODO: make sure UniqId is really uniq for any global declaration
data class UniqIdKey private constructor(val uniqId: UniqId, val moduleDescriptor: ModuleDescriptor?) {
constructor(moduleDescriptor: ModuleDescriptor?, uniqId: UniqId)
: this(uniqId, if (uniqId.isLocal) moduleDescriptor!! else null)
}
fun protoUniqId(uniqId: UniqId): KotlinIr.UniqId =
KotlinIr.UniqId.newBuilder()
.setIndex(uniqId.index)
.setIsLocal(uniqId.isLocal)
.build()
fun KotlinIr.UniqId.uniqId(): UniqId = UniqId(this.index, this.isLocal)
fun KotlinIr.UniqId.uniqIdKey(moduleDescriptor: ModuleDescriptor) =
UniqIdKey(moduleDescriptor, this.uniqId())
fun <T, M:GeneratedMessageLite.ExtendableMessage<M>> M.tryGetExtension(extension: GeneratedMessageLite.GeneratedExtension<M, T>)
= if (this.hasExtension(extension)) this.getExtension<T>(extension) else null
interface DescriptorUniqIdAware {
fun DeclarationDescriptor.getUniqId(): Long?
}
//val UniqId.declarationFileName: String get() = "$index${if (isLocal) "L" else "G"}.kjd"