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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
92 'short': 'jshort', | 92 'short': 'jshort', |
93 'boolean': 'jboolean', | 93 'boolean': 'jboolean', |
94 'long': 'jlong', | 94 'long': 'jlong', |
95 'double': 'jdouble', | 95 'double': 'jdouble', |
96 'float': 'jfloat', | 96 'float': 'jfloat', |
97 } | 97 } |
98 java_type_map = { | 98 java_type_map = { |
99 'void': 'void', | 99 'void': 'void', |
100 'String': 'jstring', | 100 'String': 'jstring', |
101 'java/lang/String': 'jstring', | 101 'java/lang/String': 'jstring', |
102 'Class': 'jclass', | |
103 'java/lang/Class': 'jclass', | 102 'java/lang/Class': 'jclass', |
104 } | 103 } |
105 | 104 |
106 if java_type in java_pod_type_map: | 105 if java_type in java_pod_type_map: |
107 return java_pod_type_map[java_type] | 106 return java_pod_type_map[java_type] |
108 elif java_type in java_type_map: | 107 elif java_type in java_type_map: |
109 return java_type_map[java_type] | 108 return java_type_map[java_type] |
110 elif java_type.endswith('[]'): | 109 elif java_type.endswith('[]'): |
111 if java_type[:-2] in java_pod_type_map: | 110 if java_type[:-2] in java_pod_type_map: |
112 return java_pod_type_map[java_type[:-2]] + 'Array' | 111 return java_pod_type_map[java_type[:-2]] + 'Array' |
113 return 'jobjectArray' | 112 return 'jobjectArray' |
113 elif java_type.startswith('Class'): | |
114 # Not a simple map lookup, because we need to handle generics. | |
rmcilroy
2013/12/13 14:14:51
nit - comment might be clearer as something like:
bulach
2013/12/13 15:23:58
Done.
| |
115 return 'jclass' | |
114 else: | 116 else: |
115 return 'jobject' | 117 return 'jobject' |
116 | 118 |
117 | 119 |
120 def JavaReturnValueToC(java_type): | |
121 """Returns a valid C return value for the given java type.""" | |
122 java_pod_type_map = { | |
123 'int': '0', | |
124 'byte': '0', | |
125 'char': '0', | |
126 'short': '0', | |
127 'boolean': 'false', | |
128 'long': '0', | |
129 'double': '0', | |
130 'float': '0', | |
131 'void': '' | |
132 } | |
133 return java_pod_type_map.get(java_type, 'NULL') | |
134 | |
135 | |
118 class JniParams(object): | 136 class JniParams(object): |
119 _imports = [] | 137 _imports = [] |
120 _fully_qualified_class = '' | 138 _fully_qualified_class = '' |
121 _package = '' | 139 _package = '' |
122 _inner_classes = [] | 140 _inner_classes = [] |
123 _remappings = [] | 141 _remappings = [] |
124 | 142 |
125 @staticmethod | 143 @staticmethod |
126 def SetFullyQualifiedClass(fully_qualified_class): | 144 def SetFullyQualifiedClass(fully_qualified_class): |
127 JniParams._fully_qualified_class = 'L' + fully_qualified_class | 145 JniParams._fully_qualified_class = 'L' + fully_qualified_class |
(...skipping 395 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
523 return jni_from_javap | 541 return jni_from_javap |
524 | 542 |
525 | 543 |
526 class JNIFromJavaSource(object): | 544 class JNIFromJavaSource(object): |
527 """Uses the given java source file to generate the JNI header file.""" | 545 """Uses the given java source file to generate the JNI header file.""" |
528 | 546 |
529 def __init__(self, contents, fully_qualified_class, options): | 547 def __init__(self, contents, fully_qualified_class, options): |
530 contents = self._RemoveComments(contents) | 548 contents = self._RemoveComments(contents) |
531 JniParams.SetFullyQualifiedClass(fully_qualified_class) | 549 JniParams.SetFullyQualifiedClass(fully_qualified_class) |
532 JniParams.ExtractImportsAndInnerClasses(contents) | 550 JniParams.ExtractImportsAndInnerClasses(contents) |
533 jni_namespace = ExtractJNINamespace(contents) | 551 jni_namespace = ExtractJNINamespace(contents) or options.namespace |
534 natives = ExtractNatives(contents, options.ptr_type) | 552 natives = ExtractNatives(contents, options.ptr_type) |
535 called_by_natives = ExtractCalledByNatives(contents) | 553 called_by_natives = ExtractCalledByNatives(contents) |
536 if len(natives) == 0 and len(called_by_natives) == 0: | 554 if len(natives) == 0 and len(called_by_natives) == 0: |
537 raise SyntaxError('Unable to find any JNI methods for %s.' % | 555 raise SyntaxError('Unable to find any JNI methods for %s.' % |
538 fully_qualified_class) | 556 fully_qualified_class) |
539 inl_header_file_generator = InlHeaderFileGenerator( | 557 inl_header_file_generator = InlHeaderFileGenerator( |
540 jni_namespace, fully_qualified_class, natives, called_by_natives, | 558 jni_namespace, fully_qualified_class, natives, called_by_natives, |
541 options) | 559 options) |
542 self.content = inl_header_file_generator.GetContent() | 560 self.content = inl_header_file_generator.GetContent() |
543 | 561 |
(...skipping 30 matching lines...) Expand all Loading... | |
574 """Generates an inline header file for JNI integration.""" | 592 """Generates an inline header file for JNI integration.""" |
575 | 593 |
576 def __init__(self, namespace, fully_qualified_class, natives, | 594 def __init__(self, namespace, fully_qualified_class, natives, |
577 called_by_natives, options): | 595 called_by_natives, options): |
578 self.namespace = namespace | 596 self.namespace = namespace |
579 self.fully_qualified_class = fully_qualified_class | 597 self.fully_qualified_class = fully_qualified_class |
580 self.class_name = self.fully_qualified_class.split('/')[-1] | 598 self.class_name = self.fully_qualified_class.split('/')[-1] |
581 self.natives = natives | 599 self.natives = natives |
582 self.called_by_natives = called_by_natives | 600 self.called_by_natives = called_by_natives |
583 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' | 601 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' |
584 self.script_name = options.script_name | 602 self.options = options |
603 self.init_native = self.ExtractInitNative(options) | |
604 | |
605 def ExtractInitNative(self, options): | |
606 for native in self.natives: | |
607 if options.jni_init_native_name == native.name: | |
608 self.natives.remove(native) | |
609 return native | |
610 return None | |
585 | 611 |
586 def GetContent(self): | 612 def GetContent(self): |
587 """Returns the content of the JNI binding file.""" | 613 """Returns the content of the JNI binding file.""" |
588 template = Template("""\ | 614 template = Template("""\ |
589 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 615 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
590 // Use of this source code is governed by a BSD-style license that can be | 616 // Use of this source code is governed by a BSD-style license that can be |
591 // found in the LICENSE file. | 617 // found in the LICENSE file. |
592 | 618 |
593 | 619 |
594 // This file is autogenerated by | 620 // This file is autogenerated by |
595 // ${SCRIPT_NAME} | 621 // ${SCRIPT_NAME} |
596 // For | 622 // For |
597 // ${FULLY_QUALIFIED_CLASS} | 623 // ${FULLY_QUALIFIED_CLASS} |
598 | 624 |
599 #ifndef ${HEADER_GUARD} | 625 #ifndef ${HEADER_GUARD} |
600 #define ${HEADER_GUARD} | 626 #define ${HEADER_GUARD} |
601 | 627 |
602 #include <jni.h> | 628 #include <jni.h> |
603 | 629 |
604 #include "base/android/jni_android.h" | 630 ${INCLUDES} |
605 #include "base/android/scoped_java_ref.h" | |
606 #include "base/basictypes.h" | |
607 #include "base/logging.h" | |
608 | |
609 using base::android::ScopedJavaLocalRef; | |
610 | 631 |
611 // Step 1: forward declarations. | 632 // Step 1: forward declarations. |
612 namespace { | 633 namespace { |
613 $CLASS_PATH_DEFINITIONS | 634 $CLASS_PATH_DEFINITIONS |
635 $METHOD_ID_DEFINITIONS | |
614 } // namespace | 636 } // namespace |
615 | 637 |
616 $OPEN_NAMESPACE | 638 $OPEN_NAMESPACE |
617 $FORWARD_DECLARATIONS | 639 $FORWARD_DECLARATIONS |
618 | 640 |
619 // Step 2: method stubs. | 641 // Step 2: method stubs. |
620 $METHOD_STUBS | 642 $METHOD_STUBS |
621 | 643 |
622 // Step 3: RegisterNatives. | 644 // Step 3: RegisterNatives. |
623 | 645 $JNI_NATIVE_METHODS |
624 static bool RegisterNativesImpl(JNIEnv* env) { | 646 $REGISTER_NATIVES |
625 $REGISTER_NATIVES_IMPL | |
626 return true; | |
627 } | |
628 $CLOSE_NAMESPACE | 647 $CLOSE_NAMESPACE |
648 $JNI_REGISTER_NATIVES | |
629 #endif // ${HEADER_GUARD} | 649 #endif // ${HEADER_GUARD} |
630 """) | 650 """) |
631 values = { | 651 values = { |
632 'SCRIPT_NAME': self.script_name, | 652 'SCRIPT_NAME': self.options.script_name, |
633 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class, | 653 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class, |
634 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(), | 654 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(), |
655 'METHOD_ID_DEFINITIONS': self.GetMethodIDDefinitionsString(), | |
635 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(), | 656 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(), |
636 'METHOD_STUBS': self.GetMethodStubsString(), | 657 'METHOD_STUBS': self.GetMethodStubsString(), |
637 'OPEN_NAMESPACE': self.GetOpenNamespaceString(), | 658 'OPEN_NAMESPACE': self.GetOpenNamespaceString(), |
638 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString(), | 659 'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(), |
660 'REGISTER_NATIVES': self.GetRegisterNativesString(), | |
639 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(), | 661 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(), |
640 'HEADER_GUARD': self.header_guard, | 662 'HEADER_GUARD': self.header_guard, |
663 'INCLUDES': self.GetIncludesString(), | |
664 'JNI_REGISTER_NATIVES': self.GetJNIRegisterNativesString() | |
641 } | 665 } |
642 return WrapOutput(template.substitute(values)) | 666 return WrapOutput(template.substitute(values)) |
643 | 667 |
644 def GetClassPathDefinitionsString(self): | 668 def GetClassPathDefinitionsString(self): |
645 ret = [] | 669 ret = [] |
646 ret += [self.GetClassPathDefinitions()] | 670 ret += [self.GetClassPathDefinitions()] |
647 return '\n'.join(ret) | 671 return '\n'.join(ret) |
648 | 672 |
673 def GetMethodIDDefinitionsString(self): | |
674 """Returns the definition of method ids for the called by native methods.""" | |
675 if not self.options.eager_called_by_natives: | |
676 return '' | |
677 template = Template("""\ | |
678 jmethodID g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = NULL;""") | |
679 ret = [] | |
680 for called_by_native in self.called_by_natives: | |
681 values = { | |
682 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, | |
683 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, | |
684 } | |
685 ret += [template.substitute(values)] | |
686 return '\n'.join(ret) | |
687 | |
649 def GetForwardDeclarationsString(self): | 688 def GetForwardDeclarationsString(self): |
650 ret = [] | 689 ret = [] |
651 for native in self.natives: | 690 for native in self.natives: |
652 if native.type != 'method': | 691 if native.type != 'method': |
653 ret += [self.GetForwardDeclaration(native)] | 692 ret += [self.GetForwardDeclaration(native)] |
654 return '\n'.join(ret) | 693 return '\n'.join(ret) |
655 | 694 |
656 def GetMethodStubsString(self): | 695 def GetMethodStubsString(self): |
696 """Returns the code corresponding to method stubs.""" | |
657 ret = [] | 697 ret = [] |
658 for native in self.natives: | 698 for native in self.natives: |
659 if native.type == 'method': | 699 if native.type == 'method': |
660 ret += [self.GetNativeMethodStub(native)] | 700 ret += [self.GetNativeMethodStubString(native)] |
661 for called_by_native in self.called_by_natives: | 701 if self.options.eager_called_by_natives: |
662 ret += [self.GetCalledByNativeMethodStub(called_by_native)] | 702 ret += self.GetEagerCalledByNativeMethodStubs() |
703 else: | |
704 ret += self.GetLazyCalledByNativeMethodStubs() | |
663 return '\n'.join(ret) | 705 return '\n'.join(ret) |
664 | 706 |
707 def GetLazyCalledByNativeMethodStubs(self): | |
708 return [self.GetLazyCalledByNativeMethodStub(called_by_native) | |
709 for called_by_native in self.called_by_natives] | |
710 | |
711 def GetEagerCalledByNativeMethodStubs(self): | |
712 ret = [] | |
713 if self.called_by_natives: | |
714 ret += ['namespace {'] | |
715 for called_by_native in self.called_by_natives: | |
716 ret += [self.GetEagerCalledByNativeMethodStub(called_by_native)] | |
717 ret += ['} // namespace'] | |
718 return ret | |
719 | |
720 def GetIncludesString(self): | |
721 if not self.options.includes: | |
722 return '' | |
723 includes = self.options.includes.split(',') | |
724 return '\n'.join('#include "%s"' % x for x in includes) | |
725 | |
665 def GetKMethodsString(self, clazz): | 726 def GetKMethodsString(self, clazz): |
666 ret = [] | 727 ret = [] |
667 for native in self.natives: | 728 for native in self.natives: |
668 if (native.java_class_name == clazz or | 729 if (native.java_class_name == clazz or |
669 (not native.java_class_name and clazz == self.class_name)): | 730 (not native.java_class_name and clazz == self.class_name)): |
670 ret += [self.GetKMethodArrayEntry(native)] | 731 ret += [self.GetKMethodArrayEntry(native)] |
671 return '\n'.join(ret) | 732 return '\n'.join(ret) |
672 | 733 |
673 def GetRegisterNativesImplString(self): | 734 def SubstituteNativeMethods(self, template): |
674 """Returns the implementation for RegisterNatives.""" | 735 """Substitutes JAVA_CLASS and KMETHODS in the provided template.""" |
675 template = Template("""\ | 736 ret = [] |
676 static const JNINativeMethod kMethods${JAVA_CLASS}[] = { | |
677 ${KMETHODS} | |
678 }; | |
679 const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS}); | |
680 | |
681 if (env->RegisterNatives(g_${JAVA_CLASS}_clazz, | |
682 kMethods${JAVA_CLASS}, | |
683 kMethods${JAVA_CLASS}Size) < 0) { | |
684 LOG(ERROR) << "RegisterNatives failed in " << __FILE__; | |
685 return false; | |
686 } | |
687 """) | |
688 ret = [self.GetFindClasses()] | |
689 all_classes = self.GetUniqueClasses(self.natives) | 737 all_classes = self.GetUniqueClasses(self.natives) |
690 all_classes[self.class_name] = self.fully_qualified_class | 738 all_classes[self.class_name] = self.fully_qualified_class |
691 for clazz in all_classes: | 739 for clazz in all_classes: |
692 kmethods = self.GetKMethodsString(clazz) | 740 kmethods = self.GetKMethodsString(clazz) |
693 if kmethods: | 741 if kmethods: |
694 values = {'JAVA_CLASS': clazz, | 742 values = {'JAVA_CLASS': clazz, |
695 'KMETHODS': kmethods} | 743 'KMETHODS': kmethods} |
696 ret += [template.substitute(values)] | 744 ret += [template.substitute(values)] |
697 if not ret: return '' | 745 if not ret: return '' |
698 return '\n' + '\n'.join(ret) | 746 return '\n' + '\n'.join(ret) |
699 | 747 |
748 def GetJNINativeMethodsString(self): | |
749 """Returns the implementation of the array of native methods.""" | |
750 template = Template("""\ | |
751 static const JNINativeMethod kMethods${JAVA_CLASS}[] = { | |
752 ${KMETHODS} | |
753 }; | |
754 """) | |
755 return self.SubstituteNativeMethods(template) | |
756 | |
757 def GetRegisterCalledByNativesImplString(self): | |
758 """Returns the code for registering the called by native methods.""" | |
759 if not self.options.eager_called_by_natives: | |
760 return '' | |
761 template = Template("""\ | |
762 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = ${GET_METHOD_ID_IMPL} | |
763 if (g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} == NULL) { | |
764 return false; | |
765 } | |
766 """) | |
767 ret = [] | |
768 for called_by_native in self.called_by_natives: | |
769 values = { | |
770 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, | |
771 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, | |
772 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native), | |
773 } | |
774 ret += [template.substitute(values)] | |
775 return '\n'.join(ret) | |
776 | |
777 def GetRegisterNativesString(self): | |
778 """Returns the code for RegisterNatives.""" | |
779 template = Template("""\ | |
780 ${REGISTER_NATIVES_SIGNATURE} { | |
781 ${CLASSES} | |
782 ${NATIVES} | |
783 ${CALLED_BY_NATIVES} | |
784 return true; | |
785 } | |
786 """) | |
787 signature = 'static bool RegisterNativesImpl(JNIEnv* env' | |
788 if self.init_native: | |
789 signature += ', jclass clazz)' | |
790 else: | |
791 signature += ')' | |
792 | |
793 natives = self.GetRegisterNativesImplString() | |
794 called_by_natives = self.GetRegisterCalledByNativesImplString() | |
795 values = {'REGISTER_NATIVES_SIGNATURE': signature, | |
796 'CLASSES': self.GetFindClasses(), | |
797 'NATIVES': natives, | |
798 'CALLED_BY_NATIVES': called_by_natives, | |
799 } | |
800 return template.substitute(values) | |
801 | |
802 def GetRegisterNativesImplString(self): | |
803 """Returns the shared implementation for RegisterNatives.""" | |
804 template = Template("""\ | |
805 const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS}); | |
806 | |
807 if (env->RegisterNatives(g_${JAVA_CLASS}_clazz, | |
808 kMethods${JAVA_CLASS}, | |
809 kMethods${JAVA_CLASS}Size) < 0) { | |
810 jni_generator::HandleRegistrationError( | |
811 env, g_${JAVA_CLASS}_clazz, __FILE__); | |
812 return false; | |
813 } | |
814 """) | |
815 return self.SubstituteNativeMethods(template) | |
816 | |
817 def GetJNIRegisterNativesString(self): | |
818 """Returns the implementation for the JNI registration of native methods.""" | |
819 if not self.init_native: | |
820 return '' | |
821 | |
822 template = Template("""\ | |
823 extern "C" JNIEXPORT bool JNICALL | |
824 Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) { | |
825 return ${NAMESPACE}RegisterNativesImpl(env, clazz); | |
826 } | |
827 """) | |
828 fully_qualified_class = self.fully_qualified_class.replace('/', '_') | |
829 namespace = '' | |
830 if self.namespace: | |
831 namespace = self.namespace + '::' | |
832 values = {'FULLY_QUALIFIED_CLASS': fully_qualified_class, | |
833 'INIT_NATIVE_NAME': self.init_native.name, | |
834 'NAMESPACE': namespace, | |
835 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString() | |
836 } | |
837 return template.substitute(values) | |
838 | |
700 def GetOpenNamespaceString(self): | 839 def GetOpenNamespaceString(self): |
701 if self.namespace: | 840 if self.namespace: |
702 all_namespaces = ['namespace %s {' % ns | 841 all_namespaces = ['namespace %s {' % ns |
703 for ns in self.namespace.split('::')] | 842 for ns in self.namespace.split('::')] |
704 return '\n'.join(all_namespaces) | 843 return '\n'.join(all_namespaces) |
705 return '' | 844 return '' |
706 | 845 |
707 def GetCloseNamespaceString(self): | 846 def GetCloseNamespaceString(self): |
708 if self.namespace: | 847 if self.namespace: |
709 all_namespaces = ['} // namespace %s' % ns | 848 all_namespaces = ['} // namespace %s' % ns |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
744 | 883 |
745 def GetForwardDeclaration(self, native): | 884 def GetForwardDeclaration(self, native): |
746 template = Template(""" | 885 template = Template(""" |
747 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS}); | 886 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS}); |
748 """) | 887 """) |
749 values = {'RETURN': JavaDataTypeToC(native.return_type), | 888 values = {'RETURN': JavaDataTypeToC(native.return_type), |
750 'NAME': native.name, | 889 'NAME': native.name, |
751 'PARAMS': self.GetParamsInDeclaration(native)} | 890 'PARAMS': self.GetParamsInDeclaration(native)} |
752 return template.substitute(values) | 891 return template.substitute(values) |
753 | 892 |
754 def GetNativeMethodStub(self, native): | 893 def GetNativeMethodStubString(self, native): |
755 """Returns stubs for native methods.""" | 894 """Returns stubs for native methods.""" |
756 template = Template("""\ | 895 template = Template("""\ |
757 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) { | 896 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) { |
758 DCHECK(${PARAM0_NAME}) << "${NAME}"; | |
759 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); | 897 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); |
760 return native->${NAME}(env, obj${PARAMS_IN_CALL})${POST_CALL}; | 898 DCHECK_NATIVE_PTR(native, "${NAME}"${OPTIONAL_ERROR_RETURN}); |
899 return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL}; | |
761 } | 900 } |
762 """) | 901 """) |
763 params_for_call = ', '.join(p.name for p in native.params[1:]) | 902 params = [] |
764 if params_for_call: | 903 if not self.options.pure_native_methods: |
765 params_for_call = ', ' + params_for_call | 904 params = ['env', 'obj'] |
905 params_in_call = ', '.join(params + [p.name for p in native.params[1:]]) | |
766 | 906 |
767 return_type = JavaDataTypeToC(native.return_type) | 907 return_type = JavaDataTypeToC(native.return_type) |
908 optional_error_return = JavaReturnValueToC(native.return_type) | |
909 if optional_error_return: | |
910 optional_error_return = ', ' + optional_error_return | |
911 post_call = '' | |
768 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): | 912 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): |
769 scoped_return_type = 'ScopedJavaLocalRef<' + return_type + '>' | |
770 post_call = '.Release()' | 913 post_call = '.Release()' |
771 else: | |
772 scoped_return_type = return_type | |
773 post_call = '' | |
774 values = { | 914 values = { |
775 'RETURN': return_type, | 915 'RETURN': return_type, |
776 'SCOPED_RETURN': scoped_return_type, | 916 'OPTIONAL_ERROR_RETURN': optional_error_return, |
777 'NAME': native.name, | 917 'NAME': native.name, |
778 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native), | 918 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native), |
779 'PARAM0_NAME': native.params[0].name, | 919 'PARAM0_NAME': native.params[0].name, |
780 'P0_TYPE': native.p0_type, | 920 'P0_TYPE': native.p0_type, |
781 'PARAMS_IN_CALL': params_for_call, | 921 'PARAMS_IN_CALL': params_in_call, |
782 'POST_CALL': post_call | 922 'POST_CALL': post_call |
783 } | 923 } |
784 return template.substitute(values) | 924 return template.substitute(values) |
785 | 925 |
786 def GetCalledByNativeMethodStub(self, called_by_native): | 926 def GetCalledByNativeValues(self, called_by_native): |
787 """Returns a string.""" | 927 """Fills in necessary values for the CalledByNative methods.""" |
788 function_signature_template = Template("""\ | |
789 static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\ | |
790 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""") | |
791 function_header_template = Template("""\ | |
792 ${FUNCTION_SIGNATURE} {""") | |
793 function_header_with_unused_template = Template("""\ | |
794 ${FUNCTION_SIGNATURE} __attribute__ ((unused)); | |
795 ${FUNCTION_SIGNATURE} {""") | |
796 template = Template(""" | |
797 static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0; | |
798 ${FUNCTION_HEADER} | |
799 /* Must call RegisterNativesImpl() */ | |
800 DCHECK(g_${JAVA_CLASS}_clazz); | |
801 jmethodID method_id = | |
802 ${GET_METHOD_ID_IMPL} | |
803 ${RETURN_DECLARATION} | |
804 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, | |
805 method_id${PARAMS_IN_CALL})${POST_CALL}; | |
806 ${CHECK_EXCEPTION} | |
807 ${RETURN_CLAUSE} | |
808 }""") | |
809 if called_by_native.static or called_by_native.is_constructor: | 928 if called_by_native.static or called_by_native.is_constructor: |
810 first_param_in_declaration = '' | 929 first_param_in_declaration = '' |
811 first_param_in_call = ('g_%s_clazz' % | 930 first_param_in_call = ('g_%s_clazz' % |
812 (called_by_native.java_class_name or | 931 (called_by_native.java_class_name or |
813 self.class_name)) | 932 self.class_name)) |
814 else: | 933 else: |
815 first_param_in_declaration = ', jobject obj' | 934 first_param_in_declaration = ', jobject obj' |
816 first_param_in_call = 'obj' | 935 first_param_in_call = 'obj' |
817 params_in_declaration = self.GetCalledByNativeParamsInDeclaration( | 936 params_in_declaration = self.GetCalledByNativeParamsInDeclaration( |
818 called_by_native) | 937 called_by_native) |
819 if params_in_declaration: | 938 if params_in_declaration: |
820 params_in_declaration = ', ' + params_in_declaration | 939 params_in_declaration = ', ' + params_in_declaration |
821 params_for_call = ', '.join(param.name | 940 params_in_call = ', '.join(param.name for param in called_by_native.params) |
822 for param in called_by_native.params) | 941 if params_in_call: |
823 if params_for_call: | 942 params_in_call = ', ' + params_in_call |
824 params_for_call = ', ' + params_for_call | |
825 pre_call = '' | 943 pre_call = '' |
826 post_call = '' | 944 post_call = '' |
827 if called_by_native.static_cast: | 945 if called_by_native.static_cast: |
828 pre_call = 'static_cast<%s>(' % called_by_native.static_cast | 946 pre_call = 'static_cast<%s>(' % called_by_native.static_cast |
829 post_call = ')' | 947 post_call = ')' |
830 check_exception = '' | 948 check_exception = '' |
831 if not called_by_native.unchecked: | 949 if not called_by_native.unchecked: |
832 check_exception = 'base::android::CheckException(env);' | 950 check_exception = 'jni_generator::CheckException(env);' |
833 return_type = JavaDataTypeToC(called_by_native.return_type) | 951 return_type = JavaDataTypeToC(called_by_native.return_type) |
952 optional_error_return = JavaReturnValueToC(called_by_native.return_type) | |
953 if optional_error_return: | |
954 optional_error_return = ', ' + optional_error_return | |
834 return_declaration = '' | 955 return_declaration = '' |
835 return_clause = '' | 956 return_clause = '' |
836 if return_type != 'void': | 957 if return_type != 'void': |
837 pre_call = ' ' + pre_call | 958 pre_call = ' ' + pre_call |
838 return_declaration = return_type + ' ret =' | 959 return_declaration = return_type + ' ret =' |
839 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): | 960 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): |
840 return_type = 'ScopedJavaLocalRef<' + return_type + '>' | 961 return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>' |
841 return_clause = 'return ' + return_type + '(env, ret);' | 962 return_clause = 'return ' + return_type + '(env, ret);' |
842 else: | 963 else: |
843 return_clause = 'return ret;' | 964 return_clause = 'return ret;' |
844 values = { | 965 return { |
845 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, | 966 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, |
846 'METHOD': called_by_native.name, | |
847 'RETURN_TYPE': return_type, | 967 'RETURN_TYPE': return_type, |
968 'OPTIONAL_ERROR_RETURN': optional_error_return, | |
848 'RETURN_DECLARATION': return_declaration, | 969 'RETURN_DECLARATION': return_declaration, |
849 'RETURN_CLAUSE': return_clause, | 970 'RETURN_CLAUSE': return_clause, |
850 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration, | 971 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration, |
851 'PARAMS_IN_DECLARATION': params_in_declaration, | 972 'PARAMS_IN_DECLARATION': params_in_declaration, |
852 'STATIC': 'Static' if called_by_native.static else '', | |
853 'PRE_CALL': pre_call, | 973 'PRE_CALL': pre_call, |
854 'POST_CALL': post_call, | 974 'POST_CALL': post_call, |
855 'ENV_CALL': called_by_native.env_call, | 975 'ENV_CALL': called_by_native.env_call, |
856 'FIRST_PARAM_IN_CALL': first_param_in_call, | 976 'FIRST_PARAM_IN_CALL': first_param_in_call, |
857 'PARAMS_IN_CALL': params_for_call, | 977 'PARAMS_IN_CALL': params_in_call, |
858 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, | 978 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, |
859 'CHECK_EXCEPTION': check_exception, | 979 'CHECK_EXCEPTION': check_exception, |
860 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native) | 980 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native) |
861 } | 981 } |
982 | |
983 def GetEagerCalledByNativeMethodStub(self, called_by_native): | |
984 """Returns the implementation of the called by native method.""" | |
985 template = Template(""" | |
986 static ${RETURN_TYPE} ${METHOD_ID_VAR_NAME}(\ | |
987 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION}) { | |
988 ${RETURN_DECLARATION}${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, | |
989 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}${PARAMS_IN_CALL})${POST_CALL}; | |
990 ${RETURN_CLAUSE} | |
991 }""") | |
992 values = self.GetCalledByNativeValues(called_by_native) | |
993 return template.substitute(values) | |
994 | |
995 def GetLazyCalledByNativeMethodStub(self, called_by_native): | |
996 """Returns a string.""" | |
997 function_signature_template = Template("""\ | |
998 static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\ | |
999 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""") | |
1000 function_header_template = Template("""\ | |
1001 ${FUNCTION_SIGNATURE} {""") | |
1002 function_header_with_unused_template = Template("""\ | |
1003 ${FUNCTION_SIGNATURE} __attribute__ ((unused)); | |
1004 ${FUNCTION_SIGNATURE} {""") | |
1005 template = Template(""" | |
1006 static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0; | |
1007 ${FUNCTION_HEADER} | |
1008 /* Must call RegisterNativesImpl() */ | |
1009 DCHECK_CLAZZ(g_${JAVA_CLASS}_clazz${OPTIONAL_ERROR_RETURN}); | |
1010 jmethodID method_id = | |
1011 ${GET_METHOD_ID_IMPL} | |
1012 ${RETURN_DECLARATION} | |
1013 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, | |
1014 method_id${PARAMS_IN_CALL})${POST_CALL}; | |
1015 ${CHECK_EXCEPTION} | |
1016 ${RETURN_CLAUSE} | |
1017 }""") | |
1018 values = self.GetCalledByNativeValues(called_by_native) | |
862 values['FUNCTION_SIGNATURE'] = ( | 1019 values['FUNCTION_SIGNATURE'] = ( |
863 function_signature_template.substitute(values)) | 1020 function_signature_template.substitute(values)) |
864 if called_by_native.system_class: | 1021 if called_by_native.system_class: |
865 values['FUNCTION_HEADER'] = ( | 1022 values['FUNCTION_HEADER'] = ( |
866 function_header_with_unused_template.substitute(values)) | 1023 function_header_with_unused_template.substitute(values)) |
867 else: | 1024 else: |
868 values['FUNCTION_HEADER'] = function_header_template.substitute(values) | 1025 values['FUNCTION_HEADER'] = function_header_template.substitute(values) |
869 return template.substitute(values) | 1026 return template.substitute(values) |
870 | 1027 |
871 def GetKMethodArrayEntry(self, native): | 1028 def GetKMethodArrayEntry(self, native): |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
909 // Leaking this jclass as we cannot use LazyInstance from some threads. | 1066 // Leaking this jclass as we cannot use LazyInstance from some threads. |
910 jclass g_${JAVA_CLASS}_clazz = NULL;""") | 1067 jclass g_${JAVA_CLASS}_clazz = NULL;""") |
911 values = { | 1068 values = { |
912 'JAVA_CLASS': clazz, | 1069 'JAVA_CLASS': clazz, |
913 } | 1070 } |
914 ret += [template.substitute(values)] | 1071 ret += [template.substitute(values)] |
915 return '\n'.join(ret) | 1072 return '\n'.join(ret) |
916 | 1073 |
917 def GetFindClasses(self): | 1074 def GetFindClasses(self): |
918 """Returns the imlementation of FindClass for all known classes.""" | 1075 """Returns the imlementation of FindClass for all known classes.""" |
919 template = Template("""\ | 1076 if self.init_native: |
1077 template = Template("""\ | |
1078 g_${JAVA_CLASS}_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));""") | |
1079 else: | |
1080 template = Template("""\ | |
920 g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( | 1081 g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( |
921 base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""") | 1082 base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""") |
922 ret = [] | 1083 ret = [] |
923 for clazz in self.GetUniqueClasses(self.called_by_natives): | 1084 for clazz in self.GetUniqueClasses(self.called_by_natives): |
924 values = {'JAVA_CLASS': clazz} | 1085 values = {'JAVA_CLASS': clazz} |
925 ret += [template.substitute(values)] | 1086 ret += [template.substitute(values)] |
926 return '\n'.join(ret) | 1087 return '\n'.join(ret) |
927 | 1088 |
928 def GetMethodIDImpl(self, called_by_native): | 1089 def GetMethodIDImpl(self, called_by_native): |
929 """Returns the implementation of GetMethodID.""" | 1090 """Returns the implementation of GetMethodID.""" |
930 template = Template("""\ | 1091 if self.options.eager_called_by_natives: |
1092 template = Template("""\ | |
1093 env->Get${STATIC_METHOD_PART}MethodID( | |
1094 g_${JAVA_CLASS}_clazz, | |
1095 "${JNI_NAME}", ${JNI_SIGNATURE});""") | |
1096 else: | |
1097 template = Template("""\ | |
931 base::android::MethodID::LazyGet< | 1098 base::android::MethodID::LazyGet< |
932 base::android::MethodID::TYPE_${STATIC}>( | 1099 base::android::MethodID::TYPE_${STATIC}>( |
933 env, g_${JAVA_CLASS}_clazz, | 1100 env, g_${JAVA_CLASS}_clazz, |
934 "${JNI_NAME}", | 1101 "${JNI_NAME}", |
935 ${JNI_SIGNATURE}, | 1102 ${JNI_SIGNATURE}, |
936 &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}); | 1103 &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}); |
937 """) | 1104 """) |
938 jni_name = called_by_native.name | 1105 jni_name = called_by_native.name |
939 jni_return_type = called_by_native.return_type | 1106 jni_return_type = called_by_native.return_type |
940 if called_by_native.is_constructor: | 1107 if called_by_native.is_constructor: |
941 jni_name = '<init>' | 1108 jni_name = '<init>' |
942 jni_return_type = 'void' | 1109 jni_return_type = 'void' |
943 if called_by_native.signature: | 1110 if called_by_native.signature: |
944 signature = called_by_native.signature | 1111 signature = called_by_native.signature |
945 else: | 1112 else: |
946 signature = JniParams.Signature(called_by_native.params, | 1113 signature = JniParams.Signature(called_by_native.params, |
947 jni_return_type, | 1114 jni_return_type, |
948 True) | 1115 True) |
949 values = { | 1116 values = { |
950 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, | 1117 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, |
951 'JNI_NAME': jni_name, | 1118 'JNI_NAME': jni_name, |
952 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, | 1119 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, |
953 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE', | 1120 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE', |
1121 'STATIC_METHOD_PART': 'Static' if called_by_native.static else '', | |
954 'JNI_SIGNATURE': signature, | 1122 'JNI_SIGNATURE': signature, |
955 } | 1123 } |
956 return template.substitute(values) | 1124 return template.substitute(values) |
957 | 1125 |
958 | 1126 |
959 def WrapOutput(output): | 1127 def WrapOutput(output): |
960 ret = [] | 1128 ret = [] |
961 for line in output.splitlines(): | 1129 for line in output.splitlines(): |
962 # Do not wrap lines under 80 characters or preprocessor directives. | 1130 # Do not wrap lines under 80 characters or preprocessor directives. |
963 if len(line) < 80 or line.lstrip()[:1] == '#': | 1131 if len(line) < 80 or line.lstrip()[:1] == '#': |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1049 See SampleForTests.java for more details. | 1217 See SampleForTests.java for more details. |
1050 """ | 1218 """ |
1051 option_parser = optparse.OptionParser(usage=usage) | 1219 option_parser = optparse.OptionParser(usage=usage) |
1052 option_parser.add_option('-j', dest='jar_file', | 1220 option_parser.add_option('-j', dest='jar_file', |
1053 help='Extract the list of input files from' | 1221 help='Extract the list of input files from' |
1054 ' a specified jar file.' | 1222 ' a specified jar file.' |
1055 ' Uses javap to extract the methods from a' | 1223 ' Uses javap to extract the methods from a' |
1056 ' pre-compiled class. --input should point' | 1224 ' pre-compiled class. --input should point' |
1057 ' to pre-compiled Java .class files.') | 1225 ' to pre-compiled Java .class files.') |
1058 option_parser.add_option('-n', dest='namespace', | 1226 option_parser.add_option('-n', dest='namespace', |
1059 help='Uses as a namespace in the generated header,' | 1227 help='Uses as a namespace in the generated header ' |
1060 ' instead of the javap class name.') | 1228 'instead of the javap class name, or when there is ' |
1229 'no JNINamespace annotation in the java source.') | |
1061 option_parser.add_option('--input_file', | 1230 option_parser.add_option('--input_file', |
1062 help='Single input file name. The output file name ' | 1231 help='Single input file name. The output file name ' |
1063 'will be derived from it. Must be used with ' | 1232 'will be derived from it. Must be used with ' |
1064 '--output_dir.') | 1233 '--output_dir.') |
1065 option_parser.add_option('--output_dir', | 1234 option_parser.add_option('--output_dir', |
1066 help='The output directory. Must be used with ' | 1235 help='The output directory. Must be used with ' |
1067 '--input') | 1236 '--input') |
1068 option_parser.add_option('--optimize_generation', type="int", | 1237 option_parser.add_option('--optimize_generation', type="int", |
1069 default=0, help='Whether we should optimize JNI ' | 1238 default=0, help='Whether we should optimize JNI ' |
1070 'generation by not regenerating files if they have ' | 1239 'generation by not regenerating files if they have ' |
1071 'not changed.') | 1240 'not changed.') |
1072 option_parser.add_option('--jarjar', | 1241 option_parser.add_option('--jarjar', |
1073 help='Path to optional jarjar rules file.') | 1242 help='Path to optional jarjar rules file.') |
1074 option_parser.add_option('--script_name', default=GetScriptName(), | 1243 option_parser.add_option('--script_name', default=GetScriptName(), |
1075 help='The name of this script in the generated ' | 1244 help='The name of this script in the generated ' |
1076 'header.') | 1245 'header.') |
1246 option_parser.add_option('--includes', | |
1247 help='The comma-separated list of header files to ' | |
1248 'include in the generated header.') | |
1249 option_parser.add_option('--pure_native_methods', | |
1250 action='store_true', dest='pure_native_methods', | |
1251 help='When true, the native methods will be called ' | |
1252 'without any JNI-specific arguments.') | |
1077 option_parser.add_option('--ptr_type', default='int', | 1253 option_parser.add_option('--ptr_type', default='int', |
1078 type='choice', choices=['int', 'long'], | 1254 type='choice', choices=['int', 'long'], |
1079 help='The type used to represent native pointers in ' | 1255 help='The type used to represent native pointers in ' |
1080 'Java code. For 32-bit, use int; ' | 1256 'Java code. For 32-bit, use int; ' |
1081 'for 64-bit, use long.') | 1257 'for 64-bit, use long.') |
1258 option_parser.add_option('--jni_init_native_name', default='', | |
1259 help='The name of the JNI registration method that ' | |
1260 'is used to initialize all native methods. If a ' | |
1261 'method with this name is not present in the Java ' | |
1262 'source file, setting this option is a no-op. When ' | |
1263 'a method with this name is found however, the ' | |
1264 'naming convention Java_<packageName>_<className> ' | |
1265 'will limit the initialization to only the ' | |
1266 'top-level class.') | |
1267 option_parser.add_option('--eager_called_by_natives', | |
1268 action='store_true', dest='eager_called_by_natives', | |
1269 help='When true, the called-by-native methods will ' | |
1270 'be initialized in a non-atomic way.') | |
1082 options, args = option_parser.parse_args(argv) | 1271 options, args = option_parser.parse_args(argv) |
1083 if options.jar_file: | 1272 if options.jar_file: |
1084 input_file = ExtractJarInputFile(options.jar_file, options.input_file, | 1273 input_file = ExtractJarInputFile(options.jar_file, options.input_file, |
1085 options.output_dir) | 1274 options.output_dir) |
1086 elif options.input_file: | 1275 elif options.input_file: |
1087 input_file = options.input_file | 1276 input_file = options.input_file |
1088 else: | 1277 else: |
1089 option_parser.print_help() | 1278 option_parser.print_help() |
1090 print '\nError: Must specify --jar_file or --input_file.' | 1279 print '\nError: Must specify --jar_file or --input_file.' |
1091 return 1 | 1280 return 1 |
1092 output_file = None | 1281 output_file = None |
1093 if options.output_dir: | 1282 if options.output_dir: |
1094 root_name = os.path.splitext(os.path.basename(input_file))[0] | 1283 root_name = os.path.splitext(os.path.basename(input_file))[0] |
1095 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' | 1284 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' |
1096 if options.jarjar: | 1285 if options.jarjar: |
1097 with open(options.jarjar) as f: | 1286 with open(options.jarjar) as f: |
1098 JniParams.SetJarJarMappings(f.read()) | 1287 JniParams.SetJarJarMappings(f.read()) |
1099 GenerateJNIHeader(input_file, output_file, options) | 1288 GenerateJNIHeader(input_file, output_file, options) |
1100 | 1289 |
1101 | 1290 |
1102 if __name__ == '__main__': | 1291 if __name__ == '__main__': |
1103 sys.exit(main(sys.argv)) | 1292 sys.exit(main(sys.argv)) |
OLD | NEW |