SDKProcessor.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. package org.mozilla.gecko.annotationProcessors;
  5. import com.android.tools.lint.checks.ApiLookup;
  6. import com.android.tools.lint.LintCliClient;
  7. import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
  8. import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
  9. import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader;
  10. import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator;
  11. import org.mozilla.gecko.annotationProcessors.utils.Utils;
  12. import java.io.File;
  13. import java.io.FileInputStream;
  14. import java.io.FileOutputStream;
  15. import java.io.IOException;
  16. import java.util.Arrays;
  17. import java.util.ArrayList;
  18. import java.util.Comparator;
  19. import java.util.Iterator;
  20. import java.util.Properties;
  21. import java.util.Scanner;
  22. import java.util.Vector;
  23. import java.net.URL;
  24. import java.net.URLClassLoader;
  25. import java.lang.reflect.Constructor;
  26. import java.lang.reflect.Field;
  27. import java.lang.reflect.Member;
  28. import java.lang.reflect.Method;
  29. import java.lang.reflect.Modifier;
  30. public class SDKProcessor {
  31. public static final String GENERATED_COMMENT =
  32. "// GENERATED CODE\n" +
  33. "// Generated by the Java program at /build/annotationProcessors at compile time\n" +
  34. "// from annotations on Java methods. To update, change the annotations on the\n" +
  35. "// corresponding Javamethods and rerun the build. Manually updating this file\n" +
  36. "// will cause your build to fail.\n" +
  37. "\n";
  38. private static ApiLookup sApiLookup;
  39. private static int sMaxSdkVersion;
  40. public static void main(String[] args) throws Exception {
  41. // We expect a list of jars on the commandline. If missing, whinge about it.
  42. if (args.length < 5) {
  43. System.err.println("Usage: java SDKProcessor sdkjar classlistfile outdir fileprefix max-sdk-version");
  44. System.exit(1);
  45. }
  46. System.out.println("Processing platform bindings...");
  47. String sdkJar = args[0];
  48. Vector classes = getClassList(args[1]);
  49. String outdir = args[2];
  50. String generatedFilePrefix = args[3];
  51. sMaxSdkVersion = Integer.parseInt(args[4]);
  52. LintCliClient lintClient = new LintCliClient();
  53. sApiLookup = ApiLookup.get(lintClient);
  54. // Start the clock!
  55. long s = System.currentTimeMillis();
  56. // Get an iterator over the classes in the jar files given...
  57. // Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
  58. StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
  59. headerFile.append(
  60. "#ifndef " + generatedFilePrefix + "_h__\n" +
  61. "#define " + generatedFilePrefix + "_h__\n" +
  62. "\n" +
  63. "#include \"mozilla/jni/Refs.h\"\n" +
  64. "\n" +
  65. "namespace mozilla {\n" +
  66. "namespace java {\n" +
  67. "namespace sdk {\n" +
  68. "\n");
  69. StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
  70. implementationFile.append(
  71. "#include \"" + generatedFilePrefix + ".h\"\n" +
  72. "#include \"mozilla/jni/Accessors.h\"\n" +
  73. "\n" +
  74. "namespace mozilla {\n" +
  75. "namespace java {\n" +
  76. "namespace sdk {\n" +
  77. "\n");
  78. // Used to track the calls to the various class-specific initialisation functions.
  79. ClassLoader loader = null;
  80. try {
  81. loader = URLClassLoader.newInstance(new URL[] { new URL("file://" + sdkJar) },
  82. SDKProcessor.class.getClassLoader());
  83. } catch (Exception e) {
  84. throw new RuntimeException(e.toString());
  85. }
  86. for (Iterator<String> i = classes.iterator(); i.hasNext(); ) {
  87. String className = i.next();
  88. System.out.println("Looking up: " + className);
  89. generateClass(Class.forName(className, true, loader),
  90. implementationFile,
  91. headerFile);
  92. }
  93. implementationFile.append(
  94. "} /* sdk */\n" +
  95. "} /* java */\n" +
  96. "} /* mozilla */\n");
  97. headerFile.append(
  98. "} /* sdk */\n" +
  99. "} /* java */\n" +
  100. "} /* mozilla */\n" +
  101. "#endif\n");
  102. writeOutputFiles(outdir, generatedFilePrefix, headerFile, implementationFile);
  103. long e = System.currentTimeMillis();
  104. System.out.println("SDK processing complete in " + (e - s) + "ms");
  105. }
  106. private static int getAPIVersion(Class<?> cls, Member m) {
  107. if (m instanceof Method || m instanceof Constructor) {
  108. return sApiLookup.getCallVersion(
  109. cls.getName().replace('.', '/'),
  110. Utils.getMemberName(m),
  111. Utils.getSignature(m));
  112. } else if (m instanceof Field) {
  113. return sApiLookup.getFieldVersion(
  114. Utils.getClassDescriptor(m.getDeclaringClass()), m.getName());
  115. } else {
  116. throw new IllegalArgumentException("expected member to be Method, Constructor, or Field");
  117. }
  118. }
  119. private static Member[] sortAndFilterMembers(Class<?> cls, Member[] members) {
  120. Arrays.sort(members, new Comparator<Member>() {
  121. @Override
  122. public int compare(Member a, Member b) {
  123. return a.getName().compareTo(b.getName());
  124. }
  125. });
  126. ArrayList<Member> list = new ArrayList<>();
  127. for (Member m : members) {
  128. // Sometimes (e.g. Bundle) has methods that moved to/from a superclass in a later SDK
  129. // version, so we check for both classes and see if we can find a minimum SDK version.
  130. int version = getAPIVersion(cls, m);
  131. final int version2 = getAPIVersion(m.getDeclaringClass(), m);
  132. if (version2 > 0 && version2 < version) {
  133. version = version2;
  134. }
  135. if (version > sMaxSdkVersion) {
  136. System.out.println("Skipping " + m.getDeclaringClass().getName() + "." + m.getName() +
  137. ", version " + version + " > " + sMaxSdkVersion);
  138. continue;
  139. }
  140. // Sometimes (e.g. KeyEvent) a field can appear in both a class and a superclass. In
  141. // that case we want to filter out the version that appears in the superclass, or
  142. // we'll have bindings with duplicate names.
  143. try {
  144. if (m instanceof Field && !m.equals(cls.getField(m.getName()))) {
  145. // m is a field in a superclass that has been hidden by
  146. // a field with the same name in a subclass.
  147. System.out.println("Skipping " + m.getName() +
  148. " from " + m.getDeclaringClass());
  149. continue;
  150. }
  151. } catch (final NoSuchFieldException e) {
  152. }
  153. list.add(m);
  154. }
  155. return list.toArray(new Member[list.size()]);
  156. }
  157. private static void generateClass(Class<?> clazz,
  158. StringBuilder implementationFile,
  159. StringBuilder headerFile) {
  160. String generatedName = clazz.getSimpleName();
  161. CodeGenerator generator = new CodeGenerator(new ClassWithOptions(clazz, generatedName));
  162. generator.generateMembers(sortAndFilterMembers(clazz, clazz.getConstructors()));
  163. generator.generateMembers(sortAndFilterMembers(clazz, clazz.getMethods()));
  164. generator.generateMembers(sortAndFilterMembers(clazz, clazz.getFields()));
  165. headerFile.append(generator.getHeaderFileContents());
  166. implementationFile.append(generator.getWrapperFileContents());
  167. }
  168. private static Vector<String> getClassList(String path) {
  169. Scanner scanner = null;
  170. try {
  171. scanner = new Scanner(new FileInputStream(path));
  172. Vector lines = new Vector();
  173. while (scanner.hasNextLine()) {
  174. lines.add(scanner.nextLine());
  175. }
  176. return lines;
  177. } catch (Exception e) {
  178. System.out.println(e.toString());
  179. return null;
  180. } finally {
  181. if (scanner != null) {
  182. scanner.close();
  183. }
  184. }
  185. }
  186. private static void writeOutputFiles(String aOutputDir, String aPrefix, StringBuilder aHeaderFile,
  187. StringBuilder aImplementationFile) {
  188. FileOutputStream implStream = null;
  189. try {
  190. implStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".cpp"));
  191. implStream.write(aImplementationFile.toString().getBytes());
  192. } catch (IOException e) {
  193. System.err.println("Unable to write " + aOutputDir + ". Perhaps a permissions issue?");
  194. e.printStackTrace(System.err);
  195. } finally {
  196. if (implStream != null) {
  197. try {
  198. implStream.close();
  199. } catch (IOException e) {
  200. System.err.println("Unable to close implStream due to "+e);
  201. e.printStackTrace(System.err);
  202. }
  203. }
  204. }
  205. FileOutputStream headerStream = null;
  206. try {
  207. headerStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".h"));
  208. headerStream.write(aHeaderFile.toString().getBytes());
  209. } catch (IOException e) {
  210. System.err.println("Unable to write " + aOutputDir + ". Perhaps a permissions issue?");
  211. e.printStackTrace(System.err);
  212. } finally {
  213. if (headerStream != null) {
  214. try {
  215. headerStream.close();
  216. } catch (IOException e) {
  217. System.err.println("Unable to close headerStream due to "+e);
  218. e.printStackTrace(System.err);
  219. }
  220. }
  221. }
  222. }
  223. }