diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-05-10 15:36:11 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-05-10 15:36:11 +0000 |
commit | 4ccc6f05da97f86b323b1c504bd27a977b9b7c81 (patch) | |
tree | d79fd260420ca7f26993cadb057551a2bdd75fcf | |
parent | 6d4628f66f86b6e6c8e0f8c078aa7851cc9264ef (diff) | |
parent | dd8c2d21015d8ebc6e9f515c348a811a8b7364dd (diff) | |
download | jarjar-busytown-mac-infra-release.tar.gz |
Snap for 11819167 from dd8c2d21015d8ebc6e9f515c348a811a8b7364dd to busytown-mac-infra-releasebusytown-mac-infra-release
Change-Id: I1d405ac9edbe8344be5c7c70147950ada5635270
59 files changed, 2463 insertions, 2025 deletions
diff --git a/.allstar/binary_artifacts.yaml b/.allstar/binary_artifacts.yaml new file mode 100644 index 0000000..c8a822f --- /dev/null +++ b/.allstar/binary_artifacts.yaml @@ -0,0 +1,4 @@ +# Exemption reason: The ant build relies on dependencies in lib/ and predates allstar +# Exemption timeframe: permanent +optConfig: + optOut: true @@ -57,6 +57,16 @@ java_library_host { ], } +java_test_host { + name: "jarjar-tests", + srcs: ["src/test/**/*.java"], + static_libs: [ + "jarjar", + "junit", + ], + java_resource_dirs: ["src/test"], +} + //################################################# java_import_host { @@ -8,7 +8,7 @@ third_party { type: GIT value: "https://github.com/google/jarjar.git" } - version: "1.4" - last_upgrade_date { year: 2014 month: 6 day: 17 } + version: "8ce1d0abd195c24a75d9ddcb0ea31b1d1b62aaa6" + last_upgrade_date { year: 2024 month: 1 day: 12 } license_type: NOTICE } @@ -5,8 +5,8 @@ <property name="javadoc.access" value="public"/> - <property name="compile.source" value="1.5"/> - <property name="compile.target" value="1.5"/> + <property name="compile.source" value="1.6"/> + <property name="compile.target" value="1.6"/> <property name="compile.bootclasspath" value=""/> <property name="compile.extdirs" value=""/> @@ -95,9 +95,9 @@ <mkdir dir="dist"/> <jarjar jarfile="${jarfile}"> <fileset dir="build/main"/> - <zipfileset src="lib/asm-4.0.jar"/> - <zipfileset src="lib/asm-commons-4.0.jar"> - <include name="org/objectweb/asm/commons/Remap*.class"/> + <zipfileset src="lib/asm-7.3.1.jar"/> + <zipfileset src="lib/asm-commons-7.3.1.jar"> + <include name="org/objectweb/asm/commons/*Remapper.class"/> <include name="org/objectweb/asm/commons/LocalVariablesSorter.class"/> </zipfileset> <keep pattern="com.tonicsystems.jarjar.Main"/> @@ -208,8 +208,8 @@ <delete file="${test.jar}"/> <jarjar2 jarfile="${test.jar}"> <fileset dir="build/main"/> - <zipfileset src="lib/asm-4.0.jar"/> - <zipfileset src="lib/asm-commons-4.0.jar"/> + <zipfileset src="lib/asm-7.3.1.jar"/> + <zipfileset src="lib/asm-commons-7.3.1.jar"/> <rule pattern="org.objectweb.asm.**" result="com.tonicsystems.jarjar.asm.@1"/> </jarjar2> <delete file="${test.jar}"/> diff --git a/lib/asm-4.0.jar b/lib/asm-4.0.jar Binary files differdeleted file mode 100644 index cca0d9c..0000000 --- a/lib/asm-4.0.jar +++ /dev/null diff --git a/lib/asm-7.3.1.jar b/lib/asm-7.3.1.jar Binary files differnew file mode 100644 index 0000000..8a50266 --- /dev/null +++ b/lib/asm-7.3.1.jar diff --git a/lib/asm-commons-4.0.jar b/lib/asm-commons-4.0.jar Binary files differdeleted file mode 100644 index 169400e..0000000 --- a/lib/asm-commons-4.0.jar +++ /dev/null diff --git a/lib/asm-commons-7.3.1.jar b/lib/asm-commons-7.3.1.jar Binary files differnew file mode 100644 index 0000000..65fb30e --- /dev/null +++ b/lib/asm-commons-7.3.1.jar diff --git a/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java b/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java index 052e358..04942b1 100644 --- a/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java +++ b/src/android/com/android/jarjar/RemoveAndroidCompatAnnotationsJarTransformer.java @@ -17,7 +17,8 @@ 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; @@ -25,98 +26,95 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.Remapper; -import java.util.Set; -import java.util.function.Supplier; - -/** - * A transformer that removes annotations from repackaged classes. - */ +/** A transformer that removes annotations from repackaged classes. */ public final class RemoveAndroidCompatAnnotationsJarTransformer extends JarTransformer { - private static int ASM_VERSION = Opcodes.ASM9; + private static int ASM_VERSION = Opcodes.ASM9; - private static final Set<String> REMOVE_ANNOTATIONS = Set.of( - "Landroid/compat/annotation/UnsupportedAppUsage;"); + private static final Set<String> REMOVE_ANNOTATIONS = + Set.of("Landroid/compat/annotation/UnsupportedAppUsage;"); - private final Remapper remapper; + private final Remapper remapper; - public RemoveAndroidCompatAnnotationsJarTransformer(Remapper remapper) { - this.remapper = remapper; - } + public RemoveAndroidCompatAnnotationsJarTransformer(Remapper remapper) { + this.remapper = remapper; + } - protected ClassVisitor transform(ClassVisitor classVisitor) { - return new AnnotationRemover(classVisitor); - } + protected ClassVisitor transform(ClassVisitor classVisitor) { + return new AnnotationRemover(classVisitor); + } - private class AnnotationRemover extends ClassVisitor { + private class AnnotationRemover extends ClassVisitor { - private boolean isClassRemapped; + private boolean isClassRemapped; - AnnotationRemover(ClassVisitor cv) { - super(ASM_VERSION, cv); - } + 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 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 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 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)); - - } - }; + 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 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)); - } - }; + 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(); - } + /** + * 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 index debd469..60fc965 100644 --- a/src/android/com/android/jarjar/StripAnnotation.java +++ b/src/android/com/android/jarjar/StripAnnotation.java @@ -18,9 +18,5 @@ package com.android.jarjar; import com.tonicsystems.jarjar.PatternElement; -/** - * Configuration element for stripping annotations in a jar file. - */ -public class StripAnnotation extends 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 index d547fac..aa71715 100644 --- a/src/android/com/android/jarjar/StripAnnotationsJarTransformer.java +++ b/src/android/com/android/jarjar/StripAnnotationsJarTransformer.java @@ -17,105 +17,98 @@ 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; -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. - */ +/** 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 static int ASM_VERSION = Opcodes.ASM9; - private final List<String> stripAnnotationList; + private final List<String> stripAnnotationList; - public StripAnnotationsJarTransformer(List<StripAnnotation> stripAnnotationList) { - this.stripAnnotationList = getAnnotationList(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 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('.', '/') + ";"; + } - 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 - protected ClassVisitor transform(ClassVisitor classVisitor) { - return new AnnotationRemover(classVisitor); + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + return visitAnnotationCommon(descriptor, () -> super.visitAnnotation(descriptor, visible)); } - private class AnnotationRemover extends ClassVisitor { - - AnnotationRemover(ClassVisitor cv) { - super(ASM_VERSION, cv); - } - + @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)); + 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 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)); - - } - }; + 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)); - } - }; + 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(); - } + /** + * 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/AbstractDepHandler.java b/src/main/com/tonicsystems/jarjar/AbstractDepHandler.java index 5a54737..5c40212 100644 --- a/src/main/com/tonicsystems/jarjar/AbstractDepHandler.java +++ b/src/main/com/tonicsystems/jarjar/AbstractDepHandler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,41 +16,45 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; -import java.io.*; -import java.util.*; - -abstract public class AbstractDepHandler implements DepHandler -{ - protected final int level; - private final Set<List<Object>> seenIt = new HashSet<List<Object>>(); - - protected AbstractDepHandler(int level) { - this.level = level; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public abstract class AbstractDepHandler implements DepHandler { + protected final int level; + private final Set<List<Object>> seenIt = new HashSet<>(); + + protected AbstractDepHandler(int level) { + this.level = level; + } + + @Override + public void handle(PathClass from, PathClass to) throws IOException { + List<Object> pair; + if (level == LEVEL_JAR) { + pair = createPair(from.getClassPath(), to.getClassPath()); + } else { + pair = createPair(from.getClassName(), to.getClassName()); } - - public void handle(PathClass from, PathClass to) throws IOException { - List<Object> pair; - if (level == LEVEL_JAR) { - pair = createPair(from.getClassPath(), to.getClassPath()); - } else { - pair = createPair(from.getClassName(), to.getClassName()); - } - if (!seenIt.contains(pair)) { - seenIt.add(pair); - handle(pair.get(0).toString(), pair.get(1).toString()); - } + if (seenIt.add(pair)) { + handle(pair.get(0).toString(), pair.get(1).toString()); } + } - abstract protected void handle(String from, String to) throws IOException; + protected abstract void handle(String from, String to) throws IOException; - public void handleStart() throws IOException { } - public void handleEnd() throws IOException { } + @Override + public void handleStart() throws IOException {} - private static List<Object> createPair(Object o1, Object o2) { - List<Object> list = new ArrayList<Object>(2); - list.add(o1); - list.add(o2); - return list; - } + @Override + public void handleEnd() throws IOException {} + + private static List<Object> createPair(Object o1, Object o2) { + List<Object> list = new ArrayList<>(2); + list.add(o1); + list.add(o2); + return list; + } } diff --git a/src/main/com/tonicsystems/jarjar/DepFind.java b/src/main/com/tonicsystems/jarjar/DepFind.java index 40f68ff..0d13c54 100644 --- a/src/main/com/tonicsystems/jarjar/DepFind.java +++ b/src/main/com/tonicsystems/jarjar/DepFind.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,65 +16,69 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; -import java.io.*; -import java.util.*; -import java.util.zip.ZipEntry; +import com.tonicsystems.jarjar.util.ClassHeaderReader; +import com.tonicsystems.jarjar.util.ClassPathEntry; +import com.tonicsystems.jarjar.util.ClassPathIterator; +import com.tonicsystems.jarjar.util.RuntimeIOException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -public class DepFind -{ - private File curDir = new File(System.getProperty("user.dir")); +public class DepFind { + private File curDir = new File(System.getProperty("user.dir")); - public void setCurrentDirectory(File curDir) { - this.curDir = curDir; - } + public void setCurrentDirectory(File curDir) { + this.curDir = curDir; + } - public void run(String from, String to, DepHandler handler) throws IOException { - try { - ClassHeaderReader header = new ClassHeaderReader(); - Map<String, String> classes = new HashMap<String, String>(); - ClassPathIterator cp = new ClassPathIterator(curDir, to, null); - try { - while (cp.hasNext()) { - ClassPathEntry entry = cp.next(); - InputStream in = entry.openStream(); - try { - header.read(in); - classes.put(header.getClassName(), entry.getSource()); - } catch (Exception e) { - System.err.println("Error reading " + entry.getName() + ": " + e.getMessage()); - } finally { - in.close(); - } - } - } finally { - cp.close(); - } + public void run(String from, String to, DepHandler handler) throws IOException { + try { + ClassHeaderReader header = new ClassHeaderReader(); + Map<String, String> classes = new HashMap<>(); + ClassPathIterator cp = new ClassPathIterator(curDir, to, null); + try { + while (cp.hasNext()) { + ClassPathEntry entry = cp.next(); + InputStream in = entry.openStream(); + try { + header.read(in); + classes.put(header.getClassName(), entry.getSource()); + } catch (Exception e) { + System.err.println("Error reading " + entry.getName() + ": " + e.getMessage()); + } finally { + in.close(); + } + } + } finally { + cp.close(); + } - handler.handleStart(); - cp = new ClassPathIterator(curDir, from, null); - try { - while (cp.hasNext()) { - ClassPathEntry entry = cp.next(); - InputStream in = entry.openStream(); - try { - new ClassReader(in).accept( - new DepFindVisitor(classes, entry.getSource(), handler), - ClassReader.SKIP_DEBUG); - } catch (Exception e) { - System.err.println("Error reading " + entry.getName() + ": " + e.getMessage()); - } finally { - in.close(); - } - } - } finally { - cp.close(); - } - handler.handleEnd(); - } catch (RuntimeIOException e) { - throw (IOException)e.getCause(); + handler.handleStart(); + cp = new ClassPathIterator(curDir, from, null); + try { + while (cp.hasNext()) { + ClassPathEntry entry = cp.next(); + InputStream in = entry.openStream(); + try { + new ClassReader(in) + .accept( + new DepFindVisitor(classes, entry.getSource(), handler), + ClassReader.SKIP_DEBUG); + } catch (Exception e) { + System.err.println("Error reading " + entry.getName() + ": " + e.getMessage()); + } finally { + in.close(); + } } + } finally { + cp.close(); + } + handler.handleEnd(); + } catch (RuntimeIOException e) { + throw (IOException) e.getCause(); } + } } diff --git a/src/main/com/tonicsystems/jarjar/DepFindVisitor.java b/src/main/com/tonicsystems/jarjar/DepFindVisitor.java index bad909e..4b817c9 100644 --- a/src/main/com/tonicsystems/jarjar/DepFindVisitor.java +++ b/src/main/com/tonicsystems/jarjar/DepFindVisitor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,56 +16,60 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; -import java.io.*; -import java.util.*; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import org.objectweb.asm.*; -import org.objectweb.asm.Type; -import org.objectweb.asm.commons.*; +import com.tonicsystems.jarjar.util.RuntimeIOException; +import java.io.IOException; +import java.util.Map; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.commons.Remapper; -class DepFindVisitor extends ClassRemapper -{ - public DepFindVisitor(Map<String, String> classes, String source, DepHandler handler) throws IOException { - super(null, new DepFindRemapper(classes, source, handler)); - } +class DepFindVisitor extends ClassRemapper { + public DepFindVisitor(Map<String, String> classes, String source, DepHandler handler) + throws IOException { + super(null, new DepFindRemapper(classes, source, handler)); + } - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - ((DepFindRemapper)remapper).setClassName(name); - super.visit(version, access, name, signature, superName, interfaces); - } + @Override + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + ((DepFindRemapper) remapper).setClassName(name); + super.visit(version, access, name, signature, superName, interfaces); + } - private static class DepFindRemapper extends Remapper - { - private final Map<String, String> classes; - private final String source; - private final DepHandler handler; - private PathClass curPathClass; + private static class DepFindRemapper extends Remapper { + private final Map<String, String> classes; + private final String source; + private final DepHandler handler; + private PathClass curPathClass; - public DepFindRemapper(Map<String, String> classes, String source, DepHandler handler) throws IOException { - this.classes = classes; - this.source = source; - this.handler = handler; - } + public DepFindRemapper(Map<String, String> classes, String source, DepHandler handler) { + this.classes = classes; + this.source = source; + this.handler = handler; + } - public void setClassName(String name) { - curPathClass = new PathClass(source, name); - } + public void setClassName(String name) { + curPathClass = new PathClass(source, name); + } - public String map(String key) { - try { - if (classes.containsKey(key)) { - String otherSource = classes.get(key); - if (!source.equals(otherSource)) { - // TODO: some escape mechanism? - handler.handle(curPathClass, new PathClass(otherSource, key)); - } - } - } catch (IOException e) { - throw new RuntimeIOException(e); - } - return null; + @Override + public String map(String key) { + try { + if (classes.containsKey(key)) { + String otherSource = classes.get(key); + if (!source.equals(otherSource)) { + // TODO: some escape mechanism? + handler.handle(curPathClass, new PathClass(otherSource, key)); + } } + } catch (IOException e) { + throw new RuntimeIOException(e); + } + return null; } + } } diff --git a/src/main/com/tonicsystems/jarjar/DepHandler.java b/src/main/com/tonicsystems/jarjar/DepHandler.java index 33107d4..4684730 100644 --- a/src/main/com/tonicsystems/jarjar/DepHandler.java +++ b/src/main/com/tonicsystems/jarjar/DepHandler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,12 +18,13 @@ package com.tonicsystems.jarjar; import java.io.IOException; -public interface DepHandler -{ - public static final int LEVEL_CLASS = 0; - public static final int LEVEL_JAR = 1; +public interface DepHandler { + public static final int LEVEL_CLASS = 0; + public static final int LEVEL_JAR = 1; - void handleStart() throws IOException; - void handle(PathClass from, PathClass to) throws IOException; - void handleEnd() throws IOException; + void handleStart() throws IOException; + + void handle(PathClass from, PathClass to) throws IOException; + + void handleEnd() throws IOException; } diff --git a/src/main/com/tonicsystems/jarjar/EmptyClassVisitor.java b/src/main/com/tonicsystems/jarjar/EmptyClassVisitor.java index 9a05516..0dffc4e 100644 --- a/src/main/com/tonicsystems/jarjar/EmptyClassVisitor.java +++ b/src/main/com/tonicsystems/jarjar/EmptyClassVisitor.java @@ -15,36 +15,37 @@ */ package com.tonicsystems.jarjar; +import org.objectweb.asm.Opcodes; 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; /** - * An EmptyVisitor replacement + * An ASM3 EmptyVisitor replacement + * * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a> */ public class EmptyClassVisitor extends ClassVisitor { - public EmptyClassVisitor() { - super(Opcodes.ASM9); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, - String signature, String[] exceptions) { - return new MethodVisitor(Opcodes.ASM9) {}; - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return new AnnotationVisitor(Opcodes.ASM9) {}; - } - - @Override - public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { - return new FieldVisitor(Opcodes.ASM9) {}; - } + public EmptyClassVisitor() { + super(Opcodes.ASM9); + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + return new MethodVisitor(Opcodes.ASM9) {}; + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new AnnotationVisitor(Opcodes.ASM9) {}; + } + @Override + public FieldVisitor visitField( + int access, String name, String desc, String signature, Object value) { + return new FieldVisitor(Opcodes.ASM9) {}; + } } diff --git a/src/main/com/tonicsystems/jarjar/ExcludeProcessor.java b/src/main/com/tonicsystems/jarjar/ExcludeProcessor.java index 191803f..7b8cbbd 100644 --- a/src/main/com/tonicsystems/jarjar/ExcludeProcessor.java +++ b/src/main/com/tonicsystems/jarjar/ExcludeProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,25 +16,26 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; +import com.tonicsystems.jarjar.util.EntryStruct; +import com.tonicsystems.jarjar.util.JarProcessor; import java.io.IOException; -import java.util.*; +import java.util.Set; -class ExcludeProcessor implements JarProcessor -{ - private final Set<String> excludes; - private final boolean verbose; +class ExcludeProcessor implements JarProcessor { + private final Set<String> excludes; + private final boolean verbose; - public ExcludeProcessor(Set<String> excludes, boolean verbose) { - this.excludes = excludes; - this.verbose = verbose; - } + public ExcludeProcessor(Set<String> excludes, boolean verbose) { + this.excludes = excludes; + this.verbose = verbose; + } - public boolean process(EntryStruct struct) throws IOException { - boolean toKeep = !excludes.contains(struct.name); - if (verbose && !toKeep) - System.err.println("Excluding " + struct.name); - return toKeep; + @Override + public boolean process(EntryStruct struct) throws IOException { + boolean toKeep = !excludes.contains(struct.name); + if (verbose && !toKeep) { + System.err.println("Excluding " + struct.name); } + return toKeep; + } } - diff --git a/src/main/com/tonicsystems/jarjar/JarJarMojo.java b/src/main/com/tonicsystems/jarjar/JarJarMojo.java index 9d37b77..2cec5fc 100644 --- a/src/main/com/tonicsystems/jarjar/JarJarMojo.java +++ b/src/main/com/tonicsystems/jarjar/JarJarMojo.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,31 +23,31 @@ import java.util.*; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; -public class JarJarMojo extends AbstractMojo -{ - private File fromJar; - private File toJar; - private File rulesFile; - private String rules; - private boolean verbose; - - public void execute() throws MojoExecutionException { - if (!((rulesFile == null || !rulesFile.exists()) ^ (rules == null))) - throw new MojoExecutionException("Exactly one of rules or rulesFile is required"); +public class JarJarMojo extends AbstractMojo { + private File fromJar; + private File toJar; + private File rulesFile; + private String rules; + private boolean verbose; - try { - List<PatternElement> patterns; - if (rules != null) { - patterns = RulesFileParser.parse(rules); - } else { - patterns = RulesFileParser.parse(rulesFile); - } - // TODO: refactor with Main.java - MainProcessor proc = new MainProcessor(patterns, verbose, true); - StandaloneJarProcessor.run(fromJar, toJar, proc); - proc.strip(toJar); - } catch (IOException e) { - throw new MojoExecutionException(e.getMessage(), e); - } + public void execute() throws MojoExecutionException { + if (!((rulesFile == null || !rulesFile.exists()) ^ (rules == null))) { + throw new MojoExecutionException("Exactly one of rules or rulesFile is required"); } + + try { + List<PatternElement> patterns; + if (rules != null) { + patterns = RulesFileParser.parse(rules); + } else { + patterns = RulesFileParser.parse(rulesFile); + } + // TODO: refactor with Main.java + MainProcessor proc = new MainProcessor(patterns, verbose, true); + StandaloneJarProcessor.run(fromJar, toJar, proc); + proc.strip(toJar); + } catch (IOException e) { + throw new MojoExecutionException(e.getMessage(), e); + } + } } diff --git a/src/main/com/tonicsystems/jarjar/JarJarTask.java b/src/main/com/tonicsystems/jarjar/JarJarTask.java index 2c183c9..39e1677 100644 --- a/src/main/com/tonicsystems/jarjar/JarJarTask.java +++ b/src/main/com/tonicsystems/jarjar/JarJarTask.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,45 +17,47 @@ package com.tonicsystems.jarjar; import com.tonicsystems.jarjar.util.*; -import java.io.File; import java.io.IOException; import java.util.*; import org.apache.tools.ant.BuildException; -public class JarJarTask extends AntJarProcessor -{ - private List<PatternElement> patterns = new ArrayList<PatternElement>(); +public class JarJarTask extends AntJarProcessor { + private List<PatternElement> patterns = new ArrayList<PatternElement>(); - public void addConfiguredRule(Rule rule) { - if (rule.getPattern() == null || rule.getResult() == null) - throw new IllegalArgumentException("The <rule> element requires both \"pattern\" and \"result\" attributes."); - patterns.add(rule); + public void addConfiguredRule(Rule rule) { + if (rule.getPattern() == null || rule.getResult() == null) { + throw new IllegalArgumentException( + "The <rule> element requires both \"pattern\" and \"result\" attributes."); } + patterns.add(rule); + } - public void addConfiguredZap(Zap zap) { - if (zap.getPattern() == null) - throw new IllegalArgumentException("The <zap> element requires a \"pattern\" attribute."); - patterns.add(zap); + public void addConfiguredZap(Zap zap) { + if (zap.getPattern() == null) { + throw new IllegalArgumentException("The <zap> element requires a \"pattern\" attribute."); } + patterns.add(zap); + } - public void addConfiguredKeep(Keep keep) { - if (keep.getPattern() == null) - throw new IllegalArgumentException("The <keep> element requires a \"pattern\" attribute."); - patterns.add(keep); + public void addConfiguredKeep(Keep keep) { + if (keep.getPattern() == null) { + throw new IllegalArgumentException("The <keep> element requires a \"pattern\" attribute."); } - - public void execute() throws BuildException { - MainProcessor proc = new MainProcessor(patterns, verbose, false); - execute(proc); - try { - proc.strip(getDestFile()); - } catch (IOException e) { - throw new BuildException(e); - } + patterns.add(keep); + } + + public void execute() throws BuildException { + MainProcessor proc = new MainProcessor(patterns, verbose, false); + execute(proc); + try { + proc.strip(getDestFile()); + } catch (IOException e) { + throw new BuildException(e); } + } - protected void cleanHelper() { - super.cleanHelper(); - patterns.clear(); - } + protected void cleanHelper() { + super.cleanHelper(); + patterns.clear(); + } } diff --git a/src/main/com/tonicsystems/jarjar/Keep.java b/src/main/com/tonicsystems/jarjar/Keep.java index 14b719e..090340e 100644 --- a/src/main/com/tonicsystems/jarjar/Keep.java +++ b/src/main/com/tonicsystems/jarjar/Keep.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,4 @@ package com.tonicsystems.jarjar; -public class Keep extends PatternElement -{ -} +public class Keep extends PatternElement {} diff --git a/src/main/com/tonicsystems/jarjar/KeepProcessor.java b/src/main/com/tonicsystems/jarjar/KeepProcessor.java index 3ed4636..90ee682 100644 --- a/src/main/com/tonicsystems/jarjar/KeepProcessor.java +++ b/src/main/com/tonicsystems/jarjar/KeepProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,97 +16,114 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; -import java.io.*; -import java.util.*; -import org.objectweb.asm.*; -import org.objectweb.asm.Type; -import org.objectweb.asm.commons.*; +import com.tonicsystems.jarjar.util.EntryStruct; +import com.tonicsystems.jarjar.util.JarProcessor; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.commons.Remapper; // TODO: this can probably be refactored into JarClassVisitor, etc. -class KeepProcessor extends Remapper implements JarProcessor -{ - private final ClassVisitor cv = new ClassRemapper(new EmptyClassVisitor(), this); - private final List<Wildcard> wildcards; - private final List<String> roots = new ArrayList<String>(); - private final Map<String, Set<String>> depend = new HashMap<String, Set<String>>(); - - public KeepProcessor(List<Keep> patterns) { - wildcards = PatternElement.createWildcards(patterns); - } +class KeepProcessor extends Remapper implements JarProcessor { + private final ClassVisitor cv = new ClassRemapper(new EmptyClassVisitor(), this); + private final List<Wildcard> wildcards; + private final List<String> roots = new ArrayList<>(); + private final Map<String, Set<String>> depend = new HashMap<>(); - public boolean isEnabled() { - return !wildcards.isEmpty(); - } + public KeepProcessor(List<Keep> patterns) { + wildcards = PatternElement.createWildcards(patterns); + } - public Set<String> getExcludes() { - Set<String> closure = new HashSet<String>(); - closureHelper(closure, roots); - Set<String> removable = new HashSet<String>(depend.keySet()); - removable.removeAll(closure); - return removable; - } + public boolean isEnabled() { + return !wildcards.isEmpty(); + } - private void closureHelper(Set<String> closure, Collection<String> process) { - if (process == null) - return; - for (String name : process) { - if (closure.add(name)) - closureHelper(closure, depend.get(name)); - } + public Set<String> getExcludes() { + Set<String> closure = new HashSet<>(); + closureHelper(closure, roots); + Set<String> removable = new HashSet<>(depend.keySet()); + removable.removeAll(closure); + return removable; + } + + private void closureHelper(Set<String> closure, Collection<String> process) { + if (process == null) { + return; } + for (String name : process) { + if (closure.add(name)) { + closureHelper(closure, depend.get(name)); + } + } + } - private Set<String> curSet; - private byte[] buf = new byte[0x2000]; + private Set<String> curSet; - public boolean process(EntryStruct struct) throws IOException { - try { - if (struct.name.endsWith(".class")) { - String name = struct.name.substring(0, struct.name.length() - 6); - for (Wildcard wildcard : wildcards) - if (wildcard.matches(name)) - roots.add(name); - depend.put(name, curSet = new HashSet<String>()); - new ClassReader(new ByteArrayInputStream(struct.data)).accept(cv, - ClassReader.EXPAND_FRAMES); - curSet.remove(name); - } - } catch (Exception e) { - System.err.println("Error reading " + struct.name + ": " + e.getMessage()); + @Override + public boolean process(EntryStruct struct) throws IOException { + try { + if (struct.isClass()) { + String name = struct.name.substring(0, struct.name.length() - 6); + for (Wildcard wildcard : wildcards) { + if (wildcard.matches(name)) { + roots.add(name); + } } - return true; + depend.put(name, curSet = new HashSet<>()); + new ClassReader(new ByteArrayInputStream(struct.data)) + .accept(cv, ClassReader.EXPAND_FRAMES); + curSet.remove(name); + } + } catch (Exception e) { + System.err.println("Error reading " + struct.name + ": " + e.getMessage()); } + return true; + } - public String map(String key) { - if (key.startsWith("java/") || key.startsWith("javax/")) - return null; - curSet.add(key); - return null; + @Override + public String map(String key) { + if (key.startsWith("java/") || key.startsWith("javax/")) { + return null; } + curSet.add(key); + return null; + } - public Object mapValue(Object value) { - if (value instanceof String) { - String s = (String)value; - if (PackageRemapper.isArrayForName(s)) { - mapDesc(s.replace('.', '/')); - } else if (isForName(s)) { - map(s.replace('.', '/')); - } - return value; - } else { - return super.mapValue(value); - } + @Override + public Object mapValue(Object value) { + if (value instanceof String) { + String s = (String) value; + if (PackageRemapper.isArrayForName(s)) { + mapDesc(s.replace('.', '/')); + } else if (isForName(s)) { + map(s.replace('.', '/')); + } + return value; + } else { + return super.mapValue(value); } + } - // TODO: use this for package remapping too? - private static boolean isForName(String value) { - if (value.equals("")) - return false; - for (int i = 0, len = value.length(); i < len; i++) { - char c = value.charAt(i); - if (c != '.' && !Character.isJavaIdentifierPart(c)) - return false; - } - return true; + // TODO: use this for package remapping too? + private static boolean isForName(String value) { + if (value.isEmpty()) { + return false; + } + for (int i = 0, len = value.length(); i < len; i++) { + char c = value.charAt(i); + if (c != '.' && !Character.isJavaIdentifierPart(c)) { + return false; + } } + return true; + } } diff --git a/src/main/com/tonicsystems/jarjar/Main.java b/src/main/com/tonicsystems/jarjar/Main.java index 1eaa3e3..aa78836 100644 --- a/src/main/com/tonicsystems/jarjar/Main.java +++ b/src/main/com/tonicsystems/jarjar/Main.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,9 +16,19 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; -import java.io.*; -import java.util.*; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.tonicsystems.jarjar.util.RuntimeIOException; +import com.tonicsystems.jarjar.util.StandaloneJarProcessor; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.List; public class Main { @@ -34,18 +44,15 @@ public class Main { } private static String readIntoString(InputStream in) throws IOException { - StringBuilder sb = new StringBuilder(); - BufferedReader r = new BufferedReader(new InputStreamReader(in, "UTF-8")); - String line = null; - while ((line = r.readLine()) != null) - sb.append(line).append(LINE_SEPARATOR); - return sb.toString(); + StringBuilder sb = new StringBuilder(); + BufferedReader r = new BufferedReader(new InputStreamReader(in, UTF_8)); + String line = null; + while ((line = r.readLine()) != null) { + sb.append(line).append(LINE_SEPARATOR); + } + return sb.toString(); } - private boolean verbose; - private List patterns; - private int level = DepHandler.LEVEL_CLASS; - public static void main(String[] args) throws Exception { MainUtil.runMain(new Main(), args, "help"); } @@ -58,7 +65,8 @@ public class Main { if (cp == null) { throw new IllegalArgumentException("cp is required"); } - new StringDumper().run(cp, new PrintWriter(System.out)); + new StringDumper() + .run(cp, new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out, UTF_8)))); } // TODO: make level an enum @@ -77,7 +85,7 @@ public class Main { } else { throw new IllegalArgumentException("unknown level " + level); } - PrintWriter w = new PrintWriter(System.out); + PrintWriter w = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out, UTF_8))); DepHandler handler = new TextDepHandler(w, levelFlag); new DepFind().run(cp1, cp2, handler); w.flush(); @@ -92,8 +100,8 @@ public class Main { boolean skipManifest = Boolean.getBoolean("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); + 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 a0ef4c7..5839719 100644 --- a/src/main/com/tonicsystems/jarjar/MainProcessor.java +++ b/src/main/com/tonicsystems/jarjar/MainProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,118 +16,138 @@ package com.tonicsystems.jarjar; +import com.android.jarjar.RemoveAndroidCompatAnnotationsJarTransformer; import com.android.jarjar.StripAnnotation; -import com.tonicsystems.jarjar.util.*; +import com.android.jarjar.StripAnnotationsJarTransformer; +import com.tonicsystems.jarjar.util.EntryStruct; +import com.tonicsystems.jarjar.util.JarProcessor; +import com.tonicsystems.jarjar.util.JarProcessorChain; +import com.tonicsystems.jarjar.util.JarTransformerChain; +import com.tonicsystems.jarjar.util.RemappingClassTransformer; +import com.tonicsystems.jarjar.util.StandaloneJarProcessor; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; -import com.android.jarjar.RemoveAndroidCompatAnnotationsJarTransformer; -import com.android.jarjar.StripAnnotationsJarTransformer; +class MainProcessor implements JarProcessor { + private final boolean verbose; + private final JarProcessorChain chain; + private final KeepProcessor kp; + private final Map<String, String> renames = new HashMap<>(); -class MainProcessor implements JarProcessor -{ - private final boolean verbose; - private final JarProcessorChain chain; - private final KeepProcessor kp; - private final Map<String, String> renames = new HashMap<String, String>(); + // 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 */); + } - // 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); + } else if (pattern instanceof Rule) { + 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 } - 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<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); - } else if (pattern instanceof Rule) { - 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); - kp = keepList.isEmpty() ? null : new KeepProcessor(keepList); + PackageRemapper pr = new PackageRemapper(ruleList, verbose); + kp = keepList.isEmpty() ? null : new KeepProcessor(keepList); - List<JarProcessor> processors = new ArrayList<JarProcessor>(); - if (skipManifest) - processors.add(ManifestProcessor.getInstance()); - if (kp != null) - 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) })); - processors.add(new ResourceProcessor(pr)); - chain = new JarProcessorChain(processors.toArray(new JarProcessor[processors.size()])); + List<JarProcessor> processors = new ArrayList<>(); + if (skipManifest) { + processors.add(ManifestProcessor.getInstance()); + } + if (kp != null) { + 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)})); + processors.add(new ResourceProcessor(pr)); + processors.add(new ServiceProcessor(pr)); + chain = new JarProcessorChain(processors.toArray(new JarProcessor[0])); + } - public void strip(File file) throws IOException { - if (kp == null) - return; - Set<String> excludes = getExcludes(); - if (!excludes.isEmpty()) - StandaloneJarProcessor.run(file, file, new ExcludeProcessor(excludes, verbose)); + public void strip(File file) throws IOException { + if (kp == null) { + return; } + Set<String> excludes = getExcludes(); + if (!excludes.isEmpty()) { + StandaloneJarProcessor.run(file, file, new ExcludeProcessor(excludes, verbose)); + } + } - /** - * Returns the <code>.class</code> files to delete. As well the root-parameter as the rename ones - * are taken in consideration, so that the concerned files are not listed in the result. - * - * @return the paths of the files in the jar-archive, including the <code>.class</code> suffix - */ - private Set<String> getExcludes() { - Set<String> result = new HashSet<String>(); - for (String exclude : kp.getExcludes()) { - String name = exclude + ".class"; - String renamed = renames.get(name); - result.add((renamed != null) ? renamed : name); - } - return result; + /** + * Returns the <code>.class</code> files to delete. As well the root-parameter as the rename ones + * are taken in consideration, so that the concerned files are not listed in the result. + * + * @return the paths of the files in the jar-archive, including the <code>.class</code> suffix + */ + private Set<String> getExcludes() { + Set<String> result = new HashSet<>(); + for (String exclude : kp.getExcludes()) { + String name = exclude + ".class"; + String renamed = renames.get(name); + result.add((renamed != null) ? renamed : name); } + return result; + } - /** - * - * @param struct - * @return <code>true</code> if the entry is to include in the output jar - * @throws IOException - */ - public boolean process(EntryStruct struct) throws IOException { - String name = struct.name; - boolean keepIt = chain.process(struct); - if (keepIt) { - if (!name.equals(struct.name)) { - if (kp != null) - renames.put(name, struct.name); - if (verbose) - System.err.println("Renamed " + name + " -> " + struct.name); - } - } else { - if (verbose) - System.err.println("Removed " + name); + /** + * @param struct + * @return <code>true</code> if the entry is to include in the output jar + * @throws IOException + */ + public boolean process(EntryStruct struct) throws IOException { + String name = struct.name; + boolean keepIt = chain.process(struct); + if (keepIt) { + if (!name.equals(struct.name)) { + if (kp != null) { + renames.put(name, struct.name); + } + if (verbose) { + System.err.println("Renamed " + name + " -> " + struct.name); } - return keepIt; + } + } else { + if (verbose) { + System.err.println("Removed " + name); + } } + return keepIt; + } } diff --git a/src/main/com/tonicsystems/jarjar/MainUtil.java b/src/main/com/tonicsystems/jarjar/MainUtil.java index 068ece6..3ad978d 100644 --- a/src/main/com/tonicsystems/jarjar/MainUtil.java +++ b/src/main/com/tonicsystems/jarjar/MainUtil.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,71 +16,75 @@ package com.tonicsystems.jarjar; +import static java.lang.Math.max; + import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -class MainUtil -{ - public static void runMain(Object main, String[] args, String defCommand) throws Exception { - if (args.length > 0) { - String command = args[0]; - Method[] methods = main.getClass().getMethods(); - for (int i = 0; i < methods.length; i++) { - Method method = methods[i]; - if (method.getName().equals(command)) { - String[] remaining = new String[args.length - 1]; - System.arraycopy(args, 1, remaining, 0, remaining.length); - try { - method.invoke(main, bindParameters(method, remaining)); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof IllegalArgumentException) { - System.err.println("Syntax error: " + cause.getMessage()); - } else if (cause instanceof Exception) { - throw (Exception) cause; - } else { - throw e; - } - } - return; - } +final class MainUtil { + public static void runMain(Object main, String[] args, String defCommand) throws Exception { + if (args.length > 0) { + String command = args[0]; + Method[] methods = main.getClass().getMethods(); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + if (method.getName().equals(command)) { + String[] remaining = new String[args.length - 1]; + System.arraycopy(args, 1, remaining, 0, remaining.length); + try { + method.invoke(main, bindParameters(method, remaining)); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof IllegalArgumentException) { + System.err.println("Syntax error: " + cause.getMessage()); + } else if (cause instanceof Exception) { + throw (Exception) cause; + } else { + throw e; } + } + return; } - if (defCommand != null) - runMain(main, new String[]{ defCommand }, null); + } } + if (defCommand != null) { + runMain(main, new String[] {defCommand}, null); + } + } - private static Object[] bindParameters(Method method, String[] args) { - List<Object> parameters = new ArrayList<Object>(); - Class[] parameterTypes = method.getParameterTypes(); - for (int i = 0, len = parameterTypes.length; i < len; i++) { - Class type = parameterTypes[i]; - int remaining = Math.max(0, args.length - i); - if (type.equals(String[].class)) { - String[] rest = new String[remaining]; - System.arraycopy(args, 1, rest, 0, remaining); - parameters.add(rest); - } else if (remaining > 0) { - parameters.add(convertParameter(args[i], parameterTypes[i])); - } else { - parameters.add(null); - } - } - return parameters.toArray(); + private static Object[] bindParameters(Method method, String[] args) { + List<Object> parameters = new ArrayList<>(); + Class[] parameterTypes = method.getParameterTypes(); + for (int i = 0, len = parameterTypes.length; i < len; i++) { + Class type = parameterTypes[i]; + int remaining = max(0, args.length - i); + if (type.equals(String[].class)) { + String[] rest = new String[remaining]; + System.arraycopy(args, 1, rest, 0, remaining); + parameters.add(rest); + } else if (remaining > 0) { + parameters.add(convertParameter(args[i], parameterTypes[i])); + } else { + parameters.add(null); + } } + return parameters.toArray(); + } - private static Object convertParameter(String arg, Class type) { - if (type.equals(String.class)) { - return arg; - } else if (type.equals(Integer.class)) { - return Integer.valueOf(arg, 10); - } else if (type.equals(File.class)) { - return new File(arg); - } else { - throw new UnsupportedOperationException("Unknown type " + type); - } + private static Object convertParameter(String arg, Class type) { + if (type.equals(String.class)) { + return arg; + } else if (type.equals(Integer.class)) { + return Integer.valueOf(arg, 10); + } else if (type.equals(File.class)) { + return new File(arg); + } else { + throw new UnsupportedOperationException("Unknown type " + type); } + } + + private MainUtil() {} } diff --git a/src/main/com/tonicsystems/jarjar/ManifestProcessor.java b/src/main/com/tonicsystems/jarjar/ManifestProcessor.java index 91f1f76..6c33c23 100644 --- a/src/main/com/tonicsystems/jarjar/ManifestProcessor.java +++ b/src/main/com/tonicsystems/jarjar/ManifestProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,23 +16,22 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; +import com.tonicsystems.jarjar.util.EntryStruct; +import com.tonicsystems.jarjar.util.JarProcessor; import java.io.IOException; -import java.util.*; -class ManifestProcessor implements JarProcessor -{ - private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; - private static final ManifestProcessor INSTANCE = new ManifestProcessor(); +class ManifestProcessor implements JarProcessor { + private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; + private static final ManifestProcessor INSTANCE = new ManifestProcessor(); - public static ManifestProcessor getInstance() { - return INSTANCE; - } - - private ManifestProcessor() {} + public static ManifestProcessor getInstance() { + return INSTANCE; + } - public boolean process(EntryStruct struct) throws IOException { - return !struct.name.equalsIgnoreCase(MANIFEST_PATH); - } + private ManifestProcessor() {} + + @Override + public boolean process(EntryStruct struct) throws IOException { + return !struct.name.equalsIgnoreCase(MANIFEST_PATH); + } } - diff --git a/src/main/com/tonicsystems/jarjar/PackageRemapper.java b/src/main/com/tonicsystems/jarjar/PackageRemapper.java index e281f24..7533082 100644 --- a/src/main/com/tonicsystems/jarjar/PackageRemapper.java +++ b/src/main/com/tonicsystems/jarjar/PackageRemapper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,112 +16,124 @@ package com.tonicsystems.jarjar; -import org.objectweb.asm.commons.*; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.regex.Pattern; +import org.objectweb.asm.commons.Remapper; -class PackageRemapper extends Remapper -{ - private static final String RESOURCE_SUFFIX = "RESOURCE"; - - private static final Pattern ARRAY_FOR_NAME_PATTERN - = Pattern.compile("\\[L[\\p{javaJavaIdentifierPart}\\.]+?;"); +class PackageRemapper extends Remapper { + private static final String RESOURCE_SUFFIX = "RESOURCE"; - private final WildcardTrie wildcards; - private final Map<String, String> typeCache = new HashMap<String, String>(); - private final Map<String, String> pathCache = new HashMap<String, String>(); - private final Map<Object, String> valueCache = new HashMap<Object, String>(); - private final boolean verbose; + private static final Pattern ARRAY_FOR_NAME_PATTERN = + Pattern.compile("\\[L[\\p{javaJavaIdentifierPart}\\.]+?;"); - public PackageRemapper(List<Rule> ruleList, boolean verbose) { - this.verbose = verbose; - wildcards = new WildcardTrie(PatternElement.createWildcards(ruleList)); - } + 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<>(); + private final boolean verbose; - // also used by KeepProcessor - static boolean isArrayForName(String value) { - return ARRAY_FOR_NAME_PATTERN.matcher(value).matches(); - } + public PackageRemapper(List<Rule> ruleList, boolean verbose) { + this.verbose = verbose; + wildcards = new WildcardTrie(PatternElement.createWildcards(ruleList)); + } - public String map(String key) { - String s = typeCache.get(key); - if (s == null) { - s = replaceHelper(key); - if (key.equals(s)) - s = null; - typeCache.put(key, s); - } - return s; + // also used by KeepProcessor + static boolean isArrayForName(String value) { + return ARRAY_FOR_NAME_PATTERN.matcher(value).matches(); + } + + @Override + public String map(String key) { + String s = typeCache.get(key); + if (s == null) { + s = replaceHelper(key); + if (key.equals(s)) { + s = null; + } + typeCache.put(key, s); } + return s; + } - public String mapPath(String path) { - String s = pathCache.get(path); - if (s == null) { - s = path; - int slash = s.lastIndexOf('/'); - String end; - if (slash < 0) { - end = s; - s = RESOURCE_SUFFIX; - } else { - end = s.substring(slash + 1); - s = s.substring(0, slash + 1) + RESOURCE_SUFFIX; - } - boolean absolute = s.startsWith("/"); - if (absolute) s = s.substring(1); - - s = replaceHelper(s); - - if (absolute) s = "/" + s; - if (s.indexOf(RESOURCE_SUFFIX) < 0) - return path; - s = s.substring(0, s.length() - RESOURCE_SUFFIX.length()) + end; - pathCache.put(path, s); - } - return s; + public String mapPath(String path) { + String s = pathCache.get(path); + if (s == null) { + s = path; + int slash = s.lastIndexOf('/'); + String end; + if (slash < 0) { + end = s; + s = RESOURCE_SUFFIX; + } else { + end = s.substring(slash + 1); + s = s.substring(0, slash + 1) + RESOURCE_SUFFIX; + } + boolean absolute = s.startsWith("/"); + if (absolute) { + s = s.substring(1); + } + + s = replaceHelper(s); + + if (absolute) { + s = "/" + s; + } + if (!s.contains(RESOURCE_SUFFIX)) { + return path; + } + s = s.substring(0, s.length() - RESOURCE_SUFFIX.length()) + end; + pathCache.put(path, s); } + return s; + } - public Object mapValue(Object value) { - if (value instanceof String) { - String s = valueCache.get(value); - if (s == null) { - s = (String)value; - if (isArrayForName(s)) { - String desc1 = s.replace('.', '/'); - String desc2 = mapDesc(desc1); - if (!desc2.equals(desc1)) - return desc2.replace('/', '.'); - } else { - s = mapPath(s); - if (s.equals(value)) { - boolean hasDot = s.indexOf('.') >= 0; - boolean hasSlash = s.indexOf('/') >= 0; - if (!(hasDot && hasSlash)) { - if (hasDot) { - s = replaceHelper(s.replace('.', '/')).replace('/', '.'); - } else { - s = replaceHelper(s); - } - } - } - } - valueCache.put(value, s); - } - // TODO: add back class name to verbose message - if (verbose && !s.equals(value)) - System.err.println("Changed \"" + value + "\" -> \"" + s + "\""); - return s; + @Override + public Object mapValue(Object value) { + if (value instanceof String) { + String s = valueCache.get(value); + if (s == null) { + s = (String) value; + if (isArrayForName(s)) { + String desc1 = s.replace('.', '/'); + String desc2 = mapDesc(desc1); + if (!desc2.equals(desc1)) { + return desc2.replace('/', '.'); + } } else { - return super.mapValue(value); + s = mapPath(s); + if (s.equals(value)) { + boolean hasDot = s.indexOf('.') >= 0; + boolean hasSlash = s.indexOf('/') >= 0; + if (!(hasDot && hasSlash)) { + if (hasDot) { + s = replaceHelper(s.replace('.', '/')).replace('/', '.'); + } else { + s = replaceHelper(s); + } + } + } } + valueCache.put(value, s); + } + // TODO: add back class name to verbose message + if (verbose && !s.equals(value)) { + System.err.println("Changed \"" + value + "\" -> \"" + s + "\""); + } + return s; + } else { + return super.mapValue(value); } + } - private String replaceHelper(String value) { - for (Wildcard wildcard : wildcards.getPossibleMatches(value)) { - String test = wildcard.replace(value); - if (test != null) - return test; - } - return value; + private String replaceHelper(String value) { + for (Wildcard wildcard : wildcards.getPossibleMatches(value)) { + String test = wildcard.replace(value); + if (test != null) { + return test; + } } + return value; + } } diff --git a/src/main/com/tonicsystems/jarjar/PathClass.java b/src/main/com/tonicsystems/jarjar/PathClass.java index fbbb7ed..81a4da2 100644 --- a/src/main/com/tonicsystems/jarjar/PathClass.java +++ b/src/main/com/tonicsystems/jarjar/PathClass.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,25 +16,25 @@ package com.tonicsystems.jarjar; -public class PathClass -{ - private String classPath; - private String className; - - public PathClass(String classPath, String className) { - this.classPath = classPath; - this.className = className; - } +public class PathClass { + private final String classPath; + private final String className; - public String getClassPath() { - return classPath; - } + public PathClass(String classPath, String className) { + this.classPath = classPath; + this.className = className; + } - public String getClassName() { - return className; - } + public String getClassPath() { + return classPath; + } - public String toString() { - return classPath + "!" + className; - } + public String getClassName() { + return className; + } + + @Override + public String toString() { + return classPath + "!" + className; + } } diff --git a/src/main/com/tonicsystems/jarjar/PatternElement.java b/src/main/com/tonicsystems/jarjar/PatternElement.java index 6b852d7..399928d 100644 --- a/src/main/com/tonicsystems/jarjar/PatternElement.java +++ b/src/main/com/tonicsystems/jarjar/PatternElement.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,31 +16,32 @@ package com.tonicsystems.jarjar; -import java.util.*; +import java.util.ArrayList; +import java.util.List; -abstract public class PatternElement -{ - private String pattern; - - public void setPattern(String pattern) { - this.pattern = pattern; - } +public abstract class PatternElement { + private String pattern; - public String getPattern() { - return pattern; - } + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public String getPattern() { + return pattern; + } - static List<Wildcard> createWildcards(List<? extends PatternElement> patterns) { - List<Wildcard> wildcards = new ArrayList<Wildcard>(); - 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, ruleIndex)); - ruleIndex++; - } - return wildcards; + 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, ruleIndex)); + ruleIndex++; } + return wildcards; + } } diff --git a/src/main/com/tonicsystems/jarjar/ResourceProcessor.java b/src/main/com/tonicsystems/jarjar/ResourceProcessor.java index c07f664..8678cec 100644 --- a/src/main/com/tonicsystems/jarjar/ResourceProcessor.java +++ b/src/main/com/tonicsystems/jarjar/ResourceProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,22 +16,22 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; +import com.tonicsystems.jarjar.util.EntryStruct; +import com.tonicsystems.jarjar.util.JarProcessor; import java.io.IOException; -import java.util.*; -class ResourceProcessor implements JarProcessor -{ - private PackageRemapper pr; +class ResourceProcessor implements JarProcessor { + private final PackageRemapper pr; - public ResourceProcessor(PackageRemapper pr) { - this.pr = pr; - } + public ResourceProcessor(PackageRemapper pr) { + this.pr = pr; + } - public boolean process(EntryStruct struct) throws IOException { - if (!struct.name.endsWith(".class")) - struct.name = pr.mapPath(struct.name); - return true; + @Override + public boolean process(EntryStruct struct) throws IOException { + if (!struct.isClass()) { + struct.name = pr.mapPath(struct.name); } + return true; + } } - diff --git a/src/main/com/tonicsystems/jarjar/Rule.java b/src/main/com/tonicsystems/jarjar/Rule.java index 2e76ca8..96de530 100644 --- a/src/main/com/tonicsystems/jarjar/Rule.java +++ b/src/main/com/tonicsystems/jarjar/Rule.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,15 +16,14 @@ package com.tonicsystems.jarjar; -public class Rule extends PatternElement -{ - private String result; +public class Rule extends PatternElement { + private String result; - public void setResult(String result) { - this.result = result; - } + public void setResult(String result) { + this.result = result; + } - public String getResult() { - return result; - } + public String getResult() { + return result; + } } diff --git a/src/main/com/tonicsystems/jarjar/RulesFileParser.java b/src/main/com/tonicsystems/jarjar/RulesFileParser.java index c8c6ea4..4297ee7 100644 --- a/src/main/com/tonicsystems/jarjar/RulesFileParser.java +++ b/src/main/com/tonicsystems/jarjar/RulesFileParser.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,72 +16,85 @@ package com.tonicsystems.jarjar; -import com.android.jarjar.StripAnnotation; +import static java.nio.charset.StandardCharsets.UTF_8; -import java.io.*; -import java.util.*; +import com.android.jarjar.StripAnnotation; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; -class RulesFileParser -{ - private RulesFileParser() { - } +class RulesFileParser { + private RulesFileParser() {} - public static List<PatternElement> parse(File file) throws IOException { - return parse(new FileReader(file)); - } + public static List<PatternElement> parse(File file) throws IOException { + return parse(Files.newBufferedReader(file.toPath(), UTF_8)); + } - public static List<PatternElement> parse(String value) throws IOException { - return parse(new java.io.StringReader(value)); - } + public static List<PatternElement> parse(String value) throws IOException { + return parse(new java.io.StringReader(value)); + } - private static String stripComment(String in) { - int p = in.indexOf("#"); - return p < 0 ? in : in.substring(0, p); - } - - private static List<PatternElement> parse(Reader r) throws IOException { - try { - List<PatternElement> patterns = new ArrayList<PatternElement>(); - BufferedReader br = new BufferedReader(r); - int c = 1; - String line; - while ((line = br.readLine()) != null) { - line = stripComment(line); - if (line.isEmpty()) - continue; - String[] parts = line.split("\\s+"); - if (parts.length < 2) - error(c, parts); - String type = parts[0]; - PatternElement element = null; - if (type.equals("rule")) { - if (parts.length < 3) - error(c, parts); - Rule rule = new Rule(); - rule.setResult(parts[2]); - element = rule; - } else if (type.equals("zap")) { - 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); + private static List<PatternElement> parse(Reader r) throws IOException { + try { + List<PatternElement> patterns = new ArrayList<>(); + BufferedReader br = new BufferedReader(r); + int c = 1; + String line; + while ((line = br.readLine()) != null) { + line = stripComment(line); + if (line.isEmpty()) { + continue; + } + String[] parts = line.split("\\s+"); + if (parts.length < 2) { + error(c, parts); + } + String type = parts[0]; + PatternElement element = null; + switch (type) { + case "rule": + if (parts.length < 3) { + error(c, parts); } - element.setPattern(parts[1]); - patterns.add(element); - c++; + Rule rule = new Rule(); + rule.setResult(parts[2]); + element = rule; + break; + case "zap": + element = new Zap(); + break; + 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); } - return patterns; - } finally { - r.close(); + element.setPattern(parts[1]); + patterns.add(element); + c++; } + return patterns; + } finally { + r.close(); } + } - private static void error(int line, String[] parts) { - throw new IllegalArgumentException("Error on line " + line + ": " + Arrays.asList(parts)); - } + private static String stripComment(String in) { + int p = in.indexOf("#"); + return p < 0 ? in : in.substring(0, p); + } + + private static void error(int line, String[] parts) { + throw new IllegalArgumentException("Error on line " + line + ": " + Arrays.asList(parts)); + } } diff --git a/src/main/com/tonicsystems/jarjar/ServiceProcessor.java b/src/main/com/tonicsystems/jarjar/ServiceProcessor.java new file mode 100644 index 0000000..b59460c --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/ServiceProcessor.java @@ -0,0 +1,54 @@ +/* + * Copyright 2024 Google Inc. + * + * 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 static java.nio.charset.StandardCharsets.UTF_8; + +import com.tonicsystems.jarjar.util.EntryStruct; +import com.tonicsystems.jarjar.util.JarProcessor; +import java.io.IOException; +import java.util.stream.Collectors; + +class ServiceProcessor implements JarProcessor { + private final PackageRemapper pr; + + public ServiceProcessor(PackageRemapper pr) { + this.pr = pr; + } + + private static final String SERVICES_PREFIX = "META-INF/services/"; + + @Override + public boolean process(EntryStruct struct) throws IOException { + if (struct.name.startsWith(SERVICES_PREFIX)) { + String serviceName = struct.name.substring(SERVICES_PREFIX.length()); + struct.name = SERVICES_PREFIX + mapString(serviceName); + + struct.data = + new String(struct.data, UTF_8) + .lines() + .map(this::mapString) + .collect(Collectors.joining("\n", "", "\n")) + .getBytes(UTF_8); + } + return true; + } + + private String mapString(String s) { + return (String) pr.mapValue(s); + } +} diff --git a/src/main/com/tonicsystems/jarjar/StringDumper.java b/src/main/com/tonicsystems/jarjar/StringDumper.java index 5086314..b428e63 100644 --- a/src/main/com/tonicsystems/jarjar/StringDumper.java +++ b/src/main/com/tonicsystems/jarjar/StringDumper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,81 +16,98 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; -import java.io.*; -import org.objectweb.asm.*; +import com.tonicsystems.jarjar.util.ClassPathEntry; +import com.tonicsystems.jarjar.util.ClassPathIterator; +import com.tonicsystems.jarjar.util.RuntimeIOException; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import org.objectweb.asm.ClassReader; -class StringDumper -{ - public StringDumper() { - } +class StringDumper { + public StringDumper() {} - public void run(String classPath, PrintWriter pw) throws IOException { - StringReader stringReader = new DumpStringReader(pw); - ClassPathIterator cp = new ClassPathIterator(classPath); + public void run(String classPath, PrintWriter pw) throws IOException { + StringReader stringReader = new DumpStringReader(pw); + ClassPathIterator cp = new ClassPathIterator(classPath); + try { + while (cp.hasNext()) { + ClassPathEntry entry = cp.next(); + InputStream in = entry.openStream(); try { - while (cp.hasNext()) { - ClassPathEntry entry = cp.next(); - InputStream in = entry.openStream(); - try { - new ClassReader(in).accept(stringReader, 0); - } catch (Exception e) { - System.err.println("Error reading " + entry.getName() + ": " + e.getMessage()); - } finally { - in.close(); - } - pw.flush(); - } - } catch (RuntimeIOException e) { - throw (IOException)e.getCause(); + new ClassReader(in).accept(stringReader, 0); + } catch (Exception e) { + System.err.println("Error reading " + entry.getName() + ": " + e.getMessage()); } finally { - cp.close(); + in.close(); } + pw.flush(); + } + } catch (RuntimeIOException e) { + throw (IOException) e.getCause(); + } finally { + cp.close(); } + } - private static class DumpStringReader extends StringReader - { - private final PrintWriter pw; - private String className; + private static class DumpStringReader extends StringReader { + private final PrintWriter pw; + private String className; - public DumpStringReader(PrintWriter pw) { - this.pw = pw; - } + public DumpStringReader(PrintWriter pw) { + this.pw = pw; + } - public void visitString(String className, String value, int line) { - if (value.length() > 0) { - if (!className.equals(this.className)) { - this.className = className; - pw.println(className.replace('/', '.')); - } - pw.print("\t"); - if (line >= 0) - pw.print(line + ": "); - pw.print(escapeStringLiteral(value)); - pw.println(); - } + @Override + public void visitString(String className, String value, int line) { + if (value.length() > 0) { + if (!className.equals(this.className)) { + this.className = className; + pw.println(className.replace('/', '.')); } - }; - - private static String escapeStringLiteral(String value) { - StringBuilder sb = new StringBuilder(); - sb.append("\""); - char[] chars = value.toCharArray(); - for (int i = 0, size = chars.length; i < size; i++) { - char ch = chars[i]; - switch (ch) { - case '\n': sb.append("\\n"); break; - case '\r': sb.append("\\r"); break; - case '\b': sb.append("\\b"); break; - case '\f': sb.append("\\f"); break; - case '\t': sb.append("\\t"); break; - case '\"': sb.append("\\\""); break; - case '\\': sb.append("\\\\"); break; - default: - sb.append(ch); - } + pw.print("\t"); + if (line >= 0) { + pw.print(line + ": "); } - sb.append("\""); - return sb.toString(); + pw.print(escapeStringLiteral(value)); + pw.println(); + } + } + } + + private static String escapeStringLiteral(String value) { + StringBuilder sb = new StringBuilder(); + sb.append("\""); + char[] chars = value.toCharArray(); + for (int i = 0, size = chars.length; i < size; i++) { + char ch = chars[i]; + switch (ch) { + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\b': + sb.append("\\b"); + break; + case '\f': + sb.append("\\f"); + break; + case '\t': + sb.append("\\t"); + break; + case '\"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + default: + sb.append(ch); + } } + sb.append("\""); + return sb.toString(); + } } diff --git a/src/main/com/tonicsystems/jarjar/StringReader.java b/src/main/com/tonicsystems/jarjar/StringReader.java index c3cc273..07a5ec8 100644 --- a/src/main/com/tonicsystems/jarjar/StringReader.java +++ b/src/main/com/tonicsystems/jarjar/StringReader.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,85 +16,105 @@ package com.tonicsystems.jarjar; -import org.objectweb.asm.*; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; -abstract class StringReader extends ClassVisitor -{ - private int line = -1; - private String className; +abstract class StringReader extends ClassVisitor { + private int line = -1; + private String className; - public StringReader() { - super(Opcodes.ASM9); - } - - abstract public void visitString(String className, String value, int line); + public StringReader() { + super(Opcodes.ASM9); + } - private void handleObject(Object value) { - if (value instanceof String) - visitString(className, (String)value, line); - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - className = name; - line = -1; + public abstract void visitString(String className, String value, int line); + + private void handleObject(Object value) { + if (value instanceof String) { + visitString(className, (String) value, line); } + } + + @Override + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + className = name; + line = -1; + } - public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + @Override + public FieldVisitor visitField( + int access, String name, String desc, String signature, Object value) { + handleObject(value); + return new FieldVisitor(Opcodes.ASM9) { + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return StringReader.this.visitAnnotation(desc, visible); + } + }; + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new AnnotationVisitor(Opcodes.ASM9) { + @Override + public void visit(String name, Object value) { handleObject(value); - return new FieldVisitor(Opcodes.ASM9){ - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return StringReader.this.visitAnnotation(desc, visible); - } - }; - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return new AnnotationVisitor(Opcodes.ASM9) { - @Override - public void visit(String name, Object value) { - handleObject(value); - } - @Override - public void visitEnum(String name, String desc, String value) { - handleObject(value); - } - @Override - public AnnotationVisitor visitAnnotation(String name, String desc) { - return this; - } - }; - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, - String signature, String[] exceptions) { - MethodVisitor mv = new MethodVisitor(Opcodes.ASM9){ - @Override - public void visitLdcInsn(Object cst) { - handleObject(cst); - } - @Override - public void visitLineNumber(int line, Label start) { - StringReader.this.line = line; - } - @Override - public void visitInvokeDynamicInsn(String name, String desc, - Handle bsm, Object... bsmArgs) { - for (Object bsmArg : bsmArgs) handleObject(bsmArg); - } - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return StringReader.this.visitAnnotation(desc, visible); - } - @Override - public AnnotationVisitor visitParameterAnnotation(int parameter, - String desc, boolean visible) { - return StringReader.this.visitAnnotation(desc, visible); - } - }; - return mv; - } + } + + @Override + public void visitEnum(String name, String desc, String value) { + handleObject(value); + } + + @Override + public AnnotationVisitor visitAnnotation(String name, String desc) { + return this; + } + }; + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + return new MethodVisitor(Opcodes.ASM9) { + @Override + public void visitLdcInsn(Object cst) { + handleObject(cst); + } + + @Override + public void visitLineNumber(int line, Label start) { + StringReader.this.line = line; + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { + for (Object bsmArg : bsmArgs) { + handleObject(bsmArg); + } + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return StringReader.this.visitAnnotation(desc, visible); + } + + @Override + public AnnotationVisitor visitParameterAnnotation( + int parameter, String desc, boolean visible) { + return StringReader.this.visitAnnotation(desc, visible); + } + }; + } } diff --git a/src/main/com/tonicsystems/jarjar/TextDepHandler.java b/src/main/com/tonicsystems/jarjar/TextDepHandler.java index 3551395..82f7d28 100644 --- a/src/main/com/tonicsystems/jarjar/TextDepHandler.java +++ b/src/main/com/tonicsystems/jarjar/TextDepHandler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,19 +16,19 @@ package com.tonicsystems.jarjar; -import java.io.*; -import java.util.*; +import java.io.IOException; +import java.io.PrintWriter; -public class TextDepHandler extends AbstractDepHandler -{ - private PrintWriter w; - - public TextDepHandler(PrintWriter w, int level) { - super(level); - this.w = w; - } - - protected void handle(String from, String to) throws IOException { - w.println(from + " -> " + to); - } +public class TextDepHandler extends AbstractDepHandler { + private final PrintWriter w; + + public TextDepHandler(PrintWriter w, int level) { + super(level); + this.w = w; + } + + @Override + protected void handle(String from, String to) throws IOException { + w.println(from + " -> " + to); + } } diff --git a/src/main/com/tonicsystems/jarjar/Wildcard.java b/src/main/com/tonicsystems/jarjar/Wildcard.java index c92e0fb..c5d0c20 100644 --- a/src/main/com/tonicsystems/jarjar/Wildcard.java +++ b/src/main/com/tonicsystems/jarjar/Wildcard.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,149 +16,172 @@ package com.tonicsystems.jarjar; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.ArrayList; import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -class Wildcard -{ - private static Pattern dstar = Pattern.compile("\\*\\*"); - private static Pattern star = Pattern.compile("\\*"); - private static Pattern estar = Pattern.compile("\\+\\??\\)\\Z"); - private static Pattern dollar = Pattern.compile("\\$"); - // Apart from stars and dollar signs, wildcards are plain-text full matches - private static Pattern plainTextPrefixPattern = Pattern.compile("^[^*$]*"); +class Wildcard { + 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<Object>(16); // kept for debugging - private final String[] strings; - private final int[] refs; + 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, int ruleIndex) { - if (pattern.equals("**")) - throw new IllegalArgumentException("'**' is not a valid pattern"); - if (!checkIdentifierChars(pattern, "/*")) - throw new IllegalArgumentException("Not a valid package pattern: " + pattern); - if (pattern.indexOf("***") >= 0) - throw new IllegalArgumentException("The sequence '***' is invalid in a package pattern"); - - String regex = pattern; - regex = replaceAllLiteral(dstar, regex, "(.+?)"); - regex = replaceAllLiteral(star, regex, "([^/]+)"); - regex = replaceAllLiteral(estar, regex, "*)"); - regex = replaceAllLiteral(dollar, regex, "\\$"); - Matcher prefixMatcher = plainTextPrefixPattern.matcher(pattern); - // prefixMatcher will always match, but may match an empty string - if (!prefixMatcher.find()) { - throw new IllegalArgumentException(plainTextPrefixPattern + " 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(); + public Wildcard(String pattern, String result, int ruleIndex) { + if (pattern.equals("**")) { + throw new IllegalArgumentException("'**' is not a valid pattern"); + } + if (!checkIdentifierChars(pattern, "/*-")) { + throw new IllegalArgumentException("Not a valid package pattern: " + pattern); + } + if (pattern.contains("***")) { + throw new IllegalArgumentException("The sequence '***' is invalid in a package pattern"); + } - // TODO: check for illegal characters - char[] chars = result.toCharArray(); - int max = 0; - for (int i = 0, mark = 0, state = 0, len = chars.length; i < len + 1; i++) { - char ch = (i == len) ? '@' : chars[i]; - if (state == 0) { - if (ch == '@') { - parts.add(new String(chars, mark, i - mark)); - mark = i + 1; - state = 1; - } - } else { - switch (ch) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - break; - default: - if (i == mark) - throw new IllegalArgumentException("Backslash not followed by a digit"); - int n = Integer.parseInt(new String(chars, mark, i - mark)); - if (n > max) - max = n; - parts.add(new Integer(n)); - mark = i--; - state = 0; - } - } + String regex = pattern; + 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(); + + // TODO: check for illegal characters + char[] chars = result.toCharArray(); + int max = 0; + for (int i = 0, mark = 0, state = 0, len = chars.length; i < len + 1; i++) { + char ch = (i == len) ? '@' : chars[i]; + if (state == 0) { + if (ch == '@') { + parts.add(new String(chars, mark, i - mark)); + mark = i + 1; + state = 1; } - int size = parts.size(); - strings = new String[size]; - refs = new int[size]; - Arrays.fill(refs, -1); - for (int i = 0; i < size; i++) { - Object v = parts.get(i); - if (v instanceof String) { - strings[i] = ((String)v).replace('.', '/'); - } else { - refs[i] = ((Integer)v).intValue(); + } else { + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + if (i == mark) { + throw new IllegalArgumentException("Backslash not followed by a digit"); + } + int n = Integer.parseInt(new String(chars, mark, i - mark)); + if (n > max) { + max = n; } + parts.add(Integer.valueOf(n)); + mark = i--; + state = 0; } - if (count < max) - throw new IllegalArgumentException("Result includes impossible placeholder \"@" + max + "\": " + result); - // System.err.println(this); + } } - - public String getPlainTextPrefix() { - return plainTextPrefix; + int size = parts.size(); + strings = new String[size]; + refs = new int[size]; + Arrays.fill(refs, -1); + for (int i = 0; i < size; i++) { + Object v = parts.get(i); + if (v instanceof String) { + strings[i] = ((String) v).replace('.', '/'); + } else { + refs[i] = ((Integer) v).intValue(); + } } - - public int getRuleIndex() { - return ruleIndex; + if (count < max) { + throw new IllegalArgumentException( + "Result includes impossible placeholder \"@" + max + "\": " + result); } + // System.err.println(this); + } - public boolean matches(String value) { - return getMatcher(value) != null; - } + public String getPlainTextPrefix() { + return plainTextPrefix; + } - public String replace(String value) { - Matcher matcher = getMatcher(value); - if (matcher != null) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < strings.length; i++) - sb.append((refs[i] >= 0) ? matcher.group(refs[i]) : strings[i]); - return sb.toString(); - } - return null; + public int getRuleIndex() { + return ruleIndex; + } + + public boolean matches(String value) { + return getMatcher(value) != null; + } + + public String replace(String value) { + Matcher matcher = getMatcher(value); + if (matcher != null) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < strings.length; i++) { + sb.append((refs[i] >= 0) ? matcher.group(refs[i]) : strings[i]); + } + return sb.toString(); } + return null; + } - private Matcher getMatcher(String value) { - Matcher matcher = pattern.matcher(value); - if (matcher.matches() && checkIdentifierChars(value, "/")) - return matcher; - return null; + private Matcher getMatcher(String value) { + Matcher matcher = pattern.matcher(value); + if (matcher.matches() && checkIdentifierChars(value, "/-")) { + return matcher; } + return null; + } - private static boolean checkIdentifierChars(String expr, String extra) { - // package-info violates the spec for Java Identifiers. - // Nevertheless, expressions that end with this string are still legal. - // See 7.4.1.1 of the Java language spec for discussion. - if (expr.endsWith("package-info")) { - expr = expr.substring(0, expr.length() - "package-info".length()); + private static boolean checkIdentifierChars(String expr, String extra) { + // package-info violates the spec for Java Identifiers. + // Nevertheless, expressions that end with this string are still legal. + // See 7.4.1.1 of the Java language spec for discussion. + 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) { + continue; } - for (int i = 0, len = expr.length(); i < len; i++) { - char c = expr.charAt(i); - if (extra.indexOf(c) >= 0) - continue; - if (!Character.isJavaIdentifierPart(c)) - return false; + if (!Character.isJavaIdentifierPart(c)) { + return false; } - return true; } + return true; + } - private static String replaceAllLiteral(Pattern pattern, String value, String replace) { - replace = replace.replaceAll("([$\\\\])", "\\\\$0"); - return pattern.matcher(value).replaceAll(replace); - } + private static String replaceAllLiteral(Pattern pattern, String value, String replace) { + replace = replace.replaceAll("([$\\\\])", "\\\\$0"); + return pattern.matcher(value).replaceAll(replace); + } - public String toString() { - return "Wildcard{pattern=" + pattern + ",parts=" + parts + "}"; - } + @Override + public String toString() { + return "Wildcard{pattern=" + pattern + ",parts=" + parts + "}"; + } } diff --git a/src/main/com/tonicsystems/jarjar/WildcardTrie.java b/src/main/com/tonicsystems/jarjar/WildcardTrie.java index e80dbc9..c1e9b2e 100644 --- a/src/main/com/tonicsystems/jarjar/WildcardTrie.java +++ b/src/main/com/tonicsystems/jarjar/WildcardTrie.java @@ -23,69 +23,71 @@ import java.util.List; import java.util.TreeMap; /** - * A prefix trie of {@link Wildcard}, where the prefix is obtained from - * {@link Wildcard#getPlainTextPrefix()}. + * A prefix trie of {@link Wildcard}, where the prefix is obtained from {@link + * Wildcard#getPlainTextPrefix()}. * - * This allows quick lookup of applicable wildcards in the common case where wildcards have a + * <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; + 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); - } - } + 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 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; + 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; - } + 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/Zap.java b/src/main/com/tonicsystems/jarjar/Zap.java index ed5c828..30a341d 100644 --- a/src/main/com/tonicsystems/jarjar/Zap.java +++ b/src/main/com/tonicsystems/jarjar/Zap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,4 @@ package com.tonicsystems.jarjar; -public class Zap extends PatternElement -{ -} +public class Zap extends PatternElement {} diff --git a/src/main/com/tonicsystems/jarjar/ZapProcessor.java b/src/main/com/tonicsystems/jarjar/ZapProcessor.java index 5b6b680..5c2c845 100644 --- a/src/main/com/tonicsystems/jarjar/ZapProcessor.java +++ b/src/main/com/tonicsystems/jarjar/ZapProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,32 +16,34 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; +import com.tonicsystems.jarjar.util.EntryStruct; +import com.tonicsystems.jarjar.util.JarProcessor; import java.io.IOException; -import java.util.*; +import java.util.List; -class ZapProcessor implements JarProcessor -{ - private List<Wildcard> wildcards; +class ZapProcessor implements JarProcessor { + private final List<Wildcard> wildcards; - public ZapProcessor(List<Zap> zapList) { - wildcards = PatternElement.createWildcards(zapList); + public ZapProcessor(List<Zap> zapList) { + wildcards = PatternElement.createWildcards(zapList); + } + + @Override + public boolean process(EntryStruct struct) throws IOException { + String name = struct.name; + if (struct.isClass()) { + return !zap(name.substring(0, name.length() - 6)); } + return true; + } - public boolean process(EntryStruct struct) throws IOException { - String name = struct.name; - if (name.endsWith(".class")) - return !zap(name.substring(0, name.length() - 6)); + private boolean zap(String desc) { + // TODO: optimize + for (Wildcard wildcard : wildcards) { + if (wildcard.matches(desc)) { return true; + } } - - private boolean zap(String desc) { - // TODO: optimize - for (Wildcard wildcard : wildcards) { - if (wildcard.matches(desc)) - return true; - } - return false; - } + return false; + } } - diff --git a/src/main/com/tonicsystems/jarjar/util/AntJarProcessor.java b/src/main/com/tonicsystems/jarjar/util/AntJarProcessor.java index ac30418..85bbe6c 100644 --- a/src/main/com/tonicsystems/jarjar/util/AntJarProcessor.java +++ b/src/main/com/tonicsystems/jarjar/util/AntJarProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,83 +25,86 @@ import org.apache.tools.zip.JarMarker; import org.apache.tools.zip.ZipExtraField; import org.apache.tools.zip.ZipOutputStream; -abstract public class AntJarProcessor extends Jar -{ - private EntryStruct struct = new EntryStruct(); - private JarProcessor proc; - private byte[] buf = new byte[0x2000]; - - private Set<String> dirs = new HashSet<String>(); - private boolean filesOnly; - - protected boolean verbose; - - private static final ZipExtraField[] JAR_MARKER = new ZipExtraField[] { - JarMarker.getInstance() - }; - - public void setVerbose(boolean verbose) { - this.verbose = verbose; - } - - public abstract void execute() throws BuildException; - - public void execute(JarProcessor proc) throws BuildException { - this.proc = proc; - super.execute(); - } - - public void setFilesonly(boolean f) { - super.setFilesonly(f); - filesOnly = f; - } - - protected void zipDir(File dir, ZipOutputStream zOut, String vPath, int mode) - throws IOException { - } - - protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath, - long lastModified, File fromArchive, int mode) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - IoUtil.pipe(is, baos, buf); - struct.data = baos.toByteArray(); - struct.name = vPath; - struct.time = lastModified; - if (proc.process(struct)) { - if (mode == 0) - mode = ZipFileSet.DEFAULT_FILE_MODE; - if (!filesOnly) { - addParentDirs(struct.name, zOut); - } - super.zipFile(new ByteArrayInputStream(struct.data), - zOut, struct.name, struct.time, fromArchive, mode); - } - } - - private void addParentDirs(String file, ZipOutputStream zOut) throws IOException { - int slash = file.lastIndexOf('/'); - if (slash >= 0) { - String dir = file.substring(0, slash); - if (dirs.add(dir)) { - addParentDirs(dir, zOut); - super.zipDir((File) null, zOut, dir + "/", ZipFileSet.DEFAULT_DIR_MODE, JAR_MARKER); - } +public abstract class AntJarProcessor extends Jar { + private EntryStruct struct = new EntryStruct(); + private JarProcessor proc; + private byte[] buf = new byte[0x2000]; + + private Set<String> dirs = new HashSet<String>(); + private boolean filesOnly; + + protected boolean verbose; + + private static final ZipExtraField[] JAR_MARKER = new ZipExtraField[] {JarMarker.getInstance()}; + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public abstract void execute() throws BuildException; + + public void execute(JarProcessor proc) throws BuildException { + this.proc = proc; + super.execute(); + } + + public void setFilesonly(boolean f) { + super.setFilesonly(f); + filesOnly = f; + } + + protected void zipDir(File dir, ZipOutputStream zOut, String vPath, int mode) + throws IOException {} + + protected void zipFile( + InputStream is, + ZipOutputStream zOut, + String vPath, + long lastModified, + File fromArchive, + int mode) + throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + IoUtil.pipe(is, baos, buf); + struct.data = baos.toByteArray(); + struct.name = vPath; + struct.time = lastModified; + if (proc.process(struct)) { + if (mode == 0) { + mode = ZipFileSet.DEFAULT_FILE_MODE; } + if (!filesOnly) { + addParentDirs(struct.name, zOut); + } + super.zipFile( + new ByteArrayInputStream(struct.data), zOut, struct.name, struct.time, fromArchive, mode); } - - public void reset() { - super.reset(); - cleanHelper(); - } - - protected void cleanUp() { - super.cleanUp(); - cleanHelper(); - } - - protected void cleanHelper() { - verbose = false; - filesOnly = false; - dirs.clear(); + } + + private void addParentDirs(String file, ZipOutputStream zOut) throws IOException { + int slash = file.lastIndexOf('/'); + if (slash >= 0) { + String dir = file.substring(0, slash); + if (dirs.add(dir)) { + addParentDirs(dir, zOut); + super.zipDir((File) null, zOut, dir + "/", ZipFileSet.DEFAULT_DIR_MODE, JAR_MARKER); + } } + } + + public void reset() { + super.reset(); + cleanHelper(); + } + + protected void cleanUp() { + super.cleanUp(); + cleanHelper(); + } + + protected void cleanHelper() { + verbose = false; + filesOnly = false; + dirs.clear(); + } } diff --git a/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java b/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java index 6e448be..793b270 100644 --- a/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java +++ b/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,171 +16,180 @@ package com.tonicsystems.jarjar.util; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Array; -import java.util.*; - -public class ClassHeaderReader -{ - private int access; - private String thisClass; - private String superClass; - private String[] interfaces; - - private InputStream in; - private byte[] b = new byte[0x2000]; - private int[] items = new int[1000]; - private int bsize = 0; - private MyByteArrayInputStream bin = new MyByteArrayInputStream(); - private DataInputStream data = new DataInputStream(bin); - - public int getAccess() { - return access; - } - - public String getClassName() { - return thisClass; - } - public String getSuperName() { - return superClass; +public class ClassHeaderReader { + private int access; + private String thisClass; + private String superClass; + private String[] interfaces; + + private InputStream in; + private byte[] b = new byte[0x2000]; + private int[] items = new int[1000]; + private int bsize = 0; + private final MyByteArrayInputStream bin = new MyByteArrayInputStream(); + private final DataInputStream data = new DataInputStream(bin); + + public int getAccess() { + return access; + } + + public String getClassName() { + return thisClass; + } + + public String getSuperName() { + return superClass; + } + + public String[] getInterfaces() { + return interfaces; + } + + public void read(InputStream in) throws IOException { + try { + this.in = in; + bsize = 0; + access = 0; + thisClass = superClass = null; + interfaces = null; + + try { + buffer(4); + } catch (IOException e) { + // ignore + } + if (b[0] != (byte) 0xCA + || b[1] != (byte) 0xFE + || b[2] != (byte) 0xBA + || b[3] != (byte) 0xBE) { + throw new ClassFormatError("Bad magic number"); + } + + buffer(6); + readUnsignedShort(4); // minorVersion + readUnsignedShort(6); // majorVersion + // TODO: check version + int constantPoolCount = readUnsignedShort(8); + items = (int[]) resizeArray(items, constantPoolCount); + + int index = 10; + for (int i = 1; i < constantPoolCount; i++) { + int size; + buffer(index + 3); // TODO: reduce calls to buffer + int tag = b[index]; + items[i] = index + 1; + switch (tag) { + case 9: // Fieldref + case 10: // Methodref + case 11: // InterfaceMethodref + case 3: // Integer + case 4: // Float + case 12: // NameAndType + size = 4; + break; + case 5: // Long + case 6: // Double + size = 8; + i++; + break; + case 1: // Utf8 + size = 2 + readUnsignedShort(index + 1); + break; + case 7: // Class + case 8: // String + size = 2; + break; + default: + throw new IllegalStateException("Unknown constant pool tag " + tag); + } + index += size + 1; + } + buffer(index + 8); + access = readUnsignedShort(index); + thisClass = readClass(index + 2); + superClass = readClass(index + 4); + int interfacesCount = readUnsignedShort(index + 6); + + index += 8; + buffer(index + interfacesCount * 2); + interfaces = new String[interfacesCount]; + for (int i = 0; i < interfacesCount; i++) { + interfaces[i] = readClass(index); + index += 2; + } + } finally { + in.close(); } - - public String[] getInterfaces() { - return interfaces; + } + + private static int read(InputStream in, byte[] b, int off, int len) throws IOException { + int total = 0; + while (total < len) { + int result = in.read(b, off + total, len - total); + if (result == -1) { + break; + } + total += result; } + return total; + } - public void read(InputStream in) throws IOException { - try { - this.in = in; - bsize = 0; - access = 0; - thisClass = superClass = null; - interfaces = null; - - try { - buffer(4); - } catch (IOException e) { - // ignore - } - if (b[0] != (byte)0xCA || b[1] != (byte)0xFE || b[2] != (byte)0xBA || b[3] != (byte)0xBE) - throw new ClassFormatError("Bad magic number"); - - buffer(6); - readUnsignedShort(4); // minorVersion - readUnsignedShort(6); // majorVersion - // TODO: check version - int constant_pool_count = readUnsignedShort(8); - items = (int[])resizeArray(items, constant_pool_count); - - int index = 10; - for (int i = 1; i < constant_pool_count; i++) { - int size; - buffer(index + 3); // TODO: reduce calls to buffer - int tag = b[index]; - items[i] = index + 1; - switch (tag) { - case 9: // Fieldref - case 10: // Methodref - case 11: // InterfaceMethodref - case 3: // Integer - case 4: // Float - case 12: // NameAndType - size = 4; - break; - case 5: // Long - case 6: // Double - size = 8; - i++; - break; - case 1: // Utf8 - size = 2 + readUnsignedShort(index + 1); - break; - case 7: // Class - case 8: // String - size = 2; - break; - default: - throw new IllegalStateException("Unknown constant pool tag " + tag); - } - index += size + 1; - } - buffer(index + 8); - access = readUnsignedShort(index); - thisClass = readClass(index + 2); - superClass = readClass(index + 4); - int interfaces_count = readUnsignedShort(index + 6); - - index += 8; - buffer(index + interfaces_count * 2); - interfaces = new String[interfaces_count]; - for (int i = 0; i < interfaces_count; i++) { - interfaces[i] = readClass(index); - index += 2; - } - } finally { - in.close(); - } + private String readClass(int index) throws IOException { + index = readUnsignedShort(index); + if (index == 0) { + return null; } + index = readUnsignedShort(items[index]); + bin.readFrom(b, items[index]); + return data.readUTF(); + } - private String readClass(int index) throws IOException { - index = readUnsignedShort(index); - if (index == 0) - return null; - index = readUnsignedShort(items[index]); - bin.readFrom(b, items[index]); - return data.readUTF(); - } + private int readUnsignedShort(int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } - private int readUnsignedShort(int index) { - byte[] b = this.b; - return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); - } + private static final int CHUNK = 2048; - private static final int CHUNK = 2048; - private void buffer(int amount) throws IOException { - if (amount > b.length) - b = (byte[])resizeArray(b, b.length * 2); - if (amount > bsize) { - int rounded = (int)(CHUNK * Math.ceil((float)amount / CHUNK)); - bsize += read(in, b, bsize, rounded - bsize); - if (amount > bsize) - throw new EOFException(); - } + private void buffer(int amount) throws IOException { + if (amount > b.length) { + b = (byte[]) resizeArray(b, b.length * 2); } - - private static int read(InputStream in, byte[] b, int off, int len) throws IOException { - int total = 0; - while (total < len) { - int result = in.read(b, off + total, len - total); - if (result == -1) - break; - total += result; - } - return total; + if (amount > bsize) { + int rounded = (int) (CHUNK * Math.ceil((float) amount / CHUNK)); + bsize += read(in, b, bsize, rounded - bsize); + if (amount > bsize) { + throw new EOFException(); + } } - - private static Object resizeArray(Object array, int length) - { - if (Array.getLength(array) < length) { - Object newArray = Array.newInstance(array.getClass().getComponentType(), length); - System.arraycopy(array, 0, newArray, 0, Array.getLength(array)); - return newArray; - } else { - return array; - } + } + + private static Object resizeArray(Object array, int length) { + if (Array.getLength(array) < length) { + Object newArray = Array.newInstance(array.getClass().getComponentType(), length); + System.arraycopy(array, 0, newArray, 0, Array.getLength(array)); + return newArray; + } else { + return array; } + } - private static class MyByteArrayInputStream extends ByteArrayInputStream - { - public MyByteArrayInputStream() { - super(new byte[0]); - } + private static class MyByteArrayInputStream extends ByteArrayInputStream { + public MyByteArrayInputStream() { + super(new byte[0]); + } - public void readFrom(byte[] buf, int pos) { - this.buf = buf; - this.pos = pos; - count = buf.length; - } + public void readFrom(byte[] buf, int pos) { + this.buf = buf; + this.pos = pos; + count = buf.length; } + } } diff --git a/src/main/com/tonicsystems/jarjar/util/ClassPathEntry.java b/src/main/com/tonicsystems/jarjar/util/ClassPathEntry.java index d8de708..e90fd8c 100644 --- a/src/main/com/tonicsystems/jarjar/util/ClassPathEntry.java +++ b/src/main/com/tonicsystems/jarjar/util/ClassPathEntry.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,11 +16,13 @@ package com.tonicsystems.jarjar.util; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; public interface ClassPathEntry { String getSource() throws IOException; + String getName(); + InputStream openStream() throws IOException; -}
\ No newline at end of file +} diff --git a/src/main/com/tonicsystems/jarjar/util/ClassPathIterator.java b/src/main/com/tonicsystems/jarjar/util/ClassPathIterator.java index f4f54f0..6c166a2 100644 --- a/src/main/com/tonicsystems/jarjar/util/ClassPathIterator.java +++ b/src/main/com/tonicsystems/jarjar/util/ClassPathIterator.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,219 +16,256 @@ package com.tonicsystems.jarjar.util; -import java.util.*; -import java.util.zip.*; -import java.io.*; -import java.util.jar.*; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; -public class ClassPathIterator implements Iterator<ClassPathEntry> -{ - private static final FileFilter CLASS_FILTER = new FileFilter() { +public class ClassPathIterator implements Iterator<ClassPathEntry> { + private static final FileFilter CLASS_FILTER = + new FileFilter() { + @Override public boolean accept(File file) { - return file.isDirectory() || isClass(file.getName()); + return file.isDirectory() || isClass(file.getName()); } - }; + }; - private static final FileFilter JAR_FILTER = new FileFilter() { + private static final FileFilter JAR_FILTER = + new FileFilter() { + @Override public boolean accept(File file) { - return hasExtension(file.getName(), ".jar"); + return hasExtension(file.getName(), ".jar"); } - }; - - private final Iterator<File> files; - private Iterator<ClassPathEntry> entries = Collections.<ClassPathEntry>emptyList().iterator(); - private ClassPathEntry next; - private List<ZipFile> zips = new ArrayList<ZipFile>(); - - public ClassPathIterator(String classPath) throws IOException { - this(new File(System.getProperty("user.dir")), classPath, null); - } - - public ClassPathIterator(File parent, String classPath, String delim) throws IOException { - if (delim == null) { - delim = System.getProperty("path.separator"); + }; + + private final Iterator<File> files; + private Iterator<ClassPathEntry> entries = Collections.emptyIterator(); + private ClassPathEntry next; + private final List<ZipFile> zips = new ArrayList<>(); + + public ClassPathIterator(String classPath) throws IOException { + this(new File(System.getProperty("user.dir")), classPath, null); + } + + public ClassPathIterator(File parent, String classPath, String delim) throws IOException { + if (delim == null) { + delim = System.getProperty("path.separator"); + } + StringTokenizer st = new StringTokenizer(classPath, delim); + List<File> fileList = new ArrayList<>(); + while (st.hasMoreTokens()) { + String part = (String) st.nextElement(); + boolean wildcard = false; + if (part.endsWith("/*")) { + part = part.substring(0, part.length() - 1); + if (part.indexOf('*') >= 0) { + throw new IllegalArgumentException("Multiple wildcards are not allowed: " + part); } - StringTokenizer st = new StringTokenizer(classPath, delim); - List<File> fileList = new ArrayList<File>(); - while (st.hasMoreTokens()) { - String part = (String)st.nextElement(); - boolean wildcard = false; - if (part.endsWith("/*")) { - part = part.substring(0, part.length() - 1); - if (part.indexOf('*') >= 0) - throw new IllegalArgumentException("Multiple wildcards are not allowed: " + part); - wildcard = true; - } else if (part.indexOf('*') >= 0) { - throw new IllegalArgumentException("Incorrect wildcard usage: " + part); - } - - File file = new File(part); - if (!file.isAbsolute()) - file = new File(parent, part); - if (!file.exists()) - throw new IllegalArgumentException("File " + file + " does not exist"); - - if (wildcard) { - if (!file.isDirectory()) - throw new IllegalArgumentException("File " + file + " + is not a directory"); - fileList.addAll(findFiles(file, JAR_FILTER, false, new ArrayList<File>())); - } else { - fileList.add(file); - } + wildcard = true; + } else if (part.indexOf('*') >= 0) { + throw new IllegalArgumentException("Incorrect wildcard usage: " + part); + } + + File file = new File(part); + if (!file.isAbsolute()) { + file = new File(parent, part); + } + if (!file.exists()) { + throw new IllegalArgumentException("File " + file + " does not exist"); + } + + if (wildcard) { + if (!file.isDirectory()) { + throw new IllegalArgumentException("File " + file + " + is not a directory"); } - this.files = fileList.iterator(); - advance(); + fileList.addAll(findFiles(file, JAR_FILTER, false, new ArrayList<File>())); + } else { + fileList.add(file); + } } + this.files = fileList.iterator(); + advance(); + } - public boolean hasNext() { - return next != null; + @Override + public boolean hasNext() { + return next != null; + } + + /** Closes all zip files opened by this iterator. */ + public void close() throws IOException { + next = null; + for (ZipFile zip : zips) { + zip.close(); } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } - /** Closes all zip files opened by this iterator. */ - public void close() throws IOException { - next = null; - for (ZipFile zip : zips) { - zip.close(); + @Override + public ClassPathEntry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + ClassPathEntry result = next; + try { + advance(); + } catch (IOException e) { + throw new RuntimeIOException(e); + } + return result; + } + + private void advance() throws IOException { + if (!entries.hasNext()) { + if (!files.hasNext()) { + next = null; + return; } + File file = files.next(); + if (hasExtension(file.getName(), ".jar")) { + ZipFile zip = new JarFile(file); + zips.add(zip); + entries = new ZipIterator(zip); + } else if (hasExtension(file.getName(), ".zip")) { + ZipFile zip = new ZipFile(file); + zips.add(zip); + entries = new ZipIterator(zip); + } else if (file.isDirectory()) { + entries = new FileIterator(file); + } else { + throw new IllegalArgumentException("Do not know how to handle " + file); + } + } + + boolean foundClass = false; + while (!foundClass && entries.hasNext()) { + next = entries.next(); + foundClass = isClass(next.getName()); + } + if (!foundClass) { + advance(); + } + } + + private static class ZipIterator implements Iterator<ClassPathEntry> { + private final ZipFile zip; + private final Enumeration<? extends ZipEntry> entries; + + ZipIterator(ZipFile zip) { + this.zip = zip; + this.entries = zip.entries(); } + @Override + public boolean hasNext() { + return entries.hasMoreElements(); + } + + @Override public void remove() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(); } + @Override public ClassPathEntry next() { - if (!hasNext()) - throw new NoSuchElementException(); - ClassPathEntry result = next; - try { - advance(); - } catch (IOException e) { - throw new RuntimeIOException(e); - } - return result; - } - - private void advance() throws IOException { - if (!entries.hasNext()) { - if (!files.hasNext()) { - next = null; - return; - } - File file = files.next(); - if (hasExtension(file.getName(), ".jar")) { - ZipFile zip = new JarFile(file); - zips.add(zip); - entries = new ZipIterator(zip); - } else if (hasExtension(file.getName(), ".zip")) { - ZipFile zip = new ZipFile(file); - zips.add(zip); - entries = new ZipIterator(zip); - } else if (file.isDirectory()) { - entries = new FileIterator(file); - } else { - throw new IllegalArgumentException("Do not know how to handle " + file); - } + final ZipEntry entry = entries.nextElement(); + return new ClassPathEntry() { + @Override + public String getSource() { + return zip.getName(); } - boolean foundClass = false; - while (!foundClass && entries.hasNext()) { - next = entries.next(); - foundClass = isClass(next.getName()); + @Override + public String getName() { + return entry.getName(); } - if (!foundClass) { - advance(); + + @Override + public InputStream openStream() throws IOException { + return zip.getInputStream(entry); } + }; } + } - private static class ZipIterator implements Iterator<ClassPathEntry> { - private final ZipFile zip; - private final Enumeration<? extends ZipEntry> entries; - - ZipIterator(ZipFile zip) { - this.zip = zip; - this.entries = zip.entries(); - } - - public boolean hasNext() { - return entries.hasMoreElements(); - } - - public void remove() { - throw new UnsupportedOperationException(); - } + private static class FileIterator implements Iterator<ClassPathEntry> { + private final File dir; + private final Iterator<File> entries; - public ClassPathEntry next() { - final ZipEntry entry = entries.nextElement(); - return new ClassPathEntry() { - public String getSource() { - return zip.getName(); - } - - public String getName() { - return entry.getName(); - } - - public InputStream openStream() throws IOException { - return zip.getInputStream(entry); - } - }; - } + FileIterator(File dir) { + this.dir = dir; + this.entries = findFiles(dir, CLASS_FILTER, true, new ArrayList<File>()).iterator(); } - private static class FileIterator implements Iterator<ClassPathEntry> { - private final File dir; - private final Iterator<File> entries; + @Override + public boolean hasNext() { + return entries.hasNext(); + } - FileIterator(File dir) { - this.dir = dir; - this.entries = findFiles(dir, CLASS_FILTER, true, new ArrayList<File>()).iterator(); - } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } - public boolean hasNext() { - return entries.hasNext(); - } + @Override + public ClassPathEntry next() { + final File file = entries.next(); + return new ClassPathEntry() { + @Override + public String getSource() throws IOException { + return dir.getCanonicalPath(); + } - public void remove() { - throw new UnsupportedOperationException(); - } - - public ClassPathEntry next() { - final File file = entries.next(); - return new ClassPathEntry() { - public String getSource() throws IOException { - return dir.getCanonicalPath(); - } - - public String getName() { - return file.getName(); - } - - public InputStream openStream() throws IOException { - return new BufferedInputStream(new FileInputStream(file)); - } - }; - } - } + @Override + public String getName() { + return file.getName(); + } - private static List<File> findFiles(File dir, FileFilter filter, boolean recurse, List<File> collect) { - for (File file : dir.listFiles(filter)) { - if (recurse && file.isDirectory()) { - findFiles(file, filter, recurse, collect); - } else { - collect.add(file); - } + @Override + public InputStream openStream() throws IOException { + return new BufferedInputStream(new FileInputStream(file)); } - return collect; + }; } + } - private static boolean isClass(String name) { - return hasExtension(name, ".class"); + private static List<File> findFiles( + File dir, FileFilter filter, boolean recurse, List<File> collect) { + for (File file : dir.listFiles(filter)) { + if (recurse && file.isDirectory()) { + findFiles(file, filter, recurse, collect); + } else { + collect.add(file); + } } + return collect; + } + + private static boolean isClass(String name) { + return hasExtension(name, ".class"); + } - private static boolean hasExtension(String name, String ext) { - if (name.length() < ext.length()) - return false; - String actual = name.substring(name.length() - ext.length()); - return actual.equals(ext) || actual.equals(ext.toUpperCase()); + private static boolean hasExtension(String name, String ext) { + if (name.length() < ext.length()) { + return false; } + String actual = name.substring(name.length() - ext.length()); + return actual.equals(ext) || actual.equals(ext.toUpperCase()); + } } diff --git a/src/main/com/tonicsystems/jarjar/util/EntryStruct.java b/src/main/com/tonicsystems/jarjar/util/EntryStruct.java index 93f7622..36f9b76 100644 --- a/src/main/com/tonicsystems/jarjar/util/EntryStruct.java +++ b/src/main/com/tonicsystems/jarjar/util/EntryStruct.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,12 +16,44 @@ package com.tonicsystems.jarjar.util; -import java.io.InputStream; -import java.io.File; +import java.util.Arrays; +import java.util.Objects; -public class EntryStruct -{ - public byte[] data; - public String name; - public long time; +public class EntryStruct { + public byte[] data; + public String name; + public long time; + + /** Returns true if the entry is a class file. */ + public boolean isClass() { + if (!name.endsWith(".class")) { + return false; + } + if (name.startsWith("META-INF/version")) { + // TODO(b/69678527): handle multi-release jar files + return false; + } + return true; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (!(other instanceof EntryStruct)) { + return false; + } + + EntryStruct that = (EntryStruct) other; + return this.name.equals(that.name) && + Arrays.equals(this.data, that.data) && + this.time == that.time; + } + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(data), name, time); + } } diff --git a/src/main/com/tonicsystems/jarjar/util/GetNameClassWriter.java b/src/main/com/tonicsystems/jarjar/util/GetNameClassWriter.java index cd2cc9b..b2a8bb4 100644 --- a/src/main/com/tonicsystems/jarjar/util/GetNameClassWriter.java +++ b/src/main/com/tonicsystems/jarjar/util/GetNameClassWriter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,28 +16,34 @@ package com.tonicsystems.jarjar.util; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; -public class GetNameClassWriter extends ClassVisitor -{ - private String className; - - public GetNameClassWriter(int flags) { - super(Opcodes.ASM9,new ClassWriter(flags)); - } +public class GetNameClassWriter extends ClassVisitor { + private String className; + + public GetNameClassWriter(int flags) { + super(Opcodes.ASM9, new ClassWriter(flags)); + } + + @Override + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + className = name; + super.visit(version, access, name, signature, superName, interfaces); + } + + public String getClassName() { + return className; + } - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - className = name; - super.visit(version, access, name, signature, superName, interfaces); - } - - public String getClassName() { - return className; - } - - public byte[] toByteArray() { - return ((ClassWriter) cv).toByteArray(); - } + public byte[] toByteArray() { + return ((ClassWriter) cv).toByteArray(); + } } diff --git a/src/main/com/tonicsystems/jarjar/util/IoUtil.java b/src/main/com/tonicsystems/jarjar/util/IoUtil.java index aef7ade..e26b485 100644 --- a/src/main/com/tonicsystems/jarjar/util/IoUtil.java +++ b/src/main/com/tonicsystems/jarjar/util/IoUtil.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,13 @@ package com.tonicsystems.jarjar.util; -import java.io.*; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -26,106 +32,88 @@ import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; class IoUtil { - private IoUtil() {} + private IoUtil() {} - public static void pipe(InputStream is, OutputStream out, byte[] buf) throws IOException { - for (;;) { - int amt = is.read(buf); - if (amt < 0) - break; - out.write(buf, 0, amt); - } + public static void pipe(InputStream is, OutputStream out, byte[] buf) throws IOException { + for (; ; ) { + int amt = is.read(buf); + if (amt < 0) { + break; + } + out.write(buf, 0, amt); } + } - public static void copy(File from, File to, byte[] buf) throws IOException { - InputStream in = new FileInputStream(from); - try { - OutputStream out = new FileOutputStream(to); - try { - pipe(in, out, buf); - } finally { - out.close(); - } - } finally { - in.close(); - } + public static void copy(File from, File to, byte[] buf) throws IOException { + try (InputStream in = new FileInputStream(from); + OutputStream out = new FileOutputStream(to)) { + pipe(in, out, buf); } + } - /** - * Create a copy of an zip file without its empty directories. - * @param inputFile - * @param outputFile - * @throws IOException - */ - public static void copyZipWithoutEmptyDirectories(final File inputFile, final File outputFile) throws IOException - { - final byte[] buf = new byte[0x2000]; - - final ZipFile inputZip = new ZipFile(inputFile); - final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(outputFile)); - try - { - // read a the entries of the input zip file and sort them - final Enumeration<? extends ZipEntry> e = inputZip.entries(); - final ArrayList<ZipEntry> sortedList = new ArrayList<ZipEntry>(); - while (e.hasMoreElements()) { - final ZipEntry entry = e.nextElement(); - sortedList.add(entry); - } - - Collections.sort(sortedList, new Comparator<ZipEntry>() - { - public int compare(ZipEntry o1, ZipEntry o2) - { - return o1.getName().compareTo(o2.getName()); - } - }); - - // treat them again and write them in output, wenn they not are empty directories - for (int i = sortedList.size()-1; i>=0; i--) - { - final ZipEntry inputEntry = sortedList.get(i); - final String name = inputEntry.getName(); - final boolean isEmptyDirectory; - if (inputEntry.isDirectory()) - { - if (i == sortedList.size()-1) - { - // no item afterwards; it was an empty directory - isEmptyDirectory = true; - } - else - { - final String nextName = sortedList.get(i+1).getName(); - isEmptyDirectory = !nextName.startsWith(name); - } - } - else - { - isEmptyDirectory = false; - } + /** + * Create a copy of an zip file without its empty directories. + * + * @param inputFile the input file + * @param outputFile the output file + * @throws IOException + */ + public static void copyZipWithoutEmptyDirectories(final File inputFile, final File outputFile) + throws IOException { + final byte[] buf = new byte[0x2000]; + final ZipFile inputZip = new ZipFile(inputFile); + final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(outputFile)); + try { + // read a the entries of the input zip file and sort them + final Enumeration<? extends ZipEntry> e = inputZip.entries(); + final ArrayList<ZipEntry> sortedList = new ArrayList<>(); + while (e.hasMoreElements()) { + final ZipEntry entry = e.nextElement(); + sortedList.add(entry); + } - // write the entry - if (isEmptyDirectory) - { - sortedList.remove(inputEntry); - } - else - { - final ZipEntry outputEntry = new ZipEntry(inputEntry); - outputStream.putNextEntry(outputEntry); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final InputStream is = inputZip.getInputStream(inputEntry); - IoUtil.pipe(is, baos, buf); - is.close(); - outputStream.write(baos.toByteArray()); - } + Collections.sort( + sortedList, + new Comparator<ZipEntry>() { + @Override + public int compare(ZipEntry o1, ZipEntry o2) { + return o1.getName().compareTo(o2.getName()); } - } finally { - outputStream.close(); + }); + + // treat them again and write them in output, wenn they not are empty directories + for (int i = sortedList.size() - 1; i >= 0; i--) { + final ZipEntry inputEntry = sortedList.get(i); + final String name = inputEntry.getName(); + final boolean isEmptyDirectory; + if (inputEntry.isDirectory()) { + if (i == sortedList.size() - 1) { + // no item afterwards; it was an empty directory + isEmptyDirectory = true; + } else { + final String nextName = sortedList.get(i + 1).getName(); + isEmptyDirectory = !nextName.startsWith(name); + } + } else { + isEmptyDirectory = false; } + // write the entry + if (isEmptyDirectory) { + sortedList.remove(inputEntry); + } else { + final ZipEntry outputEntry = new ZipEntry(inputEntry); + outputStream.putNextEntry(outputEntry); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (final InputStream is = inputZip.getInputStream(inputEntry)) { + IoUtil.pipe(is, baos, buf); + } + baos.writeTo(outputStream); + } + } + } finally { + outputStream.close(); } - + } } diff --git a/src/main/com/tonicsystems/jarjar/util/JarProcessor.java b/src/main/com/tonicsystems/jarjar/util/JarProcessor.java index 1560696..9e41fb6 100644 --- a/src/main/com/tonicsystems/jarjar/util/JarProcessor.java +++ b/src/main/com/tonicsystems/jarjar/util/JarProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,18 +18,16 @@ package com.tonicsystems.jarjar.util; import java.io.IOException; -public interface JarProcessor -{ - /** - * Process the entry (p.ex. rename the file) - * <p> - * Returns <code>true</code> if the processor has has changed the entry. In this case, the entry can be removed - * from the jar file in a future time. Return <code>false</code> for the entries which do not have been changed and - * there fore are not to be deleted - * - * @param struct - * @return <code>true</code> if he process chain can continue after this process - * @throws IOException - */ - boolean process(EntryStruct struct) throws IOException; +public interface JarProcessor { + /** + * Process the entry (p.ex. rename the file) + * + * <p>Returns <code>true</code> if the processor has has changed the entry. In this case, the + * entry can be removed from the jar file in a future time. Return <code>false</code> for the + * entries which do not have been changed and there fore are not to be deleted + * + * @param struct the struct + * @return <code>true</code> if he process chain can continue after this process + */ + boolean process(EntryStruct struct) throws IOException; } diff --git a/src/main/com/tonicsystems/jarjar/util/JarProcessorChain.java b/src/main/com/tonicsystems/jarjar/util/JarProcessorChain.java index 06ac85d..6360a3f 100644 --- a/src/main/com/tonicsystems/jarjar/util/JarProcessorChain.java +++ b/src/main/com/tonicsystems/jarjar/util/JarProcessorChain.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,31 +18,25 @@ package com.tonicsystems.jarjar.util; import java.io.IOException; -public class JarProcessorChain implements JarProcessor -{ - private final JarProcessor[] chain; +public class JarProcessorChain implements JarProcessor { + private final JarProcessor[] chain; - public JarProcessorChain(JarProcessor[] chain) - { - this.chain = chain.clone(); - } + public JarProcessorChain(JarProcessor[] chain) { + this.chain = chain.clone(); + } - /** - * @param struct - * @return <code>true</code> if the entry has run the complete chain - * @throws IOException - */ - public boolean process(EntryStruct struct) throws IOException - { + /** + * @param struct + * @return <code>true</code> if the entry has run the complete chain + * @throws IOException + */ + public boolean process(EntryStruct struct) throws IOException { - for (JarProcessor aChain : chain) - { - if (!aChain.process(struct)) - { - return false; - } - } - return true; + for (JarProcessor aChain : chain) { + if (!aChain.process(struct)) { + return false; + } } + return true; + } } - diff --git a/src/main/com/tonicsystems/jarjar/util/JarTransformer.java b/src/main/com/tonicsystems/jarjar/util/JarTransformer.java index 53c7de0..aced2ad 100644 --- a/src/main/com/tonicsystems/jarjar/util/JarTransformer.java +++ b/src/main/com/tonicsystems/jarjar/util/JarTransformer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,34 +16,42 @@ package com.tonicsystems.jarjar.util; -import java.io.*; +import java.io.IOException; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; -abstract public class JarTransformer implements JarProcessor -{ - public boolean process(EntryStruct struct) throws IOException { - if (struct.name.endsWith(".class")) { - ClassReader reader; - try { - reader = new ClassReader(struct.data); - } catch (Exception e) { - // 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); - reader.accept(transform(w), ClassReader.EXPAND_FRAMES); - struct.data = w.toByteArray(); - struct.name = pathFromName(w.getClassName()); - } - return true; +public abstract class JarTransformer implements JarProcessor { + @Override + public boolean process(EntryStruct struct) throws IOException { + if (struct.isClass() && !struct.name.startsWith("META-INF")) { + ClassReader reader; + try { + reader = new ClassReader(struct.data); + } catch (RuntimeException e) { + // 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); + reader.accept(visitor, ClassReader.EXPAND_FRAMES); + + boolean updateData = true; + if (visitor instanceof RemappingClassTransformer) { + updateData = ((RemappingClassTransformer) visitor).didRemap(); + } + if (updateData) { + struct.data = w.toByteArray(); + struct.name = pathFromName(w.getClassName()); + } } + return true; + } - abstract protected ClassVisitor transform(ClassVisitor v); + protected abstract ClassVisitor transform(ClassVisitor v); - private static String pathFromName(String className) { - return className.replace('.', '/') + ".class"; - } + private static String pathFromName(String className) { + return className.replace('.', '/') + ".class"; + } } diff --git a/src/main/com/tonicsystems/jarjar/util/JarTransformerChain.java b/src/main/com/tonicsystems/jarjar/util/JarTransformerChain.java index be16d9b..056422a 100644 --- a/src/main/com/tonicsystems/jarjar/util/JarTransformerChain.java +++ b/src/main/com/tonicsystems/jarjar/util/JarTransformerChain.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,19 +18,19 @@ package com.tonicsystems.jarjar.util; import org.objectweb.asm.ClassVisitor; -public class JarTransformerChain extends JarTransformer -{ - private final RemappingClassTransformer[] chain; - - public JarTransformerChain(RemappingClassTransformer[] chain) { - this.chain = chain.clone(); - for (int i = chain.length - 1; i > 0; i--) { - chain[i - 1].setTarget(chain[i]); - } - } +public class JarTransformerChain extends JarTransformer { + private final RemappingClassTransformer[] chain; - protected ClassVisitor transform(ClassVisitor v) { - chain[chain.length - 1].setTarget(v); - return chain[0]; + public JarTransformerChain(RemappingClassTransformer[] chain) { + this.chain = chain.clone(); + for (int i = chain.length - 1; i > 0; i--) { + chain[i - 1].setTarget(chain[i]); } + } + + @Override + protected ClassVisitor transform(ClassVisitor v) { + chain[chain.length - 1].setTarget(v); + return chain[0]; + } } diff --git a/src/main/com/tonicsystems/jarjar/util/RemappingClassTransformer.java b/src/main/com/tonicsystems/jarjar/util/RemappingClassTransformer.java index 9c1d4c9..0579f71 100644 --- a/src/main/com/tonicsystems/jarjar/util/RemappingClassTransformer.java +++ b/src/main/com/tonicsystems/jarjar/util/RemappingClassTransformer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,19 +16,105 @@ package com.tonicsystems.jarjar.util; +import com.tonicsystems.jarjar.EmptyClassVisitor; +import java.util.Objects; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.Remapper; -import com.tonicsystems.jarjar.EmptyClassVisitor; +public class RemappingClassTransformer extends ClassRemapper { + public RemappingClassTransformer(Remapper pr) { + super(new EmptyClassVisitor(), new RemapperTracker(pr)); + } + + public void setTarget(ClassVisitor target) { + ((RemapperTracker) remapper).didRemap = false; + cv = target; + } + + public boolean didRemap() { + return ((RemapperTracker) remapper).didRemap; + } -public class RemappingClassTransformer extends ClassRemapper -{ - public RemappingClassTransformer(Remapper pr) { - super(new EmptyClassVisitor(), pr); + public static class RemapperTracker extends Remapper { + + private final Remapper delegate; + public boolean didRemap; + + RemapperTracker(Remapper delegate) { + this.delegate = delegate; + this.didRemap = false; } - - public void setTarget(ClassVisitor target) { - cv = target; + + @Override + public String mapDesc(String desc) { + String output = delegate.mapDesc(desc); + didRemap = didRemap || !Objects.equals(output, desc); + return output; + } + + @Override + public String mapType(String type) { + String output = delegate.mapType(type); + didRemap = didRemap || !Objects.equals(output, type); + return output; + } + + @Override + public String[] mapTypes(String[] types) { + String[] localTypes = types.clone(); + String[] output = delegate.mapTypes(types); + didRemap = didRemap || !Objects.deepEquals(output, localTypes); + return output; + } + + @Override + public String mapMethodDesc(String desc) { + String output = delegate.mapMethodDesc(desc); + didRemap = didRemap || !Objects.equals(output, desc); + return output; + } + + @Override + public Object mapValue(Object value) { + Object output = delegate.mapValue(value); + didRemap = didRemap || !Objects.equals(output, value); + return output; + } + + @Override + public String mapSignature(String signature, boolean typeSignature) { + String output = delegate.mapSignature(signature, typeSignature); + didRemap = didRemap || !Objects.equals(output, signature); + return output; + } + + @Override + public String mapMethodName(String owner, String name, String desc) { + String output = delegate.mapMethodName(owner, name, desc); + didRemap = didRemap || !Objects.equals(output, name); + return output; + } + + @Override + public String mapInvokeDynamicMethodName(String name, String desc) { + String output = delegate.mapInvokeDynamicMethodName(name, desc); + didRemap = didRemap || !Objects.equals(output, name); + return output; + } + + @Override + public String mapFieldName(String owner, String name, String desc) { + String output = delegate.mapFieldName(owner, name, desc); + didRemap = didRemap || !Objects.equals(output, name); + return output; + } + + @Override + public String map(String typeName) { + String output = delegate.map(typeName); + didRemap = didRemap || !Objects.equals(output, typeName); + return output; } + } } diff --git a/src/main/com/tonicsystems/jarjar/util/RuntimeIOException.java b/src/main/com/tonicsystems/jarjar/util/RuntimeIOException.java index 9b5d4a8..0cf0a15 100644 --- a/src/main/com/tonicsystems/jarjar/util/RuntimeIOException.java +++ b/src/main/com/tonicsystems/jarjar/util/RuntimeIOException.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,11 +18,10 @@ package com.tonicsystems.jarjar.util; import java.io.IOException; -public class RuntimeIOException extends RuntimeException -{ - private static final long serialVersionUID = 0L; +public class RuntimeIOException extends RuntimeException { + private static final long serialVersionUID = 0L; - public RuntimeIOException(IOException e) { - super(e); - } + public RuntimeIOException(IOException e) { + super(e); + } } diff --git a/src/main/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java b/src/main/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java index 70b169b..2e87104 100644 --- a/src/main/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java +++ b/src/main/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,56 +16,60 @@ package com.tonicsystems.jarjar.util; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; -import java.util.Enumeration; -import java.io.*; -import java.util.*; -public class StandaloneJarProcessor -{ - public static void run(File from, File to, JarProcessor proc) throws IOException { - byte[] buf = new byte[0x2000]; +public final class StandaloneJarProcessor { + public static void run(File from, File to, JarProcessor proc) throws IOException { + byte[] buf = new byte[0x2000]; - JarFile in = new JarFile(from); - final File tmpTo = File.createTempFile("jarjar", ".jar"); - JarOutputStream out = new JarOutputStream(new FileOutputStream(tmpTo)); - Set<String> entries = new HashSet<String>(); - try { - EntryStruct struct = new EntryStruct(); - Enumeration<JarEntry> e = in.entries(); - while (e.hasMoreElements()) { - JarEntry entry = e.nextElement(); - struct.name = entry.getName(); - struct.time = entry.getTime(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - IoUtil.pipe(in.getInputStream(entry), baos, buf); - struct.data = baos.toByteArray(); - if (proc.process(struct)) { - if (entries.add(struct.name)) { - entry = new JarEntry(struct.name); - entry.setTime(struct.time); - entry.setCompressedSize(-1); - out.putNextEntry(entry); - out.write(struct.data); - } else if (struct.name.endsWith("/")) { - // TODO(chrisn): log - } else { - throw new IllegalArgumentException("Duplicate jar entries: " + struct.name); - } - } - } - - } - finally { - in.close(); - out.close(); + JarFile in = new JarFile(from); + final File tmpTo = File.createTempFile("jarjar", ".jar"); + JarOutputStream out = new JarOutputStream(new FileOutputStream(tmpTo)); + Map<String, EntryStruct> entries = new HashMap<>(); + try { + EntryStruct struct = new EntryStruct(); + Enumeration<JarEntry> e = in.entries(); + while (e.hasMoreElements()) { + JarEntry entry = e.nextElement(); + struct.name = entry.getName(); + struct.time = entry.getTime(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + IoUtil.pipe(in.getInputStream(entry), baos, buf); + struct.data = baos.toByteArray(); + if (proc.process(struct)) { + EntryStruct existEntry = entries.putIfAbsent(struct.name, struct); + if (existEntry == null) { + entry = new JarEntry(struct.name); + entry.setTime(struct.time); + entry.setCompressedSize(-1); + out.putNextEntry(entry); + out.write(struct.data); + } else if (struct.name.endsWith("/")) { + // TODO(chrisn): log + } else if (!existEntry.equals(struct)) { + throw new IllegalArgumentException("Duplicate jar entries: " + struct.name); + } } + } - // delete the empty directories - IoUtil.copyZipWithoutEmptyDirectories(tmpTo, to); - tmpTo.delete(); - + } finally { + in.close(); + out.close(); } + + // delete the empty directories + IoUtil.copyZipWithoutEmptyDirectories(tmpTo, to); + tmpTo.delete(); + } + + private StandaloneJarProcessor() {} } diff --git a/src/test/com/tonicsystems/jarjar/GenericsTest.java b/src/test/com/tonicsystems/jarjar/GenericsTest.java index 1996a24..29e2c6e 100644 --- a/src/test/com/tonicsystems/jarjar/GenericsTest.java +++ b/src/test/com/tonicsystems/jarjar/GenericsTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,29 +16,30 @@ package com.tonicsystems.jarjar; -import com.tonicsystems.jarjar.util.*; -import junit.framework.*; -import java.util.*; +import com.tonicsystems.jarjar.util.RemappingClassTransformer; +import java.util.Arrays; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; import org.objectweb.asm.ClassReader; -public class GenericsTest -extends TestCase -{ - public void testTransform() throws Exception { - Rule rule = new Rule(); - rule.setPattern("java.lang.String"); - rule.setResult("com.tonicsystems.String"); - RemappingClassTransformer t = new RemappingClassTransformer(new PackageRemapper(Arrays.asList(rule), false)); - t.setTarget(new EmptyClassVisitor()); - ClassReader reader = new ClassReader(getClass().getResourceAsStream("/Generics.class")); - reader.accept(t, 0); - } +public class GenericsTest extends TestCase { + public void testTransform() throws Exception { + Rule rule = new Rule(); + rule.setPattern("java.lang.String"); + rule.setResult("com.tonicsystems.String"); + RemappingClassTransformer t = + new RemappingClassTransformer(new PackageRemapper(Arrays.asList(rule), false)); + t.setTarget(new EmptyClassVisitor()); + ClassReader reader = new ClassReader(getClass().getResourceAsStream("/Generics.class")); + reader.accept(t, 0); + } - public GenericsTest(String name) { - super(name); - } + public GenericsTest(String name) { + super(name); + } - public static Test suite() { - return new TestSuite(GenericsTest.class); - } + public static Test suite() { + return new TestSuite(GenericsTest.class); + } } diff --git a/src/test/com/tonicsystems/jarjar/PackageRemapperTest.java b/src/test/com/tonicsystems/jarjar/PackageRemapperTest.java index aea9d46..a097931 100644 --- a/src/test/com/tonicsystems/jarjar/PackageRemapperTest.java +++ b/src/test/com/tonicsystems/jarjar/PackageRemapperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,44 +16,42 @@ package com.tonicsystems.jarjar; -import junit.framework.*; - import java.util.Collections; - -public class PackageRemapperTest -extends TestCase -{ - protected PackageRemapper remapper; - - protected void setUp() { - Rule rule = new Rule(); - rule.setPattern("org.**"); - rule.setResult("foo.@1"); - remapper = new PackageRemapper(Collections.singletonList(rule), false); - } - - public void testMapValue() { - assertUnchangedValue("[^\\s;/@&=,.?:+$]"); - assertUnchangedValue("[Ljava/lang/Object;"); - assertUnchangedValue("[Lorg/example/Object;"); - assertUnchangedValue("[Ljava.lang.Object;"); - assertUnchangedValue("[Lorg.example/Object;"); - assertUnchangedValue("[L;"); - assertUnchangedValue("[Lorg.example.Object;;"); - assertUnchangedValue("[Lorg.example.Obj ct;"); - assertUnchangedValue("org.example/Object"); - - assertEquals("[Lfoo.example.Object;", remapper.mapValue("[Lorg.example.Object;")); - assertEquals("foo.example.Object", remapper.mapValue("org.example.Object")); - assertEquals("foo/example/Object", remapper.mapValue("org/example/Object")); - assertEquals("foo/example.Object", remapper.mapValue("org/example.Object")); // path match - - assertEquals("foo.example.package-info", remapper.mapValue("org.example.package-info")); - assertEquals("foo/example/package-info", remapper.mapValue("org/example/package-info")); - assertEquals("foo/example.package-info", remapper.mapValue("org/example.package-info")); - } - - private void assertUnchangedValue(String value) { - assertEquals(value, remapper.mapValue(value)); - } +import junit.framework.TestCase; + +public class PackageRemapperTest extends TestCase { + protected PackageRemapper remapper; + + @Override + protected void setUp() { + Rule rule = new Rule(); + rule.setPattern("org.**"); + rule.setResult("foo.@1"); + remapper = new PackageRemapper(Collections.singletonList(rule), false); + } + + public void testMapValue() { + assertUnchangedValue("[^\\s;/@&=,.?:+$]"); + assertUnchangedValue("[Ljava/lang/Object;"); + assertUnchangedValue("[Lorg/example/Object;"); + assertUnchangedValue("[Ljava.lang.Object;"); + assertUnchangedValue("[Lorg.example/Object;"); + assertUnchangedValue("[L;"); + assertUnchangedValue("[Lorg.example.Object;;"); + assertUnchangedValue("[Lorg.example.Obj ct;"); + assertUnchangedValue("org.example/Object"); + + assertEquals("[Lfoo.example.Object;", remapper.mapValue("[Lorg.example.Object;")); + assertEquals("foo.example.Object", remapper.mapValue("org.example.Object")); + assertEquals("foo/example/Object", remapper.mapValue("org/example/Object")); + assertEquals("foo/example.Object", remapper.mapValue("org/example.Object")); // path match + + assertEquals("foo.example.package-info", remapper.mapValue("org.example.package-info")); + assertEquals("foo/example/package-info", remapper.mapValue("org/example/package-info")); + assertEquals("foo/example.package-info", remapper.mapValue("org/example.package-info")); + } + + private void assertUnchangedValue(String value) { + assertEquals(value, remapper.mapValue(value)); + } } diff --git a/src/test/com/tonicsystems/jarjar/RulesFileParserTest.java b/src/test/com/tonicsystems/jarjar/RulesFileParserTest.java index 4a4c371..5b3b9cd 100644 --- a/src/test/com/tonicsystems/jarjar/RulesFileParserTest.java +++ b/src/test/com/tonicsystems/jarjar/RulesFileParserTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,21 +16,20 @@ package com.tonicsystems.jarjar; -import junit.framework.*; -import java.io.*; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; -public class RulesFileParserTest -extends TestCase -{ - public void testSimple() throws Exception { - // TODO - } +public class RulesFileParserTest extends TestCase { + public void testSimple() throws Exception { + // TODO + } - public RulesFileParserTest(String name) { - super(name); - } + public RulesFileParserTest(String name) { + super(name); + } - public static Test suite() { - return new TestSuite(RulesFileParserTest.class); - } + public static Test suite() { + return new TestSuite(RulesFileParserTest.class); + } } diff --git a/src/test/com/tonicsystems/jarjar/ServiceProcessorTest.java b/src/test/com/tonicsystems/jarjar/ServiceProcessorTest.java new file mode 100644 index 0000000..2d6ac69 --- /dev/null +++ b/src/test/com/tonicsystems/jarjar/ServiceProcessorTest.java @@ -0,0 +1,45 @@ +/* + * Copyright 2024 Google Inc. + * + * 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 static java.nio.charset.StandardCharsets.UTF_8; + +import com.tonicsystems.jarjar.util.EntryStruct; +import java.util.Arrays; +import junit.framework.TestCase; + +public class ServiceProcessorTest extends TestCase { + public void testProcess() throws Exception { + Rule rule = new Rule(); + rule.setPattern("foo.**"); + rule.setResult("bar.@1"); + PackageRemapper remapper = new PackageRemapper(Arrays.asList(rule), false); + ServiceProcessor serviceProcessor = new ServiceProcessor(remapper); + + EntryStruct struct = new EntryStruct(); + struct.name = "META-INF/services/foo.Service"; + struct.data = "foo.baz.ServiceImplementation\n".getBytes(UTF_8); + + assertTrue(serviceProcessor.process(struct)); + assertEquals("META-INF/services/bar.Service", struct.name); + assertEquals("bar.baz.ServiceImplementation\n", new String(struct.data, UTF_8)); + } + + public ServiceProcessorTest(String name) { + super(name); + } +} diff --git a/src/test/com/tonicsystems/jarjar/WildcardTest.java b/src/test/com/tonicsystems/jarjar/WildcardTest.java index 7cdcdf7..d08d75c 100644 --- a/src/test/com/tonicsystems/jarjar/WildcardTest.java +++ b/src/test/com/tonicsystems/jarjar/WildcardTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,33 +16,43 @@ package com.tonicsystems.jarjar; -import junit.framework.*; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; -public class WildcardTest -extends TestCase -{ - public void testWildcards() { - wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/proxy/Mixin$Generator", - "foo/proxy/Mixin$Generator"); - wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/Bar", "foo/Bar"); - wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/Bar/Baz", "foo/Bar/Baz"); - wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/", "foo/"); - 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"); - } +public class WildcardTest extends TestCase { + public void testWildcards() { + wildcard( + "net/sf/cglib/**", + "foo/@1", + "net/sf/cglib/proxy/Mixin$Generator", + "foo/proxy/Mixin$Generator"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/Bar", "foo/Bar"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/Bar/Baz", "foo/Bar/Baz"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/", "foo/"); + 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); - // System.err.println(wc); - assertEquals(expect, wc.replace(value)); - } - - public WildcardTest(String name) { - super(name); - } + private void wildcard(String pattern, String result, String value, String expect) { + Wildcard wc = new Wildcard(pattern, result, 0); + // System.err.println(wc); + assertEquals(expect, wc.replace(value)); + } - public static Test suite() { - return new TestSuite(WildcardTest.class); - } + public WildcardTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(WildcardTest.class); + } } diff --git a/src/test/com/tonicsystems/jarjar/example/Example.java b/src/test/com/tonicsystems/jarjar/example/Example.java index 2129962..feedf09 100644 --- a/src/test/com/tonicsystems/jarjar/example/Example.java +++ b/src/test/com/tonicsystems/jarjar/example/Example.java @@ -1,5 +1,19 @@ +/* + * Copyright 2007 Google Inc. + * + * 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.example; -public class Example -{ -} +public class Example {} |