Skip to content

Commit 0f7c127

Browse files
committed
Remove potential memory leak from MokkeryOption
1 parent 9600b4e commit 0f7c127

3 files changed

Lines changed: 51 additions & 30 deletions

File tree

mokkery-core-tooling/src/main/kotlin/dev/mokkery/internal/options/MokkeryOption.kt

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,39 @@ public data class MokkeryOption<T>(
1010
public val allowMultipleOccurrences: Boolean,
1111
public val type: MokkeryOptionType<T>,
1212
public val defaultValue: T?,
13-
public val extras: MutableMap<Any?, Any?> = mutableMapOf(),
1413
)
1514

1615
@InternalMokkeryApi
17-
public fun interface MokkeryOptionProjection<out P> {
16+
public interface MokkeryOptionProjection<out P> {
1817

19-
public fun create(option: MokkeryOption<*>): P
18+
public fun project(option: MokkeryOption<*>): P
19+
20+
@InternalMokkeryApi
21+
public companion object {
22+
23+
public operator fun <T> invoke(
24+
builder: (MokkeryOption<*>) -> T
25+
): MokkeryOptionProjection<T> = DefaultMokkeryOptionProjection(builder)
26+
27+
public fun <T> cached(
28+
builder: (MokkeryOption<*>) -> T
29+
): MokkeryOptionProjection<T> = CachedMokkeryProjection(builder)
30+
}
31+
}
32+
33+
private class DefaultMokkeryOptionProjection<T>(
34+
private val builder: (MokkeryOption<*>) -> T
35+
) : MokkeryOptionProjection<T> {
36+
override fun project(option: MokkeryOption<*>): T = builder(option)
37+
}
38+
39+
private class CachedMokkeryProjection<T>(
40+
private val builder: (MokkeryOption<*>) -> T
41+
) : MokkeryOptionProjection<T> {
42+
private val store = mutableMapOf<MokkeryOption<*>, T>()
43+
override fun project(option: MokkeryOption<*>): T = store.getOrPut(option) { builder(option) }
2044
}
2145

2246
@Suppress("UNCHECKED_CAST")
2347
@InternalMokkeryApi
24-
public fun <P> MokkeryOption<*>.get(projection: MokkeryOptionProjection<P>): P {
25-
return extras.getOrPut(projection) { projection.create(this) } as P
26-
}
48+
public fun <P> MokkeryOption<*>.get(projection: MokkeryOptionProjection<P>): P = projection.project(this)

mokkery-gradle/src/main/kotlin/dev/mokkery/gradle/MokkeryGradlePlugin.kt

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package dev.mokkery.gradle
55
import dev.mokkery.MokkeryConfig
66
import dev.mokkery.MokkeryConfig.RUNTIME_DEPENDENCY
77
import dev.mokkery.MokkeryConfig.VERSION
8-
import dev.mokkery.internal.options.MokkeryOption
98
import dev.mokkery.internal.options.MokkeryOptionProjection
109
import dev.mokkery.internal.options.MokkeryOptions
1110
import dev.mokkery.internal.options.get
@@ -34,9 +33,9 @@ public class MokkeryGradlePlugin : KotlinCompilerPluginSupportPlugin {
3433
.extensions
3534
.create("mokkery", MokkeryGradleExtension::class.java)
3635
mokkery.rule.convention(ApplicationRule.AllTests)
37-
val projection = GradlePropertyProjection(target)
36+
val gradleProperty = gradlePropertyProjection(target)
3837
MokkeryOptions.forEach {
39-
it.get(projection)?.convention(it.defaultValue)
38+
it.get(gradleProperty)?.convention(it.defaultValue)
4039
}
4140
target.configureDependencies()
4241
super.apply(target)
@@ -46,9 +45,9 @@ public class MokkeryGradlePlugin : KotlinCompilerPluginSupportPlugin {
4645
kotlinCompilation: KotlinCompilation<*>
4746
): Provider<List<SubpluginOption>> = kotlinCompilation.run {
4847
target.project.provider {
49-
val property = GradlePropertyProjection(project)
48+
val gradleProperty = gradlePropertyProjection(project)
5049
MokkeryOptions.mapNotNull {
51-
val value = it.get(property)?.get() ?: return@mapNotNull null
50+
val value = it.get(gradleProperty)?.get() ?: return@mapNotNull null
5251
SubpluginOption(it.name, it.type.serializer.serialize(value))
5352
}
5453
}
@@ -131,24 +130,24 @@ public class MokkeryGradlePlugin : KotlinCompilerPluginSupportPlugin {
131130
private fun KotlinVersion(string: String): KotlinVersion = KotlinToolingVersion(string).toKotlinVersion()
132131
}
133132

134-
private data class GradlePropertyProjection(
135-
val project: Project
133+
@Suppress("UNCHECKED_CAST")
134+
private fun gradlePropertyProjection(
135+
project: Project
136136
) : MokkeryOptionProjection<Property<Any>?> {
137-
138-
private val extension get() = project.mokkery
139-
140-
@Suppress("UNCHECKED_CAST")
141-
override fun create(option: MokkeryOption<*>): Property<Any>? = when (option) {
142-
MokkeryOptions.Core.defaultMockMode -> extension.defaultMockMode
143-
MokkeryOptions.Core.defaultVerifyMode -> extension.defaultVerifyMode
144-
MokkeryOptions.Core.ignoreFinalMembers -> extension.ignoreFinalMembers
145-
MokkeryOptions.Core.ignoreInlineMembers -> extension.ignoreInlineMembers
146-
MokkeryOptions.Core.enableFirDiagnostics -> extension.enableFirDiagnostics
147-
MokkeryOptions.Stubs.allowClassInheritance -> extension.stubs.allowClassInheritance
148-
MokkeryOptions.Stubs.allowConcreteClassInstantiation -> extension.stubs.allowConcreteClassInstantiation
149-
MokkeryOptions.Annotations.copyToMock -> extension.annotations.copyToMock
150-
else -> null
151-
} as? Property<Any>
137+
val extension = project.mokkery
138+
return MokkeryOptionProjection {
139+
when (it) {
140+
MokkeryOptions.Core.defaultMockMode -> extension.defaultMockMode
141+
MokkeryOptions.Core.defaultVerifyMode -> extension.defaultVerifyMode
142+
MokkeryOptions.Core.ignoreFinalMembers -> extension.ignoreFinalMembers
143+
MokkeryOptions.Core.ignoreInlineMembers -> extension.ignoreInlineMembers
144+
MokkeryOptions.Core.enableFirDiagnostics -> extension.enableFirDiagnostics
145+
MokkeryOptions.Stubs.allowClassInheritance -> extension.stubs.allowClassInheritance
146+
MokkeryOptions.Stubs.allowConcreteClassInstantiation -> extension.stubs.allowConcreteClassInstantiation
147+
MokkeryOptions.Annotations.copyToMock -> extension.annotations.copyToMock
148+
else -> null
149+
} as? Property<Any>
150+
}
152151
}
153152

154153
private val Project.mokkery get() = extensions.getByType(MokkeryGradleExtension::class.java)

mokkery-plugin/src/main/kotlin/dev/mokkery/plugin/MokkeryOption.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ val MokkeryOption<*>.cliOption: CliOption
1313
val <T> MokkeryOption<T>.configurationKey: CompilerConfigurationKey<List<T>>
1414
get() = get(ConfigurationKeyProjection) as CompilerConfigurationKey<List<T>>
1515

16-
private val CliOptionProjection = MokkeryOptionProjection {
16+
private val CliOptionProjection = MokkeryOptionProjection.cached {
1717
CliOption(
1818
optionName = it.name,
1919
valueDescription = it.type.description,
@@ -23,6 +23,6 @@ private val CliOptionProjection = MokkeryOptionProjection {
2323
)
2424
}
2525

26-
private val ConfigurationKeyProjection = MokkeryOptionProjection<CompilerConfigurationKey<List<Any?>>> {
26+
private val ConfigurationKeyProjection = MokkeryOptionProjection.cached<CompilerConfigurationKey<List<Any?>>> {
2727
CompilerConfigurationKey.create(it.name)
2828
}

0 commit comments

Comments
 (0)