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.dex.Constants; 8 import com.android.tools.r8.errors.Unimplemented; 9 import com.android.tools.r8.errors.Unreachable; 10 import com.android.tools.r8.graph.DexAccessFlags; 11 import com.android.tools.r8.graph.DexAnnotationSet; 12 import com.android.tools.r8.graph.DexAnnotationSetRefList; 13 import com.android.tools.r8.graph.DexClass; 14 import com.android.tools.r8.graph.DexCode; 15 import com.android.tools.r8.graph.DexEncodedField; 16 import com.android.tools.r8.graph.DexEncodedMethod; 17 import com.android.tools.r8.graph.DexField; 18 import com.android.tools.r8.graph.DexItemFactory; 19 import com.android.tools.r8.graph.DexMethod; 20 import com.android.tools.r8.graph.DexMethodHandle; 21 import com.android.tools.r8.graph.DexProgramClass; 22 import com.android.tools.r8.graph.DexProto; 23 import com.android.tools.r8.graph.DexString; 24 import com.android.tools.r8.graph.DexType; 25 import com.android.tools.r8.graph.DexTypeList; 26 import com.android.tools.r8.graph.DexValue.DexValueNull; 27 import com.android.tools.r8.ir.code.Invoke; 28 import com.android.tools.r8.ir.synthetic.SynthesizedCode; 29 import java.util.List; 30 import java.util.concurrent.atomic.AtomicBoolean; 31 32 /** 33 * Represents lambda class generated for a lambda descriptor in context 34 * of lambda instantiation point. 35 * 36 * Even though call sites, and thus lambda descriptors, are canonicalized 37 * across the application, the context may require several lambda classes 38 * to be generated for the same lambda descriptor. 39 * 40 * One reason is that we always generate a lambda class in the same package 41 * lambda instantiation point is located in, so if same call site is used in 42 * two classes from different packages (which can happen if same public method 43 * is being references via method reference expression) we generate separate 44 * lambda classes in those packages. 45 * 46 * Another reason is that if we generate an accessor, we generate it in the 47 * class referencing the call site, and thus two such classes will require two 48 * separate lambda classes. 49 */ 50 final class LambdaClass { 51 52 final LambdaRewriter rewriter; 53 final DexType type; 54 final LambdaDescriptor descriptor; 55 final DexMethod constructor; 56 final DexMethod classConstructor; 57 final DexField instanceField; 58 final Target target; 59 final AtomicBoolean addToMainDexList = new AtomicBoolean(false); 60 LambdaClass(LambdaRewriter rewriter, DexType accessedFrom, DexType lambdaClassType, LambdaDescriptor descriptor)61 LambdaClass(LambdaRewriter rewriter, DexType accessedFrom, 62 DexType lambdaClassType, LambdaDescriptor descriptor) { 63 assert rewriter != null; 64 assert lambdaClassType != null; 65 assert descriptor != null; 66 67 this.rewriter = rewriter; 68 this.type = lambdaClassType; 69 this.descriptor = descriptor; 70 71 DexItemFactory factory = rewriter.factory; 72 DexProto constructorProto = factory.createProto( 73 factory.voidType, descriptor.captures.values); 74 this.constructor = factory.createMethod( 75 lambdaClassType, constructorProto, rewriter.constructorName); 76 77 this.target = createTarget(accessedFrom); 78 79 boolean stateless = isStateless(); 80 this.classConstructor = !stateless ? null 81 : factory.createMethod(lambdaClassType, constructorProto, rewriter.classConstructorName); 82 this.instanceField = !stateless ? null 83 : factory.createField(lambdaClassType, lambdaClassType, rewriter.instanceFieldName); 84 } 85 86 // Generate unique lambda class type for lambda descriptor and instantiation point context. createLambdaClassType( LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match)87 static DexType createLambdaClassType( 88 LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match) { 89 StringBuilder lambdaClassDescriptor = new StringBuilder("L"); 90 91 // We always create lambda class in the same package where it is referenced. 92 String packageDescriptor = accessedFrom.getPackageDescriptor(); 93 if (!packageDescriptor.isEmpty()) { 94 lambdaClassDescriptor.append(packageDescriptor).append('/'); 95 } 96 97 // Lambda class name prefix 98 lambdaClassDescriptor.append(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX); 99 100 // If the lambda class should match 1:1 the class it is accessed from, we 101 // just add the name of this type to make lambda class name unique. 102 // It also helps link the class lambda originated from in some cases. 103 if (match.delegatesToLambdaImplMethod() || match.needsAccessor(accessedFrom)) { 104 lambdaClassDescriptor.append(accessedFrom.getName()).append('$'); 105 } 106 107 // Add unique lambda descriptor id 108 lambdaClassDescriptor.append(match.uniqueId).append(';'); 109 return rewriter.factory.createType(lambdaClassDescriptor.toString()); 110 } 111 synthesizeLambdaClass()112 final DexProgramClass synthesizeLambdaClass() { 113 return new DexProgramClass( 114 type, 115 null, 116 new DexAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC), 117 rewriter.factory.objectType, 118 buildInterfaces(), 119 rewriter.factory.createString("lambda"), 120 DexAnnotationSet.empty(), 121 synthesizeStaticFields(), 122 synthesizeInstanceFields(), 123 synthesizeDirectMethods(), 124 synthesizeVirtualMethods() 125 ); 126 } 127 getCaptureField(int index)128 final DexField getCaptureField(int index) { 129 return rewriter.factory.createField(this.type, 130 descriptor.captures.values[index], rewriter.factory.createString("f$" + index)); 131 } 132 isStateless()133 final boolean isStateless() { 134 return descriptor.isStateless(); 135 } 136 137 // Synthesize virtual methods. synthesizeVirtualMethods()138 private DexEncodedMethod[] synthesizeVirtualMethods() { 139 DexEncodedMethod[] methods = new DexEncodedMethod[1 + descriptor.bridges.size()]; 140 int index = 0; 141 142 // Synthesize main method. 143 DexMethod mainMethod = rewriter.factory 144 .createMethod(type, descriptor.erasedProto, descriptor.name); 145 methods[index++] = new DexEncodedMethod( 146 mainMethod, 147 new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL), 148 DexAnnotationSet.empty(), 149 DexAnnotationSetRefList.empty(), 150 new SynthesizedCode(new LambdaMainMethodSourceCode(this, mainMethod))); 151 152 // Synthesize bridge methods. 153 for (DexProto bridgeProto : descriptor.bridges) { 154 DexMethod bridgeMethod = rewriter.factory.createMethod(type, bridgeProto, descriptor.name); 155 methods[index++] = new DexEncodedMethod( 156 bridgeMethod, 157 new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL 158 | Constants.ACC_SYNTHETIC | Constants.ACC_BRIDGE), 159 DexAnnotationSet.empty(), 160 DexAnnotationSetRefList.empty(), 161 new SynthesizedCode( 162 new LambdaBridgeMethodSourceCode(this, mainMethod, bridgeMethod))); 163 } 164 return methods; 165 } 166 167 // Synthesize direct methods. synthesizeDirectMethods()168 private DexEncodedMethod[] synthesizeDirectMethods() { 169 boolean stateless = isStateless(); 170 DexEncodedMethod[] methods = new DexEncodedMethod[stateless ? 2 : 1]; 171 172 // Constructor. 173 methods[0] = new DexEncodedMethod( 174 constructor, 175 new DexAccessFlags((stateless ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC) | 176 Constants.ACC_SYNTHETIC | Constants.ACC_CONSTRUCTOR), 177 DexAnnotationSet.empty(), 178 DexAnnotationSetRefList.empty(), 179 new SynthesizedCode(new LambdaConstructorSourceCode(this))); 180 181 // Class constructor for stateless lambda classes. 182 if (stateless) { 183 methods[1] = new DexEncodedMethod( 184 classConstructor, 185 new DexAccessFlags( 186 Constants.ACC_SYNTHETIC | Constants.ACC_CONSTRUCTOR | Constants.ACC_STATIC), 187 DexAnnotationSet.empty(), 188 DexAnnotationSetRefList.empty(), 189 new SynthesizedCode(new LambdaClassConstructorSourceCode(this))); 190 } 191 return methods; 192 } 193 194 // Synthesize instance fields to represent captured values. synthesizeInstanceFields()195 private DexEncodedField[] synthesizeInstanceFields() { 196 DexType[] fieldTypes = descriptor.captures.values; 197 int fieldCount = fieldTypes.length; 198 DexEncodedField[] fields = new DexEncodedField[fieldCount]; 199 for (int i = 0; i < fieldCount; i++) { 200 DexAccessFlags accessFlags = new DexAccessFlags( 201 Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PRIVATE); 202 fields[i] = new DexEncodedField( 203 getCaptureField(i), accessFlags, DexAnnotationSet.empty(), null); 204 } 205 return fields; 206 } 207 208 // Synthesize static fields to represent singleton instance. synthesizeStaticFields()209 private DexEncodedField[] synthesizeStaticFields() { 210 if (!isStateless()) { 211 return DexEncodedField.EMPTY_ARRAY; 212 } 213 214 // Create instance field for stateless lambda. 215 assert this.instanceField != null; 216 DexEncodedField[] fields = new DexEncodedField[1]; 217 fields[0] = new DexEncodedField( 218 this.instanceField, 219 new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL 220 | Constants.ACC_SYNTHETIC | Constants.ACC_STATIC), 221 DexAnnotationSet.empty(), 222 DexValueNull.NULL); 223 return fields; 224 } 225 226 // Build a list of implemented interfaces. buildInterfaces()227 private DexTypeList buildInterfaces() { 228 List<DexType> interfaces = descriptor.interfaces; 229 return interfaces.isEmpty() ? DexTypeList.empty() 230 : new DexTypeList(interfaces.toArray(new DexType[interfaces.size()])); 231 } 232 233 // Creates a delegation target for this particular lambda class. Note that we 234 // should always be able to create targets for the lambdas we support. createTarget(DexType accessedFrom)235 private Target createTarget(DexType accessedFrom) { 236 if (descriptor.delegatesToLambdaImplMethod()) { 237 return createLambdaImplMethodTarget(accessedFrom); 238 } 239 240 // Method referenced directly, without lambda$ method. 241 switch (descriptor.implHandle.type) { 242 case INVOKE_SUPER: 243 throw new Unimplemented("Method references to super methods are not yet supported"); 244 case INVOKE_INTERFACE: 245 return createInterfaceMethodTarget(accessedFrom); 246 case INVOKE_CONSTRUCTOR: 247 return createConstructorTarget(accessedFrom); 248 case INVOKE_STATIC: 249 return createStaticMethodTarget(accessedFrom); 250 case INVOKE_INSTANCE: 251 return createInstanceMethodTarget(accessedFrom); 252 default: 253 throw new Unreachable("Unexpected method handle type in " + descriptor.implHandle); 254 } 255 } 256 createLambdaImplMethodTarget(DexType accessedFrom)257 private Target createLambdaImplMethodTarget(DexType accessedFrom) { 258 DexMethodHandle implHandle = descriptor.implHandle; 259 assert implHandle != null; 260 DexMethod implMethod = implHandle.asMethod(); 261 262 // Lambda$ method. We must always find it. 263 assert implMethod.holder == accessedFrom; 264 assert descriptor.targetFoundInClass(accessedFrom); 265 assert descriptor.getAccessibility() != null; 266 assert descriptor.getAccessibility().isPrivate(); 267 assert descriptor.getAccessibility().isSynthetic(); 268 269 if (implHandle.type.isInvokeStatic()) { 270 return new StaticLambdaImplTarget(); 271 } 272 273 assert implHandle.type.isInvokeInstance(); 274 275 // If lambda$ method is an instance method we convert it into a static methods and 276 // relax its accessibility. 277 DexProto implProto = implMethod.proto; 278 DexType[] implParams = implProto.parameters.values; 279 DexType[] newParams = new DexType[implParams.length + 1]; 280 newParams[0] = implMethod.holder; 281 System.arraycopy(implParams, 0, newParams, 1, implParams.length); 282 283 DexProto newProto = rewriter.factory.createProto(implProto.returnType, newParams); 284 return new InstanceLambdaImplTarget( 285 rewriter.factory.createMethod(implMethod.holder, newProto, implMethod.name)); 286 } 287 288 // Create targets for instance method referenced directly without 289 // lambda$ methods. It may require creation of accessors in some cases. createInstanceMethodTarget(DexType accessedFrom)290 private Target createInstanceMethodTarget(DexType accessedFrom) { 291 assert descriptor.implHandle.type.isInvokeInstance(); 292 293 if (!descriptor.needsAccessor(accessedFrom)) { 294 return new NoAccessorMethodTarget(Invoke.Type.VIRTUAL); 295 } 296 // We need to generate an accessor method in `accessedFrom` class/interface 297 // for accessing the original instance impl-method. Note that impl-method's 298 // holder does not have to be the same as `accessedFrom`. 299 DexMethod implMethod = descriptor.implHandle.asMethod(); 300 DexProto implProto = implMethod.proto; 301 DexType[] implParams = implProto.parameters.values; 302 303 // The accessor method will be static, package private, and take the 304 // receiver as the first argument. The receiver must be captured and 305 // be the first captured value in case there are more than one. 306 DexType[] accessorParams = new DexType[1 + implParams.length]; 307 accessorParams[0] = descriptor.getImplReceiverType(); 308 System.arraycopy(implParams, 0, accessorParams, 1, implParams.length); 309 DexProto accessorProto = rewriter.factory.createProto(implProto.returnType, accessorParams); 310 DexMethod accessorMethod = rewriter.factory.createMethod( 311 accessedFrom, accessorProto, generateUniqueLambdaMethodName()); 312 313 return new ClassMethodWithAccessorTarget(accessorMethod); 314 } 315 316 // Create targets for static method referenced directly without 317 // lambda$ methods. It may require creation of accessors in some cases. createStaticMethodTarget(DexType accessedFrom)318 private Target createStaticMethodTarget(DexType accessedFrom) { 319 assert descriptor.implHandle.type.isInvokeStatic(); 320 321 if (!descriptor.needsAccessor(accessedFrom)) { 322 return new NoAccessorMethodTarget(Invoke.Type.STATIC); 323 } 324 325 // We need to generate an accessor method in `accessedFrom` class/interface 326 // for accessing the original static impl-method. The accessor method will be 327 // static, package private with exactly same signature and the original method. 328 DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom, 329 descriptor.implHandle.asMethod().proto, generateUniqueLambdaMethodName()); 330 return new ClassMethodWithAccessorTarget(accessorMethod); 331 } 332 333 // Create targets for constructor referenced directly without lambda$ methods. 334 // It may require creation of accessors in some cases. createConstructorTarget(DexType accessedFrom)335 private Target createConstructorTarget(DexType accessedFrom) { 336 DexMethodHandle implHandle = descriptor.implHandle; 337 assert implHandle != null; 338 assert implHandle.type.isInvokeConstructor(); 339 340 if (!descriptor.needsAccessor(accessedFrom)) { 341 return new NoAccessorMethodTarget(Invoke.Type.DIRECT); 342 } 343 344 // We need to generate an accessor method in `accessedFrom` class/interface for 345 // instantiating the class and calling constructor on it. The accessor method will 346 // be static, package private with exactly same parameters as the constructor, 347 // and return the newly created instance. 348 DexMethod implMethod = implHandle.asMethod(); 349 DexType returnType = implMethod.holder; 350 DexProto accessorProto = rewriter.factory.createProto( 351 returnType, implMethod.proto.parameters.values); 352 DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom, 353 accessorProto, generateUniqueLambdaMethodName()); 354 return new ClassMethodWithAccessorTarget(accessorMethod); 355 } 356 357 // Create targets for interface methods. createInterfaceMethodTarget(DexType accessedFrom)358 private Target createInterfaceMethodTarget(DexType accessedFrom) { 359 assert descriptor.implHandle.type.isInvokeInterface(); 360 assert !descriptor.needsAccessor(accessedFrom); 361 return new NoAccessorMethodTarget(Invoke.Type.INTERFACE); 362 } 363 generateUniqueLambdaMethodName()364 private DexString generateUniqueLambdaMethodName() { 365 return rewriter.factory.createString( 366 LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId); 367 } 368 369 // Represents information about the method lambda class need to delegate the call to. It may 370 // be the same method as specified in lambda descriptor or a newly synthesized accessor. 371 // Also provides action for ensuring accessibility of the referenced symbols. 372 abstract class Target { 373 374 final DexMethod callTarget; 375 final Invoke.Type invokeType; 376 Target(DexMethod callTarget, Invoke.Type invokeType)377 Target(DexMethod callTarget, Invoke.Type invokeType) { 378 assert callTarget != null; 379 assert invokeType != null; 380 this.callTarget = callTarget; 381 this.invokeType = invokeType; 382 } 383 384 // Ensure access of the referenced symbol(s). ensureAccessibility()385 abstract boolean ensureAccessibility(); 386 definitionFor(DexType type)387 DexClass definitionFor(DexType type) { 388 return rewriter.converter.appInfo.app.definitionFor(type); 389 } 390 programDefinitionFor(DexType type)391 DexProgramClass programDefinitionFor(DexType type) { 392 return rewriter.converter.appInfo.app.programDefinitionFor(type); 393 } 394 } 395 396 // Used for targeting methods referenced directly without creating accessors. 397 private final class NoAccessorMethodTarget extends Target { 398 NoAccessorMethodTarget(Invoke.Type invokeType)399 NoAccessorMethodTarget(Invoke.Type invokeType) { 400 super(descriptor.implHandle.asMethod(), invokeType); 401 } 402 403 @Override ensureAccessibility()404 boolean ensureAccessibility() { 405 return true; 406 } 407 } 408 409 // Used for static private lambda$ methods. Only needs access relaxation. 410 private final class StaticLambdaImplTarget extends Target { 411 StaticLambdaImplTarget()412 StaticLambdaImplTarget() { 413 super(descriptor.implHandle.asMethod(), Invoke.Type.STATIC); 414 } 415 416 @Override ensureAccessibility()417 boolean ensureAccessibility() { 418 // We already found the static method to be called, just relax its accessibility. 419 assert descriptor.getAccessibility() != null; 420 descriptor.getAccessibility().unsetPrivate(); 421 DexClass implMethodHolder = definitionFor(descriptor.implHandle.asMethod().holder); 422 if (implMethodHolder.isInterface()) { 423 descriptor.getAccessibility().setPublic(); 424 } 425 return true; 426 } 427 } 428 429 // Used for instance private lambda$ methods. Needs to be converted to 430 // a package-private static method. 431 private class InstanceLambdaImplTarget extends Target { 432 InstanceLambdaImplTarget(DexMethod staticMethod)433 InstanceLambdaImplTarget(DexMethod staticMethod) { 434 super(staticMethod, Invoke.Type.STATIC); 435 } 436 437 @Override ensureAccessibility()438 boolean ensureAccessibility() { 439 // For all instantiation points for which compiler creates lambda$ 440 // methods, it creates these methods in the same class/interface. 441 DexMethod implMethod = descriptor.implHandle.asMethod(); 442 DexClass implMethodHolder = definitionFor(implMethod.holder); 443 444 DexEncodedMethod[] directMethods = implMethodHolder.directMethods; 445 for (int i = 0; i < directMethods.length; i++) { 446 DexEncodedMethod encodedMethod = directMethods[i]; 447 if (implMethod.match(encodedMethod)) { 448 // We need to create a new static method with the same code to be able to safely 449 // relax its accessibility without making it virtual. 450 DexEncodedMethod newMethod = new DexEncodedMethod( 451 callTarget, encodedMethod.accessFlags, encodedMethod.annotations, 452 encodedMethod.parameterAnnotations, encodedMethod.getCode()); 453 // TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'? 454 encodedMethod.accessFlags.setStatic(); 455 encodedMethod.accessFlags.unsetPrivate(); 456 if (implMethodHolder.isInterface()) { 457 encodedMethod.accessFlags.setPublic(); 458 } 459 DexCode dexCode = newMethod.getCode().asDexCode(); 460 dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(null)); 461 assert (dexCode.getDebugInfo() == null) 462 || (callTarget.proto.parameters.values.length 463 == dexCode.getDebugInfo().parameters.length); 464 directMethods[i] = newMethod; 465 return true; 466 } 467 } 468 return false; 469 } 470 } 471 472 // Used for instance/static methods or constructors accessed via 473 // synthesized accessor method. Needs accessor method to be created. 474 private class ClassMethodWithAccessorTarget extends Target { 475 ClassMethodWithAccessorTarget(DexMethod accessorMethod)476 ClassMethodWithAccessorTarget(DexMethod accessorMethod) { 477 super(accessorMethod, Invoke.Type.STATIC); 478 } 479 480 @Override ensureAccessibility()481 boolean ensureAccessibility() { 482 // Create a static accessor with proper accessibility. 483 DexProgramClass accessorClass = programDefinitionFor(callTarget.holder); 484 assert accessorClass != null; 485 486 DexAccessFlags accessorFlags = new DexAccessFlags( 487 Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | 488 (accessorClass.isInterface() ? Constants.ACC_PUBLIC : 0)); 489 DexEncodedMethod accessorEncodedMethod = new DexEncodedMethod( 490 callTarget, accessorFlags, DexAnnotationSet.empty(), DexAnnotationSetRefList.empty(), 491 new SynthesizedCode(new AccessorMethodSourceCode(LambdaClass.this))); 492 accessorClass.directMethods = appendMethod( 493 accessorClass.directMethods, accessorEncodedMethod); 494 rewriter.converter.optimizeSynthesizedMethod(accessorEncodedMethod); 495 return true; 496 } 497 appendMethod(DexEncodedMethod[] methods, DexEncodedMethod method)498 private DexEncodedMethod[] appendMethod(DexEncodedMethod[] methods, DexEncodedMethod method) { 499 int size = methods.length; 500 DexEncodedMethod[] newMethods = new DexEncodedMethod[size + 1]; 501 System.arraycopy(methods, 0, newMethods, 0, size); 502 newMethods[size] = method; 503 return newMethods; 504 } 505 } 506 } 507