diff options
author | Sorin Basca <sorinbasca@google.com> | 2022-05-09 13:24:51 +0100 |
---|---|---|
committer | Sorin Basca <sorinbasca@google.com> | 2022-05-11 11:34:41 +0100 |
commit | 47f189c1e4728977bbecac5905c8bcc58c67c07e (patch) | |
tree | 81372424da9be7970fe78f49a7f39646d70b3400 | |
parent | 1348173cadf1bc3ba94d4f54a5b8fd9995b5154a (diff) | |
download | jarjar-47f189c1e4728977bbecac5905c8bcc58c67c07e.tar.gz |
Add option to strip annotations from jar
Bug: 222743634
Test: m
Change-Id: I095140c52959965ed28f82a9bb689aec191458d2
5 files changed, 171 insertions, 0 deletions
diff --git a/res/com/tonicsystems/jarjar/help.txt b/res/com/tonicsystems/jarjar/help.txt index 8410909..6f14db6 100644 --- a/res/com/tonicsystems/jarjar/help.txt +++ b/res/com/tonicsystems/jarjar/help.txt @@ -49,6 +49,7 @@ Rules file format: rule <pattern> <result> zap <pattern> keep <pattern> + strip-annotations <annotation> The standard rule ("rule") is used to rename classes. All references to the renamed classes will also be updated. If a class name is @@ -73,3 +74,6 @@ Rules file format: via dependency analysis are discarded when writing the output jar. This is the last step in the process, after renaming and zapping. + The "strip-annotations" rule will remove all the references to a certain + annotation from a jar file. As no pattern matching is performed, the + annotations have to be provided as one per line. diff --git a/src/android/com/android/jarjar/StripAnnotation.java b/src/android/com/android/jarjar/StripAnnotation.java new file mode 100644 index 0000000..debd469 --- /dev/null +++ b/src/android/com/android/jarjar/StripAnnotation.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.jarjar; + +import com.tonicsystems.jarjar.PatternElement; + +/** + * Configuration element for stripping annotations in a jar file. + */ +public class StripAnnotation extends PatternElement +{ +} diff --git a/src/android/com/android/jarjar/StripAnnotationsJarTransformer.java b/src/android/com/android/jarjar/StripAnnotationsJarTransformer.java new file mode 100644 index 0000000..260cb30 --- /dev/null +++ b/src/android/com/android/jarjar/StripAnnotationsJarTransformer.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.jarjar; + +import com.tonicsystems.jarjar.util.JarTransformer; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * A transformer that strips annotations from all classes based on custom rules. + */ +public final class StripAnnotationsJarTransformer extends JarTransformer { + + private static int ASM_VERSION = Opcodes.ASM7; + + private final List<String> stripAnnotationList; + + public StripAnnotationsJarTransformer(List<StripAnnotation> stripAnnotationList) { + this.stripAnnotationList = getAnnotationList(stripAnnotationList); + } + + private static List<String> getAnnotationList(List<StripAnnotation> stripAnnotationList) { + return stripAnnotationList.stream().map(el -> getClassName(el)).collect(Collectors.toList()); + } + + private static String getClassName(StripAnnotation element) { + return "L" + element.getPattern().replace('.', '/') + ";"; + } + + @Override + protected ClassVisitor transform(ClassVisitor classVisitor) { + return new AnnotationRemover(classVisitor); + } + + private class AnnotationRemover extends ClassVisitor { + + AnnotationRemover(ClassVisitor cv) { + super(ASM_VERSION, cv); + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + return visitAnnotationCommon(descriptor, + () -> super.visitAnnotation(descriptor, visible)); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, + Object value) { + FieldVisitor superVisitor = + super.visitField(access, name, descriptor, signature, value); + return new FieldVisitor(ASM_VERSION, superVisitor) { + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + return visitAnnotationCommon(descriptor, + () -> super.visitAnnotation(descriptor, visible)); + + } + }; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + MethodVisitor superVisitor = + super.visitMethod(access, name, descriptor, signature, exceptions); + return new MethodVisitor(ASM_VERSION, superVisitor) { + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + return visitAnnotationCommon(descriptor, + () -> super.visitAnnotation(descriptor, visible)); + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, + String descriptor, boolean visible) { + return visitAnnotationCommon(descriptor, + () -> super.visitParameterAnnotation(parameter, descriptor, visible)); + } + }; + } + + /** + * Create an {@link AnnotationVisitor} that removes any annotations from {@link + * #stripAnnotationList}. + */ + private AnnotationVisitor visitAnnotationCommon(String annotation, + Supplier<AnnotationVisitor> defaultVisitorSupplier) { + if (stripAnnotationList.contains(annotation)) { + return null; + } + // Only get() the default AnnotationVisitor if the annotation is to be included. + // Invoking super.visitAnnotation(descriptor, visible) causes the annotation to be + // included in the output even if the resulting AnnotationVisitor is not returned or + // used. + return defaultVisitorSupplier.get(); + } + } +} diff --git a/src/main/com/tonicsystems/jarjar/MainProcessor.java b/src/main/com/tonicsystems/jarjar/MainProcessor.java index 2778cc5..a0ef4c7 100644 --- a/src/main/com/tonicsystems/jarjar/MainProcessor.java +++ b/src/main/com/tonicsystems/jarjar/MainProcessor.java @@ -16,12 +16,14 @@ package com.tonicsystems.jarjar; +import com.android.jarjar.StripAnnotation; import com.tonicsystems.jarjar.util.*; import java.io.File; import java.io.IOException; import java.util.*; import com.android.jarjar.RemoveAndroidCompatAnnotationsJarTransformer; +import com.android.jarjar.StripAnnotationsJarTransformer; class MainProcessor implements JarProcessor { @@ -42,6 +44,9 @@ class MainProcessor implements JarProcessor List<Zap> zapList = new ArrayList<Zap>(); List<Rule> ruleList = new ArrayList<Rule>(); List<Keep> keepList = new ArrayList<Keep>(); + // ANDROID-BEGIN: b/222743634 Strip annotations from system module stubs + List<StripAnnotation> stripAnnotationList = new ArrayList<StripAnnotation>(); + // ANDROID-END: b/222743634 Strip annotations from system module stubs for (PatternElement pattern : patterns) { if (pattern instanceof Zap) { zapList.add((Zap) pattern); @@ -49,7 +54,11 @@ class MainProcessor implements JarProcessor ruleList.add((Rule) pattern); } else if (pattern instanceof Keep) { keepList.add((Keep) pattern); + // ANDROID-BEGIN: b/222743634 Strip annotations from system module stubs + } else if (pattern instanceof StripAnnotation) { + stripAnnotationList.add((StripAnnotation) pattern); } + // ANDROID-END: b/222743634 Strip annotations from system module stubs } PackageRemapper pr = new PackageRemapper(ruleList, verbose); @@ -65,6 +74,11 @@ class MainProcessor implements JarProcessor if (removeAndroidCompatAnnotations) processors.add(new RemoveAndroidCompatAnnotationsJarTransformer(pr)); // ANDROID-END: b/146418363 Add an Android-specific transformer to strip compat annotation + // ANDROID-BEGIN: b/222743634 Strip annotations from system module stubs + if (!stripAnnotationList.isEmpty()) { + processors.add(new StripAnnotationsJarTransformer(stripAnnotationList)); + } + // ANDROID-END: b/222743634 Strip annotations from system module stubs processors.add(new JarTransformerChain(new RemappingClassTransformer[]{ new RemappingClassTransformer(pr) })); processors.add(new ResourceProcessor(pr)); chain = new JarProcessorChain(processors.toArray(new JarProcessor[processors.size()])); diff --git a/src/main/com/tonicsystems/jarjar/RulesFileParser.java b/src/main/com/tonicsystems/jarjar/RulesFileParser.java index f54f3b9..c8c6ea4 100644 --- a/src/main/com/tonicsystems/jarjar/RulesFileParser.java +++ b/src/main/com/tonicsystems/jarjar/RulesFileParser.java @@ -16,6 +16,8 @@ package com.tonicsystems.jarjar; +import com.android.jarjar.StripAnnotation; + import java.io.*; import java.util.*; @@ -62,6 +64,10 @@ class RulesFileParser element = new Zap(); } else if (type.equals("keep")) { element = new Keep(); + // ANDROID-BEGIN: b/222743634 Strip annotations from system module stubs + } else if (type.equals("strip-annotation")) { + element = new StripAnnotation(); + // ANDROID-END: b/222743634 Strip annotations from system module stubs } else { error(c, parts); } |