@@ -27,6 +27,7 @@ import com.squareup.kotlinpoet.ParameterizedTypeName
2727import com.squareup.kotlinpoet.PropertySpec
2828import com.squareup.kotlinpoet.TypeName
2929import com.squareup.kotlinpoet.TypeSpec
30+ import com.squareup.kotlinpoet.asTypeName
3031import com.squareup.kotlinpoet.javapoet.KotlinPoetJavaPoetPreview
3132import com.squareup.kotlinpoet.javapoet.toKClassName
3233import motif.internal.None
@@ -48,7 +49,7 @@ object KotlinCodeGenerator {
4849 addSuperinterface(superClassName.kt)
4950 objectsField?.let { addProperty(it.spec()) }
5051 addProperty(dependenciesField.spec())
51- cacheFields.forEach { addProperty(it.spec()) }
52+ cacheFields.forEach { addProperty(it.spec(useNullFieldInitialization )) }
5253 primaryConstructor(constructor .spec())
5354 alternateConstructor?.let { addFunction(it.spec()) }
5455 accessMethodImpls
@@ -59,7 +60,7 @@ object KotlinCodeGenerator {
5960 .forEach { addProperty(it.propSpec()) }
6061 childMethodImpls.forEach { addFunction(it.spec()) }
6162 addFunction(scopeProviderMethod.spec())
62- factoryProviderMethods.forEach { addFunctions(it.specs()) }
63+ factoryProviderMethods.forEach { addFunctions(it.specs(useNullFieldInitialization )) }
6364 dependencyProviderMethods.forEach { addFunction(it.spec()) }
6465 dependencies?.let { addType(it.spec()) }
6566 objectsImpl?.let { addType(it.spec()) }
@@ -96,12 +97,20 @@ object KotlinCodeGenerator {
9697 .initializer(name)
9798 .build()
9899
99- private fun CacheField.spec (): PropertySpec =
100- PropertySpec .builder(name, Any ::class , KModifier .PRIVATE )
101- .mutable(true )
102- .addAnnotation(Volatile ::class )
103- .initializer(" %T.NONE" , None ::class )
104- .build()
100+ private fun CacheField.spec (useNullFieldInitialization : Boolean ): PropertySpec =
101+ if (useNullFieldInitialization) {
102+ PropertySpec .builder(name, Any ::class .asTypeName().copy(true ), KModifier .PRIVATE )
103+ .mutable(true )
104+ .addAnnotation(Volatile ::class )
105+ .initializer(" null" )
106+ .build()
107+ } else {
108+ PropertySpec .builder(name, Any ::class , KModifier .PRIVATE )
109+ .mutable(true )
110+ .addAnnotation(Volatile ::class )
111+ .initializer(" %T.NONE" , None ::class )
112+ .build()
113+ }
105114
106115 private fun Constructor.spec (): FunSpec =
107116 FunSpec .constructorBuilder()
@@ -194,34 +203,55 @@ object KotlinCodeGenerator {
194203 .addStatement(" return this" )
195204 .build()
196205
197- private fun FactoryProviderMethod.specs (): List <FunSpec > {
206+ private fun FactoryProviderMethod.specs (useNullFieldInitialization : Boolean ): List <FunSpec > {
198207 val primarySpec =
199208 FunSpec .builder(name)
200209 .addModifiers(KModifier .INTERNAL )
201210 .returns(returnTypeName.reloadedForTypeArgs(env))
202- .addCode(body.spec())
211+ .addCode(body.spec(useNullFieldInitialization ))
203212 .build()
204213 val spreadSpecs = spreadProviderMethods.map { it.spec() }
205214 return listOf (primarySpec) + spreadSpecs
206215 }
207216
208- private fun FactoryProviderMethodBody.spec (): CodeBlock =
217+ private fun FactoryProviderMethodBody.spec (useNullFieldInitialization : Boolean ): CodeBlock =
209218 when (this ) {
210- is FactoryProviderMethodBody .Cached -> spec()
219+ is FactoryProviderMethodBody .Cached -> spec(useNullFieldInitialization )
211220 is FactoryProviderMethodBody .Uncached -> spec()
212221 }
213222
214- private fun FactoryProviderMethodBody.Cached.spec (): CodeBlock =
215- CodeBlock .builder()
216- .beginControlFlow(" if (%N == %T.NONE)" , cacheFieldName, None ::class )
217- .beginControlFlow(" synchronized (this)" )
218- .beginControlFlow(" if (%N == %T.NONE)" , cacheFieldName, None ::class )
219- .addStatement(" %N=%L" , cacheFieldName, instantiation.spec())
220- .endControlFlow()
221- .endControlFlow()
222- .endControlFlow()
223- .add(" return ( %N as %T )" , cacheFieldName, returnTypeName.reloadedForTypeArgs(env))
223+ private fun FactoryProviderMethodBody.Cached.spec (
224+ useNullFieldInitialization : Boolean ,
225+ ): CodeBlock {
226+ if (useNullFieldInitialization) {
227+ val localFieldName = " _$cacheFieldName "
228+ val codeBlockBuilder =
229+ CodeBlock .builder()
230+ // Using a local variable reduces atomic read overhead
231+ .addStatement(" var $localFieldName = %N;\n " , cacheFieldName)
232+ .beginControlFlow(" if (%N == null)" , localFieldName)
233+ .beginControlFlow(" synchronized (this)" )
234+ .beginControlFlow(" if (%N == null)" , cacheFieldName)
235+ .addStatement(" %N = %L" , localFieldName, instantiation.spec())
236+ .addStatement(" %N = %N" , cacheFieldName, localFieldName)
237+ .endControlFlow()
238+ .endControlFlow()
239+ .endControlFlow()
240+ return codeBlockBuilder
241+ .add(" return ( %N as %T )" , localFieldName, returnTypeName.reloadedForTypeArgs(env))
224242 .build()
243+ }
244+ return CodeBlock .builder()
245+ .beginControlFlow(" if (%N == %T.NONE)" , cacheFieldName, None ::class )
246+ .beginControlFlow(" synchronized (this)" )
247+ .beginControlFlow(" if (%N == %T.NONE)" , cacheFieldName, None ::class )
248+ .addStatement(" %N=%L" , cacheFieldName, instantiation.spec())
249+ .endControlFlow()
250+ .endControlFlow()
251+ .endControlFlow()
252+ .add(" return ( %N as %T )" , cacheFieldName, returnTypeName.reloadedForTypeArgs(env))
253+ .build()
254+ }
225255
226256 private fun motif.compiler.TypeName.reloadedForTypeArgs (env : XProcessingEnv ): TypeName =
227257 if (kt is ParameterizedTypeName ) {
0 commit comments