1758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross// Copyright 2017 The Bazel Authors. All rights reserved.
2758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross//
3758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross// Licensed under the Apache License, Version 2.0 (the "License");
4758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross// you may not use this file except in compliance with the License.
5758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross// You may obtain a copy of the License at
6758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross//
7758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross//    http://www.apache.org/licenses/LICENSE-2.0
8758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross//
9758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross// Unless required by applicable law or agreed to in writing, software
10758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross// distributed under the License is distributed on an "AS IS" BASIS,
11758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross// See the License for the specific language governing permissions and
13758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross// limitations under the License.
14758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crosspackage com.google.devtools.build.android.desugar;
15758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
16758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossimport java.io.IOException;
17758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossimport java.io.InputStream;
18758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossimport org.objectweb.asm.Attribute;
19758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossimport org.objectweb.asm.ClassReader;
20758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossimport org.objectweb.asm.ClassVisitor;
21758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossimport org.objectweb.asm.ClassWriter;
22758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossimport org.objectweb.asm.Opcodes;
23758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossimport org.objectweb.asm.commons.ClassRemapper;
24758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossimport org.objectweb.asm.commons.Remapper;
25758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
26758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross/** Utility class to prefix or unprefix class names of core library classes */
27758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Crossclass CoreLibraryRewriter {
28758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  private final String prefix;
29758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
30758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  public CoreLibraryRewriter(String prefix) {
31758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    this.prefix = prefix;
32758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  }
33758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
34758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  /**
35758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross   * Factory method that returns either a normal ClassReader if prefix is empty, or a ClassReader
36758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross   * with a ClassRemapper that prefixes class names of core library classes if prefix is not empty.
37758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross   */
38758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  public ClassReader reader(InputStream content) throws IOException {
394676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb    if (prefix.isEmpty()) {
40758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      return new ClassReader(content);
414676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb    } else {
424676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb      return new PrefixingClassReader(content, prefix);
43758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    }
44758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  }
45758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
46758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  /**
47758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross   * Factory method that returns a ClassVisitor that delegates to a ClassWriter, removing prefix
48758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross   * from core library class names if it is not empty.
49758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross   */
50758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  public UnprefixingClassWriter writer(int flags) {
51758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    return new UnprefixingClassWriter(flags);
52758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  }
53758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
544676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb  static boolean shouldPrefix(String typeName) {
55758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    return (typeName.startsWith("java/") || typeName.startsWith("sun/")) && !except(typeName);
56758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  }
57758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
58758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  private static boolean except(String typeName) {
59758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    if (typeName.startsWith("java/lang/invoke/")) {
60758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      return true;
61758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    }
62758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
63758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    switch (typeName) {
64758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross        // Autoboxed types
65758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Boolean":
66758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Byte":
67758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Character":
68758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Double":
69758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Float":
70758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Integer":
71758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Long":
72758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Number":
73758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Short":
74758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
75758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross        // Special types
76758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Class":
77758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Object":
78758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/String":
79758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      case "java/lang/Throwable":
80758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross        return true;
81758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
82758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      default: // fall out
83758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    }
84758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
85758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    return false;
86758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  }
87758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
88758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  /** Removes prefix from class names */
89758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  public String unprefix(String typeName) {
904676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb    if (prefix.isEmpty() || !typeName.startsWith(prefix)) {
91758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      return typeName;
92758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    }
93758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    return typeName.substring(prefix.length());
94758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  }
95758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
96ee8542f989cffb9c5070c7503a7c40d1e7bbd6b0cnsun  /** ClassReader that prefixes core library class names as they are read */
974676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb  private static class PrefixingClassReader extends ClassReader {
984676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb    private final String prefix;
994676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb
1004676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb    PrefixingClassReader(InputStream content, String prefix) throws IOException {
101758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      super(content);
1024676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb      this.prefix = prefix;
103758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    }
104758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
105758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    @Override
106758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    public void accept(ClassVisitor cv, Attribute[] attrs, int flags) {
107758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      cv =
10820d5bf85394727ffdab6672fa3f2decb5b9b20aacnsun          new ClassRemapper(
109758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross              cv,
110758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross              new Remapper() {
111758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                @Override
112758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                public String map(String typeName) {
113758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                  return prefix(typeName);
114758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                }
115758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross              });
116758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      super.accept(cv, attrs, flags);
117758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    }
11837ba42132bf5c9afe8f67024b269a7f571b9d999kmb
11937ba42132bf5c9afe8f67024b269a7f571b9d999kmb    @Override
12037ba42132bf5c9afe8f67024b269a7f571b9d999kmb    public String getClassName() {
12137ba42132bf5c9afe8f67024b269a7f571b9d999kmb      return prefix(super.getClassName());
12237ba42132bf5c9afe8f67024b269a7f571b9d999kmb    }
12337ba42132bf5c9afe8f67024b269a7f571b9d999kmb
12437ba42132bf5c9afe8f67024b269a7f571b9d999kmb    @Override
12537ba42132bf5c9afe8f67024b269a7f571b9d999kmb    public String getSuperName() {
12637ba42132bf5c9afe8f67024b269a7f571b9d999kmb      String result = super.getSuperName();
12737ba42132bf5c9afe8f67024b269a7f571b9d999kmb      return result != null ? prefix(result) : null;
12837ba42132bf5c9afe8f67024b269a7f571b9d999kmb    }
12937ba42132bf5c9afe8f67024b269a7f571b9d999kmb
13037ba42132bf5c9afe8f67024b269a7f571b9d999kmb    @Override
13137ba42132bf5c9afe8f67024b269a7f571b9d999kmb    public String[] getInterfaces() {
13237ba42132bf5c9afe8f67024b269a7f571b9d999kmb      String[] result = super.getInterfaces();
13337ba42132bf5c9afe8f67024b269a7f571b9d999kmb      for (int i = 0, len = result.length; i < len; ++i) {
13437ba42132bf5c9afe8f67024b269a7f571b9d999kmb        result[i] = prefix(result[i]);
13537ba42132bf5c9afe8f67024b269a7f571b9d999kmb      }
13637ba42132bf5c9afe8f67024b269a7f571b9d999kmb      return result;
13737ba42132bf5c9afe8f67024b269a7f571b9d999kmb    }
1384676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb
1394676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb    /** Prefixes core library class names with prefix. */
1404676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb    private String prefix(String typeName) {
1414676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb      if (shouldPrefix(typeName)) {
1424676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb        return prefix + typeName;
1434676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb      }
1444676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb      return typeName;
1454676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb    }
146758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  }
147758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
148758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  /**
149758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross   * ClassVisitor that delegates to a ClassWriter, but removes a prefix as each class is written.
150758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross   * The unprefixing is optimized out if prefix is empty.
151758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross   */
152758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  public class UnprefixingClassWriter extends ClassVisitor {
153758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    private final ClassWriter writer;
154758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
155758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    UnprefixingClassWriter(int flags) {
15669113875723a6a265673f9b2d6d5140c20fae7c4cushon      super(Opcodes.ASM6);
157758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      this.writer = new ClassWriter(flags);
158758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      this.cv = this.writer;
1594676fd2ac045b62aae0c8d8a70b3a4ae9ebd324ekmb      if (!prefix.isEmpty()) {
160758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross        this.cv =
16120d5bf85394727ffdab6672fa3f2decb5b9b20aacnsun            new ClassRemapper(
162758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                this.cv,
163758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                new Remapper() {
164758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                  @Override
165758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                  public String map(String typeName) {
166758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                    return unprefix(typeName);
167758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                  }
168758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross                });
169758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      }
170758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    }
171758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross
172758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    byte[] toByteArray() {
173758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross      return writer.toByteArray();
174758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross    }
175758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross  }
176758d2b7ce3ae079c8308c7f1fb9e740cb704f25eColin Cross}
177