aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java201
1 files changed, 201 insertions, 0 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java
new file mode 100644
index 0000000000..f3c546c1c3
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java
@@ -0,0 +1,201 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// 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.google.devtools.build.android.desugar.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import javax.annotation.Nullable;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.ClassRemapper;
+import org.objectweb.asm.commons.Remapper;
+
+/** Utility class to prefix or unprefix class names of core library classes */
+public class CoreLibraryRewriter {
+ private final String prefix;
+
+ public CoreLibraryRewriter(String prefix) {
+ this.prefix = prefix;
+ }
+
+ /**
+ * Factory method that returns either a normal ClassReader if prefix is empty, or a ClassReader
+ * with a ClassRemapper that prefixes class names of core library classes if prefix is not empty.
+ */
+ public ClassReader reader(InputStream content) throws IOException {
+ if (prefix.isEmpty()) {
+ return new ClassReader(content);
+ } else {
+ return new PrefixingClassReader(content, prefix);
+ }
+ }
+
+ /**
+ * Factory method that returns a ClassVisitor that delegates to a ClassWriter, removing prefix
+ * from core library class names if it is not empty.
+ */
+ public UnprefixingClassWriter writer(int flags) {
+ return new UnprefixingClassWriter(flags);
+ }
+
+ static boolean shouldPrefix(String typeName) {
+ return (typeName.startsWith("java/") || typeName.startsWith("sun/")) && !except(typeName);
+ }
+
+ private static boolean except(String typeName) {
+ if (typeName.startsWith("java/lang/invoke/")) {
+ return true;
+ }
+
+ switch (typeName) {
+ // Autoboxed types
+ case "java/lang/Boolean":
+ case "java/lang/Byte":
+ case "java/lang/Character":
+ case "java/lang/Double":
+ case "java/lang/Float":
+ case "java/lang/Integer":
+ case "java/lang/Long":
+ case "java/lang/Number":
+ case "java/lang/Short":
+
+ // Special types
+ case "java/lang/Class":
+ case "java/lang/Object":
+ case "java/lang/String":
+ case "java/lang/Throwable":
+ return true;
+
+ default: // fall out
+ }
+
+ return false;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /** Removes prefix from class names */
+ public String unprefix(String typeName) {
+ if (prefix.isEmpty() || !typeName.startsWith(prefix)) {
+ return typeName;
+ }
+ return typeName.substring(prefix.length());
+ }
+
+ /** ClassReader that prefixes core library class names as they are read */
+ private static class PrefixingClassReader extends ClassReader {
+ private final String prefix;
+
+ PrefixingClassReader(InputStream content, String prefix) throws IOException {
+ super(content);
+ this.prefix = prefix;
+ }
+
+ @Override
+ public void accept(ClassVisitor cv, Attribute[] attrs, int flags) {
+ cv =
+ new ClassRemapper(
+ cv,
+ new Remapper() {
+ @Override
+ public String map(String typeName) {
+ return prefix(typeName);
+ }
+ });
+ super.accept(cv, attrs, flags);
+ }
+
+ @Override
+ public String getClassName() {
+ return prefix(super.getClassName());
+ }
+
+ @Override
+ public String getSuperName() {
+ String result = super.getSuperName();
+ return result != null ? prefix(result) : null;
+ }
+
+ @Override
+ public String[] getInterfaces() {
+ String[] result = super.getInterfaces();
+ for (int i = 0, len = result.length; i < len; ++i) {
+ result[i] = prefix(result[i]);
+ }
+ return result;
+ }
+
+ /** Prefixes core library class names with prefix. */
+ private String prefix(String typeName) {
+ if (shouldPrefix(typeName)) {
+ return prefix + typeName;
+ }
+ return typeName;
+ }
+ }
+
+ /**
+ * ClassVisitor that delegates to a ClassWriter, but removes a prefix as each class is written.
+ * The unprefixing is optimized out if prefix is empty.
+ */
+ public class UnprefixingClassWriter extends ClassVisitor {
+ private final ClassWriter writer;
+
+ private String finalClassName;
+
+ UnprefixingClassWriter(int flags) {
+ super(Opcodes.ASM6);
+ this.writer = new ClassWriter(flags);
+ this.cv = this.writer;
+ if (!prefix.isEmpty()) {
+ this.cv =
+ new ClassRemapper(
+ this.writer,
+ new Remapper() {
+ @Override
+ public String map(String typeName) {
+ return unprefix(typeName);
+ }
+ });
+ }
+ }
+
+ /** Returns the (unprefixed) name of the class once written. */
+ @Nullable
+ public String getClassName() {
+ return finalClassName;
+ }
+
+ public byte[] toByteArray() {
+ return writer.toByteArray();
+ }
+
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ finalClassName = unprefix(name);
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+ }
+}