[K/JS] Add VOID optimization on object properties + align with the optimization the JS Plain Object plugin
This commit is contained in:
@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.dce.DceDumpNameCache
|
import org.jetbrains.kotlin.ir.backend.js.dce.DceDumpNameCache
|
||||||
import org.jetbrains.kotlin.ir.backend.js.dce.eliminateDeadDeclarations
|
import org.jetbrains.kotlin.ir.backend.js.dce.eliminateDeadDeclarations
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.JsStaticContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsClass
|
import org.jetbrains.kotlin.js.backend.ast.JsClass
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsFunction
|
import org.jetbrains.kotlin.js.backend.ast.JsFunction
|
||||||
@@ -35,11 +37,13 @@ fun optimizeProgramByIr(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun optimizeFragmentByJsAst(fragment: JsIrProgramFragment) {
|
fun optimizeFragmentByJsAst(fragment: JsIrProgramFragment, context: JsStaticContext) {
|
||||||
|
val voidName = context.backendContext.intrinsics.void.owner.backingField?.let(context::getNameForField)
|
||||||
|
|
||||||
val optimizer = object : RecursiveJsVisitor() {
|
val optimizer = object : RecursiveJsVisitor() {
|
||||||
override fun visitFunction(x: JsFunction) {
|
override fun visitFunction(x: JsFunction) {
|
||||||
super.visitFunction(x)
|
super.visitFunction(x)
|
||||||
FunctionPostProcessor(x).apply()
|
FunctionPostProcessor(x, voidName).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitClass(x: JsClass) {
|
override fun visitClass(x: JsClass) {
|
||||||
|
|||||||
+1
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
|||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.metadata.constant
|
||||||
|
|
||||||
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||||
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
|
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
|
||||||
|
|||||||
+1
-1
@@ -471,7 +471,7 @@ class IrModuleToJsTransformer(
|
|||||||
result.computeAndSaveDefinitions(definitionSet, fileExports)
|
result.computeAndSaveDefinitions(definitionSet, fileExports)
|
||||||
|
|
||||||
if (optimizeGeneratedJs) {
|
if (optimizeGeneratedJs) {
|
||||||
optimizeFragmentByJsAst(result)
|
optimizeFragmentByJsAst(result, staticContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
return JsIrProgramFragments(result, exportFragment)
|
return JsIrProgramFragments(result, exportFragment)
|
||||||
|
|||||||
+6
-1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
|||||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.metadata.constant
|
||||||
import org.jetbrains.kotlin.utils.DFS
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
@@ -50,7 +51,11 @@ class JsNameLinkingNamer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return declaration.getName()
|
return declaration.getName().also {
|
||||||
|
if (declaration == context.intrinsics.void.owner.backingField) {
|
||||||
|
it.constant = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNameForMemberFunction(function: IrSimpleFunction): JsName {
|
override fun getNameForMemberFunction(function: IrSimpleFunction): JsName {
|
||||||
|
|||||||
+1
@@ -459,6 +459,7 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
|
|||||||
} ?: JsDynamicScope.declareName(identifier)
|
} ?: JsDynamicScope.declareName(identifier)
|
||||||
ifTrue { name.localAlias = readLocalAlias() }
|
ifTrue { name.localAlias = readLocalAlias() }
|
||||||
ifTrue { name.imported = true }
|
ifTrue { name.imported = true }
|
||||||
|
ifTrue { name.constant = true }
|
||||||
ifTrue { name.specialFunction = specialFunctionValues[readInt()] }
|
ifTrue { name.specialFunction = specialFunctionValues[readInt()] }
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -605,6 +605,7 @@ private class JsIrAstSerializer {
|
|||||||
writeBoolean(name.isTemporary)
|
writeBoolean(name.isTemporary)
|
||||||
ifNotNull(name.localAlias) { writeLocalAlias(it) }
|
ifNotNull(name.localAlias) { writeLocalAlias(it) }
|
||||||
writeBoolean(name.imported && name !in importedNames)
|
writeBoolean(name.imported && name !in importedNames)
|
||||||
|
writeBoolean(name.constant)
|
||||||
ifNotNull(name.specialFunction) { writeByte(it.ordinal) }
|
ifNotNull(name.specialFunction) { writeByte(it.ordinal) }
|
||||||
}
|
}
|
||||||
nameMap.size
|
nameMap.size
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ var JsFunction.functionDescriptor: FunctionDescriptor? by MetadataProperty(defau
|
|||||||
*/
|
*/
|
||||||
var JsReturn.returnTarget: FunctionDescriptor? by MetadataProperty(default = null)
|
var JsReturn.returnTarget: FunctionDescriptor? by MetadataProperty(default = null)
|
||||||
|
|
||||||
|
var HasMetadata.constant: Boolean by MetadataProperty(default = false)
|
||||||
var HasMetadata.synthetic: Boolean by MetadataProperty(default = false)
|
var HasMetadata.synthetic: Boolean by MetadataProperty(default = false)
|
||||||
|
|
||||||
var HasMetadata.isInlineClassBoxing: Boolean by MetadataProperty(default = false)
|
var HasMetadata.isInlineClassBoxing: Boolean by MetadataProperty(default = false)
|
||||||
|
|||||||
@@ -17,8 +17,9 @@
|
|||||||
package org.jetbrains.kotlin.js.inline.clean
|
package org.jetbrains.kotlin.js.inline.clean
|
||||||
|
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsFunction
|
import org.jetbrains.kotlin.js.backend.ast.JsFunction
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||||
|
|
||||||
class FunctionPostProcessor(val root: JsFunction) {
|
class FunctionPostProcessor(val root: JsFunction, private val voidName: JsName? = null) {
|
||||||
val optimizations = listOf(
|
val optimizations = listOf(
|
||||||
{ RedundantLabelRemoval(root.body).apply() },
|
{ RedundantLabelRemoval(root.body).apply() },
|
||||||
{ EmptyStatementElimination(root.body).apply() },
|
{ EmptyStatementElimination(root.body).apply() },
|
||||||
@@ -32,7 +33,8 @@ class FunctionPostProcessor(val root: JsFunction) {
|
|||||||
{ RedundantStatementElimination(root).apply() },
|
{ RedundantStatementElimination(root).apply() },
|
||||||
{ CoroutineStateElimination(root.body).apply() },
|
{ CoroutineStateElimination(root.body).apply() },
|
||||||
{ BoxingUnboxingElimination(root.body).apply() },
|
{ BoxingUnboxingElimination(root.body).apply() },
|
||||||
{ MoveTemporaryVariableDeclarationToAssignment(root.body).apply() }
|
{ MoveTemporaryVariableDeclarationToAssignment(root.body).apply() },
|
||||||
|
{ voidName?.let { VoidPropertiesElimination(root.body, voidName).apply() } ?: false }
|
||||||
)
|
)
|
||||||
// TODO: reduce to A || B, A && B if possible
|
// TODO: reduce to A || B, A && B if possible
|
||||||
|
|
||||||
|
|||||||
+15
-16
@@ -17,10 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.js.inline.clean
|
package org.jetbrains.kotlin.js.inline.clean
|
||||||
|
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind
|
import org.jetbrains.kotlin.js.backend.ast.metadata.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.metadata.imported
|
|
||||||
import org.jetbrains.kotlin.js.backend.ast.metadata.sideEffects
|
|
||||||
import org.jetbrains.kotlin.js.backend.ast.metadata.synthetic
|
|
||||||
import org.jetbrains.kotlin.js.inline.util.collectFreeVariables
|
import org.jetbrains.kotlin.js.inline.util.collectFreeVariables
|
||||||
import org.jetbrains.kotlin.js.inline.util.collectLocalVariables
|
import org.jetbrains.kotlin.js.inline.util.collectLocalVariables
|
||||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||||
@@ -481,8 +478,8 @@ internal class TemporaryVariableElimination(private val function: JsFunction) {
|
|||||||
|
|
||||||
override fun visitNameRef(nameRef: JsNameRef) {
|
override fun visitNameRef(nameRef: JsNameRef) {
|
||||||
val name = nameRef.name
|
val name = nameRef.name
|
||||||
if (name != null && name in localVariables) {
|
if (name != null && (name in localVariables || name.constant)) {
|
||||||
if (name !in namesToSubstitute && shouldConsiderTemporary(name)) {
|
if (name.constant || (name !in namesToSubstitute && shouldConsiderTemporary(name))) {
|
||||||
if (!sideEffectOccurred) {
|
if (!sideEffectOccurred) {
|
||||||
substitutableVariableReferences += name
|
substitutableVariableReferences += name
|
||||||
}
|
}
|
||||||
@@ -637,16 +634,18 @@ internal class TemporaryVariableElimination(private val function: JsFunction) {
|
|||||||
private fun isTrivial(expr: JsExpression): Boolean = when (expr) {
|
private fun isTrivial(expr: JsExpression): Boolean = when (expr) {
|
||||||
is JsNameRef -> {
|
is JsNameRef -> {
|
||||||
val qualifier = expr.qualifier
|
val qualifier = expr.qualifier
|
||||||
if (expr.sideEffects == SideEffectKind.PURE && (qualifier == null || isTrivial(qualifier))) {
|
when {
|
||||||
expr.name !in temporary
|
expr.name?.constant == true -> true
|
||||||
}
|
expr.sideEffects == SideEffectKind.PURE && (qualifier == null || isTrivial(qualifier)) ->
|
||||||
else {
|
expr.name !in temporary
|
||||||
val name = expr.name
|
else -> {
|
||||||
name in localVariables && when (definitions[name]) {
|
val name = expr.name
|
||||||
// Local variables with zero definitions are function parameters. We can relocate and copy them.
|
name in localVariables && when (definitions[name]) {
|
||||||
null, 0 -> true
|
// Local variables with zero definitions are function parameters. We can relocate and copy them.
|
||||||
1 -> name !in namesToSubstitute || definedValues[name]?.let { isTrivial(it) } ?: false
|
null, 0 -> true
|
||||||
else -> false
|
1 -> name !in namesToSubstitute || definedValues[name]?.let { isTrivial(it) } ?: false
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.js.inline.clean
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassBoxing
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassUnboxing
|
||||||
|
|
||||||
|
// Replaces { a: 2, b: VOID, c: VOID } with { a: 2 }
|
||||||
|
class VoidPropertiesElimination(private val root: JsBlock, private val voidName: JsName) {
|
||||||
|
private var changed = false
|
||||||
|
|
||||||
|
fun apply(): Boolean {
|
||||||
|
val visitor = object : JsVisitorWithContextImpl() {
|
||||||
|
override fun endVisit(x: JsPropertyInitializer, ctx: JsContext<JsNode>) {
|
||||||
|
super.endVisit(x, ctx)
|
||||||
|
if ((x.valueExpr as? JsNameRef)?.name === voidName) {
|
||||||
|
ctx.removeMe()
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitor.accept(root)
|
||||||
|
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
}
|
||||||
+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.backend.common.lower
|
||||||
import org.jetbrains.kotlin.builtins.StandardNames
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
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.IrBlockBody
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI
|
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.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsPluginKey
|
import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsPluginKey
|
||||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.StandardIds
|
import org.jetbrains.kotlinx.jspo.compiler.resolve.StandardIds
|
||||||
@@ -32,7 +35,8 @@ private class MoveExternalInlineFunctionsWithBodiesOutsideLowering(private val c
|
|||||||
file.declarations.add(declaration)
|
file.declarations.add(declaration)
|
||||||
|
|
||||||
declaration.body = when (declaration.name) {
|
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}`")
|
else -> error("Unexpected function with name `${declaration.name.identifier}`")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +46,7 @@ private class MoveExternalInlineFunctionsWithBodiesOutsideLowering(private val c
|
|||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrSimpleFunction.generateBodyForFactoryAndCopyFunction(): IrBlockBody {
|
private fun IrSimpleFunction.generateBodyForFactoryFunction(): IrBlockBody {
|
||||||
val declaration = this
|
val declaration = this
|
||||||
return context.irFactory.createBlockBody(startOffset, declaration.endOffset).apply {
|
return context.irFactory.createBlockBody(startOffset, declaration.endOffset).apply {
|
||||||
statements += IrReturnImpl(
|
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 {
|
open class JsPlainObjectsLoweringExtension : IrGenerationExtension {
|
||||||
|
|||||||
+1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
object StandardIds {
|
object StandardIds {
|
||||||
val KOTLIN_JS_FQN = FqName("kotlin.js")
|
val KOTLIN_JS_FQN = FqName("kotlin.js")
|
||||||
val JS_FUNCTION_ID = CallableId(KOTLIN_JS_FQN, Name.identifier("js"))
|
val JS_FUNCTION_ID = CallableId(KOTLIN_JS_FQN, Name.identifier("js"))
|
||||||
|
val VOID_PROPERTY_NAME = Name.identifier("VOID")
|
||||||
}
|
}
|
||||||
|
|
||||||
object JsPlainObjectsAnnotations {
|
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.NestedClassGenerationContext
|
||||||
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
|
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
|
||||||
import org.jetbrains.kotlin.fir.moduleData
|
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.references.builder.buildResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
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.scopes.kotlinScopeProvider
|
||||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
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.CallableId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.name.SpecialNames
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
import org.jetbrains.kotlin.types.ConstantValueKind
|
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
import org.jetbrains.kotlinx.jspo.compiler.fir.services.jsPlainObjectPropertiesProvider
|
import org.jetbrains.kotlinx.jspo.compiler.fir.services.jsPlainObjectPropertiesProvider
|
||||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsPluginKey
|
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
|
* 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
|
* @JsPlainObjects
|
||||||
* external interface Admin {
|
* external interface Admin {
|
||||||
* val chat: Chat
|
* val chat: Chat
|
||||||
|
* val email: String?
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
@@ -64,21 +65,26 @@ import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsPluginKey
|
|||||||
* external interface Admin {
|
* external interface Admin {
|
||||||
* val chat: Chat
|
* 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)
|
* Admin.Companion.invoke(chat, name)
|
||||||
*
|
*
|
||||||
* companion object {
|
* 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 }")
|
* js("{ chat: chat, name: name }")
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
class JsPlainObjectsFunctionsGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {
|
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 {
|
private val matchedInterfaces by lazy {
|
||||||
predicateBasedProvider.getSymbolsByPredicate(JsPlainObjectsPredicates.AnnotatedWithJsPlainObject.LOOKUP)
|
session.predicateBasedProvider
|
||||||
|
.getSymbolsByPredicate(JsPlainObjectsPredicates.AnnotatedWithJsPlainObject.LOOKUP)
|
||||||
.filterIsInstance<FirRegularClassSymbol>()
|
.filterIsInstance<FirRegularClassSymbol>()
|
||||||
.toSet()
|
.toSet()
|
||||||
}
|
}
|
||||||
@@ -102,7 +108,7 @@ class JsPlainObjectsFunctionsGenerator(session: FirSession) : FirDeclarationGene
|
|||||||
return if (
|
return if (
|
||||||
owner is FirRegularClassSymbol &&
|
owner is FirRegularClassSymbol &&
|
||||||
owner.isJsPlainObject &&
|
owner.isJsPlainObject &&
|
||||||
name == org.jetbrains.kotlin.name.SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
|
name == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
|
||||||
) generateCompanionDeclaration(owner)
|
) generateCompanionDeclaration(owner)
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
@@ -167,12 +173,13 @@ class JsPlainObjectsFunctionsGenerator(session: FirSession) : FirDeclarationGene
|
|||||||
): FirSimpleFunction {
|
): FirSimpleFunction {
|
||||||
return createJsPlainObjectsFunction(callableId, parent, jsPlainObjectInterface) {
|
return createJsPlainObjectsFunction(callableId, parent, jsPlainObjectInterface) {
|
||||||
runIf(resolvedReturnTypeRef.type.isNullable) {
|
runIf(resolvedReturnTypeRef.type.isNullable) {
|
||||||
buildConstExpression(
|
buildPropertyAccessExpression {
|
||||||
source = null,
|
calleeReference = buildResolvedNamedReference {
|
||||||
value = null,
|
name = StandardIds.VOID_PROPERTY_NAME
|
||||||
kind = ConstantValueKind.Null,
|
resolvedSymbol = voidPropertySymbol
|
||||||
setType = true
|
}
|
||||||
)
|
coneTypeOrNull = voidPropertySymbol.resolvedReturnType
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,15 +192,11 @@ class JsPlainObjectsFunctionsGenerator(session: FirSession) : FirDeclarationGene
|
|||||||
val interfaceType = jsPlainObjectInterface.defaultType()
|
val interfaceType = jsPlainObjectInterface.defaultType()
|
||||||
return createJsPlainObjectsFunction(callableId, parent, jsPlainObjectInterface) {
|
return createJsPlainObjectsFunction(callableId, parent, jsPlainObjectInterface) {
|
||||||
buildPropertyAccessExpression {
|
buildPropertyAccessExpression {
|
||||||
dispatchReceiver = buildThisReceiverExpression {
|
|
||||||
calleeReference = buildImplicitThisReference { boundSymbol = jsPlainObjectInterface }
|
|
||||||
coneTypeOrNull = interfaceType
|
|
||||||
}
|
|
||||||
calleeReference = buildResolvedNamedReference {
|
calleeReference = buildResolvedNamedReference {
|
||||||
name = this@createJsPlainObjectsFunction.name
|
name = StandardIds.VOID_PROPERTY_NAME
|
||||||
resolvedSymbol = this@createJsPlainObjectsFunction
|
resolvedSymbol = voidPropertySymbol
|
||||||
}
|
}
|
||||||
coneTypeOrNull = resolvedReturnType
|
coneTypeOrNull = voidPropertySymbol.resolvedReturnType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fun box(): String {
|
|||||||
if (user.age != 10) return "Fail: problem with `age` property"
|
if (user.age != 10) return "Fail: problem with `age` property"
|
||||||
|
|
||||||
val json = js("JSON.stringify(user)")
|
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")
|
val withEmail = User(name = "Name", age = 10, email = "test@test")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user