Skip to content

Commit

Permalink
Merge pull request #524
Browse files Browse the repository at this point in the history
context_params
  • Loading branch information
IVIanuu authored Feb 21, 2024
2 parents 4dd23ce + 5b2ce8b commit 585f237
Show file tree
Hide file tree
Showing 40 changed files with 187 additions and 766 deletions.
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,13 @@ or to break circular dependencies.
You can do this by injecting a function.
```kotlin
// inject a function to create multiple Tokens
fun run(@Inject tokenFactory: () -> Token) {
fun run(tokenFactory: () -> Token = inject) {
val tokenA = tokenFactory()
val tokenB = tokenFactory()
}

// inject a function to create a MyViewModel with the additional String parameter
@Composable fun MyScreen(@Inject viewModelFactory: (String) -> MyViewModel) {
@Composable fun MyScreen(viewModelFactory: (String) -> MyViewModel = inject) {
val viewModel = remember { viewModelFactory("user_id") }
}

Expand All @@ -139,25 +139,31 @@ Value classes:
@JvmInline value class PlaylistId(val value: String)
@JvmInline value class TrackId(val value: String)

fun loadPlaylistTracks(@Inject playlistId: PlaylistId, @Inject trackId: TrackId): List<Track> = ...
fun loadPlaylistTracks(playlistId: PlaylistId = inject, trackId: TrackId = inject): List<Track> = ...
```

Tags:
```kotlin
@Tag annotation class PlaylistId
@Tag annotation class TrackId

fun loadPlaylistTracks(@Inject playlistId: @PlaylistId String, @Inject trackId: @TrackId String): List<Track> = ...
@Tag
@Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPE)
annotation class PlaylistId
@Tag
@Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPE)
annotation class TrackId

fun loadPlaylistTracks(playlistId: @PlaylistId String = inject, trackId: @TrackId String = inject): List<Track> = ...
```

Optionally you can add a typealias for your tag to make it easier to use
```kotlin
@Tag annotation class PlaylistIdTag
@Tag @Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPE)
annotation class PlaylistIdTag
typealias PlaylistId = @PlaylistIdTag String
@Tag annotation class TrackIdTag
@Tag @Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPE)
annotation class TrackIdTag
typealias TrackId = @TrackIdTag String

fun loadPlaylistTracks(@Inject playlistId: PlaylistId, @Inject trackId: TrackId): List<Track> = ...
fun loadPlaylistTracks(playlistId: PlaylistId = inject, trackId: TrackId = inject): List<Track> = ...
```

# Type keys
Expand Down Expand Up @@ -188,7 +194,5 @@ dependencies {
}
```

# Ide plugin is required to remove errors in the IDE (code should still compile)

# More complex uses can be found in my essentials project(base project for my apps)
# https://github.com/IVIanuu/essentials
4 changes: 1 addition & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
* Copyright 2022 Manuel Wrage. Use of this source code is governed by the Apache 2.0 license.
*/

import com.vanniktech.maven.publish.MavenPublishBaseExtension
import com.vanniktech.maven.publish.SonatypeHost
import com.vanniktech.maven.publish.*

buildscript {
repositories {
Expand All @@ -23,7 +22,6 @@ buildscript {
classpath(Deps.KotlinSerialization.gradlePlugin)
classpath(Deps.Ksp.gradlePlugin)
classpath(Deps.mavenPublishGradlePlugin)
classpath(Deps.shadowGradlePlugin)
}
}

Expand Down
4 changes: 0 additions & 4 deletions buildSrc/src/main/kotlin/dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,4 @@ object Deps {
const val mavenPublishGradlePlugin = "com.vanniktech:gradle-maven-publish-plugin:0.24.0"

const val mockk = "io.mockk:mockk:1.11.0"

const val roboelectric = "org.robolectric:robolectric:4.10.3"

const val shadowGradlePlugin = "gradle.plugin.com.github.johnrengelman:shadow:7.1.2"
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ class Scope<N> : SynchronizedObject() {
(if (value !== NULL) value else null) as T
}

inline operator fun <T> invoke(@Inject key: TypeKey<T>, init: () -> T): T =
inline operator fun <T> invoke(key: TypeKey<T> = inject, init: () -> T): T =
invoke(key.value, init)

companion object {
@PublishedApi internal val NULL = Any()
}
}

@Tag annotation class Scoped<N> {
@Tag
@Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPE)
annotation class Scoped<N> {
@Provide companion object {
@Provide inline fun <@Spread T : @Scoped<N> S, S : Any, N> scoped(
scope: Scope<N>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ import com.ivianuu.injekt.*
/**
* Returns the [TypeKey] of [T]
*/
inline fun <T> typeKeyOf(@Inject x: TypeKey<T>): TypeKey<T> = x
inline fun <T> typeKeyOf(x: TypeKey<T> = inject): TypeKey<T> = x
20 changes: 0 additions & 20 deletions compiler/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,12 @@
* Copyright 2022 Manuel Wrage. Use of this source code is governed by the Apache 2.0 license.
*/

import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

plugins {
kotlin("jvm")
kotlin("plugin.serialization")
id("com.github.johnrengelman.shadow")
id("com.google.devtools.ksp")
}

val shadowJar = tasks.getByName<ShadowJar>("shadowJar") {
relocate("org.jetbrains.kotlin.com.intellij", "com.intellij")
dependencies {
exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib"))
exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib-common"))
exclude(dependency("org.jetbrains:annotations"))

exclude(dependency("com.intellij:openapi"))
exclude(dependency("com.intellij:extensions"))
exclude(dependency("com.intellij:annotations"))
}
}

artifacts {
archives(shadowJar)
}

dependencies {
implementation(Deps.AutoService.annotations)
ksp(Deps.AutoService.processor)
Expand Down
34 changes: 24 additions & 10 deletions compiler/src/main/kotlin/com/ivianuu/injekt/compiler/Infos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@

package com.ivianuu.injekt.compiler

import com.ivianuu.injekt.compiler.analysis.*
import com.ivianuu.injekt.compiler.resolution.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import org.jetbrains.kotlin.backend.common.descriptors.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.*
import org.jetbrains.kotlin.js.resolve.diagnostics.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.resolve.descriptorUtil.*
Expand All @@ -26,7 +27,11 @@ import org.jetbrains.kotlin.utils.addToStdlib.*
* Stores information about a callable which is NOT stored by the kotlin compiler
* but is critical to injekt
*/
data class CallableInfo(val type: TypeRef, val parameterTypes: Map<Int, TypeRef>)
data class CallableInfo(
val type: TypeRef,
val parameterTypes: Map<Int, TypeRef>,
val injectParameters: Set<Int>
)

fun CallableDescriptor.callableInfo(ctx: Context): CallableInfo =
if (this is PropertyAccessorDescriptor) correspondingProperty.callableInfo(ctx)
Expand Down Expand Up @@ -88,7 +93,14 @@ fun CallableDescriptor.callableInfo(ctx: Context): CallableInfo =
this[parameter.injektIndex()] = parameter.type.toTypeRef(ctx)
}

val info = CallableInfo(type, parameterTypes)
val injectParameters = valueParameters
.filter {
it.findPsi().safeAs<KtParameter>()?.defaultValue?.text ==
InjektFqNames.inject.shortName().asString()
}
.mapTo(mutableSetOf()) { it.index }

val info = CallableInfo(type, parameterTypes, injectParameters)

// important to cache the info before persisting it
ctx.trace!!.record(sliceOf("callable_info"), this, info)
Expand All @@ -105,10 +117,10 @@ private fun CallableDescriptor.persistInfoIfNeeded(info: CallableInfo, ctx: Cont
safeAs<ConstructorDescriptor>()?.visibility?.shouldPersistInfo() != true)
return

if (hasAnnotation(InjektFqNames.DeclarationInfo))
return
if (hasAnnotation(InjektFqNames.DeclarationInfo)) return

val shouldPersistInfo = info.type.shouldBePersisted() ||
val shouldPersistInfo = info.injectParameters.isNotEmpty() ||
info.type.shouldBePersisted() ||
info.parameterTypes.any { (_, parameterType) -> parameterType.shouldBePersisted() }

if (!shouldPersistInfo) return
Expand All @@ -128,19 +140,22 @@ private fun CallableDescriptor.persistInfoIfNeeded(info: CallableInfo, ctx: Cont

@Serializable data class PersistedCallableInfo(
val type: PersistedTypeRef,
val parameterTypes: Map<Int, PersistedTypeRef>
val parameterTypes: Map<Int, PersistedTypeRef>,
val injectParameters: Set<Int>
)

fun CallableInfo.toPersistedCallableInfo(ctx: Context) = PersistedCallableInfo(
type = type.toPersistedTypeRef(ctx),
parameterTypes = parameterTypes
.mapValues { it.value.toPersistedTypeRef(ctx) }
.mapValues { it.value.toPersistedTypeRef(ctx) },
injectParameters = injectParameters
)

fun PersistedCallableInfo.toCallableInfo(ctx: Context) = CallableInfo(
type = type.toTypeRef(ctx),
parameterTypes = parameterTypes
.mapValues { it.value.toTypeRef(ctx) }
.mapValues { it.value.toTypeRef(ctx) },
injectParameters = injectParameters
)

/**
Expand Down Expand Up @@ -319,7 +334,6 @@ private fun Annotated.updateAnnotation(annotation: AnnotationDescriptor) {
LazyClassDescriptor::class,
"annotations"
) { newAnnotations }
is InjectFunctionDescriptor -> underlyingDescriptor.updateAnnotation(annotation)
is FunctionImportedFromObject -> callableFromObject.updateAnnotation(annotation)
else -> throw AssertionError("Cannot add annotation to $this $javaClass")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,6 @@ class InjektComponentRegistrar : ComponentRegistrar {
project,
InjectCallChecker()
)

// extension point does not exist CLI for some reason
// but it's still queried later
SyntheticScopeProviderExtension.registerExtensionPoint(project)
SyntheticScopeProviderExtension.registerExtension(
project,
InjectSyntheticScopeProviderExtension()
)

@Suppress("DEPRECATION")
Extensions.getRootArea().getExtensionPoint(DiagnosticSuppressor.EP_NAME)
.registerExtension(InjektDiagnosticSuppressor(), project)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import org.jetbrains.kotlin.name.*

object InjektFqNames {
val InjektPackage = FqName("com.ivianuu.injekt")
val Inject = InjektPackage.child("Inject".asNameId())
val Provide = InjektPackage.child("Provide".asNameId())
val inject = InjektPackage.child("inject".asNameId())
val Tag = InjektPackage.child("Tag".asNameId())
val Spread = InjektPackage.child("Spread".asNameId())

Expand Down
23 changes: 0 additions & 23 deletions compiler/src/main/kotlin/com/ivianuu/injekt/compiler/InjektUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@

package com.ivianuu.injekt.compiler

import com.ivianuu.injekt.compiler.analysis.*
import org.jetbrains.kotlin.builtins.functions.*
import org.jetbrains.kotlin.com.intellij.openapi.project.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.*
import org.jetbrains.kotlin.incremental.components.*
Expand Down Expand Up @@ -37,8 +35,6 @@ fun KtFunction.getArgumentDescriptor(ctx: Context): ValueParameterDescriptor? {
return mapping.valueParameter
}

val isIde = Project::class.java.name == "com.intellij.openapi.project.Project"

fun <D : DeclarationDescriptor> KtDeclaration.descriptor(ctx: Context) =
ctx.trace!!.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this] as? D

Expand All @@ -47,7 +43,6 @@ fun DeclarationDescriptor.isExternalDeclaration(ctx: Context): Boolean =

fun DeclarationDescriptor.isDeserializedDeclaration(): Boolean = this is DeserializedDescriptor ||
(this is PropertyAccessorDescriptor && correspondingProperty.isDeserializedDeclaration()) ||
(this is InjectFunctionDescriptor && underlyingDescriptor.isDeserializedDeclaration()) ||
this is DeserializedTypeParameterDescriptor ||
this is JavaClassDescriptor ||
this is FunctionClassDescriptor
Expand Down Expand Up @@ -198,24 +193,6 @@ fun ParameterDescriptor.injektIndex(): Int = if (this is ValueParameterDescripto
}
}

fun <T> Any.readPrivateFinalField(clazz: KClass<*>, fieldName: String): T {
val field = clazz.java.declaredFields
.single { it.name == fieldName }
field.isAccessible = true
val modifiersField = try {
Field::class.java.getDeclaredField("modifiers")
} catch (e: Throwable) {
val getDeclaredFields0 = Class::class.java.getDeclaredMethod("getDeclaredFields0", Boolean::class.java)
getDeclaredFields0.isAccessible = true
getDeclaredFields0.invoke(Field::class.java, false)
.cast<Array<Field>>()
.single { it.name == "modifiers" }
}
modifiersField.isAccessible = true
modifiersField.setInt(field, field.modifiers and Modifier.FINAL.inv())
return field.get(this) as T
}

fun <T> Any.updatePrivateFinalField(clazz: KClass<*>, fieldName: String, transform: T.() -> T): T {
val field = clazz.java.declaredFields
.single { it.name == fieldName }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import org.jetbrains.kotlin.types.model.*
val isStarProjection: Boolean,
val variance: TypeVariance,
val isMarkedNullable: Boolean,
val isProvide: Boolean,
val isInject: Boolean
val isProvide: Boolean
)

fun TypeRef.toPersistedTypeRef(ctx: Context): PersistedTypeRef =
Expand All @@ -25,8 +24,7 @@ fun TypeRef.toPersistedTypeRef(ctx: Context): PersistedTypeRef =
isStarProjection = isStarProjection,
variance = variance,
isMarkedNullable = isMarkedNullable,
isProvide = isProvide,
isInject = isInject
isProvide = isProvide
)

fun PersistedTypeRef.toTypeRef(ctx: Context): TypeRef {
Expand All @@ -46,7 +44,6 @@ fun PersistedTypeRef.toTypeRef(ctx: Context): TypeRef {
arguments = arguments,
variance = variance,
isMarkedNullable = isMarkedNullable,
isProvide = isProvide,
isInject = isInject
isProvide = isProvide
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ import org.jetbrains.kotlin.utils.*

private fun checkCall(resolvedCall: ResolvedCall<*>, ctx: Context) {
val resultingDescriptor = resolvedCall.resultingDescriptor
if (resultingDescriptor !is InjectFunctionDescriptor) return

val info = resultingDescriptor.callableInfo(ctx)
if (info.injectParameters.isEmpty()) return

val callExpression = resolvedCall.call.callElement

Expand Down Expand Up @@ -87,8 +89,8 @@ import org.jetbrains.kotlin.utils.*
val requests = callee.callable.allParameters
.transform {
val index = it.injektIndex()
if (valueArgumentsByIndex[index] is DefaultValueArgument && it.isInject(ctx))
add(it.toInjectableRequest(callee, ctx))
if (valueArgumentsByIndex[index] is DefaultValueArgument && index in info.injectParameters)
add(it.toInjectableRequest(callee))
}

if (requests.isEmpty()) return
Expand Down
Loading

0 comments on commit 585f237

Please sign in to comment.