Skip to content
This repository was archived by the owner on Apr 20, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import java.io.File
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.ir.remapTypeParameters
import org.jetbrains.kotlin.backend.common.ir.setDeclarationsParent
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
Expand Down Expand Up @@ -56,6 +57,7 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeArgument
import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.remapTypes
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
Expand Down Expand Up @@ -125,6 +127,11 @@ class ReactFunctionCallTransformer(
result = false
}

if (declaration.isInline) {
messageCollector.report(CompilerMessageSeverity.ERROR, "RFunction annotated function cannot be inline", location)
result = false
}

if (declaration.extensionReceiverParameter?.type != classes.react.RBuilder) {
messageCollector.report(CompilerMessageSeverity.ERROR, "RFunction annotated function must be an extension function of react.RBuilder", location)
result = false
Expand All @@ -148,35 +155,40 @@ class ReactFunctionCallTransformer(
val body = (declaration.body as? IrBlockBody) ?: return
val parent = declaration.parent as IrDeclarationContainer

val props = context.buildPropsInterface(declaration)
props.parent = parent
newDeclarations.add(props)
val propsClass = context.buildPropsInterface(declaration)
propsClass.parent = parent
newDeclarations.add(propsClass)

val component = buildRFunctionProperty(parent, declaration, props, body)
val component = buildRFunctionProperty(parent, declaration, propsClass, body).apply {
val substitutionMap = (propsClass.typeParameters + declaration.typeParameters).associate { it.symbol to context.irBuiltIns.anyNType }
if (substitutionMap.isNotEmpty()) remapTypes(TypeSubstituteRemapper(substitutionMap))
}
newDeclarations.add(component)

declaration.body = buildNewBody(props, component, declaration)
val propsType = propsClass.symbol.createType(false, propsClass.typeParameters.map { context.irBuiltIns.anyNType as IrTypeArgument })
declaration.body = buildNewBody(propsClass, propsType, component, declaration)
}

private fun IrGeneratorContext.buildPropsInterface(declaration: IrSimpleFunction): IrClass {
val irClass = buildExternalInterface(
name = "${declaration.name}FuncProps",
visibility = DescriptorVisibilities.PRIVATE,
superTypes = listOf(classes.react.RProps)
superTypes = listOf(classes.react.RProps),
typeParameters = declaration.typeParameters
)
for (valueParameter in declaration.valueParameters) {
addExternalVarProperty(
container = irClass,
name = valueParameter.name,
type = valueParameter.type
type = valueParameter.type.remapTypeParameters(declaration, irClass)
)
}
return irClass
}

private fun buildRFunctionProperty(parent: IrDeclarationParent, declaration: IrSimpleFunction, propsClass: IrClass, body: IrBlockBody): IrProperty {
val fieldType = classes.react.RClass(propsClass.defaultType)
val name = "${declaration.name}_RFUNC".toUpperCase()
val name = "${declaration.name}_RFUNC".uppercase()

return context.buildStaticProperty(parent, fieldType, name) {
irExprBody(irCall_rFunction(propsClass.defaultType, "${declaration.name}") { function ->
Expand Down Expand Up @@ -247,11 +259,11 @@ class ReactFunctionCallTransformer(
}
}

private fun buildNewBody(propsClass: IrClass, componentProperty: IrProperty, declaration: IrSimpleFunction): IrBody {
private fun buildNewBody(propsClass: IrClass, propsType: IrType, componentProperty: IrProperty, declaration: IrSimpleFunction): IrBody {
return context.irBuilder(declaration.symbol).run {
irBlockBody {
+irCall_invoke(
propsClass.defaultType,
propsType,
irGet(declaration.extensionReceiverParameter!!),
irCall(componentProperty.getter!!, origin = IrStatementOrigin.GET_PROPERTY)
) { function ->
Expand All @@ -263,7 +275,7 @@ class ReactFunctionCallTransformer(

+irCall(property.setter!!, origin = IrStatementOrigin.EQ).apply {
val callee = functions.react.RElementBuilder.attrs.owner.getter!!
this.dispatchReceiver = IrCallImpl(startOffset, endOffset, propsClass.defaultType, callee.symbol, callee.typeParameters.size, callee.valueParameters.size, IrStatementOrigin.GET_PROPERTY).apply {
this.dispatchReceiver = IrCallImpl(startOffset, endOffset, propsType, callee.symbol, callee.typeParameters.size, callee.valueParameters.size, IrStatementOrigin.GET_PROPERTY).apply {
this.dispatchReceiver = irGet(rElementBuilder)
}
this.putValueArgument(0, irGet(valueParameter))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.builders.IrGeneratorContext
import org.jetbrains.kotlin.ir.builders.declarations.IrFunctionBuilder
import org.jetbrains.kotlin.ir.builders.declarations.addGetter
import org.jetbrains.kotlin.ir.builders.declarations.addProperty
import org.jetbrains.kotlin.ir.builders.declarations.addTypeParameter
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.builders.declarations.buildField
Expand All @@ -28,13 +29,19 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.TypeRemapper
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.substitute
import org.jetbrains.kotlin.name.Name

fun IrGeneratorContext.irBuilder(
Expand All @@ -46,16 +53,26 @@ fun IrGeneratorContext.irBuilder(
fun IrGeneratorContext.buildExternalInterface(
name: String,
visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
superTypes: List<IrType>? = listOf(irBuiltIns.anyType)
superTypes: List<IrType>? = listOf(irBuiltIns.anyType),
typeParameters: List<IrTypeParameter>
): IrClass {
val irClass = irFactory.buildClass {
this.visibility = visibility
kind = ClassKind.INTERFACE
modality = Modality.ABSTRACT
isExternal = true
this.kind = ClassKind.INTERFACE
this.modality = Modality.ABSTRACT
this.isExternal = true
this.name = Name.identifier(name)
}
superTypes?.let { irClass.superTypes = it }
for (typeParameter in typeParameters) {
irClass.addTypeParameter {
this.name = typeParameter.name
this.origin = typeParameter.origin
this.index = typeParameter.index
this.superTypes.addAll(typeParameter.superTypes)
this.variance = typeParameter.variance
}
}
irClass.createImplicitParameterDeclarationWithWrappedDescriptor()
return irClass
}
Expand Down Expand Up @@ -89,7 +106,7 @@ PROPERTY name:name visibility:public modality:ABSTRACT [var]
returnType = type
origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
}
irGetter.dispatchReceiverParameter = container.thisReceiver!!.copyTo(irGetter, IrDeclarationOrigin.DEFINED)
irGetter.dispatchReceiverParameter = container.thisReceiver!!.copyTo(irGetter, IrDeclarationOrigin.DEFINED, type = container.defaultType)
irGetter.correspondingPropertySymbol = irProperty.symbol

val irSetter = irProperty.addSetter {
Expand All @@ -98,7 +115,7 @@ PROPERTY name:name visibility:public modality:ABSTRACT [var]
returnType = irBuiltIns.unitType
origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
}
irSetter.dispatchReceiverParameter = container.thisReceiver!!.copyTo(irSetter, IrDeclarationOrigin.DEFINED)
irSetter.dispatchReceiverParameter = container.thisReceiver!!.copyTo(irSetter, IrDeclarationOrigin.DEFINED, type = container.defaultType)
irSetter.correspondingPropertySymbol = irProperty.symbol
irSetter.addValueParameter {
this.name = Name.special("<set-?>")
Expand Down Expand Up @@ -199,3 +216,12 @@ internal fun IrFactory.buildFunction(builder: IrFunctionBuilder): IrSimpleFuncti
containerSource
)
}

class TypeSubstituteRemapper(
private val substitutionMap: Map<IrTypeParameterSymbol, IrType>
) : TypeRemapper {
override fun remapType(type: IrType): IrType = type.substitute(substitutionMap)

override fun enterScope(irTypeParametersContainer: IrTypeParametersContainer) = Unit
override fun leaveScope() = Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,26 @@ fun RBuilder.Welcome(name: String) {
assertTrue("var WELCOME_RFUNC" in javascript)
assertTrue("WELCOME_RFUNC = rFunction('Welcome'," in javascript)
}

@Test
fun genericComponent() {
val output = compile("""
import com.bnorm.react.*
import react.*
import react.dom.div

@RFunction
fun <T> RBuilder.GenericList(items: List<T>, onItem: RBuilder.(T) -> Unit) {
div {
for (item in items) {
onItem(item)
}
}
}
""")
val javascript = output.readText()
assertTrue("function GenericList(_this_, items, onItem)" in javascript)
assertTrue("var GENERICLIST_RFUNC" in javascript)
assertTrue("GENERICLIST_RFUNC = rFunction('GenericList'," in javascript)
}
}