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 5package com.android.tools.r8.ir.desugar; 6 7import com.android.tools.r8.errors.Unreachable; 8import com.android.tools.r8.graph.DexField; 9import com.android.tools.r8.graph.DexItemFactory; 10import com.android.tools.r8.graph.DexMethod; 11import com.android.tools.r8.graph.DexProto; 12import com.android.tools.r8.graph.DexString; 13import com.android.tools.r8.graph.DexType; 14import com.android.tools.r8.ir.code.Invoke; 15import com.android.tools.r8.ir.code.MemberType; 16import com.android.tools.r8.ir.code.MoveType; 17import com.android.tools.r8.ir.code.NumericType; 18import com.android.tools.r8.ir.conversion.IRBuilder; 19import com.google.common.collect.Lists; 20import java.util.ArrayList; 21import java.util.Collections; 22import java.util.List; 23 24// Source code representing synthesized lambda main method 25final class LambdaMainMethodSourceCode extends SynthesizedLambdaSourceCode { 26 27 LambdaMainMethodSourceCode(LambdaClass lambda, DexMethod mainMethod) { 28 super(lambda, mainMethod); 29 } 30 31 private boolean checkSignatures( 32 DexType[] captures, DexType[] enforcedParams, DexType enforcedReturnType, 33 List<DexType> implReceiverAndArgs, DexType implReturnType) { 34 List<DexType> capturesAndParams = new ArrayList<>(); 35 capturesAndParams.addAll(Lists.newArrayList(captures)); 36 capturesAndParams.addAll(Lists.newArrayList(enforcedParams)); 37 38 int size = capturesAndParams.size(); 39 if (size != implReceiverAndArgs.size()) { 40 return false; 41 } 42 43 for (int i = 0; i < size; i++) { 44 if (!isSameOrAdaptableTo(capturesAndParams.get(i), implReceiverAndArgs.get(i))) { 45 return false; 46 } 47 } 48 49 if (!enforcedReturnType.isVoidType() 50 && !isSameOrAdaptableTo(implReturnType, enforcedReturnType)) { 51 return false; 52 } 53 return true; 54 } 55 56 private DexType getPrimitiveFromBoxed(DexType boxedPrimitive) { 57 DexString descriptor = boxedPrimitive.descriptor; 58 DexItemFactory factory = factory(); 59 if (descriptor == factory.boxedBooleanDescriptor) { 60 return factory.booleanType; 61 } 62 if (descriptor == factory.boxedByteDescriptor) { 63 return factory.byteType; 64 } 65 if (descriptor == factory.boxedCharDescriptor) { 66 return factory.charType; 67 } 68 if (descriptor == factory.boxedShortDescriptor) { 69 return factory.shortType; 70 } 71 if (descriptor == factory.boxedIntDescriptor) { 72 return factory.intType; 73 } 74 if (descriptor == factory.boxedLongDescriptor) { 75 return factory.longType; 76 } 77 if (descriptor == factory.boxedFloatDescriptor) { 78 return factory.floatType; 79 } 80 if (descriptor == factory.boxedDoubleDescriptor) { 81 return factory.doubleType; 82 } 83 return null; 84 } 85 86 private DexType getBoxedForPrimitiveType(DexType primitive) { 87 switch (primitive.descriptor.content[0]) { 88 case 'Z': // byte 89 return factory().boxedBooleanType; 90 case 'B': // byte 91 return factory().boxedByteType; 92 case 'S': // short 93 return factory().boxedShortType; 94 case 'C': // char 95 return factory().boxedCharType; 96 case 'I': // int 97 return factory().boxedIntType; 98 case 'J': // long 99 return factory().boxedLongType; 100 case 'F': // float 101 return factory().boxedFloatType; 102 case 'D': // double 103 return factory().boxedDoubleType; 104 default: 105 throw new Unreachable("Invalid primitive type descriptor: " + primitive); 106 } 107 } 108 109 // Checks if the types are the same OR type `a` is adaptable to type `b`. 110 private boolean isSameOrAdaptableTo(DexType a, DexType b) { 111 if (a == b) { 112 return true; 113 } 114 115 DexItemFactory factory = factory(); 116 if (a.isArrayType()) { 117 // Arrays are only adaptable to java.lang.Object. 118 return b == factory.objectType; 119 } 120 121 if (a.isPrimitiveType()) { 122 if (b.isPrimitiveType()) { 123 return isSameOrAdaptableTo(a.descriptor.content[0], b.descriptor.content[0]); 124 } 125 126 // `a` is primitive and `b` is a supertype of the boxed type `a`. 127 DexType boxedPrimitiveType = getBoxedForPrimitiveType(a); 128 if (b == boxedPrimitiveType || b == factory.objectType) { 129 return true; 130 } 131 return boxedPrimitiveType != factory.boxedCharType 132 && boxedPrimitiveType != factory.boxedBooleanType 133 && b.descriptor == factory.boxedNumberDescriptor; 134 } 135 136 if (b.isPrimitiveType()) { 137 // `a` is a boxed type for `a*` which can be 138 // widened to primitive type `b`. 139 DexType unboxedA = getPrimitiveFromBoxed(a); 140 return unboxedA != null && 141 isSameOrAdaptableTo(unboxedA.descriptor.content[0], b.descriptor.content[0]); 142 } 143 144 // Otherwise `a` should be a reference type derived from `b`. 145 // NOTE: we don't check `b` for being actually a supertype, since we 146 // might not have full classpath with inheritance information to do that. 147 // We assume this requirement stands and will be caught by cast 148 // instruction anyways in most cases. 149 return a.isClassType() && b.isClassType(); 150 } 151 152 // For two primitive types `a` is adjustable to `b` iff `a` is the same as `b` 153 // or can be converted to `b` via a primitive widening conversion. 154 private boolean isSameOrAdaptableTo(byte from, byte to) { 155 if (from == to) { 156 return true; 157 } 158 switch (from) { 159 case 'B': // byte 160 return to == 'S' || to == 'I' || to == 'J' || to == 'F' || to == 'D'; 161 case 'S': // short 162 case 'C': // char 163 return to == 'I' || to == 'J' || to == 'F' || to == 'D'; 164 case 'I': // int 165 return to == 'J' || to == 'F' || to == 'D'; 166 case 'J': // long 167 return to == 'F' || to == 'D'; 168 case 'F': // float 169 return to == 'D'; 170 case 'Z': // boolean 171 case 'D': // double 172 return false; 173 default: 174 throw new Unreachable("Invalid primitive type descriptor: " + from); 175 } 176 } 177 178 @Override 179 protected void prepareInstructions() { 180 DexType[] capturedTypes = captures(); 181 DexType[] erasedParams = descriptor().erasedProto.parameters.values; 182 DexType erasedReturnType = descriptor().erasedProto.returnType; 183 DexType[] enforcedParams = descriptor().enforcedProto.parameters.values; 184 DexType enforcedReturnType = descriptor().enforcedProto.returnType; 185 186 LambdaClass.Target target = lambda.target; 187 DexMethod methodToCall = target.callTarget; 188 189 // Only constructor call should use direct invoke type since super 190 // and private methods require accessor methods. 191 boolean constructorTarget = target.invokeType == Invoke.Type.DIRECT; 192 assert !constructorTarget || methodToCall.name == factory().constructorMethodName; 193 194 List<DexType> implReceiverAndArgs = new ArrayList<>(); 195 if (target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE) { 196 implReceiverAndArgs.add(methodToCall.holder); 197 } 198 implReceiverAndArgs.addAll(Lists.newArrayList(methodToCall.proto.parameters.values)); 199 DexType implReturnType = methodToCall.proto.returnType; 200 201 assert target.invokeType == Invoke.Type.STATIC 202 || target.invokeType == Invoke.Type.VIRTUAL 203 || target.invokeType == Invoke.Type.DIRECT 204 || target.invokeType == Invoke.Type.INTERFACE; 205 assert checkSignatures(capturedTypes, enforcedParams, 206 enforcedReturnType, implReceiverAndArgs, 207 constructorTarget ? target.callTarget.holder : implReturnType); 208 209 // Prepare call arguments. 210 List<MoveType> argMoveTypes = new ArrayList<>(); 211 List<Integer> argRegisters = new ArrayList<>(); 212 213 // If the target is a constructor, we need to create the instance first. 214 // This instance will be the first argument to the call. 215 if (constructorTarget) { 216 int instance = nextRegister(MoveType.OBJECT); 217 add(builder -> builder.addNewInstance(instance, methodToCall.holder)); 218 argMoveTypes.add(MoveType.OBJECT); 219 argRegisters.add(instance); 220 } 221 222 // Load captures if needed. 223 int capturedValues = capturedTypes.length; 224 for (int i = 0; i < capturedValues; i++) { 225 MemberType memberType = MemberType.fromDexType(capturedTypes[i]); 226 MoveType moveType = MemberType.moveTypeFor(memberType); 227 int register = nextRegister(moveType); 228 229 argMoveTypes.add(moveType); 230 argRegisters.add(register); 231 232 // Read field into tmp local. 233 DexField field = lambda.getCaptureField(i); 234 add(builder -> builder.addInstanceGet( 235 memberType, register, getReceiverRegister(), field)); 236 } 237 238 // Prepare arguments. 239 for (int i = 0; i < erasedParams.length; i++) { 240 DexType expectedParamType = implReceiverAndArgs.get(i + capturedValues); 241 argMoveTypes.add(MoveType.fromDexType(expectedParamType)); 242 argRegisters.add(prepareParameterValue( 243 getParamRegister(i), erasedParams[i], enforcedParams[i], expectedParamType)); 244 } 245 246 // Method call to the method implementing lambda or method-ref. 247 add(builder -> builder.addInvoke(target.invokeType, 248 methodToCall, methodToCall.proto, argMoveTypes, argRegisters)); 249 250 // Does the method have return value? 251 if (enforcedReturnType.isVoidType()) { 252 add(IRBuilder::addReturn); 253 } else if (constructorTarget) { 254 // Return newly created instance 255 int instanceRegister = argRegisters.get(0); 256 int adjustedValue = prepareReturnValue(instanceRegister, 257 erasedReturnType, enforcedReturnType, methodToCall.holder); 258 add(builder -> builder.addReturn( 259 MoveType.fromDexType(erasedReturnType), adjustedValue)); 260 } else { 261 MoveType implMoveType = MoveType.fromDexType(implReturnType); 262 int tempValue = nextRegister(implMoveType); 263 add(builder -> builder.addMoveResult(implMoveType, tempValue)); 264 int adjustedValue = prepareReturnValue(tempValue, 265 erasedReturnType, enforcedReturnType, methodToCall.proto.returnType); 266 MoveType adjustedMoveType = MoveType.fromDexType(erasedReturnType); 267 add(builder -> builder.addReturn(adjustedMoveType, adjustedValue)); 268 } 269 } 270 271 // Adds necessary casts and transformations to adjust the value 272 // returned by impl-method to expected return type of the method. 273 private int prepareReturnValue(int register, 274 DexType erasedType, DexType enforcedType, DexType actualType) { 275 // `actualType` must be adjusted to `enforcedType` first. 276 register = adjustType(register, actualType, enforcedType); 277 278 // `erasedType` and `enforcedType` may only differ when they both 279 // are class types and `erasedType` is a base type of `enforcedType`, 280 // so no transformation is actually needed. 281 assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, erasedType); 282 return register; 283 } 284 285 // Adds necessary casts and transformations to adjust parameter 286 // value to the expected type of method-impl argument. 287 // 288 // Note that the original parameter type (`erasedType`) may need to 289 // be converted to enforced parameter type (`enforcedType`), which, 290 // in its turn, may need to be adjusted to the parameter type of 291 // the impl-method (`expectedType`). 292 private int prepareParameterValue(int register, 293 DexType erasedType, DexType enforcedType, DexType expectedType) { 294 register = enforceParameterType(register, erasedType, enforcedType); 295 register = adjustType(register, enforcedType, expectedType); 296 return register; 297 } 298 299 private int adjustType(int register, DexType fromType, DexType toType) { 300 if (fromType == toType) { 301 return register; 302 } 303 304 boolean fromTypePrimitive = fromType.isPrimitiveType(); 305 boolean toTypePrimitive = toType.isPrimitiveType(); 306 307 // If both are primitive they must be convertible via primitive widening conversion. 308 if (fromTypePrimitive && toTypePrimitive) { 309 return addPrimitiveWideningConversion(register, fromType, toType); 310 } 311 312 // If the first one is a boxed primitive type and the second one is a primitive 313 // type, the value must be unboxed and converted to the resulting type via primitive 314 // widening conversion. 315 if (toTypePrimitive) { 316 DexType fromTypeAsPrimitive = getPrimitiveFromBoxed(fromType); 317 if (fromTypeAsPrimitive != null) { 318 int unboxedRegister = addPrimitiveUnboxing(register, fromTypeAsPrimitive, fromType); 319 return addPrimitiveWideningConversion(unboxedRegister, fromTypeAsPrimitive, toType); 320 } 321 } 322 323 // If the first one is a primitive type and the second one is a boxed 324 // type for this primitive type, just box the value. 325 if (fromTypePrimitive) { 326 DexType boxedFromType = getBoxedForPrimitiveType(fromType); 327 if (toType == boxedFromType || 328 toType == factory().objectType || 329 (boxedFromType != factory().booleanType && 330 boxedFromType != factory().charType && 331 toType == factory().boxedNumberType)) { 332 return addPrimitiveBoxing(register, fromType, boxedFromType); 333 } 334 } 335 336 if (fromType.isArrayType() && toType == factory().objectType) { 337 // If `fromType` is an array and `toType` is java.lang.Object, no cast is needed. 338 return register; 339 } 340 341 if (fromType.isClassType() && toType.isClassType()) { 342 // If `fromType` and `toType` are both reference types, `fromType` must 343 // be deriving from `toType`. 344 // NOTE: we don't check `toType` for being actually a supertype, since we 345 // might not have full classpath with inheritance information to do that. 346 return register; 347 } 348 349 throw new Unreachable("Unexpected type adjustment from " 350 + fromType.toSourceString() + " to " + toType); 351 } 352 353 private int addPrimitiveWideningConversion(int register, DexType fromType, DexType toType) { 354 assert fromType.isPrimitiveType() && toType.isPrimitiveType(); 355 if (fromType == toType) { 356 return register; 357 } 358 359 NumericType from = NumericType.fromDexType(fromType); 360 NumericType to = NumericType.fromDexType(toType); 361 362 if (from != null && to != null) { 363 assert from != to; 364 365 switch (to) { 366 case SHORT: { 367 if (from != NumericType.BYTE) { 368 break; // Only BYTE can be converted to SHORT via widening conversion. 369 } 370 int result = nextRegister(MoveType.SINGLE); 371 add(builder -> builder.addConversion(to, NumericType.INT, result, register)); 372 return result; 373 } 374 375 case INT: 376 if (from == NumericType.BYTE || from == NumericType.CHAR || from == NumericType.SHORT) { 377 return register; // No actual conversion is needed. 378 } 379 break; 380 381 case LONG: { 382 if (from == NumericType.FLOAT || from == NumericType.DOUBLE) { 383 break; // Not a widening conversion. 384 } 385 int result = nextRegister(MoveType.WIDE); 386 add(builder -> builder.addConversion(to, NumericType.INT, result, register)); 387 return result; 388 } 389 390 case FLOAT: { 391 if (from == NumericType.DOUBLE) { 392 break; // Not a widening conversion. 393 } 394 int result = nextRegister(MoveType.SINGLE); 395 NumericType type = (from == NumericType.LONG) ? NumericType.LONG : NumericType.INT; 396 add(builder -> builder.addConversion(to, type, result, register)); 397 return result; 398 } 399 400 case DOUBLE: { 401 int result = nextRegister(MoveType.WIDE); 402 NumericType type = (from == NumericType.FLOAT || from == NumericType.LONG) 403 ? from : NumericType.INT; 404 add(builder -> builder.addConversion(to, type, result, register)); 405 return result; 406 } 407 default: 408 // exception is thrown below 409 break; 410 } 411 } 412 413 throw new Unreachable("Type " + fromType.toSourceString() + " cannot be " + 414 "converted to " + toType.toSourceString() + " via primitive widening conversion."); 415 } 416 417 private DexMethod getUnboxMethod(byte primitive, DexType boxType) { 418 DexItemFactory factory = factory(); 419 DexProto proto; 420 switch (primitive) { 421 case 'Z': // byte 422 proto = factory.createProto(factory.booleanType); 423 return factory.createMethod(boxType, proto, factory.unboxBooleanMethodName); 424 case 'B': // byte 425 proto = factory.createProto(factory.byteType); 426 return factory.createMethod(boxType, proto, factory.unboxByteMethodName); 427 case 'S': // short 428 proto = factory.createProto(factory.shortType); 429 return factory.createMethod(boxType, proto, factory.unboxShortMethodName); 430 case 'C': // char 431 proto = factory.createProto(factory.charType); 432 return factory.createMethod(boxType, proto, factory.unboxCharMethodName); 433 case 'I': // int 434 proto = factory.createProto(factory.intType); 435 return factory.createMethod(boxType, proto, factory.unboxIntMethodName); 436 case 'J': // long 437 proto = factory.createProto(factory.longType); 438 return factory.createMethod(boxType, proto, factory.unboxLongMethodName); 439 case 'F': // float 440 proto = factory.createProto(factory.floatType); 441 return factory.createMethod(boxType, proto, factory.unboxFloatMethodName); 442 case 'D': // double 443 proto = factory.createProto(factory.doubleType); 444 return factory.createMethod(boxType, proto, factory.unboxDoubleMethodName); 445 default: 446 throw new Unreachable("Invalid primitive type descriptor: " + primitive); 447 } 448 } 449 450 private int addPrimitiveUnboxing(int register, DexType primitiveType, DexType boxType) { 451 DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType); 452 453 List<MoveType> argMoveTypes = Collections.singletonList(MoveType.OBJECT); 454 List<Integer> argRegisters = Collections.singletonList(register); 455 add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL, 456 method, method.proto, argMoveTypes, argRegisters)); 457 458 MoveType moveType = MoveType.fromDexType(primitiveType); 459 int result = nextRegister(moveType); 460 add(builder -> builder.addMoveResult(moveType, result)); 461 return result; 462 } 463 464 private int addPrimitiveBoxing(int register, DexType primitiveType, DexType boxType) { 465 // Generate factory method fo boxing. 466 DexItemFactory factory = factory(); 467 DexProto proto = factory.createProto(boxType, new DexType[]{primitiveType}); 468 DexMethod method = factory.createMethod(boxType, proto, factory.valueOfMethodName); 469 470 MoveType moveType = MoveType.fromDexType(primitiveType); 471 List<MoveType> argMoveTypes = Collections.singletonList(moveType); 472 List<Integer> argRegisters = Collections.singletonList(register); 473 add(builder -> builder.addInvoke(Invoke.Type.STATIC, 474 method, method.proto, argMoveTypes, argRegisters)); 475 476 int result = nextRegister(MoveType.OBJECT); 477 add(builder -> builder.addMoveResult(MoveType.OBJECT, result)); 478 return result; 479 } 480} 481