[K/JS] Add VOID optimization on object properties + align with the optimization the JS Plain Object plugin
This commit is contained in:
+49
-2
@@ -10,10 +10,13 @@ import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.common.lower
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsPluginKey
|
||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.StandardIds
|
||||
@@ -32,7 +35,8 @@ private class MoveExternalInlineFunctionsWithBodiesOutsideLowering(private val c
|
||||
file.declarations.add(declaration)
|
||||
|
||||
declaration.body = when (declaration.name) {
|
||||
StandardNames.DATA_CLASS_COPY, OperatorNameConventions.INVOKE -> declaration.generateBodyForFactoryAndCopyFunction()
|
||||
StandardNames.DATA_CLASS_COPY -> declaration.generateBodyForCopyFunction()
|
||||
OperatorNameConventions.INVOKE -> declaration.generateBodyForFactoryFunction()
|
||||
else -> error("Unexpected function with name `${declaration.name.identifier}`")
|
||||
}
|
||||
|
||||
@@ -42,7 +46,7 @@ private class MoveExternalInlineFunctionsWithBodiesOutsideLowering(private val c
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.generateBodyForFactoryAndCopyFunction(): IrBlockBody {
|
||||
private fun IrSimpleFunction.generateBodyForFactoryFunction(): IrBlockBody {
|
||||
val declaration = this
|
||||
return context.irFactory.createBlockBody(startOffset, declaration.endOffset).apply {
|
||||
statements += IrReturnImpl(
|
||||
@@ -64,6 +68,49 @@ private class MoveExternalInlineFunctionsWithBodiesOutsideLowering(private val c
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.generateBodyForCopyFunction(): IrBlockBody {
|
||||
val declaration = this
|
||||
return context.irFactory.createBlockBody(startOffset, declaration.endOffset).apply {
|
||||
val selfName = Name.identifier("${"$$"}tmp_self${"$$"}")
|
||||
statements += IrVariableImpl(
|
||||
declaration.startOffset,
|
||||
declaration.endOffset,
|
||||
IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
|
||||
IrVariableSymbolImpl(),
|
||||
selfName,
|
||||
context.irBuiltIns.nothingType,
|
||||
isVar = false,
|
||||
isConst = false,
|
||||
isLateinit = false
|
||||
).apply {
|
||||
parent = declaration
|
||||
initializer = IrGetValueImpl(
|
||||
declaration.startOffset,
|
||||
declaration.endOffset,
|
||||
declaration.dispatchReceiverParameter!!.symbol
|
||||
)
|
||||
}
|
||||
statements += IrReturnImpl(
|
||||
declaration.startOffset,
|
||||
declaration.endOffset,
|
||||
declaration.returnType,
|
||||
declaration.symbol,
|
||||
IrCallImpl(
|
||||
declaration.startOffset,
|
||||
declaration.endOffset,
|
||||
declaration.returnType,
|
||||
jsFunction,
|
||||
0,
|
||||
1,
|
||||
).apply {
|
||||
val jsObject = "{ ${declaration.valueParameters.joinToString(", ") { "${it.name.identifier}:${it.name.identifier}" }} }"
|
||||
val objectAssignCall = "Object.assign({}, ${selfName.identifier}, $jsObject)"
|
||||
putValueArgument(0, objectAssignCall.toIrConst(context.irBuiltIns.stringType))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class JsPlainObjectsLoweringExtension : IrGenerationExtension {
|
||||
|
||||
+1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.name.Name
|
||||
object StandardIds {
|
||||
val KOTLIN_JS_FQN = FqName("kotlin.js")
|
||||
val JS_FUNCTION_ID = CallableId(KOTLIN_JS_FQN, Name.identifier("js"))
|
||||
val VOID_PROPERTY_NAME = Name.identifier("VOID")
|
||||
}
|
||||
|
||||
object JsPlainObjectsAnnotations {
|
||||
|
||||
+24
-21
@@ -28,9 +28,9 @@ import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
|
||||
import org.jetbrains.kotlin.fir.extensions.NestedClassGenerationContext
|
||||
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
|
||||
import org.jetbrains.kotlin.fir.moduleData
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildImplicitThisReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.kotlinScopeProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
@@ -40,11 +40,11 @@ import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.types.ConstantValueKind
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlinx.jspo.compiler.fir.services.jsPlainObjectPropertiesProvider
|
||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsPluginKey
|
||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.StandardIds
|
||||
|
||||
/**
|
||||
* The extension generate a synthetic factory and copy-method for an `external interface` annotated with @JsPlainObjects
|
||||
@@ -56,6 +56,7 @@ import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsPluginKey
|
||||
* @JsPlainObjects
|
||||
* external interface Admin {
|
||||
* val chat: Chat
|
||||
* val email: String?
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
@@ -64,21 +65,26 @@ import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsPluginKey
|
||||
* external interface Admin {
|
||||
* val chat: Chat
|
||||
*
|
||||
* inline fun copy(chat: Chat = this.chat, name: String = this.name): Admin =
|
||||
* inline fun copy(chat: Chat = this.chat, email: String = this.email): Admin =
|
||||
* Admin.Companion.invoke(chat, name)
|
||||
*
|
||||
* companion object {
|
||||
* inline operator fun invoke(chat: Chat, name: String): Admin =
|
||||
* inline operator fun invoke(chat: Chat, email: String? = VOID): Admin =
|
||||
* js("{ chat: chat, name: name }")
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class JsPlainObjectsFunctionsGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {
|
||||
private val predicateBasedProvider = session.predicateBasedProvider
|
||||
private val voidPropertySymbol by lazy {
|
||||
session.symbolProvider
|
||||
.getTopLevelPropertySymbols(StandardIds.KOTLIN_JS_FQN, StandardIds.VOID_PROPERTY_NAME)
|
||||
.single()
|
||||
}
|
||||
|
||||
private val matchedInterfaces by lazy {
|
||||
predicateBasedProvider.getSymbolsByPredicate(JsPlainObjectsPredicates.AnnotatedWithJsPlainObject.LOOKUP)
|
||||
private val matchedInterfaces by lazy {
|
||||
session.predicateBasedProvider
|
||||
.getSymbolsByPredicate(JsPlainObjectsPredicates.AnnotatedWithJsPlainObject.LOOKUP)
|
||||
.filterIsInstance<FirRegularClassSymbol>()
|
||||
.toSet()
|
||||
}
|
||||
@@ -102,7 +108,7 @@ class JsPlainObjectsFunctionsGenerator(session: FirSession) : FirDeclarationGene
|
||||
return if (
|
||||
owner is FirRegularClassSymbol &&
|
||||
owner.isJsPlainObject &&
|
||||
name == org.jetbrains.kotlin.name.SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
|
||||
name == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
|
||||
) generateCompanionDeclaration(owner)
|
||||
else null
|
||||
}
|
||||
@@ -167,12 +173,13 @@ class JsPlainObjectsFunctionsGenerator(session: FirSession) : FirDeclarationGene
|
||||
): FirSimpleFunction {
|
||||
return createJsPlainObjectsFunction(callableId, parent, jsPlainObjectInterface) {
|
||||
runIf(resolvedReturnTypeRef.type.isNullable) {
|
||||
buildConstExpression(
|
||||
source = null,
|
||||
value = null,
|
||||
kind = ConstantValueKind.Null,
|
||||
setType = true
|
||||
)
|
||||
buildPropertyAccessExpression {
|
||||
calleeReference = buildResolvedNamedReference {
|
||||
name = StandardIds.VOID_PROPERTY_NAME
|
||||
resolvedSymbol = voidPropertySymbol
|
||||
}
|
||||
coneTypeOrNull = voidPropertySymbol.resolvedReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,15 +192,11 @@ class JsPlainObjectsFunctionsGenerator(session: FirSession) : FirDeclarationGene
|
||||
val interfaceType = jsPlainObjectInterface.defaultType()
|
||||
return createJsPlainObjectsFunction(callableId, parent, jsPlainObjectInterface) {
|
||||
buildPropertyAccessExpression {
|
||||
dispatchReceiver = buildThisReceiverExpression {
|
||||
calleeReference = buildImplicitThisReference { boundSymbol = jsPlainObjectInterface }
|
||||
coneTypeOrNull = interfaceType
|
||||
}
|
||||
calleeReference = buildResolvedNamedReference {
|
||||
name = this@createJsPlainObjectsFunction.name
|
||||
resolvedSymbol = this@createJsPlainObjectsFunction
|
||||
name = StandardIds.VOID_PROPERTY_NAME
|
||||
resolvedSymbol = voidPropertySymbol
|
||||
}
|
||||
coneTypeOrNull = resolvedReturnType
|
||||
coneTypeOrNull = voidPropertySymbol.resolvedReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fun box(): String {
|
||||
if (user.age != 10) return "Fail: problem with `age` property"
|
||||
|
||||
val json = js("JSON.stringify(user)")
|
||||
if (json != "{\"email\":null,\"age\":10,\"name\":\"Name\"}") return "Fail: got the next json: $json"
|
||||
if (json != "{\"age\":10,\"name\":\"Name\"}") return "Fail: got the next json: $json"
|
||||
|
||||
val withEmail = User(name = "Name", age = 10, email = "test@test")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user