| // 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.kstyle; |
| |
| import com.android.tools.r8.graph.AppInfoWithSubtyping; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexField; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.graph.DexProto; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.ir.optimize.lambda.CaptureSignature; |
| import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy; |
| import com.android.tools.r8.ir.optimize.lambda.LambdaGroup; |
| import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder; |
| import com.android.tools.r8.kotlin.Kotlin; |
| import com.android.tools.r8.utils.ThrowingConsumer; |
| |
| // 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: |
| // |
| // ----------------------------------------------------------------------------------------------- |
| // // 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 is 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 LambdaGroup { |
| private final Strategy strategy = new KStyleLambdaGroupCodeStrategy(this); |
| |
| KStyleLambdaGroup(KStyleLambdaGroupId id) { |
| super(id); |
| } |
| |
| final KStyleLambdaGroupId id() { |
| return (KStyleLambdaGroupId) id; |
| } |
| |
| final boolean isStateless() { |
| return id().capture.isEmpty(); |
| } |
| |
| // Field referencing singleton instance for a lambda with specified id. |
| final DexField getSingletonInstanceField(DexItemFactory factory, int id) { |
| return factory.createField(this.getGroupClassType(), |
| this.getGroupClassType(), factory.createString("INSTANCE$" + id)); |
| } |
| |
| @Override |
| protected String getTypePackage() { |
| String pkg = id().pkg; |
| return pkg.isEmpty() ? "" : (pkg + "/"); |
| } |
| |
| final DexProto createConstructorProto(DexItemFactory factory) { |
| String capture = id().capture; |
| DexType[] newParameters = new DexType[capture.length() + 1]; |
| newParameters[0] = factory.intType; // Lambda id. |
| for (int i = 0; i < capture.length(); i++) { |
| newParameters[i + 1] = CaptureSignature.fieldType(factory, capture, i); |
| } |
| return factory.createProto(factory.voidType, newParameters); |
| } |
| |
| final DexField getLambdaIdField(DexItemFactory factory) { |
| return factory.createField(this.getGroupClassType(), factory.intType, "$id$"); |
| } |
| |
| final int mapFieldIntoCaptureIndex(DexType lambda, DexField field) { |
| return CaptureSignature.mapFieldIntoCaptureIndex( |
| id().capture, lambdaCaptureFields(lambda), field); |
| } |
| |
| final DexField getCaptureField(DexItemFactory factory, int index) { |
| assert index >= 0 && index < id().capture.length(); |
| return factory.createField(this.getGroupClassType(), |
| CaptureSignature.fieldType(factory, id().capture, index), "$capture$" + index); |
| } |
| |
| @Override |
| protected LambdaGroupClassBuilder getBuilder(DexItemFactory factory) { |
| return new KStyleLambdaGroupClassBuilder(factory, this, "kotlin-style lambda group"); |
| } |
| |
| @Override |
| public Strategy getCodeStrategy() { |
| return strategy; |
| } |
| |
| @Override |
| public ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator( |
| Kotlin kotlin, AppInfoWithSubtyping appInfo) { |
| return new KStyleLambdaClassValidator(kotlin, this, appInfo); |
| } |
| } |