| // Copyright (c) 2019, 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.internal.proto; |
| |
| import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getInfoValueFromMessageInfoConstructionInvoke; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker; |
| import com.android.tools.r8.ir.analysis.proto.ProtoReferences; |
| import com.android.tools.r8.ir.analysis.proto.RawMessageInfoDecoder; |
| import com.android.tools.r8.ir.code.IRCode; |
| import com.android.tools.r8.ir.code.InvokeMethod; |
| import com.android.tools.r8.utils.StringUtils; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.android.tools.r8.utils.codeinspector.FoundClassSubject; |
| import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; |
| import it.unimi.dsi.fastutil.ints.IntArrayList; |
| import it.unimi.dsi.fastutil.ints.IntList; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| public abstract class ProtoShrinkingTestBase extends TestBase { |
| |
| public static final Path PROTOBUF_LITE_JAR = |
| Paths.get("third_party/protobuf-lite/libprotobuf_lite.jar"); |
| |
| public static final Path PROTOBUF_LITE_PROGUARD_RULES = |
| Paths.get("third_party/protobuf-lite/lite_proguard.pgcfg"); |
| |
| // Test classes for proto2. |
| public static final Path PROTO2_EXAMPLES_JAR = |
| Paths.get(ToolHelper.EXAMPLES_PROTO_BUILD_DIR).resolve("proto2.jar"); |
| |
| // Proto definitions used by test classes for proto2. |
| public static final Path PROTO2_PROTO_JAR = |
| Paths.get(ToolHelper.GENERATED_PROTO_BUILD_DIR).resolve("proto2.jar"); |
| |
| // Test classes for proto3. |
| public static final Path PROTO3_EXAMPLES_JAR = |
| Paths.get(ToolHelper.EXAMPLES_PROTO_BUILD_DIR).resolve("proto3.jar"); |
| |
| // Proto definitions used by test classes for proto3. |
| public static final Path PROTO3_PROTO_JAR = |
| Paths.get(ToolHelper.GENERATED_PROTO_BUILD_DIR).resolve("proto3.jar"); |
| |
| static void assertRewrittenProtoSchemasMatch( |
| CodeInspector expectedInspector, CodeInspector actualInspector) throws Exception { |
| Map<String, IntList> actualInfos = getInfoValues(actualInspector); |
| |
| // Ensure that this cannot fail silently. |
| assertTrue(actualInfos.size() > 0); |
| |
| Map<String, IntList> expectedInfos = getInfoValues(expectedInspector); |
| for (Map.Entry<String, IntList> entry : actualInfos.entrySet()) { |
| String className = entry.getKey(); |
| IntList actualInfo = entry.getValue(); |
| IntList expectedInfo = expectedInfos.get(className); |
| assertNotNull("Expected info value missing for class `" + className + "`", expectedInfo); |
| assertEquals("Unexpected info value for class `" + className + "`", expectedInfo, actualInfo); |
| } |
| } |
| |
| static String allGeneratedMessageLiteSubtypesAreInstantiatedRule() { |
| return StringUtils.lines( |
| "-if class * extends com.google.protobuf.GeneratedMessageLite", |
| "-keep,allowobfuscation class <1> {", |
| " <init>(...);", |
| "}"); |
| } |
| |
| static String keepAllProtosRule() { |
| return StringUtils.lines( |
| "-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }"); |
| } |
| |
| static String keepDynamicMethodSignatureRule() { |
| return StringUtils.lines( |
| "-keepclassmembers,includedescriptorclasses class com.google.protobuf.GeneratedMessageLite " |
| + "{", |
| " java.lang.Object dynamicMethod(com.google.protobuf.GeneratedMessageLite$MethodToInvoke," |
| + " java.lang.Object, java.lang.Object);", |
| "}"); |
| } |
| |
| static String keepNewMessageInfoSignatureRule() { |
| return StringUtils.lines( |
| "-keepclassmembers,includedescriptorclasses class com.google.protobuf.GeneratedMessageLite " |
| + "{", |
| " java.lang.Object newMessageInfo(com.google.protobuf.MessageLite," |
| + " java.lang.String, java.lang.Object[]);", |
| "}"); |
| } |
| |
| /** |
| * Finds all proto messages and creates a mapping from the type name of the message to the |
| * expected info value of the message. |
| */ |
| static Map<String, IntList> getInfoValues(CodeInspector inspector) throws Exception { |
| Map<String, IntList> result = new HashMap<>(); |
| DexItemFactory dexItemFactory = inspector.getFactory(); |
| ProtoReferences references = new ProtoReferences(dexItemFactory); |
| for (FoundClassSubject classSubject : inspector.allClasses()) { |
| for (FoundMethodSubject methodSubject : classSubject.virtualMethods()) { |
| if (!methodSubject.hasCode() || !references.isDynamicMethod(methodSubject.getMethod())) { |
| continue; |
| } |
| |
| IRCode code = methodSubject.buildIR(dexItemFactory); |
| InvokeMethod invoke = |
| GeneratedMessageLiteShrinker.getNewMessageInfoInvoke(code, references); |
| assert invoke != null; |
| |
| IntList info = new IntArrayList(); |
| RawMessageInfoDecoder.createInfoIterator( |
| getInfoValueFromMessageInfoConstructionInvoke(invoke, references)) |
| .forEachRemaining(info::add); |
| result.put(classSubject.getOriginalName(), info); |
| } |
| } |
| return result; |
| } |
| } |