1 // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 5 package com.android.tools.r8.ir.desugar; 6 7 import com.android.tools.r8.errors.Unreachable; 8 import com.android.tools.r8.graph.AppInfo; 9 import com.android.tools.r8.graph.DexAccessFlags; 10 import com.android.tools.r8.graph.DexCallSite; 11 import com.android.tools.r8.graph.DexEncodedMethod; 12 import com.android.tools.r8.graph.DexItemFactory; 13 import com.android.tools.r8.graph.DexMethod; 14 import com.android.tools.r8.graph.DexMethodHandle; 15 import com.android.tools.r8.graph.DexProto; 16 import com.android.tools.r8.graph.DexString; 17 import com.android.tools.r8.graph.DexType; 18 import com.android.tools.r8.graph.DexTypeList; 19 import com.android.tools.r8.graph.DexValue; 20 import com.google.common.collect.Sets; 21 import java.util.ArrayList; 22 import java.util.List; 23 import java.util.Set; 24 25 // Represents the lambda descriptor inferred from calls site. 26 final class LambdaDescriptor { 27 private static final int LAMBDA_ALT_SERIALIZABLE = 1; 28 private static final int LAMBDA_ALT_HAS_EXTRA_INTERFACES = 2; 29 private static final int LAMBDA_ALT_HAS_BRIDGES = 4; 30 private static final int LAMBDA_ALT_MASK = LAMBDA_ALT_SERIALIZABLE 31 | LAMBDA_ALT_HAS_EXTRA_INTERFACES | LAMBDA_ALT_HAS_BRIDGES; 32 33 static final LambdaDescriptor MATCH_FAILED = new LambdaDescriptor(); 34 35 final String uniqueId; 36 final DexString name; 37 final DexProto erasedProto; 38 final DexProto enforcedProto; 39 final DexMethodHandle implHandle; 40 41 final List<DexType> interfaces = new ArrayList<>(); 42 final Set<DexProto> bridges = Sets.newIdentityHashSet(); 43 final DexTypeList captures; 44 45 // Used for accessibility analysis and few assertions only. 46 private final DexEncodedMethod targetMethod; 47 LambdaDescriptor()48 private LambdaDescriptor() { 49 uniqueId = null; 50 name = null; 51 erasedProto = null; 52 enforcedProto = null; 53 implHandle = null; 54 captures = null; 55 targetMethod = null; 56 } 57 LambdaDescriptor(LambdaRewriter rewriter, DexCallSite callSite, DexString name, DexProto erasedProto, DexProto enforcedProto, DexMethodHandle implHandle, DexType mainInterface, DexTypeList captures)58 private LambdaDescriptor(LambdaRewriter rewriter, DexCallSite callSite, 59 DexString name, DexProto erasedProto, DexProto enforcedProto, 60 DexMethodHandle implHandle, DexType mainInterface, DexTypeList captures) { 61 assert rewriter != null; 62 assert callSite != null; 63 assert name != null; 64 assert erasedProto != null; 65 assert enforcedProto != null; 66 assert implHandle != null; 67 assert mainInterface != null; 68 assert captures != null; 69 70 this.uniqueId = callSite.getHash(); 71 this.name = name; 72 this.erasedProto = erasedProto; 73 this.enforcedProto = enforcedProto; 74 this.implHandle = implHandle; 75 this.captures = captures; 76 77 this.interfaces.add(mainInterface); 78 this.targetMethod = lookupTargetMethod(rewriter); 79 } 80 getImplReceiverType()81 final DexType getImplReceiverType() { 82 // The receiver of instance impl-method is captured as the first captured 83 // value or should be the first argument of the enforced method signature. 84 DexType[] params = enforcedProto.parameters.values; 85 DexType[] captures = this.captures.values; 86 assert captures.length > 0 || params.length > 0; 87 return captures.length > 0 ? captures[0] : params[0]; 88 } 89 lookupTargetMethod(LambdaRewriter rewriter)90 private DexEncodedMethod lookupTargetMethod(LambdaRewriter rewriter) { 91 // Find the lambda's impl-method target. 92 DexMethod method = implHandle.asMethod(); 93 switch (implHandle.type) { 94 case INVOKE_INSTANCE: { 95 AppInfo appInfo = rewriter.converter.appInfo; 96 DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method); 97 if (target == null) { 98 target = appInfo.lookupDirectTarget(method); 99 } 100 assert target == null || 101 (!target.accessFlags.isConstructor() && !target.accessFlags.isStatic()); 102 return target; 103 } 104 105 case INVOKE_STATIC: { 106 AppInfo appInfo = rewriter.converter.appInfo; 107 DexEncodedMethod target = appInfo.lookupStaticTarget(method); 108 assert target == null || target.accessFlags.isStatic(); 109 return target; 110 } 111 112 case INVOKE_CONSTRUCTOR: { 113 AppInfo appInfo = rewriter.converter.appInfo; 114 DexEncodedMethod target = appInfo.lookupDirectTarget(method); 115 assert target == null || target.accessFlags.isConstructor(); 116 return target; 117 } 118 119 case INVOKE_INTERFACE: { 120 AppInfo appInfo = rewriter.converter.appInfo; 121 DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method); 122 assert target == null || 123 (!target.accessFlags.isConstructor() && !target.accessFlags.isStatic()); 124 return target; 125 } 126 127 default: 128 throw new Unreachable("Unexpected method handle kind in " + implHandle); 129 } 130 } 131 getAccessibility()132 final DexAccessFlags getAccessibility() { 133 return targetMethod == null ? null : targetMethod.accessFlags; 134 } 135 targetFoundInClass(DexType type)136 final boolean targetFoundInClass(DexType type) { 137 return targetMethod != null && targetMethod.method.holder == type; 138 } 139 140 /** If the lambda delegates to lambda$ method. */ delegatesToLambdaImplMethod()141 boolean delegatesToLambdaImplMethod() { 142 DexString methodName = implHandle.asMethod().name; 143 return methodName.toString().startsWith(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX); 144 } 145 146 /** Is a stateless lambda, i.e. lambda does not capture any values */ isStateless()147 final boolean isStateless() { 148 return captures.isEmpty(); 149 } 150 151 /** Checks if call site needs a accessor when referenced from `accessedFrom`. */ needsAccessor(DexType accessedFrom)152 boolean needsAccessor(DexType accessedFrom) { 153 if (delegatesToLambdaImplMethod()) { 154 return false; 155 } 156 157 if (implHandle.type.isInvokeInterface()) { 158 // Interface methods must be public. 159 return false; 160 } 161 162 boolean staticTarget = implHandle.type.isInvokeStatic(); 163 boolean instanceTarget = implHandle.type.isInvokeInstance(); 164 boolean initTarget = implHandle.type.isInvokeConstructor(); 165 assert instanceTarget || staticTarget | initTarget; 166 167 if (targetMethod == null) { 168 // The target cannot be a private method, since otherwise it 169 // should have been found. 170 171 if (staticTarget || initTarget) { 172 // Create accessor only in case it is accessed from other 173 // package, since otherwise it can be called directly. 174 // NOTE: This case is different from regular instance method case 175 // because the method being called must be present in method holder, 176 // and not in one from its supertypes. 177 boolean accessedFromSamePackage = 178 accessedFrom.getPackageDescriptor().equals( 179 implHandle.asMethod().holder.getPackageDescriptor()); 180 return !accessedFromSamePackage; 181 } 182 183 // Since instance method was not found, always generate an accessor 184 // since it may be a protected method located in another package. 185 return true; 186 } 187 188 DexAccessFlags flags = targetMethod.accessFlags; 189 190 // Private methods always need accessors. 191 if (flags.isPrivate()) { 192 return true; 193 } 194 if (flags.isPublic()) { 195 return false; 196 } 197 198 boolean accessedFromSamePackage = 199 accessedFrom.getPackageDescriptor().equals( 200 targetMethod.method.holder.getPackageDescriptor()); 201 assert flags.isProtected() || accessedFromSamePackage; 202 return flags.isProtected() && !accessedFromSamePackage; 203 } 204 205 /** 206 * Matches call site for lambda metafactory invocation pattern and 207 * returns extracted match information, or null if match failed. 208 */ infer(LambdaRewriter rewriter, DexCallSite callSite)209 static LambdaDescriptor infer(LambdaRewriter rewriter, DexCallSite callSite) { 210 // We expect bootstrap method to be either `metafactory` or `altMetafactory` method 211 // of `java.lang.invoke.LambdaMetafactory` class. Both methods are static. 212 if (!callSite.bootstrapMethod.type.isInvokeStatic()) { 213 return LambdaDescriptor.MATCH_FAILED; 214 } 215 216 DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod(); 217 boolean isMetafactoryMethod = bootstrapMethod == rewriter.metafactoryMethod; 218 boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.metafactoryAltMethod; 219 if (!isMetafactoryMethod && !isAltMetafactoryMethod) { 220 // It is not a lambda, thus no need to manage this call site. 221 return LambdaDescriptor.MATCH_FAILED; 222 } 223 224 // 'Method name' operand of the invoke-custom instruction represents 225 // the name of the functional interface main method. 226 DexString funcMethodName = callSite.methodName; 227 228 // Signature of main functional interface method. 229 DexValue.DexValueMethodType funcErasedSignature = 230 getBootstrapArgument(callSite, 0, DexValue.DexValueMethodType.class); 231 232 // Method handle of the implementation method. 233 DexMethodHandle lambdaImplMethodHandle = 234 getBootstrapArgument(callSite, 1, DexValue.DexValueMethodHandle.class).value; 235 // Even though there are some limitations on which method handle kinds are 236 // allowed for lambda impl-methods, there is no way to detect unsupported 237 // handle kinds after they are transformed into DEX method handle. 238 239 // Signature to be enforced on main method. 240 DexValue.DexValueMethodType funcEnforcedSignature = 241 getBootstrapArgument(callSite, 2, DexValue.DexValueMethodType.class); 242 if (!isEnforcedSignatureValid( 243 rewriter, funcEnforcedSignature.value, funcErasedSignature.value)) { 244 throw new Unreachable( 245 "Enforced and erased signatures are inconsistent in " + callSite.toString()); 246 } 247 248 // 'Method type' of the invoke-custom instruction represents the signature 249 // of the lambda method factory. 250 DexProto lambdaFactoryProto = callSite.methodProto; 251 // Main functional interface is the return type of the lambda factory method. 252 DexType mainFuncInterface = lambdaFactoryProto.returnType; 253 // Lambda captures are represented as parameters of the lambda factory method. 254 DexTypeList captures = lambdaFactoryProto.parameters; 255 256 // Create a match. 257 LambdaDescriptor match = new LambdaDescriptor(rewriter, callSite, 258 funcMethodName, funcErasedSignature.value, funcEnforcedSignature.value, 259 lambdaImplMethodHandle, mainFuncInterface, captures); 260 261 if (isMetafactoryMethod) { 262 if (callSite.bootstrapArgs.size() != 3) { 263 throw new Unreachable( 264 "Unexpected number of metafactory method arguments in " + callSite.toString()); 265 } 266 } else { 267 extractExtraLambdaInfo(rewriter, callSite, match); 268 } 269 270 return match; 271 } 272 extractExtraLambdaInfo( LambdaRewriter rewriter, DexCallSite callSite, LambdaDescriptor match)273 private static void extractExtraLambdaInfo( 274 LambdaRewriter rewriter, DexCallSite callSite, LambdaDescriptor match) { 275 int argIndex = 3; 276 int flagsArg = getBootstrapArgument( 277 callSite, argIndex++, DexValue.DexValueInt.class).value; 278 assert (flagsArg & ~LAMBDA_ALT_MASK) == 0; 279 280 // Load extra interfaces if any. 281 if ((flagsArg & LAMBDA_ALT_HAS_EXTRA_INTERFACES) != 0) { 282 int count = getBootstrapArgument( 283 callSite, argIndex++, DexValue.DexValueInt.class).value; 284 for (int i = 0; i < count; i++) { 285 DexType type = getBootstrapArgument( 286 callSite, argIndex++, DexValue.DexValueType.class).value; 287 if (!match.interfaces.contains(type)) { 288 match.interfaces.add(type); 289 } 290 } 291 } 292 293 // If the lambda is serializable, add it. 294 if ((flagsArg & LAMBDA_ALT_SERIALIZABLE) != 0) { 295 if (!match.interfaces.contains(rewriter.serializableType)) { 296 match.interfaces.add(rewriter.serializableType); 297 } 298 } 299 300 // Load bridges if any. 301 if ((flagsArg & LAMBDA_ALT_HAS_BRIDGES) != 0) { 302 int count = getBootstrapArgument( 303 callSite, argIndex++, DexValue.DexValueInt.class).value; 304 for (int i = 0; i < count; i++) { 305 DexProto bridgeProto = getBootstrapArgument( 306 callSite, argIndex++, DexValue.DexValueMethodType.class).value; 307 match.bridges.add(bridgeProto); 308 } 309 } 310 311 if (callSite.bootstrapArgs.size() != argIndex) { 312 throw new Unreachable( 313 "Unexpected number of metafactory method arguments in " + callSite.toString()); 314 } 315 } 316 317 @SuppressWarnings("unchecked") getBootstrapArgument(DexCallSite callSite, int i, Class<T> clazz)318 private static <T> T getBootstrapArgument(DexCallSite callSite, int i, Class<T> clazz) { 319 List<DexValue> bootstrapArgs = callSite.bootstrapArgs; 320 if (bootstrapArgs.size() < i) { 321 throw new Unreachable("Expected to find at least " 322 + i + " bootstrap arguments in " + callSite.toString()); 323 } 324 DexValue value = bootstrapArgs.get(i); 325 if (!clazz.isAssignableFrom(value.getClass())) { 326 throw new Unreachable("Unexpected type of " 327 + "bootstrap arguments #" + i + " in " + callSite.toString()); 328 } 329 return (T) value; 330 } 331 isEnforcedSignatureValid( LambdaRewriter rewriter, DexProto enforced, DexProto erased)332 private static boolean isEnforcedSignatureValid( 333 LambdaRewriter rewriter, DexProto enforced, DexProto erased) { 334 if (!isSameOrDerived(rewriter.factory, enforced.returnType, erased.returnType)) { 335 return false; 336 } 337 DexType[] enforcedValues = enforced.parameters.values; 338 DexType[] erasedValues = erased.parameters.values; 339 int count = enforcedValues.length; 340 if (count != erasedValues.length) { 341 return false; 342 } 343 for (int i = 0; i < count; i++) { 344 if (!isSameOrDerived(rewriter.factory, enforcedValues[i], erasedValues[i])) { 345 return false; 346 } 347 } 348 return true; 349 } 350 351 // Checks if the types are the same OR both types are reference types and 352 // `subType` is derived from `b`. Note that in the latter case we only check if 353 // both types are class types, for the reasons mentioned in isSameOrAdaptableTo(...). isSameOrDerived( DexItemFactory factory, DexType subType, DexType superType)354 static boolean isSameOrDerived( 355 DexItemFactory factory, DexType subType, DexType superType) { 356 if (subType == superType || (subType.isClassType() && superType.isClassType())) { 357 return true; 358 } 359 360 if (subType.isArrayType()) { 361 if (superType.isArrayType()) { 362 // X[] -> Y[]. 363 return isSameOrDerived(factory, 364 subType.toArrayElementType(factory), superType.toArrayElementType(factory)); 365 } 366 return superType == factory.objectType; // T[] -> Object. 367 } 368 369 return false; 370 } 371 } 372