| // Copyright (c) 2021, 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.kotlin.lambda; |
| |
| import static junit.framework.TestCase.assertTrue; |
| |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexProgramClass; |
| import com.android.tools.r8.references.ClassReference; |
| import com.android.tools.r8.references.Reference; |
| import com.android.tools.r8.utils.SetUtils; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.android.tools.r8.utils.codeinspector.FoundClassSubject; |
| import java.io.IOException; |
| import java.nio.file.Path; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| public class KotlinLambdasInInput { |
| |
| private final Set<ClassReference> jStyleLambdas; |
| private final Set<ClassReference> kStyleLambdas; |
| |
| private KotlinLambdasInInput( |
| Set<ClassReference> jStyleLambdas, Set<ClassReference> kStyleLambdas) { |
| this.jStyleLambdas = jStyleLambdas; |
| this.kStyleLambdas = kStyleLambdas; |
| } |
| |
| public static KotlinLambdasInInput create(List<Path> programFiles, String testName) |
| throws IOException { |
| CodeInspector inputInspector = new CodeInspector(programFiles); |
| Set<ClassReference> jStyleLambdas = new HashSet<>(); |
| Set<ClassReference> kStyleLambdas = new HashSet<>(); |
| for (FoundClassSubject classSubject : inputInspector.allClasses()) { |
| DexProgramClass clazz = classSubject.getDexProgramClass(); |
| if (!clazz.getType().getPackageName().startsWith(testName)) { |
| continue; |
| } |
| if (internalIsJStyleLambda(clazz)) { |
| jStyleLambdas.add(Reference.classFromTypeName(clazz.getTypeName())); |
| } else if (internalIsKStyleLambda(clazz)) { |
| kStyleLambdas.add(Reference.classFromTypeName(clazz.getTypeName())); |
| } |
| } |
| return new KotlinLambdasInInput(jStyleLambdas, kStyleLambdas); |
| } |
| |
| private static boolean internalIsKStyleLambda(DexProgramClass clazz) { |
| return clazz.getSuperType().getTypeName().equals("kotlin.jvm.internal.Lambda"); |
| } |
| |
| private static boolean internalIsJStyleLambda(DexProgramClass clazz) { |
| if (!clazz.getSuperType().getTypeName().equals(Object.class.getTypeName()) |
| || clazz.getInterfaces().size() != 1 |
| || clazz.getMethodCollection().numberOfVirtualMethods() == 0) { |
| return false; |
| } |
| if (clazz |
| .getMethodCollection() |
| .hasDirectMethods(method -> method.isStatic() && !method.isClassInitializer())) { |
| return false; |
| } |
| int numberOfFinalNonBridgeNonSyntheticMethods = 0; |
| for (DexEncodedMethod method : clazz.virtualMethods()) { |
| if (method.isFinal() && !method.isBridge() && !method.isSyntheticMethod()) { |
| numberOfFinalNonBridgeNonSyntheticMethods++; |
| } |
| } |
| return numberOfFinalNonBridgeNonSyntheticMethods == 1; |
| } |
| |
| public Set<ClassReference> getAllLambdas() { |
| return SetUtils.newIdentityHashSet(jStyleLambdas, kStyleLambdas); |
| } |
| |
| public Set<ClassReference> getJStyleLambdas() { |
| return jStyleLambdas; |
| } |
| |
| public ClassReference getJStyleLambdaReferenceFromTypeName(String testName, String simpleName) { |
| ClassReference classReference = Reference.classFromTypeName(testName + "." + simpleName); |
| assertTrue(jStyleLambdas.contains(classReference)); |
| return classReference; |
| } |
| |
| public Set<ClassReference> getKStyleLambdas() { |
| return kStyleLambdas; |
| } |
| |
| public ClassReference getKStyleLambdaReferenceFromTypeName(String testName, String simpleName) { |
| ClassReference classReference = Reference.classFromTypeName(testName + "." + simpleName); |
| assertTrue( |
| "Class is not a Kotlin-style lambda: " + classReference.getTypeName(), |
| kStyleLambdas.contains(classReference)); |
| return classReference; |
| } |
| |
| public int getNumberOfJStyleLambdas() { |
| return jStyleLambdas.size(); |
| } |
| |
| public int getNumberOfKStyleLambdas() { |
| return kStyleLambdas.size(); |
| } |
| |
| public boolean isJStyleLambda(ClassReference classReference) { |
| return jStyleLambdas.contains(classReference); |
| } |
| |
| public boolean isKStyleLambda(ClassReference classReference) { |
| return kStyleLambdas.contains(classReference); |
| } |
| |
| public void print() { |
| System.out.println("Java-style Kotlin lambdas:"); |
| jStyleLambdas.forEach(lambda -> System.out.println(lambda.getTypeName())); |
| System.out.println(); |
| System.out.println("Kotlin-style Kotlin lambdas:"); |
| kStyleLambdas.forEach(lambda -> System.out.println(lambda.getTypeName())); |
| } |
| } |