Skip to content

Commit

Permalink
[KT] Enable K2 in J2CL.
Browse files Browse the repository at this point in the history
As K2 allows us to declare our own version of IrDeserializer, this cl is also fixing the orphaned node issue and the SOE in the inliner.

PiperOrigin-RevId: 696305329
  • Loading branch information
jDramaix authored and copybara-github committed Nov 14, 2024
1 parent da07252 commit d1de80c
Show file tree
Hide file tree
Showing 8 changed files with 2,500 additions and 417 deletions.
2 changes: 0 additions & 2 deletions build_defs/internal_do_not_use/j2cl_common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -367,8 +367,6 @@ DEFAULT_J2CL_KOTLINCOPTS = [
# Have kotlinc's IR lowering passes generate objects for SAM implementations.
# J2CL lowering passes cannot handle invokedynamic-based representations.
"-Xsam-conversions=class",
# TODO(b/317551802): Remove this once the language version is updated.
"-language-version=1.9",
# TODO(b/347052390): Remove once the const evaluation optimization crash is fixed (KT-70391).
"-Xignore-const-optimization-errors",
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.google.j2cl.transpiler.frontend.common.PackageInfoCache
import com.google.j2cl.transpiler.frontend.jdt.JdtParser
import com.google.j2cl.transpiler.frontend.jdt.PackageAnnotationsResolver
import com.google.j2cl.transpiler.frontend.kotlin.ir.IntrinsicMethods
import com.google.j2cl.transpiler.frontend.kotlin.ir.JvmIrDeserializerImpl
import com.google.j2cl.transpiler.frontend.kotlin.lower.LoweringPasses
import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project
Expand All @@ -34,7 +35,6 @@ import java.io.File
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.backend.jvm.JvmIrDeserializerImpl
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys.ORIGINAL_MESSAGE_COLLECTOR_KEY
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys.PHASE_CONFIG
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/

package com.google.j2cl.transpiler.frontend.kotlin.ir

import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageSupportForLinker
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideDeclarationTable
import org.jetbrains.kotlin.backend.common.overrides.FileLocalAwareLinker
import org.jetbrains.kotlin.backend.common.overrides.IrLinkerFakeOverrideProvider
import org.jetbrains.kotlin.backend.common.serialization.*
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData
import org.jetbrains.kotlin.backend.common.serialization.proto.IdSignature as ProtoIdSignature
import org.jetbrains.kotlin.backend.common.serialization.proto.IrDeclaration as ProtoDeclaration
import org.jetbrains.kotlin.backend.common.serialization.proto.IrExpression as ProtoExpression
import org.jetbrains.kotlin.backend.common.serialization.proto.IrStatement as ProtoStatement
import org.jetbrains.kotlin.backend.common.serialization.proto.IrType as ProtoType
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureFactory
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
import org.jetbrains.kotlin.backend.jvm.JvmIrDeserializer
import org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext
import org.jetbrains.kotlin.backend.jvm.lower.SingletonObjectJvmStaticTransformer
import org.jetbrains.kotlin.backend.jvm.serialization.proto.JvmIr
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrFileSymbolImpl
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement

// Copied and modified from org.jetbrains.kotlin.backend.jvm.JvmIrDeserializerImpl and
// org.jetbrains.kotlin.backend.jvm.serialization.deserializeLazyDeclarations.kt
class JvmIrDeserializerImpl : JvmIrDeserializer {
override fun deserializeTopLevelClass(
irClass: IrClass,
irBuiltIns: IrBuiltIns,
symbolTable: SymbolTable,
irProviders: List<IrProvider>,
extensions: JvmGeneratorExtensions,
): Boolean {
val serializedIr =
when (val source = irClass.source) {
is KotlinJvmBinarySourceElement -> source.binaryClass.classHeader.serializedIr
is JvmPackagePartSource -> source.knownJvmBinaryClass?.classHeader?.serializedIr
else -> null
} ?: return false
deserializeFromByteArray(
serializedIr,
irBuiltIns,
symbolTable,
irProviders,
irClass,
JvmIrTypeSystemContext(irBuiltIns),
)

irClass.transform(
SingletonObjectJvmStaticTransformer(irBuiltIns, extensions.cachedFields),
null,
)

return true
}
}

fun deserializeFromByteArray(
byteArray: ByteArray,
irBuiltIns: IrBuiltIns,
symbolTable: SymbolTable,
irProviders: List<IrProvider>,
toplevelParent: IrClass,
typeSystemContext: IrTypeSystemContext,
) {
// MODIFIED BY GOOGLE
// Ensure symbols of all class declarations are populated in the symbol table to avoid the
// deserializer to create new IR nodes instead of modifying the existing ones.
symbolTable.addSymbolsRecursively(toplevelParent.symbol)
// END OF MODIFICATIONS

val irInterner = IrInterningService()
val irProto = JvmIr.ClassOrFile.parseFrom(byteArray.codedInputStream)
val irLibraryFile =
IrLibraryFileFromAnnotation(
irProto.typeList,
irProto.signatureList,
irProto.stringList,
irProto.bodyList,
irProto.debugInfoList,
)

// Only needed for local signature computation.
val dummyIrFile =
IrFileImpl(
NaiveSourceBasedFileEntryImpl("<unknown>"),
IrFileSymbolImpl(),
toplevelParent.packageFqName!!,
)
// On JVM, file-scope private declarations are uniquely identified by file facade's fq name.
val dummyFileSignature =
IdSignature.FileSignature(irProto.fileFacadeFqName, toplevelParent.packageFqName!!, "<unknown>")

val symbolDeserializer =
IrSymbolDeserializer(
symbolTable,
irLibraryFile,
fileSymbol = dummyIrFile.symbol,
fileSignature = dummyFileSignature,
enqueueLocalTopLevelDeclaration = {}, // just link to it in symbolTable
irInterner = irInterner,
) { idSignature, symbolKind ->
referencePublicSymbol(symbolTable, idSignature, symbolKind)
}

// We have to supply topLevelParent here, but this results in wrong values for parent fields in
// deeply embedded declarations.
// Patching will be needed.
val deserializer =
IrDeclarationDeserializer(
irBuiltIns,
symbolTable,
irBuiltIns.irFactory,
irLibraryFile,
toplevelParent,
allowAlreadyBoundSymbols = true,
allowErrorNodes = false,
deserializeInlineFunctions = true,
deserializeBodies = true,
symbolDeserializer,
onDeserializedClass = { _, _ -> },
needToDeserializeFakeOverrides = { false },
specialProcessingForMismatchedSymbolKind = null,
irInterner = irInterner,
)
for (declarationProto in irProto.declarationList) {
// MODIFIED BY GOOGLE
// Ensure that the deserializer set a parent for top level inline members.
// original code:
// deserializer.deserializeDeclaration(declarationProto, setParent = false)
val unused =
deserializer.deserializeDeclaration(
declarationProto,
setParent = toplevelParent.isFacadeClass,
)
// END OF MODIFICATION.
}

val signaturer = symbolTable.signaturer
if (signaturer == null) {
ExternalDependenciesGenerator(symbolTable, irProviders).generateUnboundSymbolsAsDependencies()
} else {
signaturer.withFileSignature(dummyFileSignature) {
ExternalDependenciesGenerator(symbolTable, irProviders).generateUnboundSymbolsAsDependencies()
}
}

toplevelParent.safelyInitializeAllLazyDescendants()
toplevelParent.patchDeclarationParents()
buildFakeOverridesForLocalClasses(
symbolTable,
typeSystemContext,
symbolDeserializer,
toplevelParent,
)
}

private fun IrElement.safelyInitializeAllLazyDescendants() {
// Traversal may trigger initialization of some child declaration,
// which may trigger initialization of some other IR element (e.g., IrProperty -> its
// getter/setter IrFunctions).,
// which may trigger adding it to its parent element (e.g. JvmFileFacadeClass),
// which may happen to be some element we are currently traversing,
// which would throw ConcurrentModificationException.
// The workaround is to traverse the subtree over snapshots first.

acceptVoid(
object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
val directChildrenSnapshot = mutableListOf<IrElement>()
element.acceptChildrenVoid(
object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
directChildrenSnapshot += element
}
}
)

for (child in directChildrenSnapshot) {
child.acceptChildrenVoid(this)
}
}
}
)
}

private class IrLibraryFileFromAnnotation(
private val types: List<ProtoType>,
private val signatures: List<ProtoIdSignature>,
private val strings: List<String>,
private val bodies: List<JvmIr.XStatementOrExpression>,
private val debugInfo: List<String>,
) : IrLibraryFile() {
override fun declaration(index: Int): ProtoDeclaration {
error("This method is never supposed to be called")
}

override fun type(index: Int): ProtoType = types[index]

override fun signature(index: Int): ProtoIdSignature = signatures[index]

override fun string(index: Int): String = strings[index]

override fun debugInfo(index: Int): String = debugInfo[index]

override fun expressionBody(index: Int): ProtoExpression =
bodies[index].also { require(it.hasExpression()) }.expression

override fun statementBody(index: Int): ProtoStatement =
bodies[index].also { require(it.hasStatement()) }.statement
}

// TODO: try to use plugin context here
private fun referencePublicSymbol(
symbolTable: SymbolTable,
idSig: IdSignature,
symbolKind: BinarySymbolData.SymbolKind,
): IrSymbol {
with(symbolTable) {
return when (symbolKind) {
BinarySymbolData.SymbolKind.CLASS_SYMBOL -> referenceClass(idSig)
BinarySymbolData.SymbolKind.CONSTRUCTOR_SYMBOL -> referenceConstructor(idSig)
BinarySymbolData.SymbolKind.ENUM_ENTRY_SYMBOL -> referenceEnumEntry(idSig)
BinarySymbolData.SymbolKind.STANDALONE_FIELD_SYMBOL,
BinarySymbolData.SymbolKind.FIELD_SYMBOL -> referenceField(idSig)
BinarySymbolData.SymbolKind.FUNCTION_SYMBOL -> referenceSimpleFunction(idSig)
BinarySymbolData.SymbolKind.TYPEALIAS_SYMBOL -> referenceTypeAlias(idSig)
BinarySymbolData.SymbolKind.PROPERTY_SYMBOL -> referenceProperty(idSig)
BinarySymbolData.SymbolKind.TYPE_PARAMETER_SYMBOL -> referenceTypeParameter(idSig)
else -> error("Unexpected classifier symbol kind: $symbolKind for signature $idSig")
}
}
}

// TODO: implement properly
fun makeSimpleFakeOverrideBuilder(
symbolTable: SymbolTable,
typeSystemContext: IrTypeSystemContext,
symbolDeserializer: IrSymbolDeserializer,
): IrLinkerFakeOverrideProvider {
return IrLinkerFakeOverrideProvider(
object : FileLocalAwareLinker {
override fun tryReferencingPropertyByLocalSignature(
parent: IrDeclaration,
idSignature: IdSignature,
): IrPropertySymbol = symbolDeserializer.referencePropertyByLocalSignature(idSignature)

override fun tryReferencingSimpleFunctionByLocalSignature(
parent: IrDeclaration,
idSignature: IdSignature,
): IrSimpleFunctionSymbol =
symbolDeserializer.referenceSimpleFunctionByLocalSignature(idSignature)
},
symbolTable,
JvmIrMangler,
typeSystemContext,
fakeOverrideDeclarationTable =
PrePopulatedDeclarationTable(symbolDeserializer.deserializedSymbols),
friendModules = emptyMap(), // TODO: provide friend modules
partialLinkageSupport = PartialLinkageSupportForLinker.DISABLED,
)
}

private fun buildFakeOverridesForLocalClasses(
symbolTable: SymbolTable,
typeSystemContext: IrTypeSystemContext,
symbolDeserializer: IrSymbolDeserializer,
toplevel: IrClass,
) {
val builder = makeSimpleFakeOverrideBuilder(symbolTable, typeSystemContext, symbolDeserializer)
toplevel.acceptChildrenVoid(
object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}

override fun visitClass(declaration: IrClass) {
if (declaration.visibility == DescriptorVisibilities.LOCAL) {
builder.provideFakeOverrides(declaration, CompatibilityMode.CURRENT)
}
super.visitClass(declaration)
}
}
)
}

class PrePopulatedDeclarationTable(sig2symbol: Map<IdSignature, IrSymbol>) :
FakeOverrideDeclarationTable(JvmIrMangler, signatureSerializerFactory = ::IdSignatureFactory) {
private val symbol2Sig = sig2symbol.entries.associate { (x, y) -> y to x }

override fun tryComputeBackendSpecificSignature(declaration: IrDeclaration): IdSignature? {
symbol2Sig[declaration.symbol]?.let {
return it
}
return super.tryComputeBackendSpecificSignature(declaration)
}
}
Loading

0 comments on commit d1de80c

Please sign in to comment.