• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 package com.android.tools.r8.jsr45;
5 
6 import com.android.tools.r8.CompilationException;
7 import com.android.tools.r8.D8;
8 import com.android.tools.r8.D8Command;
9 import com.android.tools.r8.R8;
10 import com.android.tools.r8.R8Command;
11 import com.android.tools.r8.ToolHelper;
12 import com.android.tools.r8.dex.Constants;
13 import com.android.tools.r8.graph.DexAnnotationElement;
14 import com.android.tools.r8.graph.DexValue.DexValueString;
15 import com.android.tools.r8.shaking.ProguardRuleParserException;
16 import com.android.tools.r8.utils.AndroidApp;
17 import com.android.tools.r8.utils.DexInspector;
18 import com.android.tools.r8.utils.DexInspector.AnnotationSubject;
19 import com.android.tools.r8.utils.DexInspector.ClassSubject;
20 import com.google.common.io.Closer;
21 import java.io.FileInputStream;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.concurrent.ExecutionException;
28 import org.junit.Assert;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.junit.rules.TemporaryFolder;
32 import org.objectweb.asm.ClassReader;
33 import org.objectweb.asm.ClassVisitor;
34 import org.objectweb.asm.Opcodes;
35 
36 public class JSR45Tests {
37 
38   private static final String DEFAULT_MAP_FILENAME = "proguard.map";
39   private static final Path INPUT_PATH =
40       Paths.get("src/test/java/com/android/tools/r8/jsr45/HelloKt.class");
41   private static final Path DONT_SHRINK_DONT_OBFUSCATE_CONFIG =
42       Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-1.txt");
43   private static final Path DONT_SHRINK_CONFIG =
44       Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-2.txt");
45   private static final Path SHRINK_KEEP_CONFIG =
46       Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-3.txt");
47   private static final Path SHRINK_NO_KEEP_CONFIG =
48       Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-4.txt");
49 
50   @Rule
51   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
52 
compileWithD8(Path intputPath, Path outputPath)53   void compileWithD8(Path intputPath, Path outputPath) throws IOException, CompilationException {
54     D8.run(
55         D8Command.builder()
56             .setMinApiLevel(Constants.ANDROID_O_API)
57             .addProgramFiles(intputPath)
58             .setOutputPath(outputPath)
59             .build());
60   }
61 
compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)62   void compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)
63       throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
64     AndroidApp androidApp =
65         R8.run(
66             R8Command.builder()
67                 .addProgramFiles(inputPath)
68                 .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
69                 .setOutputPath(outputPath)
70                 .addProguardConfigurationFiles(keepRulesPath)
71                 .build());
72     if (androidApp.hasProguardMap()) {
73       try (Closer closer = Closer.create()) {
74         androidApp.writeProguardMap(closer, new FileOutputStream(
75             Paths.get(tmpOutputDir.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME).toFile()));
76       }
77     }
78   }
79 
80   static class ReadSourceDebugExtensionAttribute extends ClassVisitor {
81 
ReadSourceDebugExtensionAttribute(int api, ClassVisitor cv)82     private ReadSourceDebugExtensionAttribute(int api, ClassVisitor cv) {
83       super(api, cv);
84     }
85 
86     private String debugSourceExtension = null;
87 
88     @Override
visitSource(String source, String debug)89     public void visitSource(String source, String debug) {
90       debugSourceExtension = debug;
91       super.visitSource(source, debug);
92     }
93   }
94 
95   @Test
testSourceDebugExtensionWithD8()96   public void testSourceDebugExtensionWithD8()
97       throws IOException, CompilationException, ExecutionException {
98     Path outputPath = tmpOutputDir.newFolder().toPath();
99 
100     compileWithD8(INPUT_PATH, outputPath);
101 
102     checkAnnotationContent(INPUT_PATH, outputPath);
103   }
104 
105   /**
106    * Check that when dontshrink and dontobfuscate is used the annotation is transmitted.
107    */
108   @Test
testSourceDebugExtensionWithShriking1()109   public void testSourceDebugExtensionWithShriking1()
110       throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
111     Path outputPath = tmpOutputDir.newFolder().toPath();
112     compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_DONT_OBFUSCATE_CONFIG);
113     checkAnnotationContent(INPUT_PATH, outputPath);
114   }
115 
116   /**
117    * Check that when dontshrink is used the annotation is not removed due to obfuscation.
118    */
119   @Test
testSourceDebugExtensionWithShrinking2()120   public void testSourceDebugExtensionWithShrinking2()
121       throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
122     Path outputPath = tmpOutputDir.newFolder().toPath();
123     compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_CONFIG);
124     checkAnnotationContent(INPUT_PATH, outputPath);
125   }
126 
127   /**
128    * Check that the annotation is transmitted when shrinking is enabled with a keepattribute option.
129    */
130   @Test
testSourceDebugExtensionWithShrinking3()131   public void testSourceDebugExtensionWithShrinking3()
132       throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
133     Path outputPath = tmpOutputDir.newFolder().toPath();
134 
135     compileWithR8(INPUT_PATH, outputPath, SHRINK_KEEP_CONFIG);
136 
137     checkAnnotationContent(INPUT_PATH, outputPath);
138   }
139 
140   /**
141    * Check that the annotation is removed when shriking is enabled and that there is not
142    * keepattributes option.
143    */
144   @Test
testSourceDebugExtensionWithShrinking4()145   public void testSourceDebugExtensionWithShrinking4()
146       throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
147     Path outputPath = tmpOutputDir.newFolder().toPath();
148 
149     compileWithR8(INPUT_PATH, outputPath, SHRINK_NO_KEEP_CONFIG);
150 
151     DexInspector dexInspector =
152         new DexInspector(outputPath.resolve("classes.dex"), getGeneratedProguardMap());
153     ClassSubject classSubject = dexInspector.clazz("HelloKt");
154     AnnotationSubject annotationSubject =
155         classSubject.annotation("dalvik.annotation.SourceDebugExtension");
156     Assert.assertFalse(annotationSubject.isPresent());
157   }
158 
checkAnnotationContent(Path inputPath, Path outputPath)159   private void checkAnnotationContent(Path inputPath, Path outputPath)
160       throws IOException, ExecutionException {
161     ClassReader classReader = new ClassReader(new FileInputStream(inputPath.toFile()));
162     ReadSourceDebugExtensionAttribute sourceDebugExtensionReader =
163         new ReadSourceDebugExtensionAttribute(Opcodes.ASM5, null);
164     classReader.accept(sourceDebugExtensionReader, 0);
165 
166     DexInspector dexInspector =
167         new DexInspector(outputPath.resolve("classes.dex"), getGeneratedProguardMap());
168     ClassSubject classSubject = dexInspector.clazz("HelloKt");
169 
170     AnnotationSubject annotationSubject =
171         classSubject.annotation("dalvik.annotation.SourceDebugExtension");
172     Assert.assertTrue(annotationSubject.isPresent());
173     DexAnnotationElement[] annotationElement = annotationSubject.getAnnotation().elements;
174     Assert.assertNotNull(annotationElement);
175     Assert.assertTrue(annotationElement.length == 1);
176     Assert.assertEquals("value", annotationElement[0].name.toString());
177     Assert.assertTrue(annotationElement[0].value instanceof DexValueString);
178     Assert.assertEquals(
179         sourceDebugExtensionReader.debugSourceExtension,
180         ((DexValueString) annotationElement[0].value).value.toSourceString());
181   }
182 
getGeneratedProguardMap()183   private String getGeneratedProguardMap() throws IOException {
184     Path mapFile = Paths.get(tmpOutputDir.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME);
185     if (Files.exists(mapFile)) {
186       return mapFile.toAbsolutePath().toString();
187     }
188     return null;
189   }
190 }
191