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