1 /*
2  * Copyright (C) 2020 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.moshi.kotlin.codegen.api
17 
18 import com.squareup.kotlinpoet.ClassName
19 
20 /**
21  * Represents a proguard configuration for a given spec. This covers three main areas:
22  * - Keeping the target class name to Moshi's reflective lookup of the adapter.
23  * - Keeping the generated adapter class name + public constructor for reflective lookup.
24  * - Keeping any used JsonQualifier annotations and the properties they are attached to.
25  * - If the target class has default parameter values, also keeping the associated synthetic
26  *   constructor as well as the DefaultConstructorMarker type Kotlin adds to it.
27  *
28  * Each rule is intended to be as specific and targeted as possible to reduce footprint, and each is
29  * conditioned on usage of the original target type.
30  *
31  * To keep this processor as an ISOLATING incremental processor, we generate one file per target
32  * class with a deterministic name (see [outputFilePathWithoutExtension]) with an appropriate
33  * originating element.
34  */
35 @InternalMoshiCodegenApi
36 public data class ProguardConfig(
37   val targetClass: ClassName,
38   val adapterName: String,
39   val adapterConstructorParams: List<String>,
40   val targetConstructorHasDefaults: Boolean,
41   val targetConstructorParams: List<String>,
42 ) {
outputFilePathWithoutExtensionnull43   public fun outputFilePathWithoutExtension(canonicalName: String): String {
44     return "META-INF/proguard/moshi-$canonicalName"
45   }
46 
<lambda>null47   public fun writeTo(out: Appendable): Unit = out.run {
48     //
49     // -if class {the target class}
50     // -keepnames class {the target class}
51     // -if class {the target class}
52     // -keep class {the generated adapter} {
53     //    <init>(...);
54     //    private final {adapter fields}
55     // }
56     //
57     val targetName = targetClass.reflectionName()
58     val adapterCanonicalName = ClassName(targetClass.packageName, adapterName).canonicalName
59     // Keep the class name for Moshi's reflective lookup based on it
60     appendLine("-if class $targetName")
61     appendLine("-keepnames class $targetName")
62 
63     appendLine("-if class $targetName")
64     appendLine("-keep class $adapterCanonicalName {")
65     // Keep the constructor for Moshi's reflective lookup
66     val constructorArgs = adapterConstructorParams.joinToString(",")
67     appendLine("    public <init>($constructorArgs);")
68     appendLine("}")
69 
70     if (targetConstructorHasDefaults) {
71       // If the target class has default parameter values, keep its synthetic constructor
72       //
73       // -keepnames class kotlin.jvm.internal.DefaultConstructorMarker
74       // -keepclassmembers @com.squareup.moshi.JsonClass @kotlin.Metadata class * {
75       //     synthetic <init>(...);
76       // }
77       //
78       appendLine("-if class $targetName")
79       appendLine("-keepnames class kotlin.jvm.internal.DefaultConstructorMarker")
80       appendLine("-if class $targetName")
81       appendLine("-keepclassmembers class $targetName {")
82       val allParams = targetConstructorParams.toMutableList()
83       val maskCount = if (targetConstructorParams.isEmpty()) {
84         0
85       } else {
86         (targetConstructorParams.size + 31) / 32
87       }
88       repeat(maskCount) {
89         allParams += "int"
90       }
91       allParams += "kotlin.jvm.internal.DefaultConstructorMarker"
92       val params = allParams.joinToString(",")
93       appendLine("    public synthetic <init>($params);")
94       appendLine("}")
95     }
96   }
97 }
98 
99 /**
100  * Represents a qualified property with its [name] in the adapter fields and list of [qualifiers]
101  * associated with it.
102  */
103 @InternalMoshiCodegenApi
104 public data class QualifierAdapterProperty(val name: String, val qualifiers: Set<ClassName>)
105