Skip to content

Commit

Permalink
Support generic view type #24.
Browse files Browse the repository at this point in the history
  • Loading branch information
bennyhuo committed Aug 17, 2023
1 parent 95ca1dd commit 4653d0e
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 316 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ kotlin.code.style=official
android.nonTransitiveRClass=true

KOTLIN_PLUGIN_ID=com.kanyun.kace
VERSION_NAME=1.8.20-1.1.0
VERSION_NAME=1.8.20-1.2.0-SNAPSHOT

GROUP=com.kanyun.kace

Expand Down
2 changes: 1 addition & 1 deletion kace-compiler/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ dependencies {
testImplementation(kotlin("test-junit"))
testImplementation("org.jetbrains.kotlin:kotlin-compiler-embeddable")

testImplementation("com.bennyhuo.kotlin:kotlin-compile-testing-extensions:1.8.0.1")
testImplementation("com.bennyhuo.kotlin:kotlin-compile-testing-extensions:1.8.20-1.1.0")
}

val compileKotlin: KotlinCompile by tasks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.kanyun.kace.compiler.utils.irThis
import com.kanyun.kace.compiler.utils.isAndroidExtensions
import com.kanyun.kace.compiler.utils.symbolOfAndroidExtensionImpl
import com.kanyun.kace.compiler.utils.typeOfAndroidExtensionsBase
import com.kanyun.kace.compiler.utils.typeOfJavaClass
import com.kanyun.kace.compiler.utils.typeOfView
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
Expand Down Expand Up @@ -85,6 +86,7 @@ class KaceIrTransformer(private val context: IrPluginContext) : IrElementTransfo

addValueParameter("owner", context.typeOfAndroidExtensionsBase())
addValueParameter("id", context.symbols.int.defaultType)
addValueParameter("viewClass", context.typeOfJavaClass())

body = IrBlockBodyBuilder(
context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ val ANDROID_VIEW_CLASS_ID = classId("android.view", "View")
val ANDROID_ACTIVITY_CLASS_ID = classId("android.app", "Activity")
val ANDROIDX_FRAGMENT_CLASS_ID = classId("androidx.fragment.app", "Fragment")

val JAVA_CLASS_CLASS_ID = classId("java.lang", "Class")

val IMPLICIT_ANDROID_EXTENSIONS_CLASS_IDS = setOf(
ANDROID_ACTIVITY_CLASS_ID,
ANDROIDX_FRAGMENT_CLASS_ID,
Expand Down
12 changes: 8 additions & 4 deletions kace-compiler/src/main/java/com/kanyun/kace/compiler/utils/Ir.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.types.classFqName
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.functions

Expand All @@ -32,10 +33,11 @@ fun IrClass.findViewByIdCached(pluginContext: IrPluginContext): IrSimpleFunction

fun IrFunction.isFindViewByIdCached(pluginContext: IrPluginContext): Boolean {
return name.identifier == FIND_VIEW_BY_ID_CACHED_NAME &&
valueParameters.size == 2 &&
valueParameters[0].type ==
pluginContext.referenceClass(ANDROID_EXTENSIONS_BASE_CLASS_ID)?.defaultType &&
valueParameters[1].type == pluginContext.symbols.int.defaultType
valueParameters.size == 3 &&
valueParameters[0].type == pluginContext.typeOfAndroidExtensionsBase() &&
valueParameters[1].type == pluginContext.symbols.int.defaultType &&
// java.lang.Class<T> -> java.lang.Class
valueParameters[2].type.classifierOrNull?.defaultType == pluginContext.typeOfJavaClass()
}

fun IrClass.isAndroidExtensions(): Boolean {
Expand All @@ -47,5 +49,7 @@ fun IrPluginContext.typeOfAndroidExtensionsBase() =

fun IrPluginContext.typeOfView() = referenceClass(ANDROID_VIEW_CLASS_ID)!!.defaultType

fun IrPluginContext.typeOfJavaClass() = referenceClass(JAVA_CLASS_CLASS_ID)!!.defaultType

fun IrPluginContext.symbolOfAndroidExtensionImpl() =
referenceClass(ANDROID_EXTENSIONS_IMPL_CLASS_ID)!!
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ package com.kanyun.kace.compiler

import com.bennyhuo.kotlin.compiletesting.extensions.module.KotlinModule
import com.bennyhuo.kotlin.compiletesting.extensions.module.checkResult
import com.bennyhuo.kotlin.compiletesting.extensions.source.FileBasedModuleInfoLoader
import com.bennyhuo.kotlin.compiletesting.extensions.source.TextBasedModuleInfoLoader
import com.kanyun.kace.compiler.options.Options
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.junit.Test
import java.io.File

class KaceTest {

Expand All @@ -37,7 +38,9 @@ class KaceTest {

@OptIn(ExperimentalCompilerApi::class)
private fun testBase(fileName: String, useK2: Boolean) {
val loader = FileBasedModuleInfoLoader("testData/$fileName")
val source = File("testData/source.txt").readText()
val expect = File("testData/$fileName").readText()
val loader = TextBasedModuleInfoLoader("$source\n$expect")
val sourceModuleInfos = loader.loadSourceModuleInfos()

Options.isEnabled.set(true)
Expand Down
168 changes: 16 additions & 152 deletions kace-compiler/testData/K1.txt
Original file line number Diff line number Diff line change
@@ -1,139 +1,3 @@
// SOURCE
// FILE: Activity.java
package android.app;

public class Activity {

}
// FILE: BaseActivity.java
package android.app;

import android.app.Activity;

public class BaseActivity extends Activity {

}
// FILE: BaseKotlinActivity.kt
package android.app

import android.app.Activity

open class BaseKotlinActivity : Activity() {

}
// FILE: Base2Activity.java
package android.app;

import android.app.Activity;

public class Base2Activity extends BaseKotlinActivity {

}
// FILE: View.java
package android.view;

public class View {
public String value;
public View(String value) {
this.value = value;
}

public String toString() {
return value;
}
}
// FILE: AndroidExtensionsBase.java
package com.kanyun.kace;

import android.view.View;

public interface AndroidExtensionsBase {

<T extends View> T findViewByIdCached(AndroidExtensionsBase owner, int id);

}
// FILE: common.kt
package com.kanyun.kace

import android.view.View

interface AndroidExtensions: AndroidExtensionsBase {
override fun <T: View?> findViewByIdCached(owner: AndroidExtensionsBase, id: Int): T? = error("Never called!")
}
class AndroidExtensionsImpl: AndroidExtensions {

private val map = HashMap<Int, View>()

init {
List(10) {
map[it] = View("$it")
}
}

override fun <T: View?> findViewByIdCached(owner: AndroidExtensionsBase, id: Int): T? {
println(owner::class)
return map[id] as T?
}
}

// FILE: Main.kt [MainKt#main]
import com.kanyun.kace.AndroidExtensions
import com.kanyun.kace.AndroidExtensionsBase
import com.kanyun.kace.AndroidExtensionsImpl
import android.view.View
import android.app.Activity
import android.app.BaseActivity
import android.app.Base2Activity

class MainActivity : AndroidExtensions by AndroidExtensionsImpl() {

}

class SecondActivity : AndroidExtensions {

}

class ThirdActivity : AndroidExtensions {

override fun <T: View?> findViewByIdCached(owner: AndroidExtensionsBase, id: Int): T? {
println(owner::class)
return View("Third!!") as T?
}
}

open class FourthActivity : Activity() {

}


class FifthActivity : BaseActivity() {

}

class SixthActivity : Base2Activity() {

}

fun main() {
val main = MainActivity()
println(main.findViewByIdCached<View>(main, 0))

val second = SecondActivity()
println(second.findViewByIdCached<View>(second, 1))

val third = ThirdActivity()
println(third.findViewByIdCached<View>(third, 2))

val fouth = FourthActivity()
println(fouth.findViewByIdCached<View>(fouth, 3))

val fifth = FifthActivity()
println(fifth.findViewByIdCached<View>(fifth, 4))

val sixth = SixthActivity()
println(sixth.findViewByIdCached<View>(sixth, 7))
}

// EXPECT
// FILE: compiles.log
OK
Expand All @@ -153,46 +17,46 @@ class SixthActivity
// FILE: Main.kt.ir
class MainActivity : AndroidExtensions {
private val $$delegate_0: AndroidExtensionsImpl = AndroidExtensionsImpl()
override fun <T: View?> findViewByIdCached(owner: AndroidExtensionsBase, id: Int): T? {
return <this>.$$delegate_0.findViewByIdCached(owner, id)
override fun <T: View?> findViewByIdCached(owner: AndroidExtensionsBase, id: Int, viewClass: Class<T>): T? {
return <this>.$$delegate_0.findViewByIdCached(owner, id, viewClass)
}
}
class SecondActivity : AndroidExtensions {
private var $$androidExtensionsImpl: AndroidExtensionsImpl = AndroidExtensionsImpl()
override fun <T: View> findViewByIdCached(owner: AndroidExtensionsBase, id: Int): T? {
return <this>.$$androidExtensionsImpl.findViewByIdCached(owner, id)
override fun <T: View> findViewByIdCached(owner: AndroidExtensionsBase, id: Int, viewClass: Class<T>): T? {
return <this>.$$androidExtensionsImpl.findViewByIdCached(owner, id, viewClass)
}
}
class ThirdActivity : AndroidExtensions {
override fun <T: View?> findViewByIdCached(owner: AndroidExtensionsBase, id: Int): T? {
override fun <T: View?> findViewByIdCached(owner: AndroidExtensionsBase, id: Int, viewClass: Class<T>): T? {
println(<<IrGetClassImpl>>)
return View("Third!!")
}
}
open class FourthActivity : Activity, AndroidExtensions {
private var $$androidExtensionsImpl: AndroidExtensionsImpl = AndroidExtensionsImpl()
override fun <T: View> findViewByIdCached(owner: AndroidExtensionsBase, id: Int): T? {
return <this>.$$androidExtensionsImpl.findViewByIdCached(owner, id)
override fun <T: View> findViewByIdCached(owner: AndroidExtensionsBase, id: Int, viewClass: Class<T>): T? {
return <this>.$$androidExtensionsImpl.findViewByIdCached(owner, id, viewClass)
}
}
class FifthActivity : BaseActivity, AndroidExtensions {
private var $$androidExtensionsImpl: AndroidExtensionsImpl = AndroidExtensionsImpl()
override fun <T: View> findViewByIdCached(owner: AndroidExtensionsBase, id: Int): T? {
return <this>.$$androidExtensionsImpl.findViewByIdCached(owner, id)
override fun <T: View> findViewByIdCached(owner: AndroidExtensionsBase, id: Int, viewClass: Class<T>): T? {
return <this>.$$androidExtensionsImpl.findViewByIdCached(owner, id, viewClass)
}
}
class SixthActivity : Base2Activity
class SixthActivity : Base2Activity
fun main() {
val main = MainActivity()
println(main.findViewByIdCached(main, 0))
println(main.findViewByIdCached(main, 0, View::class.java))
val second = SecondActivity()
println(second.findViewByIdCached(second, 1))
println(second.findViewByIdCached(second, 1, View::class.java))
val third = ThirdActivity()
println(third.findViewByIdCached(third, 2))
println(third.findViewByIdCached(third, 2, View::class.java))
val fouth = FourthActivity()
println(fouth.findViewByIdCached(fouth, 3))
println(fouth.findViewByIdCached(fouth, 3, View::class.java))
val fifth = FifthActivity()
println(fifth.findViewByIdCached(fifth, 4))
println(fifth.findViewByIdCached(fifth, 4, View::class.java))
val sixth = SixthActivity()
println(sixth.findViewByIdCached(sixth, 7))
println(sixth.findViewByIdCached(sixth, 7, View::class.java))
}

0 comments on commit 4653d0e

Please sign in to comment.