| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Extracts native methods from a Java file and generates the JNI bindings. | 6 """Extracts native methods from a Java file and generates the JNI bindings. |
| 7 If you change this, please run and update the tests.""" | 7 If you change this, please run and update the tests.""" |
| 8 | 8 |
| 9 import collections | 9 import collections |
| 10 import errno | 10 import errno |
| (...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 | 311 |
| 312 def ExtractNatives(contents, ptr_type): | 312 def ExtractNatives(contents, ptr_type): |
| 313 """Returns a list of dict containing information about a native method.""" | 313 """Returns a list of dict containing information about a native method.""" |
| 314 contents = contents.replace('\n', '') | 314 contents = contents.replace('\n', '') |
| 315 natives = [] | 315 natives = [] |
| 316 re_native = re.compile(r'(@NativeClassQualifiedName' | 316 re_native = re.compile(r'(@NativeClassQualifiedName' |
| 317 '\(\"(?P<native_class_name>.*?)\"\))?\s*' | 317 '\(\"(?P<native_class_name>.*?)\"\))?\s*' |
| 318 '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\)))?\s*' | 318 '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\)))?\s*' |
| 319 '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*?native ' | 319 '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*?native ' |
| 320 '(?P<return_type>\S*?) ' | 320 '(?P<return_type>\S*?) ' |
| 321 '(?P<name>\w+?)\((?P<params>.*?)\);') | 321 '(?P<name>native\w+?)\((?P<params>.*?)\);') |
| 322 for match in re.finditer(re_native, contents): | 322 for match in re.finditer(re_native, contents): |
| 323 native = NativeMethod( | 323 native = NativeMethod( |
| 324 static='static' in match.group('qualifiers'), | 324 static='static' in match.group('qualifiers'), |
| 325 java_class_name=match.group('java_class_name'), | 325 java_class_name=match.group('java_class_name'), |
| 326 native_class_name=match.group('native_class_name'), | 326 native_class_name=match.group('native_class_name'), |
| 327 return_type=match.group('return_type'), | 327 return_type=match.group('return_type'), |
| 328 name=match.group('name').replace('native', ''), | 328 name=match.group('name').replace('native', ''), |
| 329 params=JniParams.Parse(match.group('params')), | 329 params=JniParams.Parse(match.group('params')), |
| 330 ptr_type=ptr_type) | 330 ptr_type=ptr_type) |
| 331 natives += [native] | 331 natives += [native] |
| (...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 526 self.inl_header_file_generator = InlHeaderFileGenerator( | 526 self.inl_header_file_generator = InlHeaderFileGenerator( |
| 527 self.namespace, self.fully_qualified_class, [], | 527 self.namespace, self.fully_qualified_class, [], |
| 528 self.called_by_natives, options) | 528 self.called_by_natives, options) |
| 529 | 529 |
| 530 def GetContent(self): | 530 def GetContent(self): |
| 531 return self.inl_header_file_generator.GetContent() | 531 return self.inl_header_file_generator.GetContent() |
| 532 | 532 |
| 533 @staticmethod | 533 @staticmethod |
| 534 def CreateFromClass(class_file, options): | 534 def CreateFromClass(class_file, options): |
| 535 class_name = os.path.splitext(os.path.basename(class_file))[0] | 535 class_name = os.path.splitext(os.path.basename(class_file))[0] |
| 536 p = subprocess.Popen(args=['javap', '-s', class_name], | 536 p = subprocess.Popen(args=[options.javap, '-s', class_name], |
| 537 cwd=os.path.dirname(class_file), | 537 cwd=os.path.dirname(class_file), |
| 538 stdout=subprocess.PIPE, | 538 stdout=subprocess.PIPE, |
| 539 stderr=subprocess.PIPE) | 539 stderr=subprocess.PIPE) |
| 540 stdout, _ = p.communicate() | 540 stdout, _ = p.communicate() |
| 541 jni_from_javap = JNIFromJavaP(stdout.split('\n'), options) | 541 jni_from_javap = JNIFromJavaP(stdout.split('\n'), options) |
| 542 return jni_from_javap | 542 return jni_from_javap |
| 543 | 543 |
| 544 | 544 |
| 545 class JNIFromJavaSource(object): | 545 class JNIFromJavaSource(object): |
| 546 """Uses the given java source file to generate the JNI header file.""" | 546 """Uses the given java source file to generate the JNI header file.""" |
| 547 | 547 |
| 548 def __init__(self, contents, fully_qualified_class, options): | 548 def __init__(self, contents, fully_qualified_class, options): |
| 549 contents = self._RemoveComments(contents) | 549 contents = self._RemoveComments(contents, options) |
| 550 JniParams.SetFullyQualifiedClass(fully_qualified_class) | 550 JniParams.SetFullyQualifiedClass(fully_qualified_class) |
| 551 JniParams.ExtractImportsAndInnerClasses(contents) | 551 JniParams.ExtractImportsAndInnerClasses(contents) |
| 552 jni_namespace = ExtractJNINamespace(contents) or options.namespace | 552 jni_namespace = ExtractJNINamespace(contents) or options.namespace |
| 553 natives = ExtractNatives(contents, options.ptr_type) | 553 natives = ExtractNatives(contents, options.ptr_type) |
| 554 called_by_natives = ExtractCalledByNatives(contents) | 554 called_by_natives = ExtractCalledByNatives(contents) |
| 555 if len(natives) == 0 and len(called_by_natives) == 0: | 555 if len(natives) == 0 and len(called_by_natives) == 0: |
| 556 raise SyntaxError('Unable to find any JNI methods for %s.' % | 556 raise SyntaxError('Unable to find any JNI methods for %s.' % |
| 557 fully_qualified_class) | 557 fully_qualified_class) |
| 558 inl_header_file_generator = InlHeaderFileGenerator( | 558 inl_header_file_generator = InlHeaderFileGenerator( |
| 559 jni_namespace, fully_qualified_class, natives, called_by_natives, | 559 jni_namespace, fully_qualified_class, natives, called_by_natives, |
| 560 options) | 560 options) |
| 561 self.content = inl_header_file_generator.GetContent() | 561 self.content = inl_header_file_generator.GetContent() |
| 562 | 562 |
| 563 def _RemoveComments(self, contents): | 563 def _RemoveComments(self, contents, options): |
| 564 # We need to support both inline and block comments, and we need to handle | 564 # We need to support both inline and block comments, and we need to handle |
| 565 # strings that contain '//' or '/*'. Rather than trying to do all that with | 565 # strings that contain '//' or '/*'. Rather than trying to do all that with |
| 566 # regexps, we just pipe the contents through the C preprocessor. We tell cpp | 566 # regexps, we just pipe the contents through the C preprocessor. We tell cpp |
| 567 # the file has already been preprocessed, so it just removes comments and | 567 # the file has already been preprocessed, so it just removes comments and |
| 568 # doesn't try to parse #include, #pragma etc. | 568 # doesn't try to parse #include, #pragma etc. |
| 569 # | 569 # |
| 570 # TODO(husky): This is a bit hacky. It would be cleaner to use a real Java | 570 # TODO(husky): This is a bit hacky. It would be cleaner to use a real Java |
| 571 # parser. Maybe we could ditch JNIFromJavaSource and just always use | 571 # parser. Maybe we could ditch JNIFromJavaSource and just always use |
| 572 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT. | 572 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT. |
| 573 # http://code.google.com/p/chromium/issues/detail?id=138941 | 573 # http://code.google.com/p/chromium/issues/detail?id=138941 |
| 574 p = subprocess.Popen(args=['cpp', '-fpreprocessed'], | 574 p = subprocess.Popen(args=[options.cpp, '-fpreprocessed'], |
| 575 stdin=subprocess.PIPE, | 575 stdin=subprocess.PIPE, |
| 576 stdout=subprocess.PIPE, | 576 stdout=subprocess.PIPE, |
| 577 stderr=subprocess.PIPE) | 577 stderr=subprocess.PIPE) |
| 578 stdout, _ = p.communicate(contents) | 578 stdout, _ = p.communicate(contents) |
| 579 return stdout | 579 return stdout |
| 580 | 580 |
| 581 def GetContent(self): | 581 def GetContent(self): |
| 582 return self.content | 582 return self.content |
| 583 | 583 |
| 584 @staticmethod | 584 @staticmethod |
| (...skipping 13 matching lines...) Expand all Loading... |
| 598 self.fully_qualified_class = fully_qualified_class | 598 self.fully_qualified_class = fully_qualified_class |
| 599 self.class_name = self.fully_qualified_class.split('/')[-1] | 599 self.class_name = self.fully_qualified_class.split('/')[-1] |
| 600 self.natives = natives | 600 self.natives = natives |
| 601 self.called_by_natives = called_by_natives | 601 self.called_by_natives = called_by_natives |
| 602 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' | 602 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' |
| 603 self.options = options | 603 self.options = options |
| 604 self.init_native = self.ExtractInitNative(options) | 604 self.init_native = self.ExtractInitNative(options) |
| 605 | 605 |
| 606 def ExtractInitNative(self, options): | 606 def ExtractInitNative(self, options): |
| 607 for native in self.natives: | 607 for native in self.natives: |
| 608 if options.jni_init_native_name == native.name: | 608 if options.jni_init_native_name == 'native' + native.name: |
| 609 self.natives.remove(native) | 609 self.natives.remove(native) |
| 610 return native | 610 return native |
| 611 return None | 611 return None |
| 612 | 612 |
| 613 def GetContent(self): | 613 def GetContent(self): |
| 614 """Returns the content of the JNI binding file.""" | 614 """Returns the content of the JNI binding file.""" |
| 615 template = Template("""\ | 615 template = Template("""\ |
| 616 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 616 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 617 // Use of this source code is governed by a BSD-style license that can be | 617 // Use of this source code is governed by a BSD-style license that can be |
| 618 // found in the LICENSE file. | 618 // found in the LICENSE file. |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 824 extern "C" JNIEXPORT bool JNICALL | 824 extern "C" JNIEXPORT bool JNICALL |
| 825 Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) { | 825 Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) { |
| 826 return ${NAMESPACE}RegisterNativesImpl(env, clazz); | 826 return ${NAMESPACE}RegisterNativesImpl(env, clazz); |
| 827 } | 827 } |
| 828 """) | 828 """) |
| 829 fully_qualified_class = self.fully_qualified_class.replace('/', '_') | 829 fully_qualified_class = self.fully_qualified_class.replace('/', '_') |
| 830 namespace = '' | 830 namespace = '' |
| 831 if self.namespace: | 831 if self.namespace: |
| 832 namespace = self.namespace + '::' | 832 namespace = self.namespace + '::' |
| 833 values = {'FULLY_QUALIFIED_CLASS': fully_qualified_class, | 833 values = {'FULLY_QUALIFIED_CLASS': fully_qualified_class, |
| 834 'INIT_NATIVE_NAME': self.init_native.name, | 834 'INIT_NATIVE_NAME': 'native' + self.init_native.name, |
| 835 'NAMESPACE': namespace, | 835 'NAMESPACE': namespace, |
| 836 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString() | 836 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString() |
| 837 } | 837 } |
| 838 return template.substitute(values) | 838 return template.substitute(values) |
| 839 | 839 |
| 840 def GetOpenNamespaceString(self): | 840 def GetOpenNamespaceString(self): |
| 841 if self.namespace: | 841 if self.namespace: |
| 842 all_namespaces = ['namespace %s {' % ns | 842 all_namespaces = ['namespace %s {' % ns |
| 843 for ns in self.namespace.split('::')] | 843 for ns in self.namespace.split('::')] |
| 844 return '\n'.join(all_namespaces) | 844 return '\n'.join(all_namespaces) |
| (...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1263 'method with this name is not present in the Java ' | 1263 'method with this name is not present in the Java ' |
| 1264 'source file, setting this option is a no-op. When ' | 1264 'source file, setting this option is a no-op. When ' |
| 1265 'a method with this name is found however, the ' | 1265 'a method with this name is found however, the ' |
| 1266 'naming convention Java_<packageName>_<className> ' | 1266 'naming convention Java_<packageName>_<className> ' |
| 1267 'will limit the initialization to only the ' | 1267 'will limit the initialization to only the ' |
| 1268 'top-level class.') | 1268 'top-level class.') |
| 1269 option_parser.add_option('--eager_called_by_natives', | 1269 option_parser.add_option('--eager_called_by_natives', |
| 1270 action='store_true', dest='eager_called_by_natives', | 1270 action='store_true', dest='eager_called_by_natives', |
| 1271 help='When true, the called-by-native methods will ' | 1271 help='When true, the called-by-native methods will ' |
| 1272 'be initialized in a non-atomic way.') | 1272 'be initialized in a non-atomic way.') |
| 1273 option_parser.add_option('--cpp', default='cpp', |
| 1274 help='The path to cpp command.') |
| 1275 option_parser.add_option('--javap', default='javap', |
| 1276 help='The path to javap command.') |
| 1273 options, args = option_parser.parse_args(argv) | 1277 options, args = option_parser.parse_args(argv) |
| 1274 if options.jar_file: | 1278 if options.jar_file: |
| 1275 input_file = ExtractJarInputFile(options.jar_file, options.input_file, | 1279 input_file = ExtractJarInputFile(options.jar_file, options.input_file, |
| 1276 options.output_dir) | 1280 options.output_dir) |
| 1277 elif options.input_file: | 1281 elif options.input_file: |
| 1278 input_file = options.input_file | 1282 input_file = options.input_file |
| 1279 else: | 1283 else: |
| 1280 option_parser.print_help() | 1284 option_parser.print_help() |
| 1281 print '\nError: Must specify --jar_file or --input_file.' | 1285 print '\nError: Must specify --jar_file or --input_file.' |
| 1282 return 1 | 1286 return 1 |
| 1283 output_file = None | 1287 output_file = None |
| 1284 if options.output_dir: | 1288 if options.output_dir: |
| 1285 root_name = os.path.splitext(os.path.basename(input_file))[0] | 1289 root_name = os.path.splitext(os.path.basename(input_file))[0] |
| 1286 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' | 1290 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' |
| 1287 if options.jarjar: | 1291 if options.jarjar: |
| 1288 with open(options.jarjar) as f: | 1292 with open(options.jarjar) as f: |
| 1289 JniParams.SetJarJarMappings(f.read()) | 1293 JniParams.SetJarJarMappings(f.read()) |
| 1290 GenerateJNIHeader(input_file, output_file, options) | 1294 GenerateJNIHeader(input_file, output_file, options) |
| 1291 | 1295 |
| 1292 | 1296 |
| 1293 if __name__ == '__main__': | 1297 if __name__ == '__main__': |
| 1294 sys.exit(main(sys.argv)) | 1298 sys.exit(main(sys.argv)) |
| OLD | NEW |