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