Merge remote-tracking branch 'aosp/upstream-master'
* aosp/upstream-master:
Add ServiceProcessor to MainProcessor
Bug: 318750023
Test: m javac-check
Change-Id: Ie073f84085384b0cbfece22ad857bfe8eaa4e96a
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index d38d89d..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-build
-target
-dist
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..3608320
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,80 @@
+// Copyright (C) 2014 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 {
+ default_applicable_licenses: ["external_jarjar_license"],
+}
+
+// Added automatically by a large-scale-change
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+ name: "external_jarjar_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "COPYING",
+ "LICENSE.txt",
+ "NOTICE",
+ ],
+}
+
+java_library_host {
+ name: "jarjar",
+ manifest: "manifest.txt",
+
+ srcs: [
+ "src/android/**/*.java",
+ "src/main/**/*.java",
+ ],
+ java_resource_dirs: ["res"],
+
+ static_libs: [
+ "ow2-asm",
+ "ow2-asm-commons",
+ ],
+
+ libs: [
+ "jarjar-maven-plugin-api",
+ "jarjar-apache-ant",
+ ],
+}
+
+java_test_host {
+ name: "jarjar-tests",
+ srcs: ["src/test/**/*.java"],
+ static_libs: [
+ "jarjar",
+ "junit",
+ ],
+ java_resource_dirs: ["src/test"],
+}
+
+//#################################################
+
+java_import_host {
+ name: "jarjar-maven-plugin-api",
+ jars: ["lib/maven-plugin-api.jar"],
+}
+
+java_import_host {
+ name: "jarjar-apache-ant",
+ jars: ["lib/apache-ant-1.9.4.jar"],
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index c980350..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# How to Contribute
-
-We'd love to accept your patches and contributions to this project. There are
-just a few small guidelines you need to follow.
-
-## Contributor License Agreement
-
-Contributions to this project must be accompanied by a Contributor License
-Agreement. You (or your employer) retain the copyright to your contribution;
-this simply gives us permission to use and redistribute your contributions as
-part of the project. Head over to <https://cla.developers.google.com/> to see
-your current agreements on file or to sign a new one.
-
-You generally only need to submit a CLA once, so if you've already submitted one
-(even if it was for a different project), you probably don't need to do it
-again.
-
-## Code reviews
-
-All submissions, including submissions by project members, require review. We
-use GitHub pull requests for this purpose. Consult
-[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
-information on using pull requests.
diff --git a/LICENSE b/COPYING
similarity index 100%
copy from LICENSE
copy to COPYING
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..85de3d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE.txt
\ No newline at end of file
diff --git a/LICENSE b/LICENSE.txt
similarity index 100%
copy from LICENSE
copy to LICENSE.txt
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..7f221e5
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,14 @@
+name: "jarjar"
+description:
+ "Utility that makes it easy to repackage Java libraries and embed them into "
+ "your own distribution"
+
+third_party {
+ url {
+ type: GIT
+ value: "https://github.com/google/jarjar.git"
+ }
+ version: "8ce1d0abd195c24a75d9ddcb0ea31b1d1b62aaa6"
+ last_upgrade_date { year: 2024 month: 1 day: 12 }
+ license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/LICENSE b/NOTICE
similarity index 100%
rename from LICENSE
rename to NOTICE
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..87a5dbe
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/libcore:/OWNERS
diff --git a/README.md b/README.md
deleted file mode 100644
index bd59829..0000000
--- a/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-This is not an officially supported Google product.
-
-*Embedding Java libraries since 2004*
-
-**Jar Jar Links** is a utility that makes it easy to repackage Java libraries
-and embed them into your own distribution. This is useful for two reasons:
-
-* You can easily ship a single jar file with no external dependencies.
-
-* You can avoid problems where your library depends on a specific version of a
- library, which may conflict with the dependencies of another library.
-
-## How does it work?
-
-Jar Jar Links includes an Ant task that extends the built-in jar task. The
-normal `zipfileset` element is used to embed jar files. A new rule element is
-added which uses wildcards patterns to rename the embedded class files. Bytecode
-transformation (via ASM) is used to change references to the renamed classes,
-and special handling is provided for moving resource files and transforming
-string literals.
diff --git a/README.version b/README.version
new file mode 100644
index 0000000..9a1d0c4
--- /dev/null
+++ b/README.version
@@ -0,0 +1,3 @@
+URL: https://code.google.com/p/jarjar/downloads/detail?name=jarjar-src-1.4.zip&can=2&q=
+Version: 1.4
+BugComponent: 33621
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..1a061f2
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+apply plugin: 'java'
+
+sourceSets {
+ main {
+ java.srcDirs = ['src/main']
+ resources.srcDirs = ['res']
+ }
+}
+jar.manifest.from file('manifest.txt')
+
+dependencies {
+ compile files('lib/apache-ant-1.9.4.jar')
+ compile files('lib/asm-4.0.jar')
+ compile files('lib/asm-commons-4.0.jar')
+ compile files('lib/maven-plugin-api.jar')
+}
diff --git a/lib/apache-ant-1.9.4.jar b/lib/apache-ant-1.9.4.jar
new file mode 100644
index 0000000..24641e7
--- /dev/null
+++ b/lib/apache-ant-1.9.4.jar
Binary files differ
diff --git a/lib/junit-4.8.1.jar b/lib/junit-4.8.1.jar
deleted file mode 100644
index f28b4ef..0000000
--- a/lib/junit-4.8.1.jar
+++ /dev/null
Binary files differ
diff --git a/manifest.txt b/manifest.txt
new file mode 100644
index 0000000..63c68e0
--- /dev/null
+++ b/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.tonicsystems.jarjar.Main
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 72bf3f1..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.sonatype.oss</groupId>
- <artifactId>oss-parent</artifactId>
- <version>7</version>
- </parent>
-
- <groupId>com.googlecode.jarjar</groupId>
- <artifactId>jarjar</artifactId>
- <packaging>jar</packaging>
- <version>1.3</version>
- <name>Jar Jar Links</name>
- <description>Jar Jar Links is a utility that makes it easy to repackage Java libraries and embed them into your own distribution.</description>
- <url>http://jarjar.googlecode.com/</url>
- <licenses>
- <license>
- <name>Apache License version 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
- <scm>
- <connection>scm:svn:http://jarjar.googlecode.com/svn/trunk/</connection>
- <url>http://code.google.com/p/jarjar/source/checkout</url>
- </scm>
- <issueManagement>
- <system>Google Code</system>
- <url>http://code.google.com/p/jarjar/issues</url>
- </issueManagement>
- <dependencies></dependencies>
- <developers>
- <developer>
- <id>cnokleberg</id>
- <name>Chris Nokleberg</name>
- </developer>
- <developer>
- <id>aglenning</id>
- <name>A. Glenning</name>
- </developer>
- </developers>
-</project>
diff --git a/src/main/com/tonicsystems/jarjar/help.txt b/res/com/tonicsystems/jarjar/help.txt
similarity index 92%
rename from src/main/com/tonicsystems/jarjar/help.txt
rename to res/com/tonicsystems/jarjar/help.txt
index 8410909..6f14db6 100644
--- a/src/main/com/tonicsystems/jarjar/help.txt
+++ b/res/com/tonicsystems/jarjar/help.txt
@@ -49,6 +49,7 @@
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 @@
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/Jarjar.iml b/src/Jarjar.iml
new file mode 100644
index 0000000..70f1faf
--- /dev/null
+++ b/src/Jarjar.iml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/main" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="lib" level="project" />
+ <orderEntry type="module-library">
+ <library>
+ <CLASSES>
+ <root url="jar://$MAVEN_REPOSITORY$/org/apache/ant/ant/1.7.1/ant-1.7.1.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ </component>
+</module>
+
diff --git a/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java b/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java
new file mode 100644
index 0000000..04942b1
--- /dev/null
+++ b/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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 java.util.Set;
+import java.util.function.Supplier;
+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 org.objectweb.asm.commons.Remapper;
+
+/** A transformer that removes annotations from repackaged classes. */
+public final class RemoveAndroidCompatAnnotationsJarTransformer extends JarTransformer {
+
+ private static int ASM_VERSION = Opcodes.ASM9;
+
+ private static final Set<String> REMOVE_ANNOTATIONS =
+ Set.of("Landroid/compat/annotation/UnsupportedAppUsage;");
+
+ private final Remapper remapper;
+
+ public RemoveAndroidCompatAnnotationsJarTransformer(Remapper remapper) {
+ this.remapper = remapper;
+ }
+
+ protected ClassVisitor transform(ClassVisitor classVisitor) {
+ return new AnnotationRemover(classVisitor);
+ }
+
+ private class AnnotationRemover extends ClassVisitor {
+
+ private boolean isClassRemapped;
+
+ AnnotationRemover(ClassVisitor cv) {
+ super(ASM_VERSION, cv);
+ }
+
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ String newName = remapper.map(name);
+ // On every new class header visit, remember whether the class is repackaged.
+ isClassRemapped = newName != null && !newName.equals(name);
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @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));
+ }
+ };
+ }
+
+ /**
+ * Create an {@link AnnotationVisitor} that removes any annotations from {@link
+ * #REMOVE_ANNOTATIONS} if the class is being repackaged.
+ *
+ * <p>For the annotations to be dropped correctly, do not visit the annotation beforehand,
+ * provide a supplier instead.
+ */
+ private AnnotationVisitor visitAnnotationCommon(
+ String annotation, Supplier<AnnotationVisitor> defaultVisitorSupplier) {
+ if (isClassRemapped && REMOVE_ANNOTATIONS.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/android/com/android/jarjar/StripAnnotation.java b/src/android/com/android/jarjar/StripAnnotation.java
new file mode 100644
index 0000000..60fc965
--- /dev/null
+++ b/src/android/com/android/jarjar/StripAnnotation.java
@@ -0,0 +1,22 @@
+/*
+ * 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..aa71715
--- /dev/null
+++ b/src/android/com/android/jarjar/StripAnnotationsJarTransformer.java
@@ -0,0 +1,114 @@
+/*
+ * 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 java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+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;
+
+/** A transformer that strips annotations from all classes based on custom rules. */
+public final class StripAnnotationsJarTransformer extends JarTransformer {
+
+ private static int ASM_VERSION = Opcodes.ASM9;
+
+ 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/Main.java b/src/main/com/tonicsystems/jarjar/Main.java
index afa9a1e..aa78836 100644
--- a/src/main/com/tonicsystems/jarjar/Main.java
+++ b/src/main/com/tonicsystems/jarjar/Main.java
@@ -53,7 +53,6 @@
return sb.toString();
}
-
public static void main(String[] args) throws Exception {
MainUtil.runMain(new Main(), args, "help");
}
@@ -99,7 +98,11 @@
List<PatternElement> rules = RulesFileParser.parse(rulesFile);
boolean verbose = Boolean.getBoolean("verbose");
boolean skipManifest = Boolean.getBoolean("skipManifest");
- MainProcessor proc = new MainProcessor(rules, verbose, skipManifest);
+ // ANDROID-BEGIN: b/146418363 Add an Android-specific transformer to strip compat annotation
+ boolean removeAndroidCompatAnnotations = Boolean.getBoolean("removeAndroidCompatAnnotations");
+ MainProcessor proc =
+ new MainProcessor(rules, verbose, skipManifest, removeAndroidCompatAnnotations);
+ // ANDROID-END: b/146418363 Add an Android-specific transformer to strip compat annotation
StandaloneJarProcessor.run(inJar, outJar, proc);
proc.strip(outJar);
}
diff --git a/src/main/com/tonicsystems/jarjar/MainProcessor.java b/src/main/com/tonicsystems/jarjar/MainProcessor.java
index 2e2a7be..5839719 100644
--- a/src/main/com/tonicsystems/jarjar/MainProcessor.java
+++ b/src/main/com/tonicsystems/jarjar/MainProcessor.java
@@ -16,6 +16,9 @@
package com.tonicsystems.jarjar;
+import com.android.jarjar.RemoveAndroidCompatAnnotationsJarTransformer;
+import com.android.jarjar.StripAnnotation;
+import com.android.jarjar.StripAnnotationsJarTransformer;
import com.tonicsystems.jarjar.util.EntryStruct;
import com.tonicsystems.jarjar.util.JarProcessor;
import com.tonicsystems.jarjar.util.JarProcessorChain;
@@ -37,11 +40,24 @@
private final KeepProcessor kp;
private final Map<String, String> renames = new HashMap<>();
+ // ANDROID-BEGIN: b/146418363 Add an Android-specific transformer to strip compat annotation
public MainProcessor(List<PatternElement> patterns, boolean verbose, boolean skipManifest) {
+ this(patterns, verbose, skipManifest, false /* removeAndroidCompatAnnotations */);
+ }
+
+ public MainProcessor(
+ List<PatternElement> patterns,
+ boolean verbose,
+ boolean skipManifest,
+ boolean removeAndroidCompatAnnotations) {
+ // ANDROID-END: b/146418363 Add an Android-specific transformer to strip compat annotation
this.verbose = verbose;
List<Zap> zapList = new ArrayList<>();
List<Rule> ruleList = new ArrayList<>();
List<Keep> keepList = new ArrayList<>();
+ // 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 +65,11 @@
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);
@@ -63,6 +83,15 @@
processors.add(kp);
}
processors.add(new ZapProcessor(zapList));
+ // ANDROID-BEGIN: b/146418363 Add an Android-specific transformer to strip compat annotation
+ 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)}));
diff --git a/src/main/com/tonicsystems/jarjar/PackageRemapper.java b/src/main/com/tonicsystems/jarjar/PackageRemapper.java
index 22f4dce..7533082 100644
--- a/src/main/com/tonicsystems/jarjar/PackageRemapper.java
+++ b/src/main/com/tonicsystems/jarjar/PackageRemapper.java
@@ -28,7 +28,7 @@
private static final Pattern ARRAY_FOR_NAME_PATTERN =
Pattern.compile("\\[L[\\p{javaJavaIdentifierPart}\\.]+?;");
- private final List<Wildcard> wildcards;
+ private final WildcardTrie wildcards;
private final Map<String, String> typeCache = new HashMap<>();
private final Map<String, String> pathCache = new HashMap<>();
private final Map<Object, String> valueCache = new HashMap<>();
@@ -36,7 +36,7 @@
public PackageRemapper(List<Rule> ruleList, boolean verbose) {
this.verbose = verbose;
- wildcards = PatternElement.createWildcards(ruleList);
+ wildcards = new WildcardTrie(PatternElement.createWildcards(ruleList));
}
// also used by KeepProcessor
@@ -128,7 +128,7 @@
}
private String replaceHelper(String value) {
- for (Wildcard wildcard : wildcards) {
+ for (Wildcard wildcard : wildcards.getPossibleMatches(value)) {
String test = wildcard.replace(value);
if (test != null) {
return test;
diff --git a/src/main/com/tonicsystems/jarjar/PatternElement.java b/src/main/com/tonicsystems/jarjar/PatternElement.java
index eeae88f..399928d 100644
--- a/src/main/com/tonicsystems/jarjar/PatternElement.java
+++ b/src/main/com/tonicsystems/jarjar/PatternElement.java
@@ -32,13 +32,15 @@
static List<Wildcard> createWildcards(List<? extends PatternElement> patterns) {
List<Wildcard> wildcards = new ArrayList<>();
+ int ruleIndex = 0;
for (PatternElement pattern : patterns) {
String result = (pattern instanceof Rule) ? ((Rule) pattern).getResult() : "";
String expr = pattern.getPattern();
if (expr.indexOf('/') >= 0) {
throw new IllegalArgumentException("Patterns cannot contain slashes");
}
- wildcards.add(new Wildcard(expr.replace('.', '/'), result));
+ wildcards.add(new Wildcard(expr.replace('.', '/'), result, ruleIndex));
+ ruleIndex++;
}
return wildcards;
}
diff --git a/src/main/com/tonicsystems/jarjar/RulesFileParser.java b/src/main/com/tonicsystems/jarjar/RulesFileParser.java
index 8364aaf..4297ee7 100644
--- a/src/main/com/tonicsystems/jarjar/RulesFileParser.java
+++ b/src/main/com/tonicsystems/jarjar/RulesFileParser.java
@@ -18,6 +18,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.android.jarjar.StripAnnotation;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@@ -70,6 +71,11 @@
case "keep":
element = new Keep();
break;
+ // ANDROID-BEGIN: b/222743634 Strip annotations from system module stubs
+ case "strip-annotation":
+ element = new StripAnnotation();
+ break;
+ // ANDROID-END: b/222743634 Strip annotations from system module stubs
default:
error(c, parts);
}
diff --git a/src/main/com/tonicsystems/jarjar/Wildcard.java b/src/main/com/tonicsystems/jarjar/Wildcard.java
index d32e2a4..c5d0c20 100644
--- a/src/main/com/tonicsystems/jarjar/Wildcard.java
+++ b/src/main/com/tonicsystems/jarjar/Wildcard.java
@@ -25,14 +25,19 @@
private static final Pattern DSTAR = Pattern.compile("\\*\\*");
private static final Pattern STAR = Pattern.compile("\\*");
private static final Pattern ESTAR = Pattern.compile("\\+\\??\\)\\Z");
+ private static final Pattern DOLLAR = Pattern.compile("\\$");
+ // Apart from stars and dollar signs, wildcards are plain-text full matches
+ private static final Pattern PLAIN_TEXT_PREFIX_PATTERN = Pattern.compile("^[^*$]*");
private final Pattern pattern;
+ private final String plainTextPrefix;
+ private final int ruleIndex;
private final int count;
private final ArrayList<Object> parts = new ArrayList<>(16); // kept for debugging
private final String[] strings;
private final int[] refs;
- public Wildcard(String pattern, String result) {
+ public Wildcard(String pattern, String result, int ruleIndex) {
if (pattern.equals("**")) {
throw new IllegalArgumentException("'**' is not a valid pattern");
}
@@ -47,6 +52,14 @@
regex = replaceAllLiteral(DSTAR, regex, "(.+?)");
regex = replaceAllLiteral(STAR, regex, "([^/]+)");
regex = replaceAllLiteral(ESTAR, regex, "*)");
+ regex = replaceAllLiteral(DOLLAR, regex, "\\$");
+ Matcher prefixMatcher = PLAIN_TEXT_PREFIX_PATTERN.matcher(pattern);
+ // prefixMatcher will always match, but may match an empty string
+ if (!prefixMatcher.find()) {
+ throw new IllegalArgumentException(PLAIN_TEXT_PREFIX_PATTERN + " not found in " + pattern);
+ }
+ this.plainTextPrefix = prefixMatcher.group();
+ this.ruleIndex = ruleIndex;
this.pattern = Pattern.compile("\\A" + regex + "\\Z");
this.count = this.pattern.matcher("foo").groupCount();
@@ -107,6 +120,14 @@
// System.err.println(this);
}
+ public String getPlainTextPrefix() {
+ return plainTextPrefix;
+ }
+
+ public int getRuleIndex() {
+ return ruleIndex;
+ }
+
public boolean matches(String value) {
return getMatcher(value) != null;
}
@@ -138,6 +159,10 @@
if (expr.endsWith("package-info")) {
expr = expr.substring(0, expr.length() - "package-info".length());
}
+ // Android-changed: also include module-info
+ if (expr.endsWith("module-info")) {
+ expr = expr.substring(0, expr.length() - "module-info".length());
+ }
for (int i = 0, len = expr.length(); i < len; i++) {
char c = expr.charAt(i);
if (extra.indexOf(c) >= 0) {
diff --git a/src/main/com/tonicsystems/jarjar/WildcardTrie.java b/src/main/com/tonicsystems/jarjar/WildcardTrie.java
new file mode 100644
index 0000000..c1e9b2e
--- /dev/null
+++ b/src/main/com/tonicsystems/jarjar/WildcardTrie.java
@@ -0,0 +1,93 @@
+/*
+ * 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.tonicsystems.jarjar;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ * A prefix trie of {@link Wildcard}, where the prefix is obtained from {@link
+ * Wildcard#getPlainTextPrefix()}.
+ *
+ * <p>This allows quick lookup of applicable wildcards in the common case where wildcards have a
+ * non-empty plain-text prefix.
+ */
+public class WildcardTrie {
+ private final TreeMap<String, WildcardTrie> subTries = new TreeMap<>();
+ private final List<Wildcard> wildcards = new ArrayList<>();
+ private final String prefix;
+
+ public WildcardTrie(List<Wildcard> wildcards) {
+ this("");
+ final ArrayList<Wildcard> lst = new ArrayList<>(wildcards);
+ // Sort values to ensure that wildcards that prefix others are added first
+ lst.sort(Comparator.comparing(Wildcard::getPlainTextPrefix));
+ for (Wildcard w : lst) {
+ final String prefix = w.getPlainTextPrefix();
+ final WildcardTrie prefixTrie = findSubTrieWhichPrefixes(prefix, this);
+ if (prefixTrie.prefix.equals(prefix)) {
+ prefixTrie.wildcards.add(w);
+ } else {
+ final WildcardTrie newTrie = new WildcardTrie(prefix);
+ newTrie.wildcards.add(w);
+ prefixTrie.subTries.put(prefix, newTrie);
+ }
+ }
+ }
+
+ private WildcardTrie(String prefix) {
+ this.prefix = prefix;
+ }
+
+ private static WildcardTrie findSubTrieWhichPrefixes(String value, WildcardTrie baseTrie) {
+ final String possiblePrefix = baseTrie.subTries.floorKey(value);
+ // Because each level of the trie does not contain keys that are prefixes of each other,
+ // there can be at most one prefix of the value at that level, and that prefix will be the
+ // highest key ordered before the value (any non-prefix key would have a character
+ // difference with the prefix and so be ordered before the prefix or after the value).
+ if (possiblePrefix != null && value.startsWith(possiblePrefix)) {
+ return findSubTrieWhichPrefixes(value, baseTrie.subTries.get(possiblePrefix));
+ }
+ return baseTrie;
+ }
+
+ public List<Wildcard> getPossibleMatches(String value) {
+ WildcardTrie baseTrie = this;
+ List<Wildcard> prefixMatches =
+ wildcards.isEmpty()
+ // If there's no match, don't even allocate a list and use the singleton emptyList
+ ? Collections.emptyList()
+ : new ArrayList<>(wildcards);
+ while (true) {
+ final String possiblePrefix = baseTrie.subTries.floorKey(value);
+ if (possiblePrefix != null && value.startsWith(possiblePrefix)) {
+ baseTrie = baseTrie.subTries.get(possiblePrefix);
+ if (prefixMatches.isEmpty()) {
+ prefixMatches = new ArrayList<>(baseTrie.wildcards);
+ } else {
+ prefixMatches.addAll(baseTrie.wildcards);
+ }
+ } else {
+ prefixMatches.sort(Comparator.comparing(Wildcard::getRuleIndex));
+ return prefixMatches;
+ }
+ }
+ }
+}
diff --git a/src/main/com/tonicsystems/jarjar/util/JarTransformer.java b/src/main/com/tonicsystems/jarjar/util/JarTransformer.java
index d109083..aced2ad 100644
--- a/src/main/com/tonicsystems/jarjar/util/JarTransformer.java
+++ b/src/main/com/tonicsystems/jarjar/util/JarTransformer.java
@@ -24,12 +24,14 @@
public abstract class JarTransformer implements JarProcessor {
@Override
public boolean process(EntryStruct struct) throws IOException {
- if (struct.isClass()) {
+ if (struct.isClass() && !struct.name.startsWith("META-INF")) {
ClassReader reader;
try {
reader = new ClassReader(struct.data);
} catch (RuntimeException e) {
- return true; // TODO?
+ // Android-changed: Made this failure fatal to highlight class version issues.
+ // http://b/27637680
+ throw new RuntimeException("Failed to load " + struct.name, e);
}
GetNameClassWriter w = new GetNameClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor visitor = transform(w);
diff --git a/src/test/com/tonicsystems/jarjar/WildcardTest.java b/src/test/com/tonicsystems/jarjar/WildcardTest.java
index a37ff95..d08d75c 100644
--- a/src/test/com/tonicsystems/jarjar/WildcardTest.java
+++ b/src/test/com/tonicsystems/jarjar/WildcardTest.java
@@ -33,10 +33,17 @@
wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/!", null);
wildcard("net/sf/cglib/*", "foo/@1", "net/sf/cglib/Bar", "foo/Bar");
wildcard("net/sf/cglib/*/*", "foo/@2/@1", "net/sf/cglib/Bar/Baz", "foo/Baz/Bar");
+ wildcard(
+ "**/package-info",
+ "bar/baz/@1/package-info",
+ "foo/package-info",
+ "bar/baz/foo/package-info");
+ wildcard(
+ "**/module-info", "bar/baz/@1/module-info", "foo/module-info", "bar/baz/foo/module-info");
}
private void wildcard(String pattern, String result, String value, String expect) {
- Wildcard wc = new Wildcard(pattern, result);
+ Wildcard wc = new Wildcard(pattern, result, 0);
// System.err.println(wc);
assertEquals(expect, wc.replace(value));
}