| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2012 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 """Extracts native methods from a Java file and generates the JNI bindings. | |
| 7 If you change this, please run and update the tests.""" | |
| 8 | |
| 9 import collections | |
| 10 import errno | |
| 11 import optparse | |
| 12 import os | |
| 13 import re | |
| 14 import string | |
| 15 from string import Template | |
| 16 import subprocess | |
| 17 import sys | |
| 18 import textwrap | |
| 19 import zipfile | |
| 20 | |
| 21 CHROMIUM_SRC = os.path.join( | |
| 22 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir) | |
| 23 BUILD_ANDROID_GYP = os.path.join( | |
| 24 CHROMIUM_SRC, 'build', 'android', 'gyp') | |
| 25 | |
| 26 sys.path.append(BUILD_ANDROID_GYP) | |
| 27 | |
| 28 from util import build_utils | |
| 29 | |
| 30 | |
| 31 class ParseError(Exception): | |
| 32 """Exception thrown when we can't parse the input file.""" | |
| 33 | |
| 34 def __init__(self, description, *context_lines): | |
| 35 Exception.__init__(self) | |
| 36 self.description = description | |
| 37 self.context_lines = context_lines | |
| 38 | |
| 39 def __str__(self): | |
| 40 context = '\n'.join(self.context_lines) | |
| 41 return '***\nERROR: %s\n\n%s\n***' % (self.description, context) | |
| 42 | |
| 43 | |
| 44 class Param(object): | |
| 45 """Describes a param for a method, either java or native.""" | |
| 46 | |
| 47 def __init__(self, **kwargs): | |
| 48 self.datatype = kwargs['datatype'] | |
| 49 self.name = kwargs['name'] | |
| 50 | |
| 51 | |
| 52 class NativeMethod(object): | |
| 53 """Describes a C/C++ method that is called by Java code""" | |
| 54 | |
| 55 def __init__(self, **kwargs): | |
| 56 self.static = kwargs['static'] | |
| 57 self.java_class_name = kwargs['java_class_name'] | |
| 58 self.return_type = kwargs['return_type'] | |
| 59 self.name = kwargs['name'] | |
| 60 self.params = kwargs['params'] | |
| 61 if self.params: | |
| 62 assert type(self.params) is list | |
| 63 assert type(self.params[0]) is Param | |
| 64 if (self.params and | |
| 65 self.params[0].datatype == kwargs.get('ptr_type', 'int') and | |
| 66 self.params[0].name.startswith('native')): | |
| 67 self.type = 'method' | |
| 68 self.p0_type = self.params[0].name[len('native'):] | |
| 69 if kwargs.get('native_class_name'): | |
| 70 self.p0_type = kwargs['native_class_name'] | |
| 71 else: | |
| 72 self.type = 'function' | |
| 73 self.method_id_var_name = kwargs.get('method_id_var_name', None) | |
| 74 | |
| 75 | |
| 76 class CalledByNative(object): | |
| 77 """Describes a java method exported to c/c++""" | |
| 78 | |
| 79 def __init__(self, **kwargs): | |
| 80 self.system_class = kwargs['system_class'] | |
| 81 self.unchecked = kwargs['unchecked'] | |
| 82 self.static = kwargs['static'] | |
| 83 self.java_class_name = kwargs['java_class_name'] | |
| 84 self.return_type = kwargs['return_type'] | |
| 85 self.name = kwargs['name'] | |
| 86 self.params = kwargs['params'] | |
| 87 self.method_id_var_name = kwargs.get('method_id_var_name', None) | |
| 88 self.signature = kwargs.get('signature') | |
| 89 self.is_constructor = kwargs.get('is_constructor', False) | |
| 90 self.env_call = GetEnvCall(self.is_constructor, self.static, | |
| 91 self.return_type) | |
| 92 self.static_cast = GetStaticCastForReturnType(self.return_type) | |
| 93 | |
| 94 | |
| 95 class ConstantField(object): | |
| 96 def __init__(self, **kwargs): | |
| 97 self.name = kwargs['name'] | |
| 98 self.value = kwargs['value'] | |
| 99 | |
| 100 | |
| 101 def JavaDataTypeToC(java_type): | |
| 102 """Returns a C datatype for the given java type.""" | |
| 103 java_pod_type_map = { | |
| 104 'int': 'jint', | |
| 105 'byte': 'jbyte', | |
| 106 'char': 'jchar', | |
| 107 'short': 'jshort', | |
| 108 'boolean': 'jboolean', | |
| 109 'long': 'jlong', | |
| 110 'double': 'jdouble', | |
| 111 'float': 'jfloat', | |
| 112 } | |
| 113 java_type_map = { | |
| 114 'void': 'void', | |
| 115 'String': 'jstring', | |
| 116 'java/lang/String': 'jstring', | |
| 117 'java/lang/Class': 'jclass', | |
| 118 } | |
| 119 | |
| 120 if java_type in java_pod_type_map: | |
| 121 return java_pod_type_map[java_type] | |
| 122 elif java_type in java_type_map: | |
| 123 return java_type_map[java_type] | |
| 124 elif java_type.endswith('[]'): | |
| 125 if java_type[:-2] in java_pod_type_map: | |
| 126 return java_pod_type_map[java_type[:-2]] + 'Array' | |
| 127 return 'jobjectArray' | |
| 128 elif java_type.startswith('Class'): | |
| 129 # Checking just the start of the name, rather than a direct comparison, | |
| 130 # in order to handle generics. | |
| 131 return 'jclass' | |
| 132 else: | |
| 133 return 'jobject' | |
| 134 | |
| 135 | |
| 136 def JavaDataTypeToCForCalledByNativeParam(java_type): | |
| 137 """Returns a C datatype to be when calling from native.""" | |
| 138 if java_type == 'int': | |
| 139 return 'JniIntWrapper' | |
| 140 else: | |
| 141 return JavaDataTypeToC(java_type) | |
| 142 | |
| 143 | |
| 144 def JavaReturnValueToC(java_type): | |
| 145 """Returns a valid C return value for the given java type.""" | |
| 146 java_pod_type_map = { | |
| 147 'int': '0', | |
| 148 'byte': '0', | |
| 149 'char': '0', | |
| 150 'short': '0', | |
| 151 'boolean': 'false', | |
| 152 'long': '0', | |
| 153 'double': '0', | |
| 154 'float': '0', | |
| 155 'void': '' | |
| 156 } | |
| 157 return java_pod_type_map.get(java_type, 'NULL') | |
| 158 | |
| 159 | |
| 160 class JniParams(object): | |
| 161 _imports = [] | |
| 162 _fully_qualified_class = '' | |
| 163 _package = '' | |
| 164 _inner_classes = [] | |
| 165 _remappings = [] | |
| 166 _implicit_imports = [] | |
| 167 | |
| 168 @staticmethod | |
| 169 def SetFullyQualifiedClass(fully_qualified_class): | |
| 170 JniParams._fully_qualified_class = 'L' + fully_qualified_class | |
| 171 JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1]) | |
| 172 | |
| 173 @staticmethod | |
| 174 def AddAdditionalImport(class_name): | |
| 175 assert class_name.endswith('.class') | |
| 176 raw_class_name = class_name[:-len('.class')] | |
| 177 if '.' in raw_class_name: | |
| 178 raise SyntaxError('%s cannot be used in @JNIAdditionalImport. ' | |
| 179 'Only import unqualified outer classes.' % class_name) | |
| 180 new_import = 'L%s/%s' % (JniParams._package, raw_class_name) | |
| 181 if new_import in JniParams._imports: | |
| 182 raise SyntaxError('Do not use JNIAdditionalImport on an already ' | |
| 183 'imported class: %s' % (new_import.replace('/', '.'))) | |
| 184 JniParams._imports += [new_import] | |
| 185 | |
| 186 @staticmethod | |
| 187 def ExtractImportsAndInnerClasses(contents): | |
| 188 if not JniParams._package: | |
| 189 raise RuntimeError('SetFullyQualifiedClass must be called before ' | |
| 190 'ExtractImportsAndInnerClasses') | |
| 191 contents = contents.replace('\n', '') | |
| 192 re_import = re.compile(r'import.*?(?P<class>\S*?);') | |
| 193 for match in re.finditer(re_import, contents): | |
| 194 JniParams._imports += ['L' + match.group('class').replace('.', '/')] | |
| 195 | |
| 196 re_inner = re.compile(r'(class|interface)\s+?(?P<name>\w+?)\W') | |
| 197 for match in re.finditer(re_inner, contents): | |
| 198 inner = match.group('name') | |
| 199 if not JniParams._fully_qualified_class.endswith(inner): | |
| 200 JniParams._inner_classes += [JniParams._fully_qualified_class + '$' + | |
| 201 inner] | |
| 202 | |
| 203 re_additional_imports = re.compile( | |
| 204 r'@JNIAdditionalImport\(\s*{?(?P<class_names>.*?)}?\s*\)') | |
| 205 for match in re.finditer(re_additional_imports, contents): | |
| 206 for class_name in match.group('class_names').split(','): | |
| 207 JniParams.AddAdditionalImport(class_name.strip()) | |
| 208 | |
| 209 @staticmethod | |
| 210 def ParseJavaPSignature(signature_line): | |
| 211 prefix = 'Signature: ' | |
| 212 index = signature_line.find(prefix) | |
| 213 if index == -1: | |
| 214 prefix = 'descriptor: ' | |
| 215 index = signature_line.index(prefix) | |
| 216 return '"%s"' % signature_line[index + len(prefix):] | |
| 217 | |
| 218 @staticmethod | |
| 219 def JavaToJni(param): | |
| 220 """Converts a java param into a JNI signature type.""" | |
| 221 pod_param_map = { | |
| 222 'int': 'I', | |
| 223 'boolean': 'Z', | |
| 224 'char': 'C', | |
| 225 'short': 'S', | |
| 226 'long': 'J', | |
| 227 'double': 'D', | |
| 228 'float': 'F', | |
| 229 'byte': 'B', | |
| 230 'void': 'V', | |
| 231 } | |
| 232 object_param_list = [ | |
| 233 'Ljava/lang/Boolean', | |
| 234 'Ljava/lang/Integer', | |
| 235 'Ljava/lang/Long', | |
| 236 'Ljava/lang/Object', | |
| 237 'Ljava/lang/String', | |
| 238 'Ljava/lang/Class', | |
| 239 'Ljava/lang/CharSequence', | |
| 240 'Ljava/lang/Runnable', | |
| 241 'Ljava/lang/Throwable', | |
| 242 ] | |
| 243 | |
| 244 prefix = '' | |
| 245 # Array? | |
| 246 while param[-2:] == '[]': | |
| 247 prefix += '[' | |
| 248 param = param[:-2] | |
| 249 # Generic? | |
| 250 if '<' in param: | |
| 251 param = param[:param.index('<')] | |
| 252 if param in pod_param_map: | |
| 253 return prefix + pod_param_map[param] | |
| 254 if '/' in param: | |
| 255 # Coming from javap, use the fully qualified param directly. | |
| 256 return prefix + 'L' + JniParams.RemapClassName(param) + ';' | |
| 257 | |
| 258 for qualified_name in (object_param_list + | |
| 259 [JniParams._fully_qualified_class] + | |
| 260 JniParams._inner_classes): | |
| 261 if (qualified_name.endswith('/' + param) or | |
| 262 qualified_name.endswith('$' + param.replace('.', '$')) or | |
| 263 qualified_name == 'L' + param): | |
| 264 return prefix + JniParams.RemapClassName(qualified_name) + ';' | |
| 265 | |
| 266 # Is it from an import? (e.g. referecing Class from import pkg.Class; | |
| 267 # note that referencing an inner class Inner from import pkg.Class.Inner | |
| 268 # is not supported). | |
| 269 for qualified_name in JniParams._imports: | |
| 270 if qualified_name.endswith('/' + param): | |
| 271 # Ensure it's not an inner class. | |
| 272 components = qualified_name.split('/') | |
| 273 if len(components) > 2 and components[-2][0].isupper(): | |
| 274 raise SyntaxError('Inner class (%s) can not be imported ' | |
| 275 'and used by JNI (%s). Please import the outer ' | |
| 276 'class and use Outer.Inner instead.' % | |
| 277 (qualified_name, param)) | |
| 278 return prefix + JniParams.RemapClassName(qualified_name) + ';' | |
| 279 | |
| 280 # Is it an inner class from an outer class import? (e.g. referencing | |
| 281 # Class.Inner from import pkg.Class). | |
| 282 if '.' in param: | |
| 283 components = param.split('.') | |
| 284 outer = '/'.join(components[:-1]) | |
| 285 inner = components[-1] | |
| 286 for qualified_name in JniParams._imports: | |
| 287 if qualified_name.endswith('/' + outer): | |
| 288 return (prefix + JniParams.RemapClassName(qualified_name) + | |
| 289 '$' + inner + ';') | |
| 290 raise SyntaxError('Inner class (%s) can not be ' | |
| 291 'used directly by JNI. Please import the outer ' | |
| 292 'class, probably:\n' | |
| 293 'import %s.%s;' % | |
| 294 (param, JniParams._package.replace('/', '.'), | |
| 295 outer.replace('/', '.'))) | |
| 296 | |
| 297 JniParams._CheckImplicitImports(param) | |
| 298 | |
| 299 # Type not found, falling back to same package as this class. | |
| 300 return (prefix + 'L' + | |
| 301 JniParams.RemapClassName(JniParams._package + '/' + param) + ';') | |
| 302 | |
| 303 @staticmethod | |
| 304 def _CheckImplicitImports(param): | |
| 305 # Ensure implicit imports, such as java.lang.*, are not being treated | |
| 306 # as being in the same package. | |
| 307 if not JniParams._implicit_imports: | |
| 308 # This file was generated from android.jar and lists | |
| 309 # all classes that are implicitly imported. | |
| 310 with file(os.path.join(os.path.dirname(sys.argv[0]), | |
| 311 'android_jar.classes'), 'r') as f: | |
| 312 JniParams._implicit_imports = f.readlines() | |
| 313 for implicit_import in JniParams._implicit_imports: | |
| 314 implicit_import = implicit_import.strip().replace('.class', '') | |
| 315 implicit_import = implicit_import.replace('/', '.') | |
| 316 if implicit_import.endswith('.' + param): | |
| 317 raise SyntaxError('Ambiguous class (%s) can not be used directly ' | |
| 318 'by JNI.\nPlease import it, probably:\n\n' | |
| 319 'import %s;' % | |
| 320 (param, implicit_import)) | |
| 321 | |
| 322 | |
| 323 @staticmethod | |
| 324 def Signature(params, returns, wrap): | |
| 325 """Returns the JNI signature for the given datatypes.""" | |
| 326 items = ['('] | |
| 327 items += [JniParams.JavaToJni(param.datatype) for param in params] | |
| 328 items += [')'] | |
| 329 items += [JniParams.JavaToJni(returns)] | |
| 330 if wrap: | |
| 331 return '\n' + '\n'.join(['"' + item + '"' for item in items]) | |
| 332 else: | |
| 333 return '"' + ''.join(items) + '"' | |
| 334 | |
| 335 @staticmethod | |
| 336 def Parse(params): | |
| 337 """Parses the params into a list of Param objects.""" | |
| 338 if not params: | |
| 339 return [] | |
| 340 ret = [] | |
| 341 for p in [p.strip() for p in params.split(',')]: | |
| 342 items = p.split(' ') | |
| 343 if 'final' in items: | |
| 344 items.remove('final') | |
| 345 param = Param( | |
| 346 datatype=items[0], | |
| 347 name=(items[1] if len(items) > 1 else 'p%s' % len(ret)), | |
| 348 ) | |
| 349 ret += [param] | |
| 350 return ret | |
| 351 | |
| 352 @staticmethod | |
| 353 def RemapClassName(class_name): | |
| 354 """Remaps class names using the jarjar mapping table.""" | |
| 355 for old, new in JniParams._remappings: | |
| 356 if old.endswith('**') and old[:-2] in class_name: | |
| 357 return class_name.replace(old[:-2], new, 1) | |
| 358 if '*' not in old and class_name.endswith(old): | |
| 359 return class_name.replace(old, new, 1) | |
| 360 | |
| 361 return class_name | |
| 362 | |
| 363 @staticmethod | |
| 364 def SetJarJarMappings(mappings): | |
| 365 """Parse jarjar mappings from a string.""" | |
| 366 JniParams._remappings = [] | |
| 367 for line in mappings.splitlines(): | |
| 368 rule = line.split() | |
| 369 if rule[0] != 'rule': | |
| 370 continue | |
| 371 _, src, dest = rule | |
| 372 src = src.replace('.', '/') | |
| 373 dest = dest.replace('.', '/') | |
| 374 if src.endswith('**'): | |
| 375 src_real_name = src[:-2] | |
| 376 else: | |
| 377 assert not '*' in src | |
| 378 src_real_name = src | |
| 379 | |
| 380 if dest.endswith('@0'): | |
| 381 JniParams._remappings.append((src, dest[:-2] + src_real_name)) | |
| 382 elif dest.endswith('@1'): | |
| 383 assert '**' in src | |
| 384 JniParams._remappings.append((src, dest[:-2])) | |
| 385 else: | |
| 386 assert not '@' in dest | |
| 387 JniParams._remappings.append((src, dest)) | |
| 388 | |
| 389 | |
| 390 def ExtractJNINamespace(contents): | |
| 391 re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)') | |
| 392 m = re.findall(re_jni_namespace, contents) | |
| 393 if not m: | |
| 394 return '' | |
| 395 return m[0] | |
| 396 | |
| 397 | |
| 398 def ExtractFullyQualifiedJavaClassName(java_file_name, contents): | |
| 399 re_package = re.compile('.*?package (.*?);') | |
| 400 matches = re.findall(re_package, contents) | |
| 401 if not matches: | |
| 402 raise SyntaxError('Unable to find "package" line in %s' % java_file_name) | |
| 403 return (matches[0].replace('.', '/') + '/' + | |
| 404 os.path.splitext(os.path.basename(java_file_name))[0]) | |
| 405 | |
| 406 | |
| 407 def ExtractNatives(contents, ptr_type): | |
| 408 """Returns a list of dict containing information about a native method.""" | |
| 409 contents = contents.replace('\n', '') | |
| 410 natives = [] | |
| 411 re_native = re.compile(r'(@NativeClassQualifiedName' | |
| 412 '\(\"(?P<native_class_name>.*?)\"\)\s+)?' | |
| 413 '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\))\s+)?' | |
| 414 '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*native ' | |
| 415 '(?P<return_type>\S*) ' | |
| 416 '(?P<name>native\w+)\((?P<params>.*?)\);') | |
| 417 for match in re.finditer(re_native, contents): | |
| 418 native = NativeMethod( | |
| 419 static='static' in match.group('qualifiers'), | |
| 420 java_class_name=match.group('java_class_name'), | |
| 421 native_class_name=match.group('native_class_name'), | |
| 422 return_type=match.group('return_type'), | |
| 423 name=match.group('name').replace('native', ''), | |
| 424 params=JniParams.Parse(match.group('params')), | |
| 425 ptr_type=ptr_type) | |
| 426 natives += [native] | |
| 427 return natives | |
| 428 | |
| 429 | |
| 430 def GetStaticCastForReturnType(return_type): | |
| 431 type_map = { 'String' : 'jstring', | |
| 432 'java/lang/String' : 'jstring', | |
| 433 'boolean[]': 'jbooleanArray', | |
| 434 'byte[]': 'jbyteArray', | |
| 435 'char[]': 'jcharArray', | |
| 436 'short[]': 'jshortArray', | |
| 437 'int[]': 'jintArray', | |
| 438 'long[]': 'jlongArray', | |
| 439 'float[]': 'jfloatArray', | |
| 440 'double[]': 'jdoubleArray' } | |
| 441 ret = type_map.get(return_type, None) | |
| 442 if ret: | |
| 443 return ret | |
| 444 if return_type.endswith('[]'): | |
| 445 return 'jobjectArray' | |
| 446 return None | |
| 447 | |
| 448 | |
| 449 def GetEnvCall(is_constructor, is_static, return_type): | |
| 450 """Maps the types availabe via env->Call__Method.""" | |
| 451 if is_constructor: | |
| 452 return 'NewObject' | |
| 453 env_call_map = {'boolean': 'Boolean', | |
| 454 'byte': 'Byte', | |
| 455 'char': 'Char', | |
| 456 'short': 'Short', | |
| 457 'int': 'Int', | |
| 458 'long': 'Long', | |
| 459 'float': 'Float', | |
| 460 'void': 'Void', | |
| 461 'double': 'Double', | |
| 462 'Object': 'Object', | |
| 463 } | |
| 464 call = env_call_map.get(return_type, 'Object') | |
| 465 if is_static: | |
| 466 call = 'Static' + call | |
| 467 return 'Call' + call + 'Method' | |
| 468 | |
| 469 | |
| 470 def GetMangledParam(datatype): | |
| 471 """Returns a mangled identifier for the datatype.""" | |
| 472 if len(datatype) <= 2: | |
| 473 return datatype.replace('[', 'A') | |
| 474 ret = '' | |
| 475 for i in range(1, len(datatype)): | |
| 476 c = datatype[i] | |
| 477 if c == '[': | |
| 478 ret += 'A' | |
| 479 elif c.isupper() or datatype[i - 1] in ['/', 'L']: | |
| 480 ret += c.upper() | |
| 481 return ret | |
| 482 | |
| 483 | |
| 484 def GetMangledMethodName(name, params, return_type): | |
| 485 """Returns a mangled method name for the given signature. | |
| 486 | |
| 487 The returned name can be used as a C identifier and will be unique for all | |
| 488 valid overloads of the same method. | |
| 489 | |
| 490 Args: | |
| 491 name: string. | |
| 492 params: list of Param. | |
| 493 return_type: string. | |
| 494 | |
| 495 Returns: | |
| 496 A mangled name. | |
| 497 """ | |
| 498 mangled_items = [] | |
| 499 for datatype in [return_type] + [x.datatype for x in params]: | |
| 500 mangled_items += [GetMangledParam(JniParams.JavaToJni(datatype))] | |
| 501 mangled_name = name + '_'.join(mangled_items) | |
| 502 assert re.match(r'[0-9a-zA-Z_]+', mangled_name) | |
| 503 return mangled_name | |
| 504 | |
| 505 | |
| 506 def MangleCalledByNatives(called_by_natives): | |
| 507 """Mangles all the overloads from the call_by_natives list.""" | |
| 508 method_counts = collections.defaultdict( | |
| 509 lambda: collections.defaultdict(lambda: 0)) | |
| 510 for called_by_native in called_by_natives: | |
| 511 java_class_name = called_by_native.java_class_name | |
| 512 name = called_by_native.name | |
| 513 method_counts[java_class_name][name] += 1 | |
| 514 for called_by_native in called_by_natives: | |
| 515 java_class_name = called_by_native.java_class_name | |
| 516 method_name = called_by_native.name | |
| 517 method_id_var_name = method_name | |
| 518 if method_counts[java_class_name][method_name] > 1: | |
| 519 method_id_var_name = GetMangledMethodName(method_name, | |
| 520 called_by_native.params, | |
| 521 called_by_native.return_type) | |
| 522 called_by_native.method_id_var_name = method_id_var_name | |
| 523 return called_by_natives | |
| 524 | |
| 525 | |
| 526 # Regex to match the JNI return types that should be included in a | |
| 527 # ScopedJavaLocalRef. | |
| 528 RE_SCOPED_JNI_RETURN_TYPES = re.compile('jobject|jclass|jstring|.*Array') | |
| 529 | |
| 530 # Regex to match a string like "@CalledByNative public void foo(int bar)". | |
| 531 RE_CALLED_BY_NATIVE = re.compile( | |
| 532 '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?' | |
| 533 '\s+(?P<prefix>[\w ]*?)' | |
| 534 '\s*(?P<return_type>\S+?)' | |
| 535 '\s+(?P<name>\w+)' | |
| 536 '\s*\((?P<params>[^\)]*)\)') | |
| 537 | |
| 538 | |
| 539 def ExtractCalledByNatives(contents): | |
| 540 """Parses all methods annotated with @CalledByNative. | |
| 541 | |
| 542 Args: | |
| 543 contents: the contents of the java file. | |
| 544 | |
| 545 Returns: | |
| 546 A list of dict with information about the annotated methods. | |
| 547 TODO(bulach): return a CalledByNative object. | |
| 548 | |
| 549 Raises: | |
| 550 ParseError: if unable to parse. | |
| 551 """ | |
| 552 called_by_natives = [] | |
| 553 for match in re.finditer(RE_CALLED_BY_NATIVE, contents): | |
| 554 called_by_natives += [CalledByNative( | |
| 555 system_class=False, | |
| 556 unchecked='Unchecked' in match.group('Unchecked'), | |
| 557 static='static' in match.group('prefix'), | |
| 558 java_class_name=match.group('annotation') or '', | |
| 559 return_type=match.group('return_type'), | |
| 560 name=match.group('name'), | |
| 561 params=JniParams.Parse(match.group('params')))] | |
| 562 # Check for any @CalledByNative occurrences that weren't matched. | |
| 563 unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n') | |
| 564 for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]): | |
| 565 if '@CalledByNative' in line1: | |
| 566 raise ParseError('could not parse @CalledByNative method signature', | |
| 567 line1, line2) | |
| 568 return MangleCalledByNatives(called_by_natives) | |
| 569 | |
| 570 | |
| 571 class JNIFromJavaP(object): | |
| 572 """Uses 'javap' to parse a .class file and generate the JNI header file.""" | |
| 573 | |
| 574 def __init__(self, contents, options): | |
| 575 self.contents = contents | |
| 576 self.namespace = options.namespace | |
| 577 for line in contents: | |
| 578 class_name = re.match( | |
| 579 '.*?(public).*?(class|interface) (?P<class_name>\S+?)( |\Z)', | |
| 580 line) | |
| 581 if class_name: | |
| 582 self.fully_qualified_class = class_name.group('class_name') | |
| 583 break | |
| 584 self.fully_qualified_class = self.fully_qualified_class.replace('.', '/') | |
| 585 # Java 7's javap includes type parameters in output, like HashSet<T>. Strip | |
| 586 # away the <...> and use the raw class name that Java 6 would've given us. | |
| 587 self.fully_qualified_class = self.fully_qualified_class.split('<', 1)[0] | |
| 588 JniParams.SetFullyQualifiedClass(self.fully_qualified_class) | |
| 589 self.java_class_name = self.fully_qualified_class.split('/')[-1] | |
| 590 if not self.namespace: | |
| 591 self.namespace = 'JNI_' + self.java_class_name | |
| 592 re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)' | |
| 593 '\((?P<params>.*?)\)') | |
| 594 self.called_by_natives = [] | |
| 595 for lineno, content in enumerate(contents[2:], 2): | |
| 596 match = re.match(re_method, content) | |
| 597 if not match: | |
| 598 continue | |
| 599 self.called_by_natives += [CalledByNative( | |
| 600 system_class=True, | |
| 601 unchecked=False, | |
| 602 static='static' in match.group('prefix'), | |
| 603 java_class_name='', | |
| 604 return_type=match.group('return_type').replace('.', '/'), | |
| 605 name=match.group('name'), | |
| 606 params=JniParams.Parse(match.group('params').replace('.', '/')), | |
| 607 signature=JniParams.ParseJavaPSignature(contents[lineno + 1]))] | |
| 608 re_constructor = re.compile('(.*?)public ' + | |
| 609 self.fully_qualified_class.replace('/', '.') + | |
| 610 '\((?P<params>.*?)\)') | |
| 611 for lineno, content in enumerate(contents[2:], 2): | |
| 612 match = re.match(re_constructor, content) | |
| 613 if not match: | |
| 614 continue | |
| 615 self.called_by_natives += [CalledByNative( | |
| 616 system_class=True, | |
| 617 unchecked=False, | |
| 618 static=False, | |
| 619 java_class_name='', | |
| 620 return_type=self.fully_qualified_class, | |
| 621 name='Constructor', | |
| 622 params=JniParams.Parse(match.group('params').replace('.', '/')), | |
| 623 signature=JniParams.ParseJavaPSignature(contents[lineno + 1]), | |
| 624 is_constructor=True)] | |
| 625 self.called_by_natives = MangleCalledByNatives(self.called_by_natives) | |
| 626 | |
| 627 self.constant_fields = [] | |
| 628 re_constant_field = re.compile('.*?public static final int (?P<name>.*?);') | |
| 629 re_constant_field_value = re.compile( | |
| 630 '.*?Constant(Value| value): int (?P<value>(-*[0-9]+)?)') | |
| 631 for lineno, content in enumerate(contents[2:], 2): | |
| 632 match = re.match(re_constant_field, content) | |
| 633 if not match: | |
| 634 continue | |
| 635 value = re.match(re_constant_field_value, contents[lineno + 2]) | |
| 636 if not value: | |
| 637 value = re.match(re_constant_field_value, contents[lineno + 3]) | |
| 638 if value: | |
| 639 self.constant_fields.append( | |
| 640 ConstantField(name=match.group('name'), | |
| 641 value=value.group('value'))) | |
| 642 | |
| 643 self.inl_header_file_generator = InlHeaderFileGenerator( | |
| 644 self.namespace, self.fully_qualified_class, [], | |
| 645 self.called_by_natives, self.constant_fields, options) | |
| 646 | |
| 647 def GetContent(self): | |
| 648 return self.inl_header_file_generator.GetContent() | |
| 649 | |
| 650 @staticmethod | |
| 651 def CreateFromClass(class_file, options): | |
| 652 class_name = os.path.splitext(os.path.basename(class_file))[0] | |
| 653 p = subprocess.Popen(args=[options.javap, '-c', '-verbose', | |
| 654 '-s', class_name], | |
| 655 cwd=os.path.dirname(class_file), | |
| 656 stdout=subprocess.PIPE, | |
| 657 stderr=subprocess.PIPE) | |
| 658 stdout, _ = p.communicate() | |
| 659 jni_from_javap = JNIFromJavaP(stdout.split('\n'), options) | |
| 660 return jni_from_javap | |
| 661 | |
| 662 | |
| 663 class JNIFromJavaSource(object): | |
| 664 """Uses the given java source file to generate the JNI header file.""" | |
| 665 | |
| 666 # Match single line comments, multiline comments, character literals, and | |
| 667 # double-quoted strings. | |
| 668 _comment_remover_regex = re.compile( | |
| 669 r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', | |
| 670 re.DOTALL | re.MULTILINE) | |
| 671 | |
| 672 def __init__(self, contents, fully_qualified_class, options): | |
| 673 contents = self._RemoveComments(contents) | |
| 674 JniParams.SetFullyQualifiedClass(fully_qualified_class) | |
| 675 JniParams.ExtractImportsAndInnerClasses(contents) | |
| 676 jni_namespace = ExtractJNINamespace(contents) or options.namespace | |
| 677 natives = ExtractNatives(contents, options.ptr_type) | |
| 678 called_by_natives = ExtractCalledByNatives(contents) | |
| 679 if len(natives) == 0 and len(called_by_natives) == 0: | |
| 680 raise SyntaxError('Unable to find any JNI methods for %s.' % | |
| 681 fully_qualified_class) | |
| 682 inl_header_file_generator = InlHeaderFileGenerator( | |
| 683 jni_namespace, fully_qualified_class, natives, called_by_natives, | |
| 684 [], options) | |
| 685 self.content = inl_header_file_generator.GetContent() | |
| 686 | |
| 687 @classmethod | |
| 688 def _RemoveComments(cls, contents): | |
| 689 # We need to support both inline and block comments, and we need to handle | |
| 690 # strings that contain '//' or '/*'. | |
| 691 # TODO(bulach): This is a bit hacky. It would be cleaner to use a real Java | |
| 692 # parser. Maybe we could ditch JNIFromJavaSource and just always use | |
| 693 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT. | |
| 694 # http://code.google.com/p/chromium/issues/detail?id=138941 | |
| 695 def replacer(match): | |
| 696 # Replace matches that are comments with nothing; return literals/strings | |
| 697 # unchanged. | |
| 698 s = match.group(0) | |
| 699 if s.startswith('/'): | |
| 700 return '' | |
| 701 else: | |
| 702 return s | |
| 703 return cls._comment_remover_regex.sub(replacer, contents) | |
| 704 | |
| 705 def GetContent(self): | |
| 706 return self.content | |
| 707 | |
| 708 @staticmethod | |
| 709 def CreateFromFile(java_file_name, options): | |
| 710 contents = file(java_file_name).read() | |
| 711 fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name, | |
| 712 contents) | |
| 713 return JNIFromJavaSource(contents, fully_qualified_class, options) | |
| 714 | |
| 715 | |
| 716 class InlHeaderFileGenerator(object): | |
| 717 """Generates an inline header file for JNI integration.""" | |
| 718 | |
| 719 def __init__(self, namespace, fully_qualified_class, natives, | |
| 720 called_by_natives, constant_fields, options): | |
| 721 self.namespace = namespace | |
| 722 self.fully_qualified_class = fully_qualified_class | |
| 723 self.class_name = self.fully_qualified_class.split('/')[-1] | |
| 724 self.natives = natives | |
| 725 self.called_by_natives = called_by_natives | |
| 726 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' | |
| 727 self.constant_fields = constant_fields | |
| 728 self.options = options | |
| 729 self.init_native = self.ExtractInitNative(options) | |
| 730 | |
| 731 def ExtractInitNative(self, options): | |
| 732 for native in self.natives: | |
| 733 if options.jni_init_native_name == 'native' + native.name: | |
| 734 self.natives.remove(native) | |
| 735 return native | |
| 736 return None | |
| 737 | |
| 738 def GetContent(self): | |
| 739 """Returns the content of the JNI binding file.""" | |
| 740 template = Template("""\ | |
| 741 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 742 // Use of this source code is governed by a BSD-style license that can be | |
| 743 // found in the LICENSE file. | |
| 744 | |
| 745 | |
| 746 // This file is autogenerated by | |
| 747 // ${SCRIPT_NAME} | |
| 748 // For | |
| 749 // ${FULLY_QUALIFIED_CLASS} | |
| 750 | |
| 751 #ifndef ${HEADER_GUARD} | |
| 752 #define ${HEADER_GUARD} | |
| 753 | |
| 754 #include <jni.h> | |
| 755 | |
| 756 ${INCLUDES} | |
| 757 | |
| 758 #include "base/android/jni_int_wrapper.h" | |
| 759 | |
| 760 // Step 1: forward declarations. | |
| 761 namespace { | |
| 762 $CLASS_PATH_DEFINITIONS | |
| 763 $METHOD_ID_DEFINITIONS | |
| 764 } // namespace | |
| 765 | |
| 766 $OPEN_NAMESPACE | |
| 767 $FORWARD_DECLARATIONS | |
| 768 | |
| 769 $CONSTANT_FIELDS | |
| 770 | |
| 771 // Step 2: method stubs. | |
| 772 $METHOD_STUBS | |
| 773 | |
| 774 // Step 3: RegisterNatives. | |
| 775 $JNI_NATIVE_METHODS | |
| 776 $REGISTER_NATIVES | |
| 777 $CLOSE_NAMESPACE | |
| 778 $JNI_REGISTER_NATIVES | |
| 779 #endif // ${HEADER_GUARD} | |
| 780 """) | |
| 781 values = { | |
| 782 'SCRIPT_NAME': self.options.script_name, | |
| 783 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class, | |
| 784 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(), | |
| 785 'METHOD_ID_DEFINITIONS': self.GetMethodIDDefinitionsString(), | |
| 786 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(), | |
| 787 'CONSTANT_FIELDS': self.GetConstantFieldsString(), | |
| 788 'METHOD_STUBS': self.GetMethodStubsString(), | |
| 789 'OPEN_NAMESPACE': self.GetOpenNamespaceString(), | |
| 790 'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(), | |
| 791 'REGISTER_NATIVES': self.GetRegisterNativesString(), | |
| 792 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(), | |
| 793 'HEADER_GUARD': self.header_guard, | |
| 794 'INCLUDES': self.GetIncludesString(), | |
| 795 'JNI_REGISTER_NATIVES': self.GetJNIRegisterNativesString() | |
| 796 } | |
| 797 return WrapOutput(template.substitute(values)) | |
| 798 | |
| 799 def GetClassPathDefinitionsString(self): | |
| 800 ret = [] | |
| 801 ret += [self.GetClassPathDefinitions()] | |
| 802 return '\n'.join(ret) | |
| 803 | |
| 804 def GetMethodIDDefinitionsString(self): | |
| 805 """Returns the definition of method ids for the called by native methods.""" | |
| 806 if not self.options.eager_called_by_natives: | |
| 807 return '' | |
| 808 template = Template("""\ | |
| 809 jmethodID g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = NULL;""") | |
| 810 ret = [] | |
| 811 for called_by_native in self.called_by_natives: | |
| 812 values = { | |
| 813 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, | |
| 814 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, | |
| 815 } | |
| 816 ret += [template.substitute(values)] | |
| 817 return '\n'.join(ret) | |
| 818 | |
| 819 def GetForwardDeclarationsString(self): | |
| 820 ret = [] | |
| 821 for native in self.natives: | |
| 822 if native.type != 'method': | |
| 823 ret += [self.GetForwardDeclaration(native)] | |
| 824 if self.options.native_exports and ret: | |
| 825 return '\nextern "C" {\n' + "\n".join(ret) + '\n}; // extern "C"' | |
| 826 return '\n'.join(ret) | |
| 827 | |
| 828 def GetConstantFieldsString(self): | |
| 829 if not self.constant_fields: | |
| 830 return '' | |
| 831 ret = ['enum Java_%s_constant_fields {' % self.class_name] | |
| 832 for c in self.constant_fields: | |
| 833 ret += [' %s = %s,' % (c.name, c.value)] | |
| 834 ret += ['};'] | |
| 835 return '\n'.join(ret) | |
| 836 | |
| 837 def GetMethodStubsString(self): | |
| 838 """Returns the code corresponding to method stubs.""" | |
| 839 ret = [] | |
| 840 for native in self.natives: | |
| 841 if native.type == 'method': | |
| 842 ret += [self.GetNativeMethodStubString(native)] | |
| 843 if self.options.eager_called_by_natives: | |
| 844 ret += self.GetEagerCalledByNativeMethodStubs() | |
| 845 else: | |
| 846 ret += self.GetLazyCalledByNativeMethodStubs() | |
| 847 | |
| 848 if self.options.native_exports and ret: | |
| 849 return '\nextern "C" {\n' + "\n".join(ret) + '\n}; // extern "C"' | |
| 850 return '\n'.join(ret) | |
| 851 | |
| 852 def GetLazyCalledByNativeMethodStubs(self): | |
| 853 return [self.GetLazyCalledByNativeMethodStub(called_by_native) | |
| 854 for called_by_native in self.called_by_natives] | |
| 855 | |
| 856 def GetEagerCalledByNativeMethodStubs(self): | |
| 857 ret = [] | |
| 858 if self.called_by_natives: | |
| 859 ret += ['namespace {'] | |
| 860 for called_by_native in self.called_by_natives: | |
| 861 ret += [self.GetEagerCalledByNativeMethodStub(called_by_native)] | |
| 862 ret += ['} // namespace'] | |
| 863 return ret | |
| 864 | |
| 865 def GetIncludesString(self): | |
| 866 if not self.options.includes: | |
| 867 return '' | |
| 868 includes = self.options.includes.split(',') | |
| 869 return '\n'.join('#include "%s"' % x for x in includes) | |
| 870 | |
| 871 def GetKMethodsString(self, clazz): | |
| 872 ret = [] | |
| 873 for native in self.natives: | |
| 874 if (native.java_class_name == clazz or | |
| 875 (not native.java_class_name and clazz == self.class_name)): | |
| 876 ret += [self.GetKMethodArrayEntry(native)] | |
| 877 return '\n'.join(ret) | |
| 878 | |
| 879 def SubstituteNativeMethods(self, template): | |
| 880 """Substitutes JAVA_CLASS and KMETHODS in the provided template.""" | |
| 881 ret = [] | |
| 882 all_classes = self.GetUniqueClasses(self.natives) | |
| 883 all_classes[self.class_name] = self.fully_qualified_class | |
| 884 for clazz in all_classes: | |
| 885 kmethods = self.GetKMethodsString(clazz) | |
| 886 if kmethods: | |
| 887 values = {'JAVA_CLASS': clazz, | |
| 888 'KMETHODS': kmethods} | |
| 889 ret += [template.substitute(values)] | |
| 890 if not ret: return '' | |
| 891 return '\n' + '\n'.join(ret) | |
| 892 | |
| 893 def GetJNINativeMethodsString(self): | |
| 894 """Returns the implementation of the array of native methods.""" | |
| 895 if self.options.native_exports and not self.options.native_exports_optional: | |
| 896 return '' | |
| 897 template = Template("""\ | |
| 898 static const JNINativeMethod kMethods${JAVA_CLASS}[] = { | |
| 899 ${KMETHODS} | |
| 900 }; | |
| 901 """) | |
| 902 return self.SubstituteNativeMethods(template) | |
| 903 | |
| 904 def GetRegisterCalledByNativesImplString(self): | |
| 905 """Returns the code for registering the called by native methods.""" | |
| 906 if not self.options.eager_called_by_natives: | |
| 907 return '' | |
| 908 template = Template("""\ | |
| 909 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = ${GET_METHOD_ID_IMPL} | |
| 910 if (g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} == NULL) { | |
| 911 return false; | |
| 912 } | |
| 913 """) | |
| 914 ret = [] | |
| 915 for called_by_native in self.called_by_natives: | |
| 916 values = { | |
| 917 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, | |
| 918 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, | |
| 919 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native), | |
| 920 } | |
| 921 ret += [template.substitute(values)] | |
| 922 return '\n'.join(ret) | |
| 923 | |
| 924 def GetRegisterNativesString(self): | |
| 925 """Returns the code for RegisterNatives.""" | |
| 926 template = Template("""\ | |
| 927 ${REGISTER_NATIVES_SIGNATURE} { | |
| 928 ${EARLY_EXIT} | |
| 929 ${CLASSES} | |
| 930 ${NATIVES} | |
| 931 ${CALLED_BY_NATIVES} | |
| 932 return true; | |
| 933 } | |
| 934 """) | |
| 935 signature = 'static bool RegisterNativesImpl(JNIEnv* env' | |
| 936 if self.init_native: | |
| 937 signature += ', jclass clazz)' | |
| 938 else: | |
| 939 signature += ')' | |
| 940 | |
| 941 early_exit = '' | |
| 942 if self.options.native_exports_optional: | |
| 943 early_exit = """\ | |
| 944 if (base::android::IsManualJniRegistrationDisabled()) return true; | |
| 945 """ | |
| 946 | |
| 947 natives = self.GetRegisterNativesImplString() | |
| 948 called_by_natives = self.GetRegisterCalledByNativesImplString() | |
| 949 values = {'REGISTER_NATIVES_SIGNATURE': signature, | |
| 950 'EARLY_EXIT': early_exit, | |
| 951 'CLASSES': self.GetFindClasses(), | |
| 952 'NATIVES': natives, | |
| 953 'CALLED_BY_NATIVES': called_by_natives, | |
| 954 } | |
| 955 return template.substitute(values) | |
| 956 | |
| 957 def GetRegisterNativesImplString(self): | |
| 958 """Returns the shared implementation for RegisterNatives.""" | |
| 959 if self.options.native_exports and not self.options.native_exports_optional: | |
| 960 return '' | |
| 961 | |
| 962 template = Template("""\ | |
| 963 const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS}); | |
| 964 | |
| 965 if (env->RegisterNatives(${JAVA_CLASS}_clazz(env), | |
| 966 kMethods${JAVA_CLASS}, | |
| 967 kMethods${JAVA_CLASS}Size) < 0) { | |
| 968 jni_generator::HandleRegistrationError( | |
| 969 env, ${JAVA_CLASS}_clazz(env), __FILE__); | |
| 970 return false; | |
| 971 } | |
| 972 """) | |
| 973 return self.SubstituteNativeMethods(template) | |
| 974 | |
| 975 def GetJNIRegisterNativesString(self): | |
| 976 """Returns the implementation for the JNI registration of native methods.""" | |
| 977 if not self.init_native: | |
| 978 return '' | |
| 979 | |
| 980 template = Template("""\ | |
| 981 extern "C" JNIEXPORT bool JNICALL | |
| 982 Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) { | |
| 983 return ${NAMESPACE}RegisterNativesImpl(env, clazz); | |
| 984 } | |
| 985 """) | |
| 986 | |
| 987 if self.options.native_exports: | |
| 988 java_name = JniParams.RemapClassName(self.fully_qualified_class) | |
| 989 java_name = java_name.replace('_', '_1').replace('/', '_') | |
| 990 else: | |
| 991 java_name = self.fully_qualified_class.replace('/', '_') | |
| 992 | |
| 993 namespace = '' | |
| 994 if self.namespace: | |
| 995 namespace = self.namespace + '::' | |
| 996 values = {'FULLY_QUALIFIED_CLASS': java_name, | |
| 997 'INIT_NATIVE_NAME': 'native' + self.init_native.name, | |
| 998 'NAMESPACE': namespace, | |
| 999 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString() | |
| 1000 } | |
| 1001 return template.substitute(values) | |
| 1002 | |
| 1003 def GetOpenNamespaceString(self): | |
| 1004 if self.namespace: | |
| 1005 all_namespaces = ['namespace %s {' % ns | |
| 1006 for ns in self.namespace.split('::')] | |
| 1007 return '\n'.join(all_namespaces) | |
| 1008 return '' | |
| 1009 | |
| 1010 def GetCloseNamespaceString(self): | |
| 1011 if self.namespace: | |
| 1012 all_namespaces = ['} // namespace %s' % ns | |
| 1013 for ns in self.namespace.split('::')] | |
| 1014 all_namespaces.reverse() | |
| 1015 return '\n'.join(all_namespaces) + '\n' | |
| 1016 return '' | |
| 1017 | |
| 1018 def GetJNIFirstParam(self, native): | |
| 1019 ret = [] | |
| 1020 if native.type == 'method': | |
| 1021 ret = ['jobject jcaller'] | |
| 1022 elif native.type == 'function': | |
| 1023 if native.static: | |
| 1024 ret = ['jclass jcaller'] | |
| 1025 else: | |
| 1026 ret = ['jobject jcaller'] | |
| 1027 return ret | |
| 1028 | |
| 1029 def GetParamsInDeclaration(self, native): | |
| 1030 """Returns the params for the stub declaration. | |
| 1031 | |
| 1032 Args: | |
| 1033 native: the native dictionary describing the method. | |
| 1034 | |
| 1035 Returns: | |
| 1036 A string containing the params. | |
| 1037 """ | |
| 1038 return ',\n '.join(self.GetJNIFirstParam(native) + | |
| 1039 [JavaDataTypeToC(param.datatype) + ' ' + | |
| 1040 param.name | |
| 1041 for param in native.params]) | |
| 1042 | |
| 1043 def GetCalledByNativeParamsInDeclaration(self, called_by_native): | |
| 1044 return ',\n '.join([ | |
| 1045 JavaDataTypeToCForCalledByNativeParam(param.datatype) + ' ' + | |
| 1046 param.name | |
| 1047 for param in called_by_native.params]) | |
| 1048 | |
| 1049 def GetStubName(self, native): | |
| 1050 """Return the name of the stub function for this native method. | |
| 1051 | |
| 1052 Args: | |
| 1053 native: the native dictionary describing the method. | |
| 1054 | |
| 1055 Returns: | |
| 1056 A string with the stub function name. For native exports mode this is the | |
| 1057 Java_* symbol name required by the JVM; otherwise it is just the name of | |
| 1058 the native method itself. | |
| 1059 """ | |
| 1060 if self.options.native_exports: | |
| 1061 template = Template("Java_${JAVA_NAME}_native${NAME}") | |
| 1062 | |
| 1063 java_name = JniParams.RemapClassName(self.fully_qualified_class) | |
| 1064 java_name = java_name.replace('_', '_1').replace('/', '_') | |
| 1065 if native.java_class_name: | |
| 1066 java_name += '_00024' + native.java_class_name | |
| 1067 | |
| 1068 values = {'NAME': native.name, | |
| 1069 'JAVA_NAME': java_name} | |
| 1070 return template.substitute(values) | |
| 1071 else: | |
| 1072 return native.name | |
| 1073 | |
| 1074 def GetForwardDeclaration(self, native): | |
| 1075 template_str = """ | |
| 1076 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS}); | |
| 1077 """ | |
| 1078 if self.options.native_exports: | |
| 1079 template_str += """ | |
| 1080 __attribute__((visibility("default"))) | |
| 1081 ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS}) { | |
| 1082 return ${NAME}(${PARAMS_IN_CALL}); | |
| 1083 } | |
| 1084 """ | |
| 1085 template = Template(template_str) | |
| 1086 params_in_call = [] | |
| 1087 if not self.options.pure_native_methods: | |
| 1088 params_in_call = ['env', 'jcaller'] | |
| 1089 params_in_call = ', '.join(params_in_call + [p.name for p in native.params]) | |
| 1090 | |
| 1091 values = {'RETURN': JavaDataTypeToC(native.return_type), | |
| 1092 'NAME': native.name, | |
| 1093 'PARAMS': self.GetParamsInDeclaration(native), | |
| 1094 'PARAMS_IN_CALL': params_in_call, | |
| 1095 'STUB_NAME': self.GetStubName(native)} | |
| 1096 return template.substitute(values) | |
| 1097 | |
| 1098 def GetNativeMethodStubString(self, native): | |
| 1099 """Returns stubs for native methods.""" | |
| 1100 if self.options.native_exports: | |
| 1101 template_str = """\ | |
| 1102 __attribute__((visibility("default"))) | |
| 1103 ${RETURN} ${STUB_NAME}(JNIEnv* env, | |
| 1104 ${PARAMS_IN_DECLARATION}) {""" | |
| 1105 else: | |
| 1106 template_str = """\ | |
| 1107 static ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {""" | |
| 1108 template_str += """ | |
| 1109 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); | |
| 1110 CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN}); | |
| 1111 return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL}; | |
| 1112 } | |
| 1113 """ | |
| 1114 | |
| 1115 template = Template(template_str) | |
| 1116 params = [] | |
| 1117 if not self.options.pure_native_methods: | |
| 1118 params = ['env', 'jcaller'] | |
| 1119 params_in_call = ', '.join(params + [p.name for p in native.params[1:]]) | |
| 1120 | |
| 1121 return_type = JavaDataTypeToC(native.return_type) | |
| 1122 optional_error_return = JavaReturnValueToC(native.return_type) | |
| 1123 if optional_error_return: | |
| 1124 optional_error_return = ', ' + optional_error_return | |
| 1125 post_call = '' | |
| 1126 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): | |
| 1127 post_call = '.Release()' | |
| 1128 | |
| 1129 values = { | |
| 1130 'RETURN': return_type, | |
| 1131 'OPTIONAL_ERROR_RETURN': optional_error_return, | |
| 1132 'NAME': native.name, | |
| 1133 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native), | |
| 1134 'PARAM0_NAME': native.params[0].name, | |
| 1135 'P0_TYPE': native.p0_type, | |
| 1136 'PARAMS_IN_CALL': params_in_call, | |
| 1137 'POST_CALL': post_call, | |
| 1138 'STUB_NAME': self.GetStubName(native), | |
| 1139 } | |
| 1140 return template.substitute(values) | |
| 1141 | |
| 1142 def GetArgument(self, param): | |
| 1143 return ('as_jint(' + param.name + ')' | |
| 1144 if param.datatype == 'int' else param.name) | |
| 1145 | |
| 1146 def GetArgumentsInCall(self, params): | |
| 1147 """Return a string of arguments to call from native into Java""" | |
| 1148 return [self.GetArgument(p) for p in params] | |
| 1149 | |
| 1150 def GetCalledByNativeValues(self, called_by_native): | |
| 1151 """Fills in necessary values for the CalledByNative methods.""" | |
| 1152 java_class = called_by_native.java_class_name or self.class_name | |
| 1153 if called_by_native.static or called_by_native.is_constructor: | |
| 1154 first_param_in_declaration = '' | |
| 1155 first_param_in_call = ('%s_clazz(env)' % java_class) | |
| 1156 else: | |
| 1157 first_param_in_declaration = ', jobject obj' | |
| 1158 first_param_in_call = 'obj' | |
| 1159 params_in_declaration = self.GetCalledByNativeParamsInDeclaration( | |
| 1160 called_by_native) | |
| 1161 if params_in_declaration: | |
| 1162 params_in_declaration = ', ' + params_in_declaration | |
| 1163 params_in_call = ', '.join(self.GetArgumentsInCall(called_by_native.params)) | |
| 1164 if params_in_call: | |
| 1165 params_in_call = ', ' + params_in_call | |
| 1166 pre_call = '' | |
| 1167 post_call = '' | |
| 1168 if called_by_native.static_cast: | |
| 1169 pre_call = 'static_cast<%s>(' % called_by_native.static_cast | |
| 1170 post_call = ')' | |
| 1171 check_exception = '' | |
| 1172 if not called_by_native.unchecked: | |
| 1173 check_exception = 'jni_generator::CheckException(env);' | |
| 1174 return_type = JavaDataTypeToC(called_by_native.return_type) | |
| 1175 optional_error_return = JavaReturnValueToC(called_by_native.return_type) | |
| 1176 if optional_error_return: | |
| 1177 optional_error_return = ', ' + optional_error_return | |
| 1178 return_declaration = '' | |
| 1179 return_clause = '' | |
| 1180 if return_type != 'void': | |
| 1181 pre_call = ' ' + pre_call | |
| 1182 return_declaration = return_type + ' ret =' | |
| 1183 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): | |
| 1184 return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>' | |
| 1185 return_clause = 'return ' + return_type + '(env, ret);' | |
| 1186 else: | |
| 1187 return_clause = 'return ret;' | |
| 1188 return { | |
| 1189 'JAVA_CLASS': java_class, | |
| 1190 'RETURN_TYPE': return_type, | |
| 1191 'OPTIONAL_ERROR_RETURN': optional_error_return, | |
| 1192 'RETURN_DECLARATION': return_declaration, | |
| 1193 'RETURN_CLAUSE': return_clause, | |
| 1194 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration, | |
| 1195 'PARAMS_IN_DECLARATION': params_in_declaration, | |
| 1196 'PRE_CALL': pre_call, | |
| 1197 'POST_CALL': post_call, | |
| 1198 'ENV_CALL': called_by_native.env_call, | |
| 1199 'FIRST_PARAM_IN_CALL': first_param_in_call, | |
| 1200 'PARAMS_IN_CALL': params_in_call, | |
| 1201 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, | |
| 1202 'CHECK_EXCEPTION': check_exception, | |
| 1203 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native) | |
| 1204 } | |
| 1205 | |
| 1206 def GetEagerCalledByNativeMethodStub(self, called_by_native): | |
| 1207 """Returns the implementation of the called by native method.""" | |
| 1208 template = Template(""" | |
| 1209 static ${RETURN_TYPE} ${METHOD_ID_VAR_NAME}(\ | |
| 1210 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION}) { | |
| 1211 ${RETURN_DECLARATION}${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, | |
| 1212 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}${PARAMS_IN_CALL})${POST_CALL}; | |
| 1213 ${RETURN_CLAUSE} | |
| 1214 }""") | |
| 1215 values = self.GetCalledByNativeValues(called_by_native) | |
| 1216 return template.substitute(values) | |
| 1217 | |
| 1218 def GetLazyCalledByNativeMethodStub(self, called_by_native): | |
| 1219 """Returns a string.""" | |
| 1220 function_signature_template = Template("""\ | |
| 1221 static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\ | |
| 1222 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""") | |
| 1223 function_header_template = Template("""\ | |
| 1224 ${FUNCTION_SIGNATURE} {""") | |
| 1225 function_header_with_unused_template = Template("""\ | |
| 1226 ${FUNCTION_SIGNATURE} __attribute__ ((unused)); | |
| 1227 ${FUNCTION_SIGNATURE} {""") | |
| 1228 template = Template(""" | |
| 1229 static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0; | |
| 1230 ${FUNCTION_HEADER} | |
| 1231 /* Must call RegisterNativesImpl() */ | |
| 1232 CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL}, | |
| 1233 ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN}); | |
| 1234 jmethodID method_id = | |
| 1235 ${GET_METHOD_ID_IMPL} | |
| 1236 ${RETURN_DECLARATION} | |
| 1237 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, | |
| 1238 method_id${PARAMS_IN_CALL})${POST_CALL}; | |
| 1239 ${CHECK_EXCEPTION} | |
| 1240 ${RETURN_CLAUSE} | |
| 1241 }""") | |
| 1242 values = self.GetCalledByNativeValues(called_by_native) | |
| 1243 values['FUNCTION_SIGNATURE'] = ( | |
| 1244 function_signature_template.substitute(values)) | |
| 1245 if called_by_native.system_class: | |
| 1246 values['FUNCTION_HEADER'] = ( | |
| 1247 function_header_with_unused_template.substitute(values)) | |
| 1248 else: | |
| 1249 values['FUNCTION_HEADER'] = function_header_template.substitute(values) | |
| 1250 return template.substitute(values) | |
| 1251 | |
| 1252 def GetKMethodArrayEntry(self, native): | |
| 1253 template = Template(' { "native${NAME}", ${JNI_SIGNATURE}, ' + | |
| 1254 'reinterpret_cast<void*>(${STUB_NAME}) },') | |
| 1255 values = {'NAME': native.name, | |
| 1256 'JNI_SIGNATURE': JniParams.Signature(native.params, | |
| 1257 native.return_type, | |
| 1258 True), | |
| 1259 'STUB_NAME': self.GetStubName(native)} | |
| 1260 return template.substitute(values) | |
| 1261 | |
| 1262 def GetUniqueClasses(self, origin): | |
| 1263 ret = {self.class_name: self.fully_qualified_class} | |
| 1264 for entry in origin: | |
| 1265 class_name = self.class_name | |
| 1266 jni_class_path = self.fully_qualified_class | |
| 1267 if entry.java_class_name: | |
| 1268 class_name = entry.java_class_name | |
| 1269 jni_class_path = self.fully_qualified_class + '$' + class_name | |
| 1270 ret[class_name] = jni_class_path | |
| 1271 return ret | |
| 1272 | |
| 1273 def GetClassPathDefinitions(self): | |
| 1274 """Returns the ClassPath constants.""" | |
| 1275 ret = [] | |
| 1276 template = Template("""\ | |
| 1277 const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""") | |
| 1278 native_classes = self.GetUniqueClasses(self.natives) | |
| 1279 called_by_native_classes = self.GetUniqueClasses(self.called_by_natives) | |
| 1280 if self.options.native_exports: | |
| 1281 all_classes = called_by_native_classes | |
| 1282 else: | |
| 1283 all_classes = native_classes | |
| 1284 all_classes.update(called_by_native_classes) | |
| 1285 | |
| 1286 for clazz in all_classes: | |
| 1287 values = { | |
| 1288 'JAVA_CLASS': clazz, | |
| 1289 'JNI_CLASS_PATH': JniParams.RemapClassName(all_classes[clazz]), | |
| 1290 } | |
| 1291 ret += [template.substitute(values)] | |
| 1292 ret += '' | |
| 1293 | |
| 1294 class_getter_methods = [] | |
| 1295 if self.options.native_exports: | |
| 1296 template = Template("""\ | |
| 1297 // Leaking this jclass as we cannot use LazyInstance from some threads. | |
| 1298 base::subtle::AtomicWord g_${JAVA_CLASS}_clazz __attribute__((unused)) = 0; | |
| 1299 #define ${JAVA_CLASS}_clazz(env) \ | |
| 1300 base::android::LazyGetClass(env, k${JAVA_CLASS}ClassPath, \ | |
| 1301 &g_${JAVA_CLASS}_clazz)""") | |
| 1302 else: | |
| 1303 template = Template("""\ | |
| 1304 // Leaking this jclass as we cannot use LazyInstance from some threads. | |
| 1305 jclass g_${JAVA_CLASS}_clazz = NULL; | |
| 1306 #define ${JAVA_CLASS}_clazz(env) g_${JAVA_CLASS}_clazz""") | |
| 1307 | |
| 1308 for clazz in called_by_native_classes: | |
| 1309 values = { | |
| 1310 'JAVA_CLASS': clazz, | |
| 1311 } | |
| 1312 ret += [template.substitute(values)] | |
| 1313 | |
| 1314 return '\n'.join(ret) | |
| 1315 | |
| 1316 def GetFindClasses(self): | |
| 1317 """Returns the imlementation of FindClass for all known classes.""" | |
| 1318 if self.init_native: | |
| 1319 if self.options.native_exports: | |
| 1320 template = Template("""\ | |
| 1321 base::subtle::Release_Store(&g_${JAVA_CLASS}_clazz, | |
| 1322 static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));""") | |
| 1323 else: | |
| 1324 template = Template("""\ | |
| 1325 g_${JAVA_CLASS}_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));""") | |
| 1326 else: | |
| 1327 if self.options.native_exports: | |
| 1328 return '\n' | |
| 1329 template = Template("""\ | |
| 1330 g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( | |
| 1331 base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""") | |
| 1332 ret = [] | |
| 1333 for clazz in self.GetUniqueClasses(self.called_by_natives): | |
| 1334 values = {'JAVA_CLASS': clazz} | |
| 1335 ret += [template.substitute(values)] | |
| 1336 return '\n'.join(ret) | |
| 1337 | |
| 1338 def GetMethodIDImpl(self, called_by_native): | |
| 1339 """Returns the implementation of GetMethodID.""" | |
| 1340 if self.options.eager_called_by_natives: | |
| 1341 template = Template("""\ | |
| 1342 env->Get${STATIC_METHOD_PART}MethodID( | |
| 1343 ${JAVA_CLASS}_clazz(env), | |
| 1344 "${JNI_NAME}", ${JNI_SIGNATURE});""") | |
| 1345 else: | |
| 1346 template = Template("""\ | |
| 1347 base::android::MethodID::LazyGet< | |
| 1348 base::android::MethodID::TYPE_${STATIC}>( | |
| 1349 env, ${JAVA_CLASS}_clazz(env), | |
| 1350 "${JNI_NAME}", | |
| 1351 ${JNI_SIGNATURE}, | |
| 1352 &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}); | |
| 1353 """) | |
| 1354 jni_name = called_by_native.name | |
| 1355 jni_return_type = called_by_native.return_type | |
| 1356 if called_by_native.is_constructor: | |
| 1357 jni_name = '<init>' | |
| 1358 jni_return_type = 'void' | |
| 1359 if called_by_native.signature: | |
| 1360 signature = called_by_native.signature | |
| 1361 else: | |
| 1362 signature = JniParams.Signature(called_by_native.params, | |
| 1363 jni_return_type, | |
| 1364 True) | |
| 1365 values = { | |
| 1366 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, | |
| 1367 'JNI_NAME': jni_name, | |
| 1368 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, | |
| 1369 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE', | |
| 1370 'STATIC_METHOD_PART': 'Static' if called_by_native.static else '', | |
| 1371 'JNI_SIGNATURE': signature, | |
| 1372 } | |
| 1373 return template.substitute(values) | |
| 1374 | |
| 1375 | |
| 1376 def WrapOutput(output): | |
| 1377 ret = [] | |
| 1378 for line in output.splitlines(): | |
| 1379 # Do not wrap lines under 80 characters or preprocessor directives. | |
| 1380 if len(line) < 80 or line.lstrip()[:1] == '#': | |
| 1381 stripped = line.rstrip() | |
| 1382 if len(ret) == 0 or len(ret[-1]) or len(stripped): | |
| 1383 ret.append(stripped) | |
| 1384 else: | |
| 1385 first_line_indent = ' ' * (len(line) - len(line.lstrip())) | |
| 1386 subsequent_indent = first_line_indent + ' ' * 4 | |
| 1387 if line.startswith('//'): | |
| 1388 subsequent_indent = '//' + subsequent_indent | |
| 1389 wrapper = textwrap.TextWrapper(width=80, | |
| 1390 subsequent_indent=subsequent_indent, | |
| 1391 break_long_words=False) | |
| 1392 ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)] | |
| 1393 ret += [''] | |
| 1394 return '\n'.join(ret) | |
| 1395 | |
| 1396 | |
| 1397 def ExtractJarInputFile(jar_file, input_file, out_dir): | |
| 1398 """Extracts input file from jar and returns the filename. | |
| 1399 | |
| 1400 The input file is extracted to the same directory that the generated jni | |
| 1401 headers will be placed in. This is passed as an argument to script. | |
| 1402 | |
| 1403 Args: | |
| 1404 jar_file: the jar file containing the input files to extract. | |
| 1405 input_files: the list of files to extract from the jar file. | |
| 1406 out_dir: the name of the directories to extract to. | |
| 1407 | |
| 1408 Returns: | |
| 1409 the name of extracted input file. | |
| 1410 """ | |
| 1411 jar_file = zipfile.ZipFile(jar_file) | |
| 1412 | |
| 1413 out_dir = os.path.join(out_dir, os.path.dirname(input_file)) | |
| 1414 try: | |
| 1415 os.makedirs(out_dir) | |
| 1416 except OSError as e: | |
| 1417 if e.errno != errno.EEXIST: | |
| 1418 raise | |
| 1419 extracted_file_name = os.path.join(out_dir, os.path.basename(input_file)) | |
| 1420 with open(extracted_file_name, 'w') as outfile: | |
| 1421 outfile.write(jar_file.read(input_file)) | |
| 1422 | |
| 1423 return extracted_file_name | |
| 1424 | |
| 1425 | |
| 1426 def GenerateJNIHeader(input_file, output_file, options): | |
| 1427 try: | |
| 1428 if os.path.splitext(input_file)[1] == '.class': | |
| 1429 jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options) | |
| 1430 content = jni_from_javap.GetContent() | |
| 1431 else: | |
| 1432 jni_from_java_source = JNIFromJavaSource.CreateFromFile( | |
| 1433 input_file, options) | |
| 1434 content = jni_from_java_source.GetContent() | |
| 1435 except ParseError, e: | |
| 1436 print e | |
| 1437 sys.exit(1) | |
| 1438 if output_file: | |
| 1439 if not os.path.exists(os.path.dirname(os.path.abspath(output_file))): | |
| 1440 os.makedirs(os.path.dirname(os.path.abspath(output_file))) | |
| 1441 if options.optimize_generation and os.path.exists(output_file): | |
| 1442 with file(output_file, 'r') as f: | |
| 1443 existing_content = f.read() | |
| 1444 if existing_content == content: | |
| 1445 return | |
| 1446 with file(output_file, 'w') as f: | |
| 1447 f.write(content) | |
| 1448 else: | |
| 1449 print content | |
| 1450 | |
| 1451 | |
| 1452 def GetScriptName(): | |
| 1453 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep) | |
| 1454 base_index = 0 | |
| 1455 for idx, value in enumerate(script_components): | |
| 1456 if value == 'base' or value == 'third_party': | |
| 1457 base_index = idx | |
| 1458 break | |
| 1459 return os.sep.join(script_components[base_index:]) | |
| 1460 | |
| 1461 | |
| 1462 def main(argv): | |
| 1463 usage = """usage: %prog [OPTIONS] | |
| 1464 This script will parse the given java source code extracting the native | |
| 1465 declarations and print the header file to stdout (or a file). | |
| 1466 See SampleForTests.java for more details. | |
| 1467 """ | |
| 1468 option_parser = optparse.OptionParser(usage=usage) | |
| 1469 build_utils.AddDepfileOption(option_parser) | |
| 1470 | |
| 1471 option_parser.add_option('-j', '--jar_file', dest='jar_file', | |
| 1472 help='Extract the list of input files from' | |
| 1473 ' a specified jar file.' | |
| 1474 ' Uses javap to extract the methods from a' | |
| 1475 ' pre-compiled class. --input should point' | |
| 1476 ' to pre-compiled Java .class files.') | |
| 1477 option_parser.add_option('-n', dest='namespace', | |
| 1478 help='Uses as a namespace in the generated header ' | |
| 1479 'instead of the javap class name, or when there is ' | |
| 1480 'no JNINamespace annotation in the java source.') | |
| 1481 option_parser.add_option('--input_file', | |
| 1482 help='Single input file name. The output file name ' | |
| 1483 'will be derived from it. Must be used with ' | |
| 1484 '--output_dir.') | |
| 1485 option_parser.add_option('--output_dir', | |
| 1486 help='The output directory. Must be used with ' | |
| 1487 '--input') | |
| 1488 option_parser.add_option('--optimize_generation', type="int", | |
| 1489 default=0, help='Whether we should optimize JNI ' | |
| 1490 'generation by not regenerating files if they have ' | |
| 1491 'not changed.') | |
| 1492 option_parser.add_option('--jarjar', | |
| 1493 help='Path to optional jarjar rules file.') | |
| 1494 option_parser.add_option('--script_name', default=GetScriptName(), | |
| 1495 help='The name of this script in the generated ' | |
| 1496 'header.') | |
| 1497 option_parser.add_option('--includes', | |
| 1498 help='The comma-separated list of header files to ' | |
| 1499 'include in the generated header.') | |
| 1500 option_parser.add_option('--pure_native_methods', | |
| 1501 action='store_true', dest='pure_native_methods', | |
| 1502 help='When true, the native methods will be called ' | |
| 1503 'without any JNI-specific arguments.') | |
| 1504 option_parser.add_option('--ptr_type', default='int', | |
| 1505 type='choice', choices=['int', 'long'], | |
| 1506 help='The type used to represent native pointers in ' | |
| 1507 'Java code. For 32-bit, use int; ' | |
| 1508 'for 64-bit, use long.') | |
| 1509 option_parser.add_option('--jni_init_native_name', default='', | |
| 1510 help='The name of the JNI registration method that ' | |
| 1511 'is used to initialize all native methods. If a ' | |
| 1512 'method with this name is not present in the Java ' | |
| 1513 'source file, setting this option is a no-op. When ' | |
| 1514 'a method with this name is found however, the ' | |
| 1515 'naming convention Java_<packageName>_<className> ' | |
| 1516 'will limit the initialization to only the ' | |
| 1517 'top-level class.') | |
| 1518 option_parser.add_option('--eager_called_by_natives', | |
| 1519 action='store_true', dest='eager_called_by_natives', | |
| 1520 help='When true, the called-by-native methods will ' | |
| 1521 'be initialized in a non-atomic way.') | |
| 1522 option_parser.add_option('--cpp', default='cpp', | |
| 1523 help='The path to cpp command.') | |
| 1524 option_parser.add_option('--javap', default='javap', | |
| 1525 help='The path to javap command.') | |
| 1526 option_parser.add_option('--native_exports', action='store_true', | |
| 1527 help='Native method registration through .so ' | |
| 1528 'exports.') | |
| 1529 option_parser.add_option('--native_exports_optional', action='store_true', | |
| 1530 help='Support both explicit and native method' | |
| 1531 'registration.') | |
| 1532 options, args = option_parser.parse_args(argv) | |
| 1533 if options.native_exports_optional: | |
| 1534 options.native_exports = True | |
| 1535 if options.jar_file: | |
| 1536 input_file = ExtractJarInputFile(options.jar_file, options.input_file, | |
| 1537 options.output_dir) | |
| 1538 elif options.input_file: | |
| 1539 input_file = options.input_file | |
| 1540 else: | |
| 1541 option_parser.print_help() | |
| 1542 print '\nError: Must specify --jar_file or --input_file.' | |
| 1543 return 1 | |
| 1544 output_file = None | |
| 1545 if options.output_dir: | |
| 1546 root_name = os.path.splitext(os.path.basename(input_file))[0] | |
| 1547 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' | |
| 1548 if options.jarjar: | |
| 1549 with open(options.jarjar) as f: | |
| 1550 JniParams.SetJarJarMappings(f.read()) | |
| 1551 GenerateJNIHeader(input_file, output_file, options) | |
| 1552 | |
| 1553 if options.depfile: | |
| 1554 build_utils.WriteDepfile( | |
| 1555 options.depfile, | |
| 1556 build_utils.GetPythonDependencies()) | |
| 1557 | |
| 1558 | |
| 1559 if __name__ == '__main__': | |
| 1560 sys.exit(main(sys.argv)) | |
| OLD | NEW |