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