blob: 54f1d3a3db36c904e741c4e9317336aaf06e109e [file] [log] [blame]
// 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);
}
}