| // Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| package com.android.tools.r8.ir.optimize.lambda.kotlin; |
| |
| import com.android.tools.r8.code.Const16; |
| import com.android.tools.r8.code.Const4; |
| import com.android.tools.r8.code.ReturnVoid; |
| import com.android.tools.r8.graph.AppInfoWithSubtyping; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexEncodedField; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexField; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.EnclosingMethodAttribute; |
| import com.android.tools.r8.graph.InnerClassAttribute; |
| import com.android.tools.r8.ir.analysis.type.TypeLatticeElement; |
| import com.android.tools.r8.ir.code.Invoke.Type; |
| import com.android.tools.r8.ir.code.Position; |
| import com.android.tools.r8.ir.code.ValueType; |
| import com.android.tools.r8.ir.optimize.lambda.LambdaGroup; |
| import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder; |
| import com.android.tools.r8.ir.synthetic.SyntheticSourceCode; |
| import com.android.tools.r8.kotlin.Kotlin; |
| import com.android.tools.r8.utils.ThrowingConsumer; |
| import com.google.common.collect.Lists; |
| import java.util.function.IntFunction; |
| |
| // Represents a k-style lambda group created to combine several lambda classes |
| // generated by kotlin compiler for regular kotlin lambda expressions, like: |
| // |
| // ----------------------------------------------------------------------- |
| // fun foo(m: String, v: Int): () -> String { |
| // val lambda: (String, Int) -> String = { s, i -> s.substring(i) } |
| // return { "$m: $v" } |
| // } |
| // ----------------------------------------------------------------------- |
| // |
| // Regular stateless k-style lambda class structure looks like below: |
| // NOTE: stateless j-style lambdas do not always have INSTANCE field. |
| // |
| // ----------------------------------------------------------------------------------------------- |
| // // signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function2< |
| // Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;>; |
| // final class lambdas/LambdasKt$foo$lambda1$1 |
| // extends kotlin/jvm/internal/Lambda |
| // implements kotlin/jvm/functions/Function2 { |
| // |
| // public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; |
| // |
| // public final invoke(Ljava/lang/String;I)Ljava/lang/String; |
| // @Lorg/jetbrains/annotations/NotNull;() // invisible |
| // @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 |
| // |
| // <init>()V |
| // |
| // public final static Llambdas/LambdasKt$foo$lambda1$1; INSTANCE |
| // |
| // static <clinit>()V |
| // |
| // OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0; |
| // final static INNERCLASS lambdas/LambdasKt$foo$lambda1$1 null null |
| // } |
| // ----------------------------------------------------------------------------------------------- |
| // |
| // Regular stateful k-style lambda class structure looks like below: |
| // |
| // ----------------------------------------------------------------------------------------------- |
| // // signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function0<Ljava/lang/String;>; |
| // final class lambdas/LambdasKt$foo$1 |
| // extends kotlin/jvm/internal/Lambda |
| // implements kotlin/jvm/functions/Function0 { |
| // |
| // public synthetic bridge invoke()Ljava/lang/Object; |
| // |
| // public final invoke()Ljava/lang/String; |
| // @Lorg/jetbrains/annotations/NotNull;() // invisible |
| // |
| // <init>(Ljava/lang/String;I)V |
| // |
| // final synthetic Ljava/lang/String; $m |
| // final synthetic I $v |
| // |
| // OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0; |
| // final static INNERCLASS lambdas/LambdasKt$foo$1 null null |
| // } |
| // ----------------------------------------------------------------------------------------------- |
| // |
| // Key k-style lambda class details: |
| // - extends kotlin.jvm.internal.Lambda |
| // - implements one of kotlin.jvm.functions.Function0..Function22, or FunctionN |
| // see: https://github.com/JetBrains/kotlin/blob/master/libraries/ |
| // stdlib/jvm/runtime/kotlin/jvm/functions/Functions.kt |
| // and: https://github.com/JetBrains/kotlin/blob/master/spec-docs/function-types.md |
| // - lambda class is created as an anonymous inner class |
| // - lambda class carries generic signature and kotlin metadata attribute |
| // - class instance fields represent captured values and have an instance constructor |
| // with matching parameters initializing them (see the second class above) |
| // - stateless lambda *may* be implemented as a singleton with a static field storing the |
| // only instance and initialized in static class constructor (see the first class above) |
| // - main lambda method usually matches an exact lambda signature and may have |
| // generic signature attribute and nullability parameter annotations |
| // - optional bridge method created to satisfy interface implementation and |
| // forwarding call to lambda main method |
| // |
| final class KStyleLambdaGroup extends KotlinLambdaGroup { |
| private KStyleLambdaGroup(GroupId id) { |
| super(id); |
| } |
| |
| @Override |
| protected LambdaGroupClassBuilder getBuilder(DexItemFactory factory) { |
| return new ClassBuilder(factory, "kotlin-style lambda group"); |
| } |
| |
| @Override |
| public ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator( |
| Kotlin kotlin, AppInfoWithSubtyping appInfo) { |
| return new ClassValidator(kotlin, appInfo); |
| } |
| |
| @Override |
| protected String getGroupSuffix() { |
| return "ks$"; |
| } |
| |
| // Specialized group id. |
| final static class GroupId extends KotlinLambdaGroupId { |
| GroupId(String capture, DexType iface, |
| String pkg, String signature, DexEncodedMethod mainMethod, |
| InnerClassAttribute inner, EnclosingMethodAttribute enclosing) { |
| super(capture, iface, pkg, signature, mainMethod, inner, enclosing); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| return obj instanceof GroupId && computeEquals((KotlinLambdaGroupId) obj); |
| } |
| |
| @Override |
| String getLambdaKindDescriptor() { |
| return "Kotlin k-style lambda group"; |
| } |
| |
| @Override |
| public LambdaGroup createGroup() { |
| return new KStyleLambdaGroup(this); |
| } |
| } |
| |
| // Specialized class validator. |
| private final class ClassValidator extends KotlinLambdaClassValidator { |
| ClassValidator(Kotlin kotlin, AppInfoWithSubtyping appInfo) { |
| super(kotlin, KStyleLambdaGroup.this, appInfo); |
| } |
| |
| @Override |
| int getInstanceInitializerSize(DexEncodedField[] captures) { |
| return captures.length + 3; |
| } |
| |
| @Override |
| int validateInstanceInitializerEpilogue( |
| com.android.tools.r8.code.Instruction[] instructions, int index) |
| throws LambdaStructureError { |
| if (!(instructions[index] instanceof Const4) && |
| !(instructions[index] instanceof Const16)) { |
| throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED); |
| } |
| index++; |
| if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect |
| || instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange) |
| || instructions[index].getMethod() != kotlin.functional.lambdaInitializerMethod) { |
| throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED); |
| } |
| index++; |
| if (!(instructions[index] instanceof ReturnVoid)) { |
| throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED); |
| } |
| return index + 1; |
| } |
| } |
| |
| // Specialized class builder. |
| private final class ClassBuilder extends KotlinLambdaGroupClassBuilder<KStyleLambdaGroup> { |
| ClassBuilder(DexItemFactory factory, String origin) { |
| super(KStyleLambdaGroup.this, factory, origin); |
| } |
| |
| @Override |
| protected DexType getSuperClassType() { |
| return factory.kotlin.functional.lambdaType; |
| } |
| |
| @Override |
| SyntheticSourceCode createInstanceInitializerSourceCode( |
| DexType groupClassType, DexMethod initializerMethod, Position callerPosition) { |
| return new InstanceInitializerSourceCode( |
| factory, |
| groupClassType, |
| group.getLambdaIdField(factory), |
| id -> group.getCaptureField(factory, id), |
| initializerMethod, |
| id.mainMethodProto.parameters.size(), |
| callerPosition); |
| } |
| } |
| |
| // Specialized instance initializer code. |
| private final static class InstanceInitializerSourceCode |
| extends KotlinInstanceInitializerSourceCode { |
| private final int arity; |
| private final DexMethod lambdaInitializer; |
| |
| InstanceInitializerSourceCode( |
| DexItemFactory factory, |
| DexType lambdaGroupType, |
| DexField idField, |
| IntFunction<DexField> fieldGenerator, |
| DexMethod method, |
| int arity, |
| Position callerPosition) { |
| super(lambdaGroupType, idField, fieldGenerator, method, callerPosition); |
| this.arity = arity; |
| this.lambdaInitializer = factory.createMethod(factory.kotlin.functional.lambdaType, |
| factory.createProto(factory.voidType, factory.intType), factory.constructorMethodName); |
| } |
| |
| @Override |
| void prepareSuperConstructorCall(int receiverRegister) { |
| int arityRegister = nextRegister(ValueType.INT); |
| add(builder -> builder.addConst(TypeLatticeElement.INT, arityRegister, arity)); |
| add(builder -> builder.addInvoke(Type.DIRECT, lambdaInitializer, lambdaInitializer.proto, |
| Lists.newArrayList(ValueType.OBJECT, ValueType.INT), |
| Lists.newArrayList(receiverRegister, arityRegister))); |
| } |
| } |
| } |