Skip to content

Commit

Permalink
Basic implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
JavierSegoviaCordoba committed Dec 26, 2023
1 parent b28a6ce commit db543e9
Show file tree
Hide file tree
Showing 25 changed files with 953 additions and 32 deletions.
4 changes: 3 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
####################################################################################################
root.project.name=kopy
main.project.name=kopy-compiler
project.group=com.javiersc.kotlin.kopy
project.group=com.javiersc.kotlin
####################################################################################################
### CODE ANALYSIS ###
####################################################################################################
Expand Down Expand Up @@ -38,3 +38,5 @@ org.gradle.parallel=true
#org.gradle.unsafe.configuration-cache-problems=warn
#org.gradle.unsafe.configuration-cache.max-problems=100
signing.gnupg.executable=gpg
semver.stage=snapshot
semver.checkClean=false
6 changes: 3 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[versions]
hubdle = "0.5.0+2.0.0-dev-5387-SNAPSHOT"
hubdleCatalog = "0.1.14+2.0.0-dev-5387-SNAPSHOT"
javiersc-kotlin-kotlinCompilerExtensions = "0.1.1+2.0.0-dev-5387-SNAPSHOT"
hubdle = "0.5.0+2.0.0-dev-10501-SNAPSHOT"
hubdleCatalog = "0.1.18+2.0.0-dev-10501-SNAPSHOT"
javiersc-kotlin-kotlinCompilerExtensions = "0.1.1+2.0.0-dev-10501-SNAPSHOT"
javiersc-kotlin-kotlinStdlibAndTest = "0.1.0-SNAPSHOT"

[libraries]
Expand Down
4 changes: 0 additions & 4 deletions kopy-compiler/api/library.api

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.javiersc.kotlin.kopy.compiler

import com.javiersc.kotlin.kopy.kopy.compiler.KopyCompilerProjectData.Group
import com.javiersc.kotlin.kopy.kopy.compiler.KopyCompilerProjectData.Name
import com.javiersc.kotlin.kopy.compiler.KopyCompilerProjectData.Group
import com.javiersc.kotlin.kopy.compiler.KopyCompilerProjectData.Name
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.config.CompilerConfiguration
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.javiersc.kotlin.kopy.compiler.fir

import com.javiersc.kotlin.kopy.compiler.fir.checker.FirKopyCheckerExtension
import com.javiersc.kotlin.kopy.compiler.fir.generation.FirKopySupertypeGenerationExtension
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar

Expand All @@ -12,7 +14,11 @@ internal class FirKopyExtension(
registerGenerators()
}

private fun ExtensionRegistrarContext.registerCheckers() {}
private fun ExtensionRegistrarContext.registerCheckers() {
+::FirKopyCheckerExtension
}

private fun ExtensionRegistrarContext.registerGenerators() {}
private fun ExtensionRegistrarContext.registerGenerators() {
+::FirKopySupertypeGenerationExtension
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.javiersc.kotlin.kopy.compiler.fir.checker

import com.javiersc.kotlin.compiler.extensions.common.fqName
import com.javiersc.kotlin.kopy.KopyFunctionInvoke
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirCallChecker
import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
import org.jetbrains.kotlin.fir.expressions.FirCall
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.resolve.fqName

internal class FirKopyCheckerExtension(
session: FirSession,
) : FirAdditionalCheckersExtension(session) {

override val expressionCheckers: ExpressionCheckers =
object : ExpressionCheckers() {
override val callCheckers: Set<FirCallChecker> =
setOf(
object : FirCallChecker() {
override fun check(
expression: FirCall,
context: CheckerContext,
reporter: DiagnosticReporter
) {
if (
expression.annotations.any {
it.fqName(session) == fqName<KopyFunctionInvoke>()
}
)
println()
}
}
)
}

override fun FirDeclarationPredicateRegistrar.registerPredicates() {
// DeclarationPredicate.create {
// // metaAnnotated(fqName<KopyFunction>(), includeItself = true)
// // parentAnnotated(fqName<KopyFunction>())
// // annotated(fqName<KopyFunction>())
// // ancestorAnnotated(fqName<KopyFunction>())
// }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.javiersc.kotlin.kopy.compiler.fir.generation

import com.javiersc.kotlin.compiler.extensions.common.classId
import com.javiersc.kotlin.compiler.extensions.common.fqName
import com.javiersc.kotlin.compiler.extensions.fir.createFirResolvedTypeRef
import com.javiersc.kotlin.compiler.extensions.fir.toConeType
import com.javiersc.kotlin.kopy.Kopy as KopyAnnotation
import com.javiersc.kotlin.kopy.runtime.Kopyable
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension
import org.jetbrains.kotlin.fir.extensions.predicate.LookupPredicate
import org.jetbrains.kotlin.fir.resolve.fqName
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef

internal class FirKopySupertypeGenerationExtension(
session: FirSession,
) : FirSupertypeGenerationExtension(session) {

context(TypeResolveServiceContainer)
override fun computeAdditionalSupertypes(
classLikeDeclaration: FirClassLikeDeclaration,
resolvedSupertypes: List<FirResolvedTypeRef>
): List<FirResolvedTypeRef> {
if (classLikeDeclaration.annotations.isEmpty()) return emptyList()
if (classLikeDeclaration.annotations.none { it.isKopy }) return emptyList()
val typeArgument: ConeClassLikeType = classLikeDeclaration.classId.toConeType()
val type: ConeClassLikeType = classId<Kopyable<*>>().toConeType(typeArgument)
val supertype: FirResolvedTypeRef = createFirResolvedTypeRef(type)
return listOf(supertype)
}

override fun needTransformSupertypes(declaration: FirClassLikeDeclaration): Boolean = true

override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(LookupPredicate.create { annotated(fqName<KopyAnnotation>()) })
}

private val FirAnnotation.isKopy: Boolean
get() = fqName(session) == fqName<KopyAnnotation>()
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
package com.javiersc.kotlin.kopy.compiler.ir

import com.javiersc.kotlin.compiler.extensions.ir.asIrOrNull
import com.javiersc.kotlin.compiler.extensions.ir.name
import com.javiersc.kotlin.kopy.compiler.ir._internal.InvokeCallTransformer
import com.javiersc.kotlin.kopy.compiler.ir._internal.UpdateCallTransformer
import com.javiersc.kotlin.stdlib.tree.TreeNode
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.name.Name

internal class IrKopyGenerationExtension(
private val configuration: CompilerConfiguration,
) : IrGenerationExtension {

override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {}
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
moduleFragment.transform(InvokeCallTransformer(moduleFragment, pluginContext), null)
moduleFragment.transform(UpdateCallTransformer(moduleFragment, pluginContext), null)

println("IrKopyGenerationExtension")
}

private fun TreeNode<Name>.addNestedReceivers(call: IrFunctionAccessExpression) {
val receiver = call.extensionReceiver?.asIrOrNull<IrFunctionAccessExpression>() ?: return
addChild(TreeNode(receiver.name))
addNestedReceivers(receiver)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package com.javiersc.kotlin.kopy.compiler.ir._internal

import com.javiersc.kotlin.compiler.extensions.common.toCallableId
import com.javiersc.kotlin.compiler.extensions.common.toName
import com.javiersc.kotlin.compiler.extensions.ir.asIr
import com.javiersc.kotlin.compiler.extensions.ir.asIrOrNull
import com.javiersc.kotlin.compiler.extensions.ir.createIrFunctionExpression
import com.javiersc.kotlin.compiler.extensions.ir.declarationIrBuilder
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.builders.irSet
import org.jetbrains.kotlin.ir.builders.irTemporary
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.IrSetValue
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.statements
import org.jetbrains.kotlin.name.SpecialNames

internal class InvokeCallTransformer(
private val moduleFragment: IrModuleFragment,
private val pluginContext: IrPluginContext,
) : IrElementTransformerVoidWithContext() {

override fun visitCall(expression: IrCall): IrExpression {
fun originalCall(): IrExpression = super.visitCall(expression)

if (!expression.isKopyInvoke) return originalCall()
val runCall: IrFunctionAccessExpression = createRunCall(expression) ?: return originalCall()

return runCall
}

private fun IrSymbol.declarationIrBuilder(): DeclarationIrBuilder =
pluginContext.declarationIrBuilder(this)

private fun createRunCall(originalCall: IrCall): IrFunctionAccessExpression? {
val originalCallParent: IrDeclarationParent =
originalCall.findDeclarationParent(moduleFragment)?.asIrOrNull<IrDeclarationParent>()
?: return null
val type = originalCall.dispatchReceiver?.type ?: return null

val runFunction: IrSimpleFunction =
pluginContext
.referenceFunctions("kotlin.run".toCallableId())
.first { it.owner.typeParameters.count() == 2 }
.owner

val runBlockFunction: IrSimpleFunction =
pluginContext.irFactory
.buildFun {
name = SpecialNames.ANONYMOUS
visibility = DescriptorVisibilities.LOCAL
returnType = type
origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
}
.apply {
parent = originalCallParent
extensionReceiverParameter =
buildValueParameter(this) {
this.name = "${'$'}this${'$'}run".toName()
this.type = type
}
}

val originalStatements =
originalCall
.getValueArgument(0)
.asIrOrNull<IrFunctionExpression>()
?.function
?.body
?.statements ?: return null

val runBlockFunctionBody: IrBlockBody =
runBlockFunction.symbol.declarationIrBuilder().irBlockBody {
val thisRunReceiver: IrGetValue =
runBlockFunction.extensionReceiverParameter?.let { valueParameter ->
runBlockFunction.symbol.declarationIrBuilder().irGet(valueParameter)
} ?: return@irBlockBody

val tempVar: IrVariable = irTemporary(value = thisRunReceiver, isMutable = true)
for (statement in originalStatements) {
+statement.processStatement(originalCall, runBlockFunction, tempVar)
}
+irReturn(runBlockFunction.symbol.declarationIrBuilder().irGet(tempVar))
}

runBlockFunction.body = runBlockFunctionBody

val runCall: IrFunctionAccessExpression =
runFunction.symbol.declarationIrBuilder().irCall(runFunction).apply {
extensionReceiver = originalCall.dispatchReceiver
putTypeArgument(index = 0, type = type)
putTypeArgument(index = 1, type = type)
val kFunction1Type: IrSimpleType =
runFunction.valueParameters
.first()
.type
.asIr<IrSimpleType>()
.classifier
.typeWith(type, type)
putValueArgument(
index = 0,
valueArgument =
createIrFunctionExpression(
type = kFunction1Type,
function = runBlockFunction,
origin = IrStatementOrigin.LAMBDA,
)
)
this.type = type
}

return runCall
}

private fun IrStatement.processStatement(
originalCall: IrCall,
runBlockFunction: IrSimpleFunction,
tempVar: IrVariable,
): IrStatement {
val operatorCall: IrFunctionAccessExpression =
asIrOrNull<IrTypeOperatorCall>()?.argument?.asIrOrNull() ?: return this

val original = originalCall.dispatchReceiver.asIrOrNull<IrGetValue>() ?: return this
val originalType = original.type
val originalName = original.symbol.descriptor.name

val operator = operatorCall.dispatchReceiver.asIrOrNull<IrGetValue>() ?: return this
val operatorType = operator.type
val operatorName = operator.symbol.descriptor.name

val hasSameReceiver =
originalType == operatorType && "${'$'}this${'$'}$originalName" == "$operatorName"

if (!hasSameReceiver) return this

operatorCall.apply {
val receiver: () -> IrGetValue = {
runBlockFunction.symbol.declarationIrBuilder().irGet(tempVar)
}
dispatchReceiver = receiver()
extensionReceiver.asIrOrNull<IrFunctionAccessExpression>()?.dispatchReceiver =
receiver()
}
val tempVarSet: IrSetValue =
runBlockFunction.symbol.declarationIrBuilder().irSet(tempVar, operatorCall)

return tempVarSet
}
}
Loading

0 comments on commit db543e9

Please sign in to comment.