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 }