Merge pull request #17 from bnorm/kotlin-1.4

Kotlin 1.4
This commit is contained in:
Brian Norman
2020-08-20 19:27:23 -05:00
committed by GitHub
11 changed files with 79 additions and 124 deletions
+1 -1
View File
@@ -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
}
@@ -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 {
@@ -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<Project> {
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<List<SubpluginOption>> {
val project = kotlinCompilation.target.project
val extension = project.extensions.getByType(PowerAssertGradleExtension::class.java)
return project.provider {
extension.functions.map {
SubpluginOption(key = "function", value = it)
}
}
}
}
@@ -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<AbstractCompile> {
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<KotlinCommonOptions>?
): List<SubpluginOption> {
val extension = project.extensions.findByType(PowerAssertGradleExtension::class.java)
?: PowerAssertGradleExtension()
return extension.functions.map {
SubpluginOption(key = "function", value = it)
}
}
}
+2 -2
View File
@@ -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")
}
@@ -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<Node> 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<IrExpression> {
fun getExpressionsCopy(initialParent: IrDeclarationParent?): List<IrExpression> {
// 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 {
@@ -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())
@@ -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("<anonymous>")
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<IrFunctionSymbol> =
function.findPackage().getMemberScope()
.getContributedFunctions(function.name, NoLookupLocation.FROM_BACKEND)
.map { symbolTable.referenceFunction(it) }
private fun IrPluginContext.findOverloads(fqName: FqName): List<IrFunctionSymbol> {
return referenceFunctions(fqName).toList()
}
@@ -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
)
)
@@ -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<IrStackVariable>) -> 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<IrStackVariable>,
private val transform: List<IrExpression>
+2 -21
View File
@@ -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"))