1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
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  *      http://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.android.hoststubgen.filters
17 
18 import com.android.hoststubgen.HostStubGenErrors
19 import com.android.hoststubgen.HostStubGenInternalException
20 import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
21 import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
22 import com.android.hoststubgen.asm.ClassNodes
23 import com.android.hoststubgen.asm.isAnnotation
24 import com.android.hoststubgen.asm.isAnonymousInnerClass
25 import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
26 import com.android.hoststubgen.asm.isEnum
27 import com.android.hoststubgen.asm.isSynthetic
28 import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
29 import com.android.hoststubgen.log
30 import org.objectweb.asm.tree.ClassNode
31 
32 /**
33  * Filter implementing "implicit" rules, such as:
34  * - "keep all anonymous inner classes if the outer class is keep".
35  *   (But anonymous inner classes should never be in "stub")
36  * - For classes in stub, make sure private parameterless constructors are also in stub, if any.
37  *
38  * TODO: Do we need a way to make anonymous class methods and lambdas "throw"?
39  */
40 class ImplicitOutputFilter(
41         private val errors: HostStubGenErrors,
42         private val classes: ClassNodes,
43         fallback: OutputFilter
44 ) : DelegatingFilter(fallback) {
45     private fun getClassImplicitPolicy(className: String, cn: ClassNode): FilterPolicyWithReason? {
46         if (isAnonymousInnerClass(cn)) {
47             log.forDebug {
48 //                log.d("  anon-inner class: ${className} outer: ${cn.outerClass}  ")
49             }
50             if (cn.outerClass == null) {
51                 throw HostStubGenInternalException(
52                         "outerClass is null for anonymous inner class")
53             }
54             // If the outer class needs to be in impl, it should be in impl too.
55             val outerPolicy = outermostFilter.getPolicyForClass(cn.outerClass)
56             if (outerPolicy.policy.needsInImpl) {
57                 return FilterPolicy.KeepClass.withReason("anonymous-inner-class")
58             }
59         }
60         return null
61     }
62 
63     override fun getPolicyForClass(className: String): FilterPolicyWithReason {
64         val fallback = super.getPolicyForClass(className)
65 
66         val cn = classes.getClass(className)
67 
68         // Use the implicit policy, if any.
69         getClassImplicitPolicy(className, cn)?.let { return it }
70 
71         return fallback
72     }
73 
74     override fun getPolicyForMethod(
75             className: String,
76             methodName: String,
77             descriptor: String
78     ): FilterPolicyWithReason {
79         val fallback = super.getPolicyForMethod(className, methodName, descriptor)
80         val classPolicy = outermostFilter.getPolicyForClass(className)
81 
82         // If the class is in the stub, then we need to put the private constructor in the stub too,
83         // to prevent the class from getting instantiated.
84         if (classPolicy.policy.needsInStub &&
85                 !fallback.policy.needsInStub &&
86                 (methodName == "<init>") && // Constructor?
87                 (descriptor == "()V")) { // Has zero parameters?
88             classes.findMethod(className, methodName, descriptor)?.let { mn ->
89                 if (isVisibilityPrivateOrPackagePrivate(mn.access)) {
90                     return FilterPolicy.Stub.withReason("private constructor in stub class")
91                 }
92             }
93         }
94 
95         val cn = classes.getClass(className)
96 
97         // If we throw from the static initializer, the class would be useless, so we convert it
98         // "keep" instead.
99         // Unless it's an enum -- in that case, the below code would handle it.
100         if (!cn.isEnum() &&
101                 fallback.policy == FilterPolicy.Throw &&
102                 methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC) {
103             // TODO Maybe show a warning?? But that'd be too noisy with --default-throw.
104             return FilterPolicy.Ignore.withReason(
105                 "'throw' on static initializer is handled as 'ignore'" +
106                         " [original throw reason: ${fallback.reason}]")
107         }
108 
109         log.d("Class ${cn.name} Class policy: $classPolicy")
110         if (classPolicy.policy.needsInImpl) {
111             // Do it only when the class needs to be kept...
112 
113             // Member policy should be "keep" or "stub".
114             val memberPolicy = classPolicy.policy.resolveClassWidePolicy()
115 
116             val mn = classes.findMethod(className, methodName, descriptor)
117 
118             // Keep (or stub) the generated enum members.
119             if (cn.isEnum()) {
120                 mn?.let { mn ->
121                     if (isAutoGeneratedEnumMember(mn)) {
122                         return memberPolicy.withReason(classPolicy.reason).wrapReason("is-enum")
123                     }
124                 }
125             }
126 
127             // Keep (or stub) all members of annotations.
128             if (cn.isAnnotation()) {
129                 return memberPolicy.withReason(classPolicy.reason).wrapReason("is-annotation")
130             }
131 
132             mn?.let {
133                 if (mn.isSynthetic()) {
134                     // For synthetic methods (such as lambdas), let's just inherit the class's
135                     // policy.
136                     return memberPolicy.withReason(classPolicy.reason).wrapReason(
137                             "is-synthetic-method")
138                 }
139             }
140         }
141 
142         return fallback
143     }
144 
145     override fun getPolicyForField(
146             className: String,
147             fieldName: String
148     ): FilterPolicyWithReason {
149         val fallback = super.getPolicyForField(className, fieldName)
150 
151         val cn = classes.getClass(className)
152         val classPolicy = outermostFilter.getPolicyForClass(className)
153 
154         log.d("Class ${cn.name} Class policy: $classPolicy")
155         if (classPolicy.policy.needsInImpl) {
156             // Do it only when the class needs to be kept...
157 
158             // Member policy should be "keep" or "stub".
159             val memberPolicy = classPolicy.policy.resolveClassWidePolicy()
160 
161             // Keep (or stub) the generated enum members.
162             if (cn.isEnum()) {
163                 classes.findField(className, fieldName)?.let { fn ->
164                     if (isAutoGeneratedEnumMember(fn)) {
165                         return memberPolicy.withReason(classPolicy.reason).wrapReason("enum")
166                     }
167                 }
168             }
169 
170             // Keep (or stub) all members of annotations.
171             if (cn.isAnnotation()) {
172                 return memberPolicy.withReason(classPolicy.reason).wrapReason("annotation")
173             }
174         }
175 
176         return fallback
177     }
178 }