diff --git a/build.gradle.kts b/build.gradle.kts index 99750e8ab5f..8254a96dbc6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("jvm") version "1.3.70" apply false + kotlin("jvm") version "1.4.0" apply false id("org.jetbrains.dokka") version "0.10.0" apply false id("com.gradle.plugin-publish") version "0.11.0" apply false } diff --git a/kotlin-power-assert-gradle/build.gradle.kts b/kotlin-power-assert-gradle/build.gradle.kts index 98715dc02b1..2e1da307745 100644 --- a/kotlin-power-assert-gradle/build.gradle.kts +++ b/kotlin-power-assert-gradle/build.gradle.kts @@ -3,7 +3,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("java-gradle-plugin") kotlin("jvm") - kotlin("kapt") id("com.gradle.plugin-publish") } @@ -11,9 +10,6 @@ plugins { dependencies { implementation(kotlin("stdlib")) implementation(kotlin("gradle-plugin-api")) - - kapt("com.google.auto.service:auto-service:1.0-rc6") - compileOnly("com.google.auto.service:auto-service-annotations:1.0-rc6") } pluginBundle { diff --git a/kotlin-power-assert-gradle/src/main/kotlin/com/bnorm/power/PowerAssertGradlePlugin.kt b/kotlin-power-assert-gradle/src/main/kotlin/com/bnorm/power/PowerAssertGradlePlugin.kt index a4a4f35705d..be3591f667c 100644 --- a/kotlin-power-assert-gradle/src/main/kotlin/com/bnorm/power/PowerAssertGradlePlugin.kt +++ b/kotlin-power-assert-gradle/src/main/kotlin/com/bnorm/power/PowerAssertGradlePlugin.kt @@ -16,11 +16,37 @@ package com.bnorm.power -import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.provider.Provider +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin +import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact +import org.jetbrains.kotlin.gradle.plugin.SubpluginOption -class PowerAssertGradlePlugin : Plugin { - override fun apply(project: Project): Unit = with(project) { +class PowerAssertGradlePlugin : KotlinCompilerPluginSupportPlugin { + override fun apply(target: Project): Unit = with(target) { extensions.create("kotlinPowerAssert", PowerAssertGradleExtension::class.java) } + + override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true + + override fun getCompilerPluginId(): String = "com.bnorm.kotlin-power-assert" + + override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact( + groupId = "com.bnorm.power", + artifactId = "kotlin-power-assert", + version = "0.4.0-SNAPSHOT" + ) + + override fun applyToCompilation( + kotlinCompilation: KotlinCompilation<*> + ): Provider> { + val project = kotlinCompilation.target.project + val extension = project.extensions.getByType(PowerAssertGradleExtension::class.java) + return project.provider { + extension.functions.map { + SubpluginOption(key = "function", value = it) + } + } + } } diff --git a/kotlin-power-assert-gradle/src/main/kotlin/com/bnorm/power/PowerAssertGradleSubplugin.kt b/kotlin-power-assert-gradle/src/main/kotlin/com/bnorm/power/PowerAssertGradleSubplugin.kt deleted file mode 100644 index dda483ad410..00000000000 --- a/kotlin-power-assert-gradle/src/main/kotlin/com/bnorm/power/PowerAssertGradleSubplugin.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2020 Brian Norman - * - * 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 com.bnorm.power - -import com.google.auto.service.AutoService -import org.gradle.api.Project -import org.gradle.api.tasks.compile.AbstractCompile -import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation -import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin -import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact -import org.jetbrains.kotlin.gradle.plugin.SubpluginOption - -@AutoService(KotlinGradleSubplugin::class) -class PowerAssertGradleSubplugin : KotlinGradleSubplugin { - - override fun getCompilerPluginId(): String = "com.bnorm.kotlin-power-assert" - - override fun isApplicable(project: Project, task: AbstractCompile): Boolean = - project.plugins.hasPlugin(PowerAssertGradlePlugin::class.java) - - - override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact( - groupId = "com.bnorm.power", - artifactId = "kotlin-power-assert", - version = "0.4.0-SNAPSHOT" - ) - - override fun apply( - project: Project, - kotlinCompile: AbstractCompile, - javaCompile: AbstractCompile?, - variantData: Any?, - androidProjectHandler: Any?, - kotlinCompilation: KotlinCompilation? - ): List { - val extension = project.extensions.findByType(PowerAssertGradleExtension::class.java) - ?: PowerAssertGradleExtension() - - return extension.functions.map { - SubpluginOption(key = "function", value = it) - } - } -} diff --git a/kotlin-power-assert/build.gradle.kts b/kotlin-power-assert/build.gradle.kts index 30deba140ff..b859eddc782 100644 --- a/kotlin-power-assert/build.gradle.kts +++ b/kotlin-power-assert/build.gradle.kts @@ -11,13 +11,13 @@ plugins { dependencies { implementation(kotlin("stdlib-jdk8")) - compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.3.70") + compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable") kapt("com.google.auto.service:auto-service:1.0-rc6") compileOnly("com.google.auto.service:auto-service-annotations:1.0-rc6") testImplementation(kotlin("test-junit")) - testImplementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.3.70") + testImplementation("org.jetbrains.kotlin:kotlin-compiler-embeddable") testImplementation("com.github.tschuchortdev:kotlin-compile-testing:1.2.6") } diff --git a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/AssertTree.kt b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/AssertTree.kt index 679adff5cc9..bb02f964152 100644 --- a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/AssertTree.kt +++ b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/AssertTree.kt @@ -16,13 +16,14 @@ package com.bnorm.power -import org.jetbrains.kotlin.backend.common.deepCopyWithVariables import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.IrWhen +import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols import org.jetbrains.kotlin.ir.visitors.IrElementVisitor sealed class Node { @@ -31,7 +32,7 @@ sealed class Node { val children: List get() = mutableChildren protected fun dump(builder: StringBuilder, indent: Int) { - builder.append(" ".repeat(indent)).append(this).appendln() + builder.append(" ".repeat(indent)).append(this).appendLine() for (child in children) { child.dump(builder, indent + 1) } @@ -67,10 +68,10 @@ class ExpressionNode( _expressions.add(expression) } - fun getExpressionsCopy(): List { + fun getExpressionsCopy(initialParent: IrDeclarationParent?): List { // Return a copy of all the expression by creating a deep copy of the head // expression and running back through the assertion tree builder - val headCopy = _expressions.first().deepCopyWithVariables() + val headCopy = _expressions.first().deepCopyWithSymbols(initialParent) return (buildAssertTree(headCopy).children.single() as ExpressionNode)._expressions } @@ -101,7 +102,7 @@ fun buildAssertTree(expression: IrExpression): RootNode { } override fun visitCall(expression: IrCall, data: Node) { - if (expression.symbol.descriptor.name.asString() == "EQEQ" && expression.origin == IrStatementOrigin.EXCLEQ) { + if (expression.symbol.owner.name.asString() == "EQEQ" && expression.origin == IrStatementOrigin.EXCLEQ) { // Skip the EQEQ part of a EXCLEQ call expression.acceptChildren(this, data) } else { diff --git a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/IrStackVariable.kt b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/IrStackVariable.kt index e9e184ca669..6ef3aa0c5dc 100644 --- a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/IrStackVariable.kt +++ b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/IrStackVariable.kt @@ -63,6 +63,7 @@ fun IrBuilderWithScope.buildMessage( val columnOffset: Int = when (original) { is IrMemberAccessExpression -> { + // TODO IrFunction doesn't have 'isInfix' as a property val descriptor = original.symbol.descriptor when { descriptor is FunctionDescriptor && descriptor.isInfix -> source.indexOf(descriptor.name.asString()) diff --git a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertCallTransformer.kt b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertCallTransformer.kt index 4cad0010fe3..34b856150c4 100644 --- a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertCallTransformer.kt +++ b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertCallTransformer.kt @@ -23,11 +23,7 @@ import org.jetbrains.kotlin.backend.common.ir.asSimpleLambda import org.jetbrains.kotlin.backend.common.ir.inline import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.at -import org.jetbrains.kotlin.backend.common.serialization.findPackage -import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.declarations.buildFun @@ -37,6 +33,7 @@ import org.jetbrains.kotlin.ir.builders.irCallOp import org.jetbrains.kotlin.ir.builders.irFalse import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.builders.irString +import org.jetbrains.kotlin.ir.builders.parent import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.path @@ -47,18 +44,21 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.IrTypeArgument +import org.jetbrains.kotlin.ir.types.IrTypeProjection import org.jetbrains.kotlin.ir.types.getClass +import org.jetbrains.kotlin.ir.types.isBoolean +import org.jetbrains.kotlin.ir.types.isSubtypeOf import org.jetbrains.kotlin.ir.util.functions -import org.jetbrains.kotlin.ir.util.referenceFunction +import org.jetbrains.kotlin.ir.util.isFunctionOrKFunction import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.typeUtil.isBoolean -import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.util.OperatorNameConventions import java.io.File @@ -90,16 +90,18 @@ class PowerAssertCallTransformer( } override fun visitCall(expression: IrCall): IrExpression { - val function = expression.symbol.descriptor - if (functions.none { function.fqNameSafe == it }) + // TODO expression.symbol.owner.fqNameSafe includes the wrapping ClassKt while descriptor doesn't + val fqName = expression.symbol.descriptor.fqNameSafe + if (functions.none { fqName == it }) return super.visitCall(expression) - val delegate = findDelegate(function) ?: run { + val delegate = findDelegate(fqName) ?: run { // Find a valid delegate function or do not translate // TODO log a warning return super.visitCall(expression) } + val function = expression.symbol.owner val assertionArgument = expression.getValueArgument(0)!! val messageArgument = if (function.valueParameters.size == 2) expression.getValueArgument(1) else null @@ -114,7 +116,7 @@ class PowerAssertCallTransformer( val title = when { messageArgument is IrConst<*> -> messageArgument messageArgument is IrStringConcatenation -> messageArgument - lambda != null -> lambda.inline() + lambda != null -> lambda.inline(parent) messageArgument != null -> { val invoke = messageArgument.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INVOKE } irCallOp(invoke.symbol, invoke.returnType, messageArgument) @@ -142,13 +144,11 @@ class PowerAssertCallTransformer( fun buildCall(builder: IrBuilderWithScope, message: IrExpression): IrExpression } - private fun findDelegate(function: FunctionDescriptor): FunctionDelegate? { - fun KotlinType.toIrType() = context.typeTranslator.translateType(this) - - return context.findOverloads(function) + private fun findDelegate(fqName: FqName): FunctionDelegate? { + return context.findOverloads(fqName) .mapNotNull { overload -> // TODO allow other signatures than (Boolean, String) and (Boolean, () -> String)) - val parameters = overload.descriptor.valueParameters + val parameters = overload.owner.valueParameters if (parameters.size != 2) return@mapNotNull null if (!parameters[0].type.isBoolean()) return@mapNotNull null @@ -156,7 +156,7 @@ class PowerAssertCallTransformer( isStringSupertype(parameters[1].type) -> { object : FunctionDelegate { override fun buildCall(builder: IrBuilderWithScope, message: IrExpression): IrExpression = with(builder) { - irCall(overload, type = overload.descriptor.returnType!!.toIrType()).apply { + irCall(overload, type = overload.owner.returnType).apply { putValueArgument(0, irFalse()) putValueArgument(1, message) } @@ -166,6 +166,7 @@ class PowerAssertCallTransformer( isStringFunction(parameters[1].type) -> { object : FunctionDelegate { override fun buildCall(builder: IrBuilderWithScope, message: IrExpression): IrExpression = with(builder) { + val scope = this val lambda = buildFun { name = Name.special("") returnType = context.irBuiltIns.stringType @@ -176,30 +177,35 @@ class PowerAssertCallTransformer( body = bodyBuilder.irBlockBody { +irReturn(message) } + parent = scope.parent } val expression = IrFunctionExpressionImpl(-1, -1, context.irBuiltIns.stringType, lambda, IrStatementOrigin.LAMBDA) - irCall(overload, type = overload.descriptor.returnType!!.toIrType()).apply { + irCall(overload, type = overload.owner.returnType).apply { putValueArgument(0, irFalse()) putValueArgument(1, expression) } } } } - else -> null + else -> { + null + } } } .singleOrNull() } - private fun isStringFunction(type: KotlinType): Boolean = - type.isBuiltinFunctionalType && type.arguments.size == 1 && isStringSupertype(type.arguments.first().type) + private fun isStringFunction(type: IrType): Boolean = + type.isFunctionOrKFunction() && type is IrSimpleType && (type.arguments.size == 1 && isStringSupertype(type.arguments.first())) - private fun isStringSupertype(type: KotlinType): Boolean = - context.builtIns.stringType.isSubtypeOf(type) + private fun isStringSupertype(argument: IrTypeArgument): Boolean = + argument is IrTypeProjection && isStringSupertype(argument.type) + + private fun isStringSupertype(type: IrType): Boolean = + context.irBuiltIns.stringType.isSubtypeOf(type, context.irBuiltIns) } // TODO is this the best way to find overload functions? -private fun IrPluginContext.findOverloads(function: FunctionDescriptor): List = - function.findPackage().getMemberScope() - .getContributedFunctions(function.name, NoLookupLocation.FROM_BACKEND) - .map { symbolTable.referenceFunction(it) } +private fun IrPluginContext.findOverloads(fqName: FqName): List { + return referenceFunctions(fqName).toList() +} diff --git a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertCommandLineProcessor.kt b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertCommandLineProcessor.kt index bac096fadb0..a526f5e8e56 100644 --- a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertCommandLineProcessor.kt +++ b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertCommandLineProcessor.kt @@ -31,6 +31,7 @@ class PowerAssertCommandLineProcessor : CommandLineProcessor { optionName = "function", valueDescription = "function full-qualified name", description = "fully qualified path of function to intercept", + required = false, // TODO required for Kotlin/JS allowMultipleOccurrences = true ) ) diff --git a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertGenerator.kt b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertGenerator.kt index 74115ca5499..375b6d3bbf9 100644 --- a/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertGenerator.kt +++ b/kotlin-power-assert/src/main/kotlin/com/bnorm/power/PowerAssertGenerator.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.ir.builders.IrStatementsBuilder import org.jetbrains.kotlin.ir.builders.irBlock import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irTemporary +import org.jetbrains.kotlin.ir.builders.parent import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrGetValue import org.jetbrains.kotlin.ir.expressions.IrWhen @@ -75,13 +76,13 @@ abstract class PowerAssertGenerator { node: ExpressionNode, thenPart: IrStatementsBuilder<*>.(subStack: MutableList) -> IrExpression ): IrWhen { - val expressions = node.getExpressionsCopy() + val expressions = node.getExpressionsCopy(this.parent) val stackTransformer = StackBuilder(this, stack, expressions) val transformed = expressions.first().transform(stackTransformer, null) return irIfThen(irNot(transformed), thenPart(stack.toMutableList())) } - inner class StackBuilder( + class StackBuilder( private val builder: IrStatementsBuilder<*>, private val stack: MutableList, private val transform: List diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index e4686d7fe6b..c7fa53aca58 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -5,7 +5,7 @@ buildscript { } plugins { - kotlin("multiplatform") version "1.3.70" + kotlin("multiplatform") version "1.4.0" } apply(plugin = "com.bnorm.power.kotlin-power-assert") @@ -23,15 +23,9 @@ kotlin { } } } - js { + js(IR) { browser() nodejs() - - compilations.all { - kotlinOptions { - kotlinOptions.freeCompilerArgs += listOf("-Xir-produce-klib-dir", "-Xir-produce-js") - } - } } val osName = System.getProperty("os.name") @@ -43,9 +37,6 @@ kotlin { sourceSets { val commonMain by getting { - dependencies { - implementation(kotlin("stdlib")) - } } val commonTest by getting { dependencies { @@ -53,21 +44,11 @@ kotlin { implementation(kotlin("test-annotations-common")) } } - val jvmMain by getting { - dependencies { - implementation(kotlin("stdlib-jdk8")) - } - } val jvmTest by getting { dependencies { implementation(kotlin("test-junit")) } } - val jsMain by getting { - dependencies { - implementation(kotlin("stdlib-js")) - } - } val jsTest by getting { dependencies { implementation(kotlin("test-js"))