From c7e7232c8a58d3bdf8f7423645521a8b94c81bbd Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 29 Mar 2018 12:41:15 -0700 Subject: Add https://github.com/google/jarjar to third_party This is based on the original jarjar sources, with updates to support JDK 9. I added it to a different packages (third_party/jarjar vs. third_party/java/jarjar) because Bazel uses the first 'java/' component of the path as the resource root. Change-Id: Icdf10b1a71edf4e59f5524c1e169000b9f090ed9 --- third_party/jarjar/BUILD | 48 ++++ third_party/jarjar/BUILD.tools | 14 ++ third_party/jarjar/LICENSE | 202 +++++++++++++++++ .../tonicsystems/jarjar/AbstractDepHandler.java | 56 +++++ .../java/com/tonicsystems/jarjar/DepFind.java | 78 +++++++ .../com/tonicsystems/jarjar/DepFindVisitor.java | 74 +++++++ .../java/com/tonicsystems/jarjar/DepHandler.java | 30 +++ .../com/tonicsystems/jarjar/EmptyClassVisitor.java | 51 +++++ .../com/tonicsystems/jarjar/ExcludeProcessor.java | 39 ++++ .../java/com/tonicsystems/jarjar/JarJarMojo.java | 53 +++++ .../java/com/tonicsystems/jarjar/JarJarTask.java | 63 ++++++ .../jarjar/java/com/tonicsystems/jarjar/Keep.java | 19 ++ .../com/tonicsystems/jarjar/KeepProcessor.java | 116 ++++++++++ .../jarjar/java/com/tonicsystems/jarjar/Main.java | 96 ++++++++ .../com/tonicsystems/jarjar/MainProcessor.java | 113 ++++++++++ .../java/com/tonicsystems/jarjar/MainUtil.java | 86 ++++++++ .../com/tonicsystems/jarjar/ManifestProcessor.java | 36 +++ .../com/tonicsystems/jarjar/PackageRemapper.java | 136 ++++++++++++ .../java/com/tonicsystems/jarjar/PathClass.java | 39 ++++ .../com/tonicsystems/jarjar/PatternElement.java | 44 ++++ .../com/tonicsystems/jarjar/ResourceProcessor.java | 36 +++ .../jarjar/java/com/tonicsystems/jarjar/Rule.java | 29 +++ .../com/tonicsystems/jarjar/RulesFileParser.java | 82 +++++++ .../java/com/tonicsystems/jarjar/StringDumper.java | 108 +++++++++ .../java/com/tonicsystems/jarjar/StringReader.java | 114 ++++++++++ .../com/tonicsystems/jarjar/TextDepHandler.java | 33 +++ .../java/com/tonicsystems/jarjar/Wildcard.java | 160 ++++++++++++++ .../jarjar/java/com/tonicsystems/jarjar/Zap.java | 19 ++ .../java/com/tonicsystems/jarjar/ZapProcessor.java | 47 ++++ .../jarjar/java/com/tonicsystems/jarjar/help.txt | 75 +++++++ .../tonicsystems/jarjar/util/AntJarProcessor.java | 110 ++++++++++ .../jarjar/util/ClassHeaderReader.java | 192 ++++++++++++++++ .../tonicsystems/jarjar/util/ClassPathEntry.java | 28 +++ .../jarjar/util/ClassPathIterator.java | 242 +++++++++++++++++++++ .../com/tonicsystems/jarjar/util/EntryStruct.java | 35 +++ .../jarjar/util/GetNameClassWriter.java | 48 ++++ .../java/com/tonicsystems/jarjar/util/IoUtil.java | 119 ++++++++++ .../com/tonicsystems/jarjar/util/JarProcessor.java | 34 +++ .../jarjar/util/JarProcessorChain.java | 42 ++++ .../tonicsystems/jarjar/util/JarTransformer.java | 46 ++++ .../jarjar/util/JarTransformerChain.java | 35 +++ .../jarjar/util/RemappingClassTransformer.java | 32 +++ .../jarjar/util/RuntimeIOException.java | 27 +++ .../jarjar/util/StandaloneJarProcessor.java | 68 ++++++ 44 files changed, 3154 insertions(+) create mode 100644 third_party/jarjar/BUILD create mode 100644 third_party/jarjar/BUILD.tools create mode 100644 third_party/jarjar/LICENSE create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/AbstractDepHandler.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/DepFind.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/DepFindVisitor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/DepHandler.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/EmptyClassVisitor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/ExcludeProcessor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/JarJarMojo.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/JarJarTask.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/Keep.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/KeepProcessor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/Main.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/MainProcessor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/MainUtil.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/ManifestProcessor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/PackageRemapper.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/PathClass.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/PatternElement.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/ResourceProcessor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/Rule.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/RulesFileParser.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/StringDumper.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/StringReader.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/TextDepHandler.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/Wildcard.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/Zap.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/ZapProcessor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/help.txt create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/AntJarProcessor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassHeaderReader.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathEntry.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathIterator.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/EntryStruct.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/GetNameClassWriter.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/IoUtil.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessor.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessorChain.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformer.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformerChain.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/RemappingClassTransformer.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/RuntimeIOException.java create mode 100644 third_party/jarjar/java/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java (limited to 'third_party/jarjar') diff --git a/third_party/jarjar/BUILD b/third_party/jarjar/BUILD new file mode 100644 index 0000000000..4f4f223b08 --- /dev/null +++ b/third_party/jarjar/BUILD @@ -0,0 +1,48 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "srcs", + srcs = glob(["**"]), +) + +filegroup( + name = "embedded_tools_srcs", + srcs = [ + "BUILD.tools", + "LICENSE", + ":jarjar_command_deploy.jar", + ], +) + +java_binary( + name = "jarjar_command", + srcs = glob( + ["java/**/*.java"], + exclude = [ + "java/com/tonicsystems/jarjar/util/AntJarProcessor.java", + "java/com/tonicsystems/jarjar/JarJarMojo.java", + "java/com/tonicsystems/jarjar/JarJarTask.java", + ], + ), + main_class = "com.tonicsystems.jarjar.Main", + resources = ["java/com/tonicsystems/jarjar/help.txt"], + deps = [ + "//third_party:asm", + "//third_party:asm-commons", + "//third_party:asm-util", + "//third_party:jsr305", + ], +) + +java_import( + name = "jarjar_import", + jars = [":jarjar_command_deploy.jar"], +) + +java_binary( + name = "jarjar_bin", + main_class = "com.tonicsystems.jarjar.Main", + runtime_deps = [":jarjar_import"], +) diff --git a/third_party/jarjar/BUILD.tools b/third_party/jarjar/BUILD.tools new file mode 100644 index 0000000000..2319e6bcd5 --- /dev/null +++ b/third_party/jarjar/BUILD.tools @@ -0,0 +1,14 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +java_import( + name = "jarjar_import", + jars = [":jarjar_command_deploy.jar"], +) + +java_binary( + name = "jarjar_bin", + main_class = "com.tonicsystems.jarjar.Main", + runtime_deps = [":jarjar_import"], +) diff --git a/third_party/jarjar/LICENSE b/third_party/jarjar/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/jarjar/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/AbstractDepHandler.java b/third_party/jarjar/java/com/tonicsystems/jarjar/AbstractDepHandler.java new file mode 100644 index 0000000000..c13283eb4d --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/AbstractDepHandler.java @@ -0,0 +1,56 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.*; +import java.util.*; + +public abstract class AbstractDepHandler implements DepHandler { + protected final int level; + private final Set> seenIt = new HashSet>(); + + protected AbstractDepHandler(int level) { + this.level = level; + } + + public void handle(PathClass from, PathClass to) throws IOException { + List 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()); + } + } + + protected abstract void handle(String from, String to) throws IOException; + + public void handleStart() throws IOException {} + + public void handleEnd() throws IOException {} + + private static List createPair(Object o1, Object o2) { + List list = new ArrayList(2); + list.add(o1); + list.add(o2); + return list; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/DepFind.java b/third_party/jarjar/java/com/tonicsystems/jarjar/DepFind.java new file mode 100644 index 0000000000..088ff38ded --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/DepFind.java @@ -0,0 +1,78 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.*; +import java.util.*; +import org.objectweb.asm.ClassReader; + +public class DepFind { + private File curDir = new File(System.getProperty("user.dir")); + + 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 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(); + } + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/DepFindVisitor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/DepFindVisitor.java new file mode 100644 index 0000000000..af36dfee63 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/DepFindVisitor.java @@ -0,0 +1,74 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.*; +import java.util.*; +import org.objectweb.asm.*; +import org.objectweb.asm.commons.*; + +class DepFindVisitor extends ClassRemapper { + public DepFindVisitor(Map 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); + } + + private static class DepFindRemapper extends Remapper { + private final Map classes; + private final String source; + private final DepHandler handler; + private PathClass curPathClass; + + public DepFindRemapper(Map classes, String source, DepHandler handler) + throws IOException { + this.classes = classes; + this.source = source; + this.handler = handler; + } + + 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; + } + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/DepHandler.java b/third_party/jarjar/java/com/tonicsystems/jarjar/DepHandler.java new file mode 100644 index 0000000000..46847304b1 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/DepHandler.java @@ -0,0 +1,30 @@ +/* + * 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; + +import java.io.IOException; + +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; +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/EmptyClassVisitor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/EmptyClassVisitor.java new file mode 100644 index 0000000000..901e237992 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/EmptyClassVisitor.java @@ -0,0 +1,51 @@ +/* + * Copyright 2003-2010 the original author or authors. + * + * 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 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 ASM3 EmptyVisitor replacement + * + * @author Jochen "blackdrag" Theodorou + */ +public class EmptyClassVisitor extends ClassVisitor { + + public EmptyClassVisitor() { + super(Opcodes.ASM6); + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + return new MethodVisitor(Opcodes.ASM6) {}; + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new AnnotationVisitor(Opcodes.ASM6) {}; + } + + @Override + public FieldVisitor visitField( + int access, String name, String desc, String signature, Object value) { + return new FieldVisitor(Opcodes.ASM6) {}; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/ExcludeProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/ExcludeProcessor.java new file mode 100644 index 0000000000..79ec041e03 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/ExcludeProcessor.java @@ -0,0 +1,39 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.IOException; +import java.util.*; + +class ExcludeProcessor implements JarProcessor { + private final Set excludes; + private final boolean verbose; + + public ExcludeProcessor(Set 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; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarMojo.java b/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarMojo.java new file mode 100644 index 0000000000..2cec5fc65b --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarMojo.java @@ -0,0 +1,53 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.File; +import java.io.IOException; +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"); + } + + try { + List 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/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarTask.java b/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarTask.java new file mode 100644 index 0000000000..39e1677895 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarTask.java @@ -0,0 +1,63 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.IOException; +import java.util.*; +import org.apache.tools.ant.BuildException; + +public class JarJarTask extends AntJarProcessor { + private List patterns = new ArrayList(); + + public void addConfiguredRule(Rule rule) { + if (rule.getPattern() == null || rule.getResult() == null) { + throw new IllegalArgumentException( + "The element requires both \"pattern\" and \"result\" attributes."); + } + patterns.add(rule); + } + + public void addConfiguredZap(Zap zap) { + if (zap.getPattern() == null) { + throw new IllegalArgumentException("The element requires a \"pattern\" attribute."); + } + patterns.add(zap); + } + + public void addConfiguredKeep(Keep keep) { + if (keep.getPattern() == null) { + throw new IllegalArgumentException("The element requires a \"pattern\" attribute."); + } + 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(); + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Keep.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Keep.java new file mode 100644 index 0000000000..090340e345 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Keep.java @@ -0,0 +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; + +public class Keep extends PatternElement {} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/KeepProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/KeepProcessor.java new file mode 100644 index 0000000000..5bdd6aec00 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/KeepProcessor.java @@ -0,0 +1,116 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.*; +import java.util.*; +import org.objectweb.asm.*; +import org.objectweb.asm.commons.*; + +// 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 wildcards; + private final List roots = new ArrayList(); + private final Map> depend = new HashMap>(); + + public KeepProcessor(List patterns) { + wildcards = PatternElement.createWildcards(patterns); + } + + public boolean isEnabled() { + return !wildcards.isEmpty(); + } + + public Set getExcludes() { + Set closure = new HashSet(); + closureHelper(closure, roots); + Set removable = new HashSet(depend.keySet()); + removable.removeAll(closure); + return removable; + } + + private void closureHelper(Set closure, Collection process) { + if (process == null) { + return; + } + for (String name : process) { + if (closure.add(name)) { + closureHelper(closure, depend.get(name)); + } + } + } + + private Set curSet; + private byte[] buf = new byte[0x2000]; + + 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()); + 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; + } + + 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; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Main.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Main.java new file mode 100644 index 0000000000..55b7d24a60 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Main.java @@ -0,0 +1,96 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.*; +import java.util.*; + +public class Main { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + private static final String HELP; + + static { + try { + HELP = readIntoString(Main.class.getResourceAsStream("help.txt")); + } catch (IOException e) { + throw new RuntimeIOException(e); + } + } + + 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(); + } + + 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"); + } + + public void help() { + System.err.print(HELP); + } + + public void strings(String cp) throws IOException { + if (cp == null) { + throw new IllegalArgumentException("cp is required"); + } + new StringDumper().run(cp, new PrintWriter(System.out)); + } + + // TODO: make level an enum + public void find(String level, String cp1, String cp2) throws IOException { + if (level == null || cp1 == null) { + throw new IllegalArgumentException("level and cp1 are required"); + } + if (cp2 == null) { + cp2 = cp1; + } + int levelFlag; + if ("class".equals(level)) { + levelFlag = DepHandler.LEVEL_CLASS; + } else if ("jar".equals(level)) { + levelFlag = DepHandler.LEVEL_JAR; + } else { + throw new IllegalArgumentException("unknown level " + level); + } + PrintWriter w = new PrintWriter(System.out); + DepHandler handler = new TextDepHandler(w, levelFlag); + new DepFind().run(cp1, cp2, handler); + w.flush(); + } + + public void process(File rulesFile, File inJar, File outJar) throws IOException { + if (rulesFile == null || inJar == null || outJar == null) { + throw new IllegalArgumentException("rulesFile, inJar, and outJar are required"); + } + List rules = RulesFileParser.parse(rulesFile); + boolean verbose = Boolean.getBoolean("verbose"); + boolean skipManifest = Boolean.getBoolean("skipManifest"); + MainProcessor proc = new MainProcessor(rules, verbose, skipManifest); + StandaloneJarProcessor.run(inJar, outJar, proc); + proc.strip(outJar); + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/MainProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/MainProcessor.java new file mode 100644 index 0000000000..48ccf9c8ec --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/MainProcessor.java @@ -0,0 +1,113 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.File; +import java.io.IOException; +import java.util.*; + +class MainProcessor implements JarProcessor { + private final boolean verbose; + private final JarProcessorChain chain; + private final KeepProcessor kp; + private final Map renames = new HashMap(); + + public MainProcessor(List patterns, boolean verbose, boolean skipManifest) { + this.verbose = verbose; + List zapList = new ArrayList(); + List ruleList = new ArrayList(); + List keepList = new ArrayList(); + 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); + } + } + + PackageRemapper pr = new PackageRemapper(ruleList, verbose); + kp = keepList.isEmpty() ? null : new KeepProcessor(keepList); + + List processors = new ArrayList(); + if (skipManifest) { + processors.add(ManifestProcessor.getInstance()); + } + if (kp != null) { + processors.add(kp); + } + processors.add(new ZapProcessor(zapList)); + processors.add( + new JarTransformerChain( + new RemappingClassTransformer[] {new RemappingClassTransformer(pr)})); + processors.add(new ResourceProcessor(pr)); + chain = new JarProcessorChain(processors.toArray(new JarProcessor[processors.size()])); + } + + public void strip(File file) throws IOException { + if (kp == null) { + return; + } + Set excludes = getExcludes(); + if (!excludes.isEmpty()) { + StandaloneJarProcessor.run(file, file, new ExcludeProcessor(excludes, verbose)); + } + } + + /** + * Returns the .class 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 .class suffix + */ + private Set getExcludes() { + Set 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 true 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); + } + } + return keepIt; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/MainUtil.java b/third_party/jarjar/java/com/tonicsystems/jarjar/MainUtil.java new file mode 100644 index 0000000000..dabed739c6 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/MainUtil.java @@ -0,0 +1,86 @@ +/* + * 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; + +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; + } + } + } + if (defCommand != null) { + runMain(main, new String[] {defCommand}, null); + } + } + + private static Object[] bindParameters(Method method, String[] args) { + List parameters = new ArrayList(); + 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 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); + } + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/ManifestProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/ManifestProcessor.java new file mode 100644 index 0000000000..c8903a9db8 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/ManifestProcessor.java @@ -0,0 +1,36 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +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(); + + public static ManifestProcessor getInstance() { + return INSTANCE; + } + + private ManifestProcessor() {} + + public boolean process(EntryStruct struct) throws IOException { + return !struct.name.equalsIgnoreCase(MANIFEST_PATH); + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/PackageRemapper.java b/third_party/jarjar/java/com/tonicsystems/jarjar/PackageRemapper.java new file mode 100644 index 0000000000..3506366a81 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/PackageRemapper.java @@ -0,0 +1,136 @@ +/* + * 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; + +import java.util.*; +import java.util.regex.Pattern; +import org.objectweb.asm.*; +import org.objectweb.asm.commons.*; + +class PackageRemapper extends Remapper { + private static final String RESOURCE_SUFFIX = "RESOURCE"; + + private static final Pattern ARRAY_FOR_NAME_PATTERN = + Pattern.compile("\\[L[\\p{javaJavaIdentifierPart}\\.]+?;"); + + private final List wildcards; + private final Map typeCache = new HashMap(); + private final Map pathCache = new HashMap(); + private final Map valueCache = new HashMap(); + private final boolean verbose; + + public PackageRemapper(List ruleList, boolean verbose) { + this.verbose = verbose; + wildcards = PatternElement.createWildcards(ruleList); + } + + // also used by KeepProcessor + static boolean isArrayForName(String value) { + return ARRAY_FOR_NAME_PATTERN.matcher(value).matches(); + } + + 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 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; + } else { + return super.mapValue(value); + } + } + + private String replaceHelper(String value) { + for (Wildcard wildcard : wildcards) { + String test = wildcard.replace(value); + if (test != null) { + return test; + } + } + return value; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/PathClass.java b/third_party/jarjar/java/com/tonicsystems/jarjar/PathClass.java new file mode 100644 index 0000000000..fbfa24fd7d --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/PathClass.java @@ -0,0 +1,39 @@ +/* + * 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; + +public class PathClass { + private String classPath; + private String className; + + public PathClass(String classPath, String className) { + this.classPath = classPath; + this.className = className; + } + + public String getClassPath() { + return classPath; + } + + public String getClassName() { + return className; + } + + public String toString() { + return classPath + "!" + className; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/PatternElement.java b/third_party/jarjar/java/com/tonicsystems/jarjar/PatternElement.java new file mode 100644 index 0000000000..2b87cff8a3 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/PatternElement.java @@ -0,0 +1,44 @@ +/* + * 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; + +import java.util.*; + +public abstract class PatternElement { + private String pattern; + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public String getPattern() { + return pattern; + } + + static List createWildcards(List patterns) { + List wildcards = new ArrayList(); + 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)); + } + return wildcards; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/ResourceProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/ResourceProcessor.java new file mode 100644 index 0000000000..76f9189c11 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/ResourceProcessor.java @@ -0,0 +1,36 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.IOException; +import java.util.*; + +class ResourceProcessor implements JarProcessor { + private PackageRemapper pr; + + public ResourceProcessor(PackageRemapper pr) { + this.pr = pr; + } + + public boolean process(EntryStruct struct) throws IOException { + if (!struct.isClass()) { + struct.name = pr.mapPath(struct.name); + } + return true; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Rule.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Rule.java new file mode 100644 index 0000000000..96de530331 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Rule.java @@ -0,0 +1,29 @@ +/* + * 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; + +public class Rule extends PatternElement { + private String result; + + public void setResult(String result) { + this.result = result; + } + + public String getResult() { + return result; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/RulesFileParser.java b/third_party/jarjar/java/com/tonicsystems/jarjar/RulesFileParser.java new file mode 100644 index 0000000000..f737e494fb --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/RulesFileParser.java @@ -0,0 +1,82 @@ +/* + * 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; + +import java.io.*; +import java.util.*; + +class RulesFileParser { + private RulesFileParser() {} + + public static List parse(File file) throws IOException { + return parse(new FileReader(file)); + } + + public static List 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 parse(Reader r) throws IOException { + try { + List patterns = new ArrayList(); + BufferedReader br = new BufferedReader(r); + int c = 1; + String line; + while ((line = br.readLine()) != null) { + line = stripComment(line); + if ("".equals(line)) { + 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(); + } else { + error(c, parts); + } + 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)); + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/StringDumper.java b/third_party/jarjar/java/com/tonicsystems/jarjar/StringDumper.java new file mode 100644 index 0000000000..75c83b5d7c --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/StringDumper.java @@ -0,0 +1,108 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.*; +import org.objectweb.asm.*; + +class StringDumper { + public StringDumper() {} + + 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 { + 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(); + } finally { + cp.close(); + } + } + + private static class DumpStringReader extends StringReader { + private final PrintWriter pw; + private String className; + + 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(); + } + } + }; + + 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/third_party/jarjar/java/com/tonicsystems/jarjar/StringReader.java b/third_party/jarjar/java/com/tonicsystems/jarjar/StringReader.java new file mode 100644 index 0000000000..d1c11088e1 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/StringReader.java @@ -0,0 +1,114 @@ +/* + * 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; + +import org.objectweb.asm.*; + +abstract class StringReader extends ClassVisitor { + private int line = -1; + private String className; + + public StringReader() { + super(Opcodes.ASM6); + } + + 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) { + handleObject(value); + return new FieldVisitor(Opcodes.ASM6) { + @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.ASM6) { + @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.ASM6) { + @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; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/TextDepHandler.java b/third_party/jarjar/java/com/tonicsystems/jarjar/TextDepHandler.java new file mode 100644 index 0000000000..a84286cdd3 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/TextDepHandler.java @@ -0,0 +1,33 @@ +/* + * 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; + +import java.io.*; +import java.util.*; + +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); + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Wildcard.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Wildcard.java new file mode 100644 index 0000000000..8deb3518d0 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Wildcard.java @@ -0,0 +1,160 @@ +/* + * 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; + +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 final Pattern pattern; + private final int count; + private final ArrayList parts = new ArrayList(16); // kept for debugging + private final String[] strings; + private final int[] refs; + + public Wildcard(String pattern, String result) { + 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, "*)"); + 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; + } + } 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; + } + } + } + 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(); + } + } + 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 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 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()); + } + 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; + } + } + return true; + } + + 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 + "}"; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Zap.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Zap.java new file mode 100644 index 0000000000..30a341ddc2 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Zap.java @@ -0,0 +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; + +public class Zap extends PatternElement {} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/ZapProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/ZapProcessor.java new file mode 100644 index 0000000000..7431ec3120 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/ZapProcessor.java @@ -0,0 +1,47 @@ +/* + * 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; + +import com.tonicsystems.jarjar.util.*; +import java.io.IOException; +import java.util.*; + +class ZapProcessor implements JarProcessor { + private List wildcards; + + public ZapProcessor(List zapList) { + wildcards = PatternElement.createWildcards(zapList); + } + + public boolean process(EntryStruct struct) throws IOException { + String name = struct.name; + if (struct.isClass()) { + return !zap(name.substring(0, name.length() - 6)); + } + return true; + } + + private boolean zap(String desc) { + // TODO: optimize + for (Wildcard wildcard : wildcards) { + if (wildcard.matches(desc)) { + return true; + } + } + return false; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/help.txt b/third_party/jarjar/java/com/tonicsystems/jarjar/help.txt new file mode 100644 index 0000000000..8410909164 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/help.txt @@ -0,0 +1,75 @@ +Jar Jar Links - A utility to repackage and embed Java libraries +Copyright 2007 Google Inc. + +Command-line usage: + + java -jar jarjar.jar [help] + + Prints this help message. + + java -jar jarjar.jar strings + + Dumps all string literals in classpath . Line numbers will be + included if the classes have debug information. + + java -jar jarjar.jar find [] + + Prints dependencies on classpath in classpath . If + is omitted, is used for both arguments. + + The level argument must be "class" or "jar". The former prints + dependencies between individual classes, while the latter only + prints jar->jar dependencies. A "jar" in this context is actually + any classpath component, which can be a jar file, a zip file, or a + parent directory (see below). + + java -jar jarjar.jar process + + Transform the jar file, writing a new jar file to . + Any existing file named by will be deleted. + + The transformation is defined by a set of rules in the file specified + by the rules argument (see below). + +Classpath format: + + The classpath argument is a colon or semi-colon delimited set + (depending on platform) of directories, jar files, or zip files. See + the following page for more details: + http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/classpath.html + + Mustang-style wildcards are also supported: + http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6268383 + +Rules file format: + + The rules file is a text file, one rule per line. Leading and trailing + whitespace is ignored. There are three types of rules: + + rule + zap + keep + + The standard rule ("rule") is used to rename classes. All references + to the renamed classes will also be updated. If a class name is + matched by more than one rule, only the first one will apply. + + is a class name with optional wildcards. "**" will + match against any valid class name substring. To match a single + package component (by excluding "." from the match), a single "*" may + be used instead. + + is a class name which can optionally reference the + substrings matched by the wildcards. A numbered reference is available + for every "*" or "**" in the , starting from left to + right: "@1", "@2", etc. A special "@0" reference contains the entire + matched class name. + + The "zap" rule causes any matched class to be removed from the resulting + jar file. All zap rules are processed before renaming rules. + + The "keep" rule marks all matched classes as "roots". If any keep + rules are defined all classes which are not reachable from the roots + via dependency analysis are discarded when writing the output + jar. This is the last step in the process, after renaming and zapping. + diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/AntJarProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/AntJarProcessor.java new file mode 100644 index 0000000000..85bbe6cf8d --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/AntJarProcessor.java @@ -0,0 +1,110 @@ +/* + * 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.util; + +import java.io.*; +import java.util.*; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.Jar; +import org.apache.tools.ant.types.ZipFileSet; +import org.apache.tools.zip.JarMarker; +import org.apache.tools.zip.ZipExtraField; +import org.apache.tools.zip.ZipOutputStream; + +public abstract class AntJarProcessor extends Jar { + private EntryStruct struct = new EntryStruct(); + private JarProcessor proc; + private byte[] buf = new byte[0x2000]; + + private Set dirs = new HashSet(); + 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 void reset() { + super.reset(); + cleanHelper(); + } + + protected void cleanUp() { + super.cleanUp(); + cleanHelper(); + } + + protected void cleanHelper() { + verbose = false; + filesOnly = false; + dirs.clear(); + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassHeaderReader.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassHeaderReader.java new file mode 100644 index 0000000000..8e58951143 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassHeaderReader.java @@ -0,0 +1,192 @@ +/* + * 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.util; + +import java.io.*; +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 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 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 int readUnsignedShort(int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + 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 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; + } + + 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]); + } + + public void readFrom(byte[] buf, int pos) { + this.buf = buf; + this.pos = pos; + count = buf.length; + } + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathEntry.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathEntry.java new file mode 100644 index 0000000000..e90fd8cfa0 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathEntry.java @@ -0,0 +1,28 @@ +/* + * Copyright 2008 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.util; + +import java.io.IOException; +import java.io.InputStream; + +public interface ClassPathEntry { + String getSource() throws IOException; + + String getName(); + + InputStream openStream() throws IOException; +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathIterator.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathIterator.java new file mode 100644 index 0000000000..d2210a4ac4 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathIterator.java @@ -0,0 +1,242 @@ +/* + * 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.util; + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.zip.*; + +public class ClassPathIterator implements Iterator { + private static final FileFilter CLASS_FILTER = + new FileFilter() { + public boolean accept(File file) { + return file.isDirectory() || isClass(file.getName()); + } + }; + + private static final FileFilter JAR_FILTER = + new FileFilter() { + public boolean accept(File file) { + return hasExtension(file.getName(), ".jar"); + } + }; + + private final Iterator files; + private Iterator entries = Collections.emptyList().iterator(); + private ClassPathEntry next; + private List 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 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); + } + 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())); + } else { + fileList.add(file); + } + } + this.files = fileList.iterator(); + advance(); + } + + 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(); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + 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 { + private final ZipFile zip; + private final Enumeration entries; + + ZipIterator(ZipFile zip) { + this.zip = zip; + this.entries = zip.entries(); + } + + public boolean hasNext() { + return entries.hasMoreElements(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + 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); + } + }; + } + } + + private static class FileIterator implements Iterator { + private final File dir; + private final Iterator entries; + + FileIterator(File dir) { + this.dir = dir; + this.entries = findFiles(dir, CLASS_FILTER, true, new ArrayList()).iterator(); + } + + public boolean hasNext() { + return entries.hasNext(); + } + + 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)); + } + }; + } + } + + private static List findFiles( + File dir, FileFilter filter, boolean recurse, List 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()); + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/EntryStruct.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/EntryStruct.java new file mode 100644 index 0000000000..bf72ab5d69 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/EntryStruct.java @@ -0,0 +1,35 @@ +/* + * 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.util; + +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; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/GetNameClassWriter.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/GetNameClassWriter.java new file mode 100644 index 0000000000..19011be5ca --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/GetNameClassWriter.java @@ -0,0 +1,48 @@ +/* + * 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.util; + +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.ASM6, new ClassWriter(flags)); + } + + 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(); + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/IoUtil.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/IoUtil.java new file mode 100644 index 0000000000..988a85cafd --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/IoUtil.java @@ -0,0 +1,119 @@ +/* + * Copyright 2008 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.util; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +class 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 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(); + } + } + + /** + * 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 e = inputZip.entries(); + final ArrayList sortedList = new ArrayList(); + while (e.hasMoreElements()) { + final ZipEntry entry = e.nextElement(); + sortedList.add(entry); + } + + Collections.sort( + sortedList, + new Comparator() { + 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; + } + + // 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()); + } + } + } finally { + outputStream.close(); + } + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessor.java new file mode 100644 index 0000000000..7624080b47 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessor.java @@ -0,0 +1,34 @@ +/* + * 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.util; + +import java.io.IOException; + +public interface JarProcessor { + /** + * Process the entry (p.ex. rename the file) + * + *

Returns true 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 false for the + * entries which do not have been changed and there fore are not to be deleted + * + * @param struct + * @return true if he process chain can continue after this process + * @throws IOException + */ + boolean process(EntryStruct struct) throws IOException; +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessorChain.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessorChain.java new file mode 100644 index 0000000000..6360a3fed7 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessorChain.java @@ -0,0 +1,42 @@ +/* + * 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.util; + +import java.io.IOException; + +public class JarProcessorChain implements JarProcessor { + private final JarProcessor[] chain; + + public JarProcessorChain(JarProcessor[] chain) { + this.chain = chain.clone(); + } + + /** + * @param struct + * @return true 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; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformer.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformer.java new file mode 100644 index 0000000000..2b07a88bcc --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformer.java @@ -0,0 +1,46 @@ +/* + * 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.util; + +import java.io.*; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; + +public abstract class JarTransformer implements JarProcessor { + public boolean process(EntryStruct struct) throws IOException { + if (struct.isClass()) { + ClassReader reader; + try { + reader = new ClassReader(struct.data); + } catch (Exception e) { + return true; // TODO? + } + 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; + } + + protected abstract ClassVisitor transform(ClassVisitor v); + + private static String pathFromName(String className) { + return className.replace('.', '/') + ".class"; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformerChain.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformerChain.java new file mode 100644 index 0000000000..824115840b --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformerChain.java @@ -0,0 +1,35 @@ +/* + * 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.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]); + } + } + + protected ClassVisitor transform(ClassVisitor v) { + chain[chain.length - 1].setTarget(v); + return chain[0]; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/RemappingClassTransformer.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/RemappingClassTransformer.java new file mode 100644 index 0000000000..664ccc12b2 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/RemappingClassTransformer.java @@ -0,0 +1,32 @@ +/* + * 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.util; + +import com.tonicsystems.jarjar.EmptyClassVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.commons.Remapper; + +public class RemappingClassTransformer extends ClassRemapper { + public RemappingClassTransformer(Remapper pr) { + super(new EmptyClassVisitor(), pr); + } + + public void setTarget(ClassVisitor target) { + cv = target; + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/RuntimeIOException.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/RuntimeIOException.java new file mode 100644 index 0000000000..0cf0a15d77 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/RuntimeIOException.java @@ -0,0 +1,27 @@ +/* + * 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.util; + +import java.io.IOException; + +public class RuntimeIOException extends RuntimeException { + private static final long serialVersionUID = 0L; + + public RuntimeIOException(IOException e) { + super(e); + } +} diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java new file mode 100644 index 0000000000..3d2e424a76 --- /dev/null +++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java @@ -0,0 +1,68 @@ +/* + * 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.util; + +import java.io.*; +import java.util.*; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +public 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 entries = new HashSet(); + try { + EntryStruct struct = new EntryStruct(); + Enumeration 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(); + } + + // delete the empty directories + IoUtil.copyZipWithoutEmptyDirectories(tmpTo, to); + tmpTo.delete(); + } +} -- cgit v1.2.3