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.asm.ClassNodes
20 
21 private const val REASON = "demoted, not in intersect jars"
22 
23 /**
24  * An [OutputFilter] that will restrict what to put in stub to only what shows up in "intersecting
25  * jar" files.
26  *
27  * For example, if the Android public API stub jar is provided, then the HostStubGen's output
28  * stub will be restricted to public APIs.
29  */
30 class StubIntersectingFilter(
31         private val errors: HostStubGenErrors,
32         /**
33          * If a class / field / method is not in any of these jars, then we will not put it in
34          * stub.
35          */
36         private val intersectingJars: Map<String, ClassNodes>,
37         fallback: OutputFilter,
38 ) : DelegatingFilter(fallback) {
39     private inline fun exists(predicate: (ClassNodes) -> Boolean): Boolean {
40         intersectingJars.forEach { entry ->
41             if (predicate(entry.value)) {
42                 return true
43             }
44         }
45         return false
46     }
47 
48     /**
49      * If [origPolicy] is less than "Stub", then return it as-is.
50      *
51      * Otherwise, call [inStubChecker] to see if the API is in any of [intersectingJars].
52      * If yes, then return [origPolicy] as-is. Otherwise, demote to "Keep".
53      */
54     private fun intersectWithStub(
55             origPolicy: FilterPolicyWithReason,
56             inStubChecker: () -> Boolean,
57     ): FilterPolicyWithReason {
58         if (origPolicy.policy.needsInStub) {
59             // Only check the stub jars, when the class is supposed to be in stub otherwise.
60             if (!inStubChecker()) {
61                 return origPolicy.demoteToKeep(REASON)
62             }
63         }
64         return origPolicy
65     }
66 
67     override fun getPolicyForClass(className: String): FilterPolicyWithReason {
68         return intersectWithStub(super.getPolicyForClass(className)) {
69             exists { classes -> classes.findClass(className) != null }
70         }
71     }
72 
73     override fun getPolicyForField(
74             className: String,
75             fieldName: String
76     ): FilterPolicyWithReason {
77         return intersectWithStub(super.getPolicyForField(className, fieldName)) {
78             exists { classes -> classes.findField(className, fieldName) != null }
79         }
80     }
81 
82     override fun getPolicyForMethod(
83             className: String,
84             methodName: String,
85             descriptor: String
86     ): FilterPolicyWithReason {
87         return intersectWithStub(super.getPolicyForMethod(className, methodName, descriptor)) {
88             exists { classes -> classes.findMethod(className, methodName, descriptor) != null }
89         }
90     }
91 }