• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright 2017 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Generate JNI registration entry points
7
8Creates a header file with two static functions: RegisterMainDexNatives() and
9RegisterNonMainDexNatives(). Together, these will use manual JNI registration
10to register all native methods that exist within an application."""
11
12import argparse
13import jni_generator
14import multiprocessing
15import string
16import sys
17from util import build_utils
18
19
20# All but FULL_CLASS_NAME, which is used only for sorting.
21MERGEABLE_KEYS = [
22    'CLASS_PATH_DECLARATIONS',
23    'FORWARD_DECLARATIONS',
24    'JNI_NATIVE_METHOD',
25    'JNI_NATIVE_METHOD_ARRAY',
26    'REGISTER_MAIN_DEX_NATIVES',
27    'REGISTER_NON_MAIN_DEX_NATIVES',
28]
29
30
31def GenerateJNIHeader(java_file_paths, output_file, args):
32  """Generate a header file including two registration functions.
33
34  Forward declares all JNI registration functions created by jni_generator.py.
35  Calls the functions in RegisterMainDexNatives() if they are main dex. And
36  calls them in RegisterNonMainDexNatives() if they are non-main dex.
37
38  Args:
39      java_file_paths: A list of java file paths.
40      output_file: A relative path to output file.
41      args: All input arguments.
42  """
43  # Without multiprocessing, script takes ~13 seconds for chrome_public_apk
44  # on a z620. With multiprocessing, takes ~2 seconds.
45  pool = multiprocessing.Pool()
46  paths = (p for p in java_file_paths if p not in args.no_register_java)
47  results = [d for d in pool.imap_unordered(_DictForPath, paths) if d]
48  pool.close()
49
50  # Sort to make output deterministic.
51  results.sort(key=lambda d: d['FULL_CLASS_NAME'])
52
53  combined_dict = {}
54  for key in MERGEABLE_KEYS:
55    combined_dict[key] = ''.join(d.get(key, '') for d in results)
56
57  header_content = CreateFromDict(combined_dict)
58  if output_file:
59    jni_generator.WriteOutput(output_file, header_content)
60  else:
61    print header_content
62
63
64def _DictForPath(path):
65  with open(path) as f:
66    contents = jni_generator.RemoveComments(f.read())
67  natives = jni_generator.ExtractNatives(contents, 'long')
68  if len(natives) == 0:
69    return None
70  namespace = jni_generator.ExtractJNINamespace(contents)
71  fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName(
72      path, contents)
73  jni_params = jni_generator.JniParams(fully_qualified_class)
74  jni_params.ExtractImportsAndInnerClasses(contents)
75  main_dex = jni_generator.IsMainDexJavaClass(contents)
76  header_generator = HeaderGenerator(
77      namespace, fully_qualified_class, natives, jni_params, main_dex)
78  return header_generator.Generate()
79
80
81def CreateFromDict(registration_dict):
82  """Returns the content of the header file."""
83
84  template = string.Template("""\
85// Copyright 2017 The Chromium Authors. All rights reserved.
86// Use of this source code is governed by a BSD-style license that can be
87// found in the LICENSE file.
88
89
90// This file is autogenerated by
91//     base/android/jni_generator/jni_registration_generator.py
92// Please do not change its content.
93
94#ifndef HEADER_GUARD
95#define HEADER_GUARD
96
97#include <jni.h>
98
99#include "base/android/jni_generator/jni_generator_helper.h"
100#include "base/android/jni_int_wrapper.h"
101
102
103// Step 1: Forward declarations (classes).
104${CLASS_PATH_DECLARATIONS}
105
106// Step 2: Forward declarations (methods).
107
108${FORWARD_DECLARATIONS}
109
110// Step 3: Method declarations.
111
112${JNI_NATIVE_METHOD_ARRAY}
113${JNI_NATIVE_METHOD}
114// Step 4: Main dex and non-main dex registration functions.
115
116bool RegisterMainDexNatives(JNIEnv* env) {
117${REGISTER_MAIN_DEX_NATIVES}
118  return true;
119}
120
121bool RegisterNonMainDexNatives(JNIEnv* env) {
122${REGISTER_NON_MAIN_DEX_NATIVES}
123  return true;
124}
125
126#endif  // HEADER_GUARD
127""")
128  if len(registration_dict['FORWARD_DECLARATIONS']) == 0:
129    return ''
130
131  return template.substitute(registration_dict)
132
133
134class HeaderGenerator(object):
135  """Generates an inline header file for JNI registration."""
136
137  def __init__(self, namespace, fully_qualified_class, natives, jni_params,
138               main_dex):
139    self.namespace = namespace
140    self.natives = natives
141    self.fully_qualified_class = fully_qualified_class
142    self.jni_params = jni_params
143    self.class_name = self.fully_qualified_class.split('/')[-1]
144    self.main_dex = main_dex
145    self.helper = jni_generator.HeaderFileGeneratorHelper(
146        self.class_name, fully_qualified_class)
147    self.registration_dict = None
148
149  def Generate(self):
150    self.registration_dict = {'FULL_CLASS_NAME': self.fully_qualified_class}
151    self._AddClassPathDeclarations()
152    self._AddForwardDeclaration()
153    self._AddJNINativeMethodsArrays()
154    self._AddRegisterNativesCalls()
155    self._AddRegisterNativesFunctions()
156    return self.registration_dict
157
158  def _SetDictValue(self, key, value):
159    self.registration_dict[key] = jni_generator.WrapOutput(value)
160
161  def _AddClassPathDeclarations(self):
162    classes = self.helper.GetUniqueClasses(self.natives)
163    self._SetDictValue('CLASS_PATH_DECLARATIONS',
164        self.helper.GetClassPathLines(classes, declare_only=True))
165
166  def _AddForwardDeclaration(self):
167    """Add the content of the forward declaration to the dictionary."""
168    template = string.Template("""\
169JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}(
170    JNIEnv* env,
171    ${PARAMS_IN_STUB});
172""")
173    forward_declaration = ''
174    for native in self.natives:
175      value = {
176          'RETURN': jni_generator.JavaDataTypeToC(native.return_type),
177          'STUB_NAME': self.helper.GetStubName(native),
178          'PARAMS_IN_STUB': jni_generator.GetParamsInStub(native),
179      }
180      forward_declaration += template.substitute(value)
181    self._SetDictValue('FORWARD_DECLARATIONS', forward_declaration)
182
183  def _AddRegisterNativesCalls(self):
184    """Add the body of the RegisterNativesImpl method to the dictionary."""
185    template = string.Template("""\
186  if (!${REGISTER_NAME}(env))
187    return false;
188""")
189    value = {
190        'REGISTER_NAME':
191            jni_generator.GetRegistrationFunctionName(
192                self.fully_qualified_class)
193    }
194    register_body = template.substitute(value)
195    if self.main_dex:
196      self._SetDictValue('REGISTER_MAIN_DEX_NATIVES', register_body)
197    else:
198      self._SetDictValue('REGISTER_NON_MAIN_DEX_NATIVES', register_body)
199
200  def _AddJNINativeMethodsArrays(self):
201    """Returns the implementation of the array of native methods."""
202    template = string.Template("""\
203static const JNINativeMethod kMethods_${JAVA_CLASS}[] = {
204${KMETHODS}
205};
206
207""")
208    open_namespace = ''
209    close_namespace = ''
210    if self.namespace:
211      parts = self.namespace.split('::')
212      all_namespaces = ['namespace %s {' % ns for ns in parts]
213      open_namespace = '\n'.join(all_namespaces) + '\n'
214      all_namespaces = ['}  // namespace %s' % ns for ns in parts]
215      all_namespaces.reverse()
216      close_namespace = '\n'.join(all_namespaces) + '\n\n'
217
218    body = self._SubstituteNativeMethods(template)
219    self._SetDictValue('JNI_NATIVE_METHOD_ARRAY',
220                       ''.join((open_namespace, body, close_namespace)))
221
222  def _GetKMethodsString(self, clazz):
223    ret = []
224    for native in self.natives:
225      if (native.java_class_name == clazz or
226          (not native.java_class_name and clazz == self.class_name)):
227        ret += [self._GetKMethodArrayEntry(native)]
228    return '\n'.join(ret)
229
230  def _GetKMethodArrayEntry(self, native):
231    template = string.Template('    { "native${NAME}", ${JNI_SIGNATURE}, ' +
232                               'reinterpret_cast<void*>(${STUB_NAME}) },')
233    values = {
234        'NAME': native.name,
235        'JNI_SIGNATURE': self.jni_params.Signature(
236            native.params, native.return_type),
237        'STUB_NAME': self.helper.GetStubName(native)
238    }
239    return template.substitute(values)
240
241  def _SubstituteNativeMethods(self, template):
242    """Substitutes NAMESPACE, JAVA_CLASS and KMETHODS in the provided
243    template."""
244    ret = []
245    all_classes = self.helper.GetUniqueClasses(self.natives)
246    all_classes[self.class_name] = self.fully_qualified_class
247    for clazz, full_clazz in all_classes.iteritems():
248      kmethods = self._GetKMethodsString(clazz)
249      namespace_str = ''
250      if self.namespace:
251        namespace_str = self.namespace + '::'
252      if kmethods:
253        values = {'NAMESPACE': namespace_str,
254                  'JAVA_CLASS': jni_generator.GetBinaryClassName(full_clazz),
255                  'KMETHODS': kmethods}
256        ret += [template.substitute(values)]
257    if not ret: return ''
258    return '\n'.join(ret)
259
260  def GetJNINativeMethodsString(self):
261    """Returns the implementation of the array of native methods."""
262    template = string.Template("""\
263static const JNINativeMethod kMethods_${JAVA_CLASS}[] = {
264${KMETHODS}
265
266};
267""")
268    return self._SubstituteNativeMethods(template)
269
270  def _AddRegisterNativesFunctions(self):
271    """Returns the code for RegisterNatives."""
272    natives = self._GetRegisterNativesImplString()
273    if not natives:
274      return ''
275    template = string.Template("""\
276JNI_REGISTRATION_EXPORT bool ${REGISTER_NAME}(JNIEnv* env) {
277${NATIVES}\
278  return true;
279}
280
281""")
282    values = {
283      'REGISTER_NAME': jni_generator.GetRegistrationFunctionName(
284          self.fully_qualified_class),
285      'NATIVES': natives
286    }
287    self._SetDictValue('JNI_NATIVE_METHOD', template.substitute(values))
288
289  def _GetRegisterNativesImplString(self):
290    """Returns the shared implementation for RegisterNatives."""
291    template = string.Template("""\
292  const int kMethods_${JAVA_CLASS}Size =
293      arraysize(${NAMESPACE}kMethods_${JAVA_CLASS});
294  if (env->RegisterNatives(
295      ${JAVA_CLASS}_clazz(env),
296      ${NAMESPACE}kMethods_${JAVA_CLASS},
297      kMethods_${JAVA_CLASS}Size) < 0) {
298    jni_generator::HandleRegistrationError(env,
299        ${JAVA_CLASS}_clazz(env),
300        __FILE__);
301    return false;
302  }
303
304""")
305    return self._SubstituteNativeMethods(template)
306
307
308def main(argv):
309  arg_parser = argparse.ArgumentParser()
310  build_utils.AddDepfileOption(arg_parser)
311
312  arg_parser.add_argument('--sources_files',
313                          help='A list of .sources files which contain Java '
314                          'file paths. Must be used with --output.')
315  arg_parser.add_argument('--output',
316                          help='The output file path.')
317  arg_parser.add_argument('--no_register_java',
318                          help='A list of Java files which should be ignored '
319                          'by the parser.', default=[])
320  args = arg_parser.parse_args(build_utils.ExpandFileArgs(argv[1:]))
321  args.sources_files = build_utils.ParseGnList(args.sources_files)
322
323  if not args.sources_files:
324    print '\nError: Must specify --sources_files.'
325    return 1
326
327  java_file_paths = []
328  for f in args.sources_files:
329    # java_file_paths stores each Java file path as a string.
330    java_file_paths += build_utils.ReadSourcesList(f)
331  output_file = args.output
332  GenerateJNIHeader(java_file_paths, output_file, args)
333
334  if args.depfile:
335    build_utils.WriteDepfile(args.depfile, output_file,
336                             args.sources_files + java_file_paths)
337
338
339if __name__ == '__main__':
340  sys.exit(main(sys.argv))
341