| // Copyright (c) 2020, 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.library; |
| |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.LibraryMethod; |
| import com.android.tools.r8.ir.code.InvokeMethod; |
| import com.android.tools.r8.ir.code.Value; |
| import com.android.tools.r8.ir.optimize.library.sideeffects.JavaLangObjectsSideEffectCollection; |
| import com.android.tools.r8.utils.BiPredicateUtils; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.function.BiPredicate; |
| import java.util.function.Consumer; |
| |
| public class LibraryMethodSideEffectModelCollection { |
| |
| private final Map<DexMethod, BiPredicate<DexMethod, List<Value>>> finalMethodsWithoutSideEffects; |
| private final Set<DexMethod> unconditionalFinalMethodsWithoutSideEffects; |
| |
| private final Set<DexMethod> nonFinalMethodsWithoutSideEffects; |
| |
| public LibraryMethodSideEffectModelCollection(AppView<?> appView) { |
| DexItemFactory dexItemFactory = appView.dexItemFactory(); |
| finalMethodsWithoutSideEffects = buildFinalMethodsWithoutSideEffects(appView, dexItemFactory); |
| unconditionalFinalMethodsWithoutSideEffects = |
| buildUnconditionalFinalMethodsWithoutSideEffects(dexItemFactory); |
| nonFinalMethodsWithoutSideEffects = buildNonFinalMethodsWithoutSideEffects(dexItemFactory); |
| } |
| |
| private static Map<DexMethod, BiPredicate<DexMethod, List<Value>>> |
| buildFinalMethodsWithoutSideEffects(AppView<?> appView, DexItemFactory dexItemFactory) { |
| ImmutableMap.Builder<DexMethod, BiPredicate<DexMethod, List<Value>>> builder = |
| ImmutableMap.<DexMethod, BiPredicate<DexMethod, List<Value>>>builder() |
| .put( |
| dexItemFactory.objectsMethods.toStringWithObject, |
| (method, arguments) -> |
| !JavaLangObjectsSideEffectCollection.toStringMayHaveSideEffects( |
| appView, arguments)) |
| .put( |
| dexItemFactory.stringMembers.constructor, |
| (method, arguments) -> arguments.get(1).isNeverNull()) |
| .put( |
| dexItemFactory.stringMembers.valueOf, |
| (method, arguments) -> |
| !JavaLangObjectsSideEffectCollection.toStringMayHaveSideEffects( |
| appView, arguments)); |
| putAll( |
| builder, |
| dexItemFactory.stringBufferMethods.constructorMethods, |
| dexItemFactory.stringBufferMethods::constructorInvokeIsSideEffectFree); |
| putAll( |
| builder, |
| dexItemFactory.stringBuilderMethods.constructorMethods, |
| dexItemFactory.stringBuilderMethods::constructorInvokeIsSideEffectFree); |
| return builder.build(); |
| } |
| |
| private static Set<DexMethod> buildUnconditionalFinalMethodsWithoutSideEffects( |
| DexItemFactory dexItemFactory) { |
| return ImmutableSet.<DexMethod>builder() |
| .add(dexItemFactory.booleanMembers.toString) |
| .add(dexItemFactory.byteMembers.toString) |
| .add(dexItemFactory.charMembers.toString) |
| .add(dexItemFactory.doubleMembers.toString) |
| .add(dexItemFactory.enumMembers.constructor) |
| .add(dexItemFactory.floatMembers.toString) |
| .add(dexItemFactory.integerMembers.toString) |
| .add(dexItemFactory.longMembers.toString) |
| .add(dexItemFactory.npeMethods.init) |
| .add(dexItemFactory.npeMethods.initWithMessage) |
| .add(dexItemFactory.objectMembers.constructor) |
| .add(dexItemFactory.objectMembers.getClass) |
| .add(dexItemFactory.shortMembers.toString) |
| .add(dexItemFactory.stringBufferMethods.toString) |
| .add(dexItemFactory.stringBuilderMethods.toString) |
| .add(dexItemFactory.stringMembers.length) |
| .add(dexItemFactory.stringMembers.hashCode) |
| .add(dexItemFactory.stringMembers.isEmpty) |
| .add(dexItemFactory.stringMembers.toString) |
| .add(dexItemFactory.stringMembers.trim) |
| .addAll(dexItemFactory.classMethods.getNames) |
| .addAll(dexItemFactory.boxedValueOfMethods()) |
| .build(); |
| } |
| |
| private static Set<DexMethod> buildNonFinalMethodsWithoutSideEffects( |
| DexItemFactory dexItemFactory) { |
| return ImmutableSet.of( |
| dexItemFactory.objectMembers.equals, |
| dexItemFactory.objectMembers.hashCode, |
| dexItemFactory.objectMembers.toString); |
| } |
| |
| private static <K, V> void putAll(ImmutableMap.Builder<K, V> builder, Iterable<K> keys, V value) { |
| for (K key : keys) { |
| builder.put(key, value); |
| } |
| } |
| |
| public void forEachSideEffectFreeFinalMethod(Consumer<DexMethod> consumer) { |
| unconditionalFinalMethodsWithoutSideEffects.forEach(consumer); |
| } |
| |
| public boolean isCallToSideEffectFreeFinalMethod(InvokeMethod invoke) { |
| return isSideEffectFreeFinalMethod(invoke.getInvokedMethod(), invoke.arguments()); |
| } |
| |
| public boolean isSideEffectFreeFinalMethod(DexMethod method, List<Value> arguments) { |
| return unconditionalFinalMethodsWithoutSideEffects.contains(method) |
| || finalMethodsWithoutSideEffects |
| .getOrDefault(method, BiPredicateUtils.alwaysFalse()) |
| .test(method, arguments); |
| } |
| |
| // This intentionally takes the invoke instruction since the determination of whether a library |
| // method has side effects may depend on the arguments. |
| public boolean isSideEffectFree(InvokeMethod invoke, LibraryMethod singleTarget) { |
| return isSideEffectFreeFinalMethod(singleTarget.getReference(), invoke.arguments()) |
| || nonFinalMethodsWithoutSideEffects.contains(singleTarget.getReference()); |
| } |
| } |