Skip to content

Commit

Permalink
Add IR supporting (#53)
Browse files Browse the repository at this point in the history
* IR analysis and transformations (#41)

* Implement IR analysis and transformations

* Fixme/parametrized type util (#45)

* test transforming into parametrized type

* add parametrized types for tests, try to fix parametrized inheritance (no luck)

* Fix toParanetrizedType and add tests

* Remove unused parameter

* Add ssh debug action

* Fix github actions

* Fix tests by sorting comparing lists

* Optimize imports and remove function that not used anymore

* Add more generic tests

* Remove debug output

* Fix comment formatting

* Fix PR comments

* Update readme and add version (#46)

* Change version

* Use remote maven in examples

* Change version

* Downgrade version

* Update examples and readme to enable IR (#48)

* Update examples and readme to enable IR

* Bugfix/supertypes naming (#49)

* Fix subtypes -> supertypes

* Fix kotlin docs and small typos in comments

* Fix PR comments

* Issue 47/check error cases (#50)

* Add enum cases, refactor reflekt call testing

* Fix examples a little bit

* Add an example with start protection

Co-authored-by: elena-lyulina <[email protected]>

* Upgrade version and add more examples

Co-authored-by: Maria Malysheva <[email protected]>
Co-authored-by: Elena Lyulina <[email protected]>
Co-authored-by: elena-lyulina <[email protected]>
  • Loading branch information
4 people committed Aug 18, 2021
1 parent eb66830 commit 47762cf
Show file tree
Hide file tree
Showing 700 changed files with 5,951 additions and 4,111 deletions.
43 changes: 22 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ user analysis. The first one will be called Reflekt, and the second SmartReflekt

**Restrictions**. Reflekt analyses only `.kt` files (in the psoject and in the libraries); uses
Kotlin `1.4.20`. Reflekt does not currently support incremental compilation.

**Note**, we use [Intermediate Representation](https://kotlinlang.org/docs/whatsnew14.html#unified-backends-and-extensibility) of code in this plugin.
It means, that Reflekt can be used for all available platforms: JVM, Native and JavaScript.
___

## Table of contents
Expand All @@ -40,11 +43,11 @@ add the following lines in the `plugins` section:

```kotlin
plugins {
// Version of Kotlin should be 1.4.20+
kotlin("jvm") version "1.4.20" apply true
// Version of Kotlin should be 1.4.30+ that supports IR backend
kotlin("jvm") version "1.5.10" apply true

// Please, use the latest Reflekt version
id("io.reflekt") version "0.1.0" apply true
id("io.reflekt") version "0.2.0" apply true

// Necessary only for this example, for Kotless library
id("io.kotless") version "0.1.6" apply true
Expand Down Expand Up @@ -81,7 +84,7 @@ the following lines in the `dependencies` section:
```kotlin
dependencies {
// The version here and the version in the plugins sections should be equal
implementation("io.reflekt", "reflekt-dsl", "0.1.0")
implementation("io.reflekt", "reflekt-dsl", "0.2.0")

// Necessary for this example
compileOnly("io.kotless", "kotless-lang", "0.1.6")
Expand Down Expand Up @@ -114,21 +117,19 @@ _Please note that the `librariesToIntrospect` argument should contain only the d
use in the `dependencies` section. These dependencies may be implemented in Java or Kotlin language,
but the analysis will be made only on Kotlin files._

To avoid some bugs, please add the following compilation settings for Java and Kotlin in
the `build.gradle.kts` file:
To avoid some bugs and enable IR, please add the following compilation settings
for Java and Kotlin in the `build.gradle.kts` file:

```kotlin
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "11"
languageVersion = "1.4"
apiVersion = "1.4"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "11"
languageVersion = "1.4"
apiVersion = "1.4"
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

tasks.withType<KotlinCompile> {
kotlinOptions {
useIR = true
languageVersion = "1.5"
apiVersion = "1.5"
jvmTarget = "11"
}
}
```

Expand All @@ -150,10 +151,10 @@ kotlin.incremental = false
Now you can use the Reflekt plugin to find objects, classes, and functions in your project:

```kotlin
val objects = Reflekt.objects().withSubType<AInterface>()
val objects = Reflekt.objects().withSupertype<AInterface>()
.withAnnotations<AInterface>(FirstAnnotation::class, SecondAnnotation::class).toList()

val classes = Reflekt.classes().withSubType<BInterface>().toSet()
val classes = Reflekt.classes().withSupertype<BInterface>().toSet()

val functions = Reflekt.functions().withAnnotations<() -> Unit>().toList()
```
Expand Down Expand Up @@ -197,10 +198,10 @@ write this version in the plugins and dependencies sections.
- [x] Compile-time reflection by custom users' filters for `multi-module` projects
by [SmartReflekt DSL](./reflekt-dsl/src/main/kotlin/io/reflekt/SmartReflekt.kt)
- [x] project's files
- [x] external libraries
- [ ] external libraries
- [x] Bytecode generation -> IR generation
- [ ] Incremental compilation process
- [ ] Search in all modules of the project
- [ ] Bytecode generation -> IR generation
- [ ] Code generation.

_Note: We analyze modules independently of each other. If an object\class\function is in module A,
Expand Down
9 changes: 5 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile

group = "io.reflekt"
version = "0.1.0"
version = "0.2.0"

plugins {
id("tanvd.kosogor") version "1.0.10" apply true
kotlin("jvm") version "1.4.20" apply true
kotlin("jvm") version "1.5.10" apply true
id("com.github.gmazzo.buildconfig") version "2.0.2" apply false
`maven-publish`
kotlin("kapt") version "1.5.10" apply true
}

allprojects {
Expand All @@ -18,8 +19,8 @@ allprojects {
tasks.withType<KotlinJvmCompile> {
kotlinOptions {
jvmTarget = "11"
languageVersion = "1.4"
apiVersion = "1.4"
languageVersion = "1.5"
apiVersion = "1.5"
}
}

Expand Down
16 changes: 9 additions & 7 deletions examples/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import io.reflekt.plugin.reflekt
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

group = rootProject.group
version = rootProject.version

plugins {
id("tanvd.kosogor") version "1.0.10" apply true
id("io.reflekt") version "0.1.0" apply true
kotlin("jvm") version "1.4.20" apply true
id("io.reflekt") version "0.2.0" apply true
kotlin("jvm") version "1.5.10" apply true
}

allprojects {
Expand All @@ -17,22 +17,24 @@ allprojects {
plugin("io.reflekt")
}

tasks.withType<KotlinJvmCompile> {
tasks.withType<KotlinCompile> {
kotlinOptions {
useIR = true
languageVersion = "1.5"
apiVersion = "1.5"
jvmTarget = "11"
languageVersion = "1.4"
apiVersion = "1.4"
}
}

dependencies {
implementation("io.reflekt", "reflekt-dsl", "0.1.0")
implementation("io.reflekt", "reflekt-dsl", "0.2.0")
implementation("com.github.gumtreediff", "core", "2.1.2")
}

repositories {
mavenCentral()
google()
// mavenLocal()
maven(url = uri("https://packages.jetbrains.team/maven/p/reflekt/reflekt"))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,20 @@ class TestFunctions {
}
}
}

fun fooBoolean(): Boolean {
println("public second example foo")
return true
}

inline fun <reified T> fooArray(): Array<T> = emptyArray()

fun <T> fooList(): List<T> = emptyList()

class MyInClass<in T>

fun <T> fooMyInClass(): MyInClass<T> = MyInClass()

fun withStar(a: List<*>) { }

fun <T: Number> withBound(a: T) { }
79 changes: 62 additions & 17 deletions examples/first-module/src/main/kotlin/io/reflekt/example/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,99 @@ class Test(var a: List<Any>, b: List<Any>)

fun main() {
val tmp = Test(emptyList(), emptyList())
tmp.a = listOf(Reflekt.objects().withSubType<AInterface>().withAnnotations<AInterface>(FirstAnnotation::class))
println(tmp)
tmp.a = Reflekt.objects().withSupertype<AInterface>().withAnnotations<AInterface>(FirstAnnotation::class).toList()
println(tmp.a)

val objects = Reflekt.objects().withSubType<AInterface>().withAnnotations<AInterface>(FirstAnnotation::class, SecondAnnotation::class).toList()
val objects = Reflekt.objects().withSupertype<AInterface>().withAnnotations<AInterface>(FirstAnnotation::class, SecondAnnotation::class).toList()
println(objects)
val objects1 = Reflekt.objects().withSubType<AInterface>()
val objects1 = Reflekt.objects().withSupertype<AInterface>()
.withAnnotations<AInterface>(FirstAnnotation::class, SecondAnnotation::class).toList()
println(objects1)

val objects2 = Reflekt.objects().withSubTypes(AInterface::class, A1::class)
val objects2 = Reflekt.objects().withSupertypes(AInterface::class, A1::class)
.withAnnotations<AInterface>(FirstAnnotation::class, SecondAnnotation::class).toList()
println(objects2)

val objects3 = Reflekt.objects().withSubTypes(AInterface::class, A1::class)
val objects3 = Reflekt.objects().withSupertypes(AInterface::class, A1::class)
.withAnnotations<AInterface>(FirstAnnotation::class).toList()
println(objects3)

val objects4 = Reflekt.objects().withAnnotations<AInterface>(FirstAnnotation::class).toList()
println(objects4)
val objects5 = Reflekt.objects().withAnnotations<AInterface>(FirstAnnotation::class).toList()
println(objects5)
val objects6 = Reflekt.objects().withAnnotations<A1>(FirstAnnotation::class).withSubType<AInterface>().toList()
val objects6 = Reflekt.objects().withAnnotations<A1>(FirstAnnotation::class).withSupertype<AInterface>().toList()
println(objects6)
val objects7 = Reflekt.objects().withAnnotations<A1>(FirstAnnotation::class).withSubTypes(AInterface::class).toList()
val objects7 = Reflekt.objects().withAnnotations<A1>(FirstAnnotation::class).withSupertypes(AInterface::class).toList()
println(objects7)
val objects8 = Reflekt.objects().withSubType<AInterface>().toList()
val objects8 = Reflekt.objects().withSupertype<AInterface>().toList()
println(objects8)

val classes1 = Reflekt.classes().withSubType<AInterface>().toList()
val classes1 = Reflekt.classes().withSupertype<AInterface>().toList()
println(classes1)
val classes2 = Reflekt.classes().withSubType<BInterface>().toSet()
val classes2 = Reflekt.classes().withSupertype<BInterface>().toSet()
println(classes2)
val classes3 = Reflekt.classes().withAnnotations<B2>(FirstAnnotation::class, SecondAnnotation::class).toList()
println(classes3)

val classes4 = Reflekt.classes().withSubType<Action>().toList()
val classes4 = Reflekt.classes().withSupertype<Action>().toList()
println(classes4)

val functions = Reflekt.functions().withAnnotations<() -> Unit>(FirstAnnotation::class).toList()
println(functions)

val smartClasses = SmartReflekt.classes<BInterface>().filter { it.isData() }.resolve()
println(smartClasses)

val smartObjects = SmartReflekt.objects<BInterface>().filter { it.isCompanion() }.resolve()
println(smartObjects)
// TODO: it does not work (error with TestFunctions$Companion), see issue#52: https://github.com/JetBrains-Research/reflekt/issues/52
// val smartClasses = SmartReflekt.classes<BInterface>().filter { it.isData() }.resolve()
// println(smartClasses)
//
// val smartObjects = SmartReflekt.objects<BInterface>().filter { it.isCompanion() }.resolve()
// println(smartObjects)

val smartFunctions = SmartReflekt.functions<() -> Unit>().filter { it.isTopLevel && it.name == "foo" }.resolve()
println(smartFunctions)
smartFunctions.forEach { it() }

val fooBoolean = SmartReflekt.functions<() -> Boolean>().filter { it.isTopLevel && it.name == "fooBoolean" }.resolve().onEach { it() }
.map { it.toString() }.toSet()
println("fooBoolean: $fooBoolean")

val fooStar = SmartReflekt.functions<(List<*>) -> Unit>().filter { it.isTopLevel && it.name == "withStar" }.resolve().onEach { it(listOf(1)) }
.map { it.toString() }.toSet()
println("fooStar: $fooStar")

// TODO: we will support gnerics with bounds
// val fooBound = SmartReflekt.functions<(Number) -> Unit>().filter { it.isTopLevel && it.name == "withBound" }.resolve().onEach { it(listOf(1)) }
// .map { it.toString() }.toSet()
// println("fooBound: $fooBound")

/**
* Such calls still fail, but it seems it's not a Reflekt problem since Kotlin doesn't consider our functions as subtypes of the given signature.
*/

// val fooArray = SmartReflekt.functions<Function0<Array<*>>>().filter { it.isTopLevel && it.name == "fooArray" }.resolve().onEach { it() }.map { it.toString() }.toSet()
// println(fooArray)
//
// val fooList = SmartReflekt.functions<Function0<List<*>>>().filter { it.isTopLevel && it.name == "fooList" }.resolve().onEach { it() }.map { it.toString() }.toSet()
// println(fooList)
//
// val fooMyInClass = SmartReflekt.functions<Function0<MyInClass<*>>>().filter { it.isTopLevel && it.name == "fooMyInClass" }.resolve().onEach { it() }.map { it.toString() }.toSet()
// println(fooMyInClass)

/**
* The simplest way to check it's to pass our functions as a parameter of an argument with the given signature:
*/
// arrayTestFun(::fooArray)
// listTestFun(::fooList)
// myInClassTestFun(::fooMyInClass)

/**
* For each of the functions Kotlin says [NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER] Not enough information to infer type variable T
* Maybe we need to check https://kotlinlang.org/spec/type-system.html#mixed-site-variance
*/
}

fun arrayTestFun(funToTest: Function0<Array<*>>) {}

fun listTestFun(funToTest: Function0<List<*>>) {}

fun myInClassTestFun(funToTest: Function0<MyInClass<*>>) {}
1 change: 1 addition & 0 deletions examples/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pluginManagement {

repositories {
gradlePluginPortal()
// mavenLocal()
//add the dependency to Reflekt Maven repository
maven(url = uri("https://packages.jetbrains.team/maven/p/reflekt/reflekt"))
}
Expand Down
2 changes: 1 addition & 1 deletion reflekt-core/src/main/kotlin/io/reflekt/util/Util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ object Util {
* Just needs to be consistent with the artifactId in reflekt-plugin build.gradle.kts#publishJar
*/
const val GRADLE_ARTIFACT_ID = "reflekt-plugin"
const val VERSION = "0.1.0"
const val VERSION = "0.2.0"

val ENABLED_OPTION_INFO = MyCliOption(
name = "enabled",
Expand Down
Loading

0 comments on commit 47762cf

Please sign in to comment.