[JVM_IR] Refactor and add bytecode text tests for compose-like code.
Tests that the default argument mask is not in the local variable table.
This commit is contained in:
committed by
Alexander Udalov
parent
83588e9f22
commit
fadedc84db
@@ -2,3 +2,7 @@ fun foo(s: String = "O") = s
|
||||
|
||||
fun box() = foo() + foo("K")
|
||||
|
||||
// For Compose special default arugment handling, we still do not want
|
||||
// the default argument mask in the local variable table.
|
||||
|
||||
// 0 \$default
|
||||
@@ -1,3 +1,8 @@
|
||||
inline fun foo(s: String = "O") = s
|
||||
|
||||
fun box() = foo() + foo("K")
|
||||
|
||||
// For Compose special default arugment handling, we still do not want
|
||||
// the default argument mask in the local variable table.
|
||||
|
||||
// 0 \$default
|
||||
@@ -2,3 +2,8 @@ fun box(): String {
|
||||
fun foo(s: String = "O") = s
|
||||
return foo() + foo("K")
|
||||
}
|
||||
|
||||
// For Compose special default arugment handling, we still do not want
|
||||
// the default argument mask in the local variable table.
|
||||
|
||||
// 0 \$default
|
||||
@@ -110,6 +110,8 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
||||
myEnvironment = KotlinCoreEnvironment.createForTests(
|
||||
getTestRootDisposable(), configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
);
|
||||
|
||||
setupEnvironment(myEnvironment);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-317
@@ -5,35 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.ir
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.codegen.AbstractBlackBoxCodegenTest
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.util.hasDefaultValue
|
||||
import org.jetbrains.kotlin.ir.util.isInlined
|
||||
import org.jetbrains.kotlin.ir.util.statements
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
|
||||
abstract class AbstractComposeLikeIrBlackBoxCodegenTest : AbstractBlackBoxCodegenTest() {
|
||||
@@ -43,294 +16,4 @@ abstract class AbstractComposeLikeIrBlackBoxCodegenTest : AbstractBlackBoxCodege
|
||||
ComposeLikeExtensionRegistrar.registerComponents(environment.project)
|
||||
super.setupEnvironment(environment)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ComposeLikeExtensionRegistrar : ComponentRegistrar {
|
||||
companion object {
|
||||
fun registerComponents(project: Project) {
|
||||
IrGenerationExtension.registerExtension(project, ComposeLikeGenerationExtension())
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||
registerComponents(project)
|
||||
}
|
||||
}
|
||||
|
||||
class ComposeLikeGenerationExtension : IrGenerationExtension {
|
||||
private val rewrittenFunctions = mutableSetOf<IrFunction>()
|
||||
|
||||
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
moduleFragment.transformChildrenVoid(ComposeLikeDefaultArgumentRewriter(pluginContext, rewrittenFunctions))
|
||||
moduleFragment.transformChildrenVoid(ComposeLikeDefaultMethodCallRewriter(pluginContext, rewrittenFunctions))
|
||||
}
|
||||
}
|
||||
|
||||
class ComposeLikeDefaultMethodCallRewriter(private val context: IrPluginContext, private val rewrittenFunctions: Set<IrFunction>) :
|
||||
IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
val function = expression.symbol.owner
|
||||
return if (rewrittenFunctions.contains(function)) {
|
||||
IrCallImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
expression.symbol,
|
||||
function.typeParameters.size,
|
||||
function.valueParameters.size,
|
||||
expression.origin,
|
||||
expression.superQualifierSymbol
|
||||
).also {
|
||||
it.dispatchReceiver = expression.dispatchReceiver?.transform(this, null)
|
||||
it.extensionReceiver = expression.extensionReceiver?.transform(this, null)
|
||||
var bitmap = 0
|
||||
for (i in function.valueParameters.indices) {
|
||||
if (i < expression.valueArgumentsCount) {
|
||||
if (expression.getValueArgument(i) != null) {
|
||||
it.putValueArgument(i, expression.getValueArgument(i))
|
||||
} else {
|
||||
bitmap = bitmap or (1.shl(i))
|
||||
it.putValueArgument(
|
||||
i,
|
||||
IrConstImpl.defaultValueForType(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
function.valueParameters[i].type
|
||||
).let { defaultValue ->
|
||||
IrCompositeImpl(
|
||||
defaultValue.startOffset,
|
||||
defaultValue.endOffset,
|
||||
defaultValue.type,
|
||||
IrStatementOrigin.DEFAULT_VALUE,
|
||||
listOf(defaultValue)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
it.putValueArgument(
|
||||
function.valueParameters.size - 1,
|
||||
IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.intType, bitmap)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
super.visitCall(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ComposeLikeDefaultArgumentRewriter(
|
||||
private val context: IrPluginContext,
|
||||
private val rewrittenFunctions: MutableSet<IrFunction>
|
||||
) : IrElementTransformerVoid() {
|
||||
private val parameterMapping = mutableMapOf<IrValueParameter, IrValueParameter>()
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
parameterMapping[expression.symbol.owner]?.let {
|
||||
return IrGetValueImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
it.symbol,
|
||||
expression.origin
|
||||
)
|
||||
} ?: return super.visitGetValue(expression)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||
val hasDefaultArguments = declaration.valueParameters.any { it.defaultValue != null }
|
||||
if (!hasDefaultArguments) return super.visitFunction(declaration)
|
||||
rewrittenFunctions.add(declaration)
|
||||
val newParameters = mutableListOf<IrValueParameter>()
|
||||
declaration.valueParameters.forEach { param ->
|
||||
newParameters.add(
|
||||
if (param.defaultValue != null) {
|
||||
val result = IrValueParameterImpl(
|
||||
param.startOffset,
|
||||
param.endOffset,
|
||||
param.origin,
|
||||
IrValueParameterSymbolImpl(WrappedValueParameterDescriptor()),
|
||||
param.name,
|
||||
index = param.index,
|
||||
type = defaultParameterType(param),
|
||||
varargElementType = param.varargElementType,
|
||||
isCrossinline = param.isCrossinline,
|
||||
isNoinline = param.isNoinline,
|
||||
isAssignable = param.defaultValue != null
|
||||
).also {
|
||||
it.defaultValue = param.defaultValue
|
||||
it.parent = declaration
|
||||
}
|
||||
parameterMapping[param] = result
|
||||
result
|
||||
} else param
|
||||
)
|
||||
}
|
||||
declaration.valueParameters = newParameters
|
||||
val defaultParam = declaration.addValueParameter(
|
||||
"\$default",
|
||||
context.irBuiltIns.intType,
|
||||
IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION
|
||||
)
|
||||
declaration.transformChildrenVoid()
|
||||
val body = declaration.body!!
|
||||
val defaultSelection = mutableListOf<IrStatement>()
|
||||
declaration.valueParameters.forEach {
|
||||
if (it.hasDefaultValue()) {
|
||||
val index = defaultSelection.size
|
||||
defaultSelection.add(
|
||||
irIf(
|
||||
condition = irGetBit(defaultParam, index),
|
||||
body = irSet(it, it.defaultValue!!.expression)
|
||||
)
|
||||
)
|
||||
it.defaultValue = null
|
||||
}
|
||||
}
|
||||
|
||||
declaration.body = IrBlockBodyImpl(
|
||||
body.startOffset,
|
||||
body.endOffset,
|
||||
listOf(
|
||||
*defaultSelection.toTypedArray(),
|
||||
*body.statements.toTypedArray()
|
||||
),
|
||||
)
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun defaultParameterType(param: IrValueParameter): IrType {
|
||||
val type = param.type
|
||||
return when {
|
||||
type.isPrimitiveType() -> type
|
||||
type.isInlined() -> type
|
||||
else -> type.makeNullable()
|
||||
}
|
||||
}
|
||||
|
||||
private fun irIf(condition: IrExpression, body: IrExpression): IrExpression {
|
||||
return IrIfThenElseImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
context.irBuiltIns.unitType,
|
||||
origin = IrStatementOrigin.IF
|
||||
).also {
|
||||
it.branches.add(
|
||||
IrBranchImpl(condition, body)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun irSet(variable: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return IrSetValueImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
context.irBuiltIns.unitType,
|
||||
variable.symbol,
|
||||
value = value,
|
||||
origin = null
|
||||
)
|
||||
}
|
||||
|
||||
private fun irNotEqual(lhs: IrExpression, rhs: IrExpression): IrExpression {
|
||||
return irNot(irEqual(lhs, rhs))
|
||||
}
|
||||
|
||||
private fun irGetBit(param: IrValueParameter, index: Int): IrExpression {
|
||||
// value and (1 shl index) != 0
|
||||
return irNotEqual(
|
||||
irAnd(
|
||||
// a value of 1 in default means it was NOT provided
|
||||
irGet(param),
|
||||
irConst(0b1 shl index)
|
||||
),
|
||||
irConst(0)
|
||||
)
|
||||
}
|
||||
|
||||
private fun irConst(value: Int): IrConst<Int> = IrConstImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
context.irBuiltIns.intType,
|
||||
IrConstKind.Int,
|
||||
value
|
||||
)
|
||||
|
||||
private fun irConst(value: Boolean) = IrConstImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
context.irBuiltIns.booleanType,
|
||||
IrConstKind.Boolean,
|
||||
value
|
||||
)
|
||||
|
||||
private fun irGet(type: IrType, symbol: IrValueSymbol): IrExpression {
|
||||
return IrGetValueImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
type,
|
||||
symbol
|
||||
)
|
||||
}
|
||||
|
||||
private fun irGet(variable: IrValueDeclaration): IrExpression {
|
||||
return irGet(variable.type, variable.symbol)
|
||||
}
|
||||
|
||||
private fun IrType.binaryOperator(name: Name, paramType: IrType): IrFunctionSymbol =
|
||||
context.symbols.getBinaryOperator(name, this, paramType)
|
||||
|
||||
private fun irAnd(lhs: IrExpression, rhs: IrExpression): IrCallImpl {
|
||||
return irCall(
|
||||
lhs.type.binaryOperator(Name.identifier("and"), rhs.type),
|
||||
null,
|
||||
lhs,
|
||||
null,
|
||||
rhs
|
||||
)
|
||||
}
|
||||
|
||||
private fun irCall(
|
||||
symbol: IrFunctionSymbol,
|
||||
origin: IrStatementOrigin? = null,
|
||||
dispatchReceiver: IrExpression? = null,
|
||||
extensionReceiver: IrExpression? = null,
|
||||
vararg args: IrExpression
|
||||
): IrCallImpl {
|
||||
return IrCallImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
symbol.owner.returnType,
|
||||
symbol as IrSimpleFunctionSymbol,
|
||||
symbol.owner.typeParameters.size,
|
||||
symbol.owner.valueParameters.size,
|
||||
origin
|
||||
).also {
|
||||
if (dispatchReceiver != null) it.dispatchReceiver = dispatchReceiver
|
||||
if (extensionReceiver != null) it.extensionReceiver = extensionReceiver
|
||||
args.forEachIndexed { index, arg ->
|
||||
it.putValueArgument(index, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun irNot(value: IrExpression): IrExpression {
|
||||
return irCall(
|
||||
context.irBuiltIns.booleanNotSymbol,
|
||||
dispatchReceiver = value
|
||||
)
|
||||
}
|
||||
|
||||
private fun irEqual(lhs: IrExpression, rhs: IrExpression): IrExpression {
|
||||
return irCall(
|
||||
context.irBuiltIns.eqeqeqSymbol,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
lhs,
|
||||
rhs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -5,9 +5,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.ir
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.codegen.AbstractBytecodeTextTest
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
|
||||
abstract class AbstractComposeLikeIrBytecodeTextTest : AbstractBytecodeTextTest() {
|
||||
override val backend = TargetBackend.JVM_IR
|
||||
|
||||
override fun setupEnvironment(environment: KotlinCoreEnvironment) {
|
||||
ComposeLikeExtensionRegistrar.registerComponents(environment.project)
|
||||
super.setupEnvironment(environment)
|
||||
}
|
||||
}
|
||||
|
||||
+326
@@ -1,7 +1,333 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.
|
||||
*
|
||||
*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
*
|
||||
* 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.codegen.ir
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.util.hasDefaultValue
|
||||
import org.jetbrains.kotlin.ir.util.isInlined
|
||||
import org.jetbrains.kotlin.ir.util.statements
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class ComposeLikeExtensionRegistrar : ComponentRegistrar {
|
||||
companion object {
|
||||
fun registerComponents(project: Project) {
|
||||
IrGenerationExtension.registerExtension(project, ComposeLikeGenerationExtension())
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||
registerComponents(project)
|
||||
}
|
||||
}
|
||||
|
||||
class ComposeLikeGenerationExtension : IrGenerationExtension {
|
||||
private val rewrittenFunctions = mutableSetOf<IrFunction>()
|
||||
|
||||
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
moduleFragment.transformChildrenVoid(ComposeLikeDefaultArgumentRewriter(pluginContext, rewrittenFunctions))
|
||||
moduleFragment.transformChildrenVoid(ComposeLikeDefaultMethodCallRewriter(pluginContext, rewrittenFunctions))
|
||||
}
|
||||
}
|
||||
|
||||
class ComposeLikeDefaultMethodCallRewriter(private val context: IrPluginContext, private val rewrittenFunctions: Set<IrFunction>) :
|
||||
IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
val function = expression.symbol.owner
|
||||
return if (rewrittenFunctions.contains(function)) {
|
||||
IrCallImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
expression.symbol,
|
||||
function.typeParameters.size,
|
||||
function.valueParameters.size,
|
||||
expression.origin,
|
||||
expression.superQualifierSymbol
|
||||
).also {
|
||||
it.dispatchReceiver = expression.dispatchReceiver?.transform(this, null)
|
||||
it.extensionReceiver = expression.extensionReceiver?.transform(this, null)
|
||||
var bitmap = 0
|
||||
for (i in function.valueParameters.indices) {
|
||||
if (i < expression.valueArgumentsCount) {
|
||||
if (expression.getValueArgument(i) != null) {
|
||||
it.putValueArgument(i, expression.getValueArgument(i))
|
||||
} else {
|
||||
bitmap = bitmap or (1.shl(i))
|
||||
it.putValueArgument(
|
||||
i,
|
||||
IrConstImpl.defaultValueForType(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
function.valueParameters[i].type
|
||||
).let { defaultValue ->
|
||||
IrCompositeImpl(
|
||||
defaultValue.startOffset,
|
||||
defaultValue.endOffset,
|
||||
defaultValue.type,
|
||||
IrStatementOrigin.DEFAULT_VALUE,
|
||||
listOf(defaultValue)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
it.putValueArgument(
|
||||
function.valueParameters.size - 1,
|
||||
IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.intType, bitmap)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
super.visitCall(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ComposeLikeDefaultArgumentRewriter(
|
||||
private val context: IrPluginContext,
|
||||
private val rewrittenFunctions: MutableSet<IrFunction>
|
||||
) : IrElementTransformerVoid() {
|
||||
private val parameterMapping = mutableMapOf<IrValueParameter, IrValueParameter>()
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
parameterMapping[expression.symbol.owner]?.let {
|
||||
return IrGetValueImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
it.symbol,
|
||||
expression.origin
|
||||
)
|
||||
} ?: return super.visitGetValue(expression)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||
val hasDefaultArguments = declaration.valueParameters.any { it.defaultValue != null }
|
||||
if (!hasDefaultArguments) return super.visitFunction(declaration)
|
||||
rewrittenFunctions.add(declaration)
|
||||
val newParameters = mutableListOf<IrValueParameter>()
|
||||
declaration.valueParameters.forEach { param ->
|
||||
newParameters.add(
|
||||
if (param.defaultValue != null) {
|
||||
val descriptor = WrappedValueParameterDescriptor()
|
||||
val result = IrValueParameterImpl(
|
||||
param.startOffset,
|
||||
param.endOffset,
|
||||
param.origin,
|
||||
IrValueParameterSymbolImpl(descriptor),
|
||||
param.name,
|
||||
index = param.index,
|
||||
type = defaultParameterType(param),
|
||||
varargElementType = param.varargElementType,
|
||||
isCrossinline = param.isCrossinline,
|
||||
isNoinline = param.isNoinline,
|
||||
isHidden = false,
|
||||
isAssignable = param.defaultValue != null
|
||||
).also {
|
||||
it.defaultValue = param.defaultValue
|
||||
it.parent = declaration
|
||||
}
|
||||
descriptor.bind(result)
|
||||
parameterMapping[param] = result
|
||||
result
|
||||
} else param
|
||||
)
|
||||
}
|
||||
declaration.valueParameters = newParameters
|
||||
val defaultParam = declaration.addValueParameter(
|
||||
"\$default",
|
||||
context.irBuiltIns.intType,
|
||||
IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION
|
||||
)
|
||||
declaration.transformChildrenVoid()
|
||||
val body = declaration.body!!
|
||||
val defaultSelection = mutableListOf<IrStatement>()
|
||||
declaration.valueParameters.forEach {
|
||||
if (it.hasDefaultValue()) {
|
||||
val index = defaultSelection.size
|
||||
defaultSelection.add(
|
||||
irIf(
|
||||
condition = irGetBit(defaultParam, index),
|
||||
body = irSet(it, it.defaultValue!!.expression)
|
||||
)
|
||||
)
|
||||
it.defaultValue = null
|
||||
}
|
||||
}
|
||||
|
||||
declaration.body = IrBlockBodyImpl(
|
||||
body.startOffset,
|
||||
body.endOffset,
|
||||
listOf(
|
||||
*defaultSelection.toTypedArray(),
|
||||
*body.statements.toTypedArray()
|
||||
),
|
||||
)
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun defaultParameterType(param: IrValueParameter): IrType {
|
||||
val type = param.type
|
||||
return when {
|
||||
type.isPrimitiveType() -> type
|
||||
type.isInlined() -> type
|
||||
else -> type.makeNullable()
|
||||
}
|
||||
}
|
||||
|
||||
private fun irIf(condition: IrExpression, body: IrExpression): IrExpression {
|
||||
return IrIfThenElseImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
context.irBuiltIns.unitType,
|
||||
origin = IrStatementOrigin.IF
|
||||
).also {
|
||||
it.branches.add(
|
||||
IrBranchImpl(condition, body)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun irSet(variable: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return IrSetValueImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
context.irBuiltIns.unitType,
|
||||
variable.symbol,
|
||||
value = value,
|
||||
origin = null
|
||||
)
|
||||
}
|
||||
|
||||
private fun irNotEqual(lhs: IrExpression, rhs: IrExpression): IrExpression {
|
||||
return irNot(irEqual(lhs, rhs))
|
||||
}
|
||||
|
||||
private fun irGetBit(param: IrValueParameter, index: Int): IrExpression {
|
||||
// value and (1 shl index) != 0
|
||||
return irNotEqual(
|
||||
irAnd(
|
||||
// a value of 1 in default means it was NOT provided
|
||||
irGet(param),
|
||||
irConst(0b1 shl index)
|
||||
),
|
||||
irConst(0)
|
||||
)
|
||||
}
|
||||
|
||||
private fun irConst(value: Int): IrConst<Int> = IrConstImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
context.irBuiltIns.intType,
|
||||
IrConstKind.Int,
|
||||
value
|
||||
)
|
||||
|
||||
private fun irGet(type: IrType, symbol: IrValueSymbol): IrExpression {
|
||||
return IrGetValueImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
type,
|
||||
symbol
|
||||
)
|
||||
}
|
||||
|
||||
private fun irGet(variable: IrValueDeclaration): IrExpression {
|
||||
return irGet(variable.type, variable.symbol)
|
||||
}
|
||||
|
||||
private fun IrType.binaryOperator(name: Name, paramType: IrType): IrFunctionSymbol =
|
||||
context.symbols.getBinaryOperator(name, this, paramType)
|
||||
|
||||
private fun irAnd(lhs: IrExpression, rhs: IrExpression): IrCallImpl {
|
||||
return irCall(
|
||||
lhs.type.binaryOperator(Name.identifier("and"), rhs.type),
|
||||
null,
|
||||
lhs,
|
||||
null,
|
||||
rhs
|
||||
)
|
||||
}
|
||||
|
||||
private fun irCall(
|
||||
symbol: IrFunctionSymbol,
|
||||
origin: IrStatementOrigin? = null,
|
||||
dispatchReceiver: IrExpression? = null,
|
||||
extensionReceiver: IrExpression? = null,
|
||||
vararg args: IrExpression
|
||||
): IrCallImpl {
|
||||
return IrCallImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
symbol.owner.returnType,
|
||||
symbol as IrSimpleFunctionSymbol,
|
||||
symbol.owner.typeParameters.size,
|
||||
symbol.owner.valueParameters.size,
|
||||
origin
|
||||
).also {
|
||||
if (dispatchReceiver != null) it.dispatchReceiver = dispatchReceiver
|
||||
if (extensionReceiver != null) it.extensionReceiver = extensionReceiver
|
||||
args.forEachIndexed { index, arg ->
|
||||
it.putValueArgument(index, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun irNot(value: IrExpression): IrExpression {
|
||||
return irCall(
|
||||
context.irBuiltIns.booleanNotSymbol,
|
||||
dispatchReceiver = value
|
||||
)
|
||||
}
|
||||
|
||||
private fun irEqual(lhs: IrExpression, rhs: IrExpression): IrExpression {
|
||||
return irCall(
|
||||
context.irBuiltIns.eqeqeqSymbol,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
lhs,
|
||||
rhs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+6
-6
@@ -17,7 +17,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("compiler/testData/codegen/composeLike")
|
||||
@TestMetadata("compiler/testData/codegen/composeLikeBytecodeText")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class ComposeLikeIrBytecodeTextTestGenerated extends AbstractComposeLikeIrBytecodeTextTest {
|
||||
@@ -25,22 +25,22 @@ public class ComposeLikeIrBytecodeTextTestGenerated extends AbstractComposeLikeI
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInComposeLike() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/composeLike"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
public void testAllFilesPresentInComposeLikeBytecodeText() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/composeLikeBytecodeText"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@TestMetadata("default.kt")
|
||||
public void testDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/composeLike/default.kt");
|
||||
runTest("compiler/testData/codegen/composeLikeBytecodeText/default.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInline.kt")
|
||||
public void testDefaultInline() throws Exception {
|
||||
runTest("compiler/testData/codegen/composeLike/defaultInline.kt");
|
||||
runTest("compiler/testData/codegen/composeLikeBytecodeText/defaultInline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultLocal.kt")
|
||||
public void testDefaultLocal() throws Exception {
|
||||
runTest("compiler/testData/codegen/composeLike/defaultLocal.kt");
|
||||
runTest("compiler/testData/codegen/composeLikeBytecodeText/defaultLocal.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,6 +464,10 @@ fun main(args: Array<String>) {
|
||||
model("codegen/composeLike", targetBackend = TargetBackend.JVM_IR)
|
||||
}
|
||||
|
||||
testClass<AbstractComposeLikeIrBytecodeTextTest> {
|
||||
model("codegen/composeLikeBytecodeText", targetBackend = TargetBackend.JVM_IR)
|
||||
}
|
||||
|
||||
testClass<AbstractIrBlackBoxCodegenTest> {
|
||||
model("codegen/box", targetBackend = TargetBackend.JVM_IR, excludeDirs = listOf("oldLanguageVersions"))
|
||||
}
|
||||
|
||||
+4
-1
@@ -92,7 +92,10 @@ the Kotlin IntelliJ IDEA plugin:
|
||||
- Path: wasm/ir/src/org/jetbrains/kotlin/wasm/ir/convertors
|
||||
- License: MIT ([license/third_party/asmble_license.txt][asmble])
|
||||
- Origin: Copyright (C) 2018 Chad Retz
|
||||
|
||||
|
||||
- Path: compiler/tests-common/tests/org/jetbrains/kotlin/codegen/ir/ComposeLikeGenerationExtension.kt
|
||||
- License: Apache 2 ([license/third_party/aosp_license.txt][aosp])
|
||||
- Origin: Derived from JetPack Compose compiler plugin code, Copyright 2019 The Android Open Source Project
|
||||
|
||||
## Kotlin Test Data
|
||||
|
||||
|
||||
Reference in New Issue
Block a user