| 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 506 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 517 # Regex to match a string like "@CalledByNative public void foo(int bar)". | 517 # Regex to match a string like "@CalledByNative public void foo(int bar)". |
| 518 RE_CALLED_BY_NATIVE = re.compile( | 518 RE_CALLED_BY_NATIVE = re.compile( |
| 519 '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?' | 519 '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?' |
| 520 '\s+(?P<prefix>[\w ]*?)' | 520 '\s+(?P<prefix>[\w ]*?)' |
| 521 '(:?\s*@\w+)?' # Ignore annotations in return types. | 521 '(:?\s*@\w+)?' # Ignore annotations in return types. |
| 522 '\s*(?P<return_type>\S+?)' | 522 '\s*(?P<return_type>\S+?)' |
| 523 '\s+(?P<name>\w+)' | 523 '\s+(?P<name>\w+)' |
| 524 '\s*\((?P<params>[^\)]*)\)') | 524 '\s*\((?P<params>[^\)]*)\)') |
| 525 | 525 |
| 526 | 526 |
| 527 # Removes empty lines that are indented (i.e. start with 2x spaces). |
| 528 def RemoveIndentedEmptyLines(string): |
| 529 return re.sub('^(?: {2})+$\n', '', string, flags=re.MULTILINE) |
| 530 |
| 531 |
| 527 def ExtractCalledByNatives(contents): | 532 def ExtractCalledByNatives(contents): |
| 528 """Parses all methods annotated with @CalledByNative. | 533 """Parses all methods annotated with @CalledByNative. |
| 529 | 534 |
| 530 Args: | 535 Args: |
| 531 contents: the contents of the java file. | 536 contents: the contents of the java file. |
| 532 | 537 |
| 533 Returns: | 538 Returns: |
| 534 A list of dict with information about the annotated methods. | 539 A list of dict with information about the annotated methods. |
| 535 TODO(bulach): return a CalledByNative object. | 540 TODO(bulach): return a CalledByNative object. |
| 536 | 541 |
| (...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 994 else: | 999 else: |
| 995 params_in_call.append(p.name) | 1000 params_in_call.append(p.name) |
| 996 params_in_call = ', '.join(params_in_call) | 1001 params_in_call = ', '.join(params_in_call) |
| 997 | 1002 |
| 998 return_type = return_declaration = JavaDataTypeToC(native.return_type) | 1003 return_type = return_declaration = JavaDataTypeToC(native.return_type) |
| 999 post_call = '' | 1004 post_call = '' |
| 1000 if re.match(RE_SCOPED_JNI_TYPES, return_type): | 1005 if re.match(RE_SCOPED_JNI_TYPES, return_type): |
| 1001 post_call = '.Release()' | 1006 post_call = '.Release()' |
| 1002 return_declaration = ('base::android::ScopedJavaLocalRef<' + return_type + | 1007 return_declaration = ('base::android::ScopedJavaLocalRef<' + return_type + |
| 1003 '>') | 1008 '>') |
| 1009 profiling_entered_native = '' |
| 1010 if self.options.enable_profiling: |
| 1011 profiling_entered_native = 'JNI_LINK_SAVED_FRAME_POINTER;' |
| 1004 values = { | 1012 values = { |
| 1005 'RETURN': return_type, | 1013 'RETURN': return_type, |
| 1006 'RETURN_DECLARATION': return_declaration, | 1014 'RETURN_DECLARATION': return_declaration, |
| 1007 'NAME': native.name, | 1015 'NAME': native.name, |
| 1008 'PARAMS': self.GetParamsInDeclaration(native), | 1016 'PARAMS': self.GetParamsInDeclaration(native), |
| 1009 'PARAMS_IN_STUB': self.GetParamsInStub(native), | 1017 'PARAMS_IN_STUB': self.GetParamsInStub(native), |
| 1010 'PARAMS_IN_CALL': params_in_call, | 1018 'PARAMS_IN_CALL': params_in_call, |
| 1011 'POST_CALL': post_call, | 1019 'POST_CALL': post_call, |
| 1012 'STUB_NAME': self.GetStubName(native), | 1020 'STUB_NAME': self.GetStubName(native), |
| 1021 'PROFILING_ENTERED_NATIVE': profiling_entered_native, |
| 1013 } | 1022 } |
| 1014 | 1023 |
| 1015 if is_method: | 1024 if is_method: |
| 1016 optional_error_return = JavaReturnValueToC(native.return_type) | 1025 optional_error_return = JavaReturnValueToC(native.return_type) |
| 1017 if optional_error_return: | 1026 if optional_error_return: |
| 1018 optional_error_return = ', ' + optional_error_return | 1027 optional_error_return = ', ' + optional_error_return |
| 1019 values.update({ | 1028 values.update({ |
| 1020 'OPTIONAL_ERROR_RETURN': optional_error_return, | 1029 'OPTIONAL_ERROR_RETURN': optional_error_return, |
| 1021 'PARAM0_NAME': native.params[0].name, | 1030 'PARAM0_NAME': native.params[0].name, |
| 1022 'P0_TYPE': native.p0_type, | 1031 'P0_TYPE': native.p0_type, |
| 1023 }) | 1032 }) |
| 1024 template = Template("""\ | 1033 template = Template("""\ |
| 1025 extern "C" __attribute__((visibility("default"))) | 1034 extern "C" __attribute__((visibility("default"))) |
| 1026 ${RETURN} ${STUB_NAME}(JNIEnv* env, | 1035 ${RETURN} ${STUB_NAME}(JNIEnv* env, |
| 1027 ${PARAMS_IN_STUB}) { | 1036 ${PARAMS_IN_STUB}) { |
| 1037 ${PROFILING_ENTERED_NATIVE} |
| 1028 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); | 1038 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); |
| 1029 CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN}); | 1039 CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN}); |
| 1030 return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL}; | 1040 return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL}; |
| 1031 } | 1041 } |
| 1032 """) | 1042 """) |
| 1033 else: | 1043 else: |
| 1034 template = Template(""" | 1044 template = Template(""" |
| 1035 static ${RETURN_DECLARATION} ${NAME}(JNIEnv* env, ${PARAMS}); | 1045 static ${RETURN_DECLARATION} ${NAME}(JNIEnv* env, ${PARAMS}); |
| 1036 | 1046 |
| 1037 extern "C" __attribute__((visibility("default"))) | 1047 extern "C" __attribute__((visibility("default"))) |
| 1038 ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) { | 1048 ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) { |
| 1049 ${PROFILING_ENTERED_NATIVE} |
| 1039 return ${NAME}(${PARAMS_IN_CALL})${POST_CALL}; | 1050 return ${NAME}(${PARAMS_IN_CALL})${POST_CALL}; |
| 1040 } | 1051 } |
| 1041 """) | 1052 """) |
| 1042 | 1053 |
| 1043 return template.substitute(values) | 1054 return RemoveIndentedEmptyLines(template.substitute(values)) |
| 1044 | 1055 |
| 1045 def GetArgument(self, param): | 1056 def GetArgument(self, param): |
| 1046 if param.datatype == 'int': | 1057 if param.datatype == 'int': |
| 1047 return 'as_jint(' + param.name + ')' | 1058 return 'as_jint(' + param.name + ')' |
| 1048 elif re.match(RE_SCOPED_JNI_TYPES, JavaDataTypeToC(param.datatype)): | 1059 elif re.match(RE_SCOPED_JNI_TYPES, JavaDataTypeToC(param.datatype)): |
| 1049 return param.name + '.obj()' | 1060 return param.name + '.obj()' |
| 1050 else: | 1061 else: |
| 1051 return param.name | 1062 return param.name |
| 1052 | 1063 |
| 1053 def GetArgumentsInCall(self, params): | 1064 def GetArgumentsInCall(self, params): |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1086 return_declaration = '' | 1097 return_declaration = '' |
| 1087 return_clause = '' | 1098 return_clause = '' |
| 1088 if return_type != 'void': | 1099 if return_type != 'void': |
| 1089 pre_call = ' ' + pre_call | 1100 pre_call = ' ' + pre_call |
| 1090 return_declaration = return_type + ' ret =' | 1101 return_declaration = return_type + ' ret =' |
| 1091 if re.match(RE_SCOPED_JNI_TYPES, return_type): | 1102 if re.match(RE_SCOPED_JNI_TYPES, return_type): |
| 1092 return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>' | 1103 return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>' |
| 1093 return_clause = 'return ' + return_type + '(env, ret);' | 1104 return_clause = 'return ' + return_type + '(env, ret);' |
| 1094 else: | 1105 else: |
| 1095 return_clause = 'return ret;' | 1106 return_clause = 'return ret;' |
| 1107 profiling_leaving_native = '' |
| 1108 if self.options.enable_profiling: |
| 1109 profiling_leaving_native = 'JNI_SAVE_FRAME_POINTER;' |
| 1096 return { | 1110 return { |
| 1097 'JAVA_CLASS': java_class, | 1111 'JAVA_CLASS': java_class, |
| 1098 'RETURN_TYPE': return_type, | 1112 'RETURN_TYPE': return_type, |
| 1099 'OPTIONAL_ERROR_RETURN': optional_error_return, | 1113 'OPTIONAL_ERROR_RETURN': optional_error_return, |
| 1100 'RETURN_DECLARATION': return_declaration, | 1114 'RETURN_DECLARATION': return_declaration, |
| 1101 'RETURN_CLAUSE': return_clause, | 1115 'RETURN_CLAUSE': return_clause, |
| 1102 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration, | 1116 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration, |
| 1103 'PARAMS_IN_DECLARATION': params_in_declaration, | 1117 'PARAMS_IN_DECLARATION': params_in_declaration, |
| 1104 'PRE_CALL': pre_call, | 1118 'PRE_CALL': pre_call, |
| 1105 'POST_CALL': post_call, | 1119 'POST_CALL': post_call, |
| 1106 'ENV_CALL': called_by_native.env_call, | 1120 'ENV_CALL': called_by_native.env_call, |
| 1107 'FIRST_PARAM_IN_CALL': first_param_in_call, | 1121 'FIRST_PARAM_IN_CALL': first_param_in_call, |
| 1108 'PARAMS_IN_CALL': params_in_call, | 1122 'PARAMS_IN_CALL': params_in_call, |
| 1109 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, | 1123 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, |
| 1110 'CHECK_EXCEPTION': check_exception, | 1124 'CHECK_EXCEPTION': check_exception, |
| 1111 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native) | 1125 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native), |
| 1126 'PROFILING_LEAVING_NATIVE': profiling_leaving_native, |
| 1112 } | 1127 } |
| 1113 | 1128 |
| 1114 | 1129 |
| 1115 def GetLazyCalledByNativeMethodStub(self, called_by_native): | 1130 def GetLazyCalledByNativeMethodStub(self, called_by_native): |
| 1116 """Returns a string.""" | 1131 """Returns a string.""" |
| 1117 function_signature_template = Template("""\ | 1132 function_signature_template = Template("""\ |
| 1118 static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\ | 1133 static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\ |
| 1119 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""") | 1134 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""") |
| 1120 function_header_template = Template("""\ | 1135 function_header_template = Template("""\ |
| 1121 ${FUNCTION_SIGNATURE} {""") | 1136 ${FUNCTION_SIGNATURE} {""") |
| 1122 function_header_with_unused_template = Template("""\ | 1137 function_header_with_unused_template = Template("""\ |
| 1123 ${FUNCTION_SIGNATURE} __attribute__ ((unused)); | 1138 ${FUNCTION_SIGNATURE} __attribute__ ((unused)); |
| 1124 ${FUNCTION_SIGNATURE} {""") | 1139 ${FUNCTION_SIGNATURE} {""") |
| 1125 template = Template(""" | 1140 template = Template(""" |
| 1126 static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0; | 1141 static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0; |
| 1127 ${FUNCTION_HEADER} | 1142 ${FUNCTION_HEADER} |
| 1128 CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL}, | 1143 CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL}, |
| 1129 ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN}); | 1144 ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN}); |
| 1130 jmethodID method_id = | 1145 jmethodID method_id = |
| 1131 ${GET_METHOD_ID_IMPL} | 1146 ${GET_METHOD_ID_IMPL} |
| 1147 ${PROFILING_LEAVING_NATIVE} |
| 1132 ${RETURN_DECLARATION} | 1148 ${RETURN_DECLARATION} |
| 1133 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, | 1149 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, |
| 1134 method_id${PARAMS_IN_CALL})${POST_CALL}; | 1150 method_id${PARAMS_IN_CALL})${POST_CALL}; |
| 1135 ${CHECK_EXCEPTION} | 1151 ${CHECK_EXCEPTION} |
| 1136 ${RETURN_CLAUSE} | 1152 ${RETURN_CLAUSE} |
| 1137 }""") | 1153 }""") |
| 1138 values = self.GetCalledByNativeValues(called_by_native) | 1154 values = self.GetCalledByNativeValues(called_by_native) |
| 1139 values['FUNCTION_SIGNATURE'] = ( | 1155 values['FUNCTION_SIGNATURE'] = ( |
| 1140 function_signature_template.substitute(values)) | 1156 function_signature_template.substitute(values)) |
| 1141 if called_by_native.system_class: | 1157 if called_by_native.system_class: |
| 1142 values['FUNCTION_HEADER'] = ( | 1158 values['FUNCTION_HEADER'] = ( |
| 1143 function_header_with_unused_template.substitute(values)) | 1159 function_header_with_unused_template.substitute(values)) |
| 1144 else: | 1160 else: |
| 1145 values['FUNCTION_HEADER'] = function_header_template.substitute(values) | 1161 values['FUNCTION_HEADER'] = function_header_template.substitute(values) |
| 1146 return template.substitute(values) | 1162 return RemoveIndentedEmptyLines(template.substitute(values)) |
| 1147 | 1163 |
| 1148 def GetKMethodArrayEntry(self, native): | 1164 def GetKMethodArrayEntry(self, native): |
| 1149 template = Template(' { "native${NAME}", ${JNI_SIGNATURE}, ' + | 1165 template = Template(' { "native${NAME}", ${JNI_SIGNATURE}, ' + |
| 1150 'reinterpret_cast<void*>(${STUB_NAME}) },') | 1166 'reinterpret_cast<void*>(${STUB_NAME}) },') |
| 1151 values = {'NAME': native.name, | 1167 values = {'NAME': native.name, |
| 1152 'JNI_SIGNATURE': JniParams.Signature(native.params, | 1168 'JNI_SIGNATURE': JniParams.Signature(native.params, |
| 1153 native.return_type, | 1169 native.return_type, |
| 1154 True), | 1170 True), |
| 1155 'STUB_NAME': self.GetStubName(native)} | 1171 'STUB_NAME': self.GetStubName(native)} |
| 1156 return template.substitute(values) | 1172 return template.substitute(values) |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1356 help='The type used to represent native pointers in ' | 1372 help='The type used to represent native pointers in ' |
| 1357 'Java code. For 32-bit, use int; ' | 1373 'Java code. For 32-bit, use int; ' |
| 1358 'for 64-bit, use long.') | 1374 'for 64-bit, use long.') |
| 1359 option_parser.add_option('--cpp', default='cpp', | 1375 option_parser.add_option('--cpp', default='cpp', |
| 1360 help='The path to cpp command.') | 1376 help='The path to cpp command.') |
| 1361 option_parser.add_option('--javap', default='javap', | 1377 option_parser.add_option('--javap', default='javap', |
| 1362 help='The path to javap command.') | 1378 help='The path to javap command.') |
| 1363 option_parser.add_option('--native_exports_optional', action='store_true', | 1379 option_parser.add_option('--native_exports_optional', action='store_true', |
| 1364 help='Support both explicit and native method' | 1380 help='Support both explicit and native method' |
| 1365 'registration.') | 1381 'registration.') |
| 1382 option_parser.add_option('--enable_profiling', action='store_true', |
| 1383 help='Add additional profiling instrumentation.') |
| 1366 options, args = option_parser.parse_args(argv) | 1384 options, args = option_parser.parse_args(argv) |
| 1367 if options.jar_file: | 1385 if options.jar_file: |
| 1368 input_file = ExtractJarInputFile(options.jar_file, options.input_file, | 1386 input_file = ExtractJarInputFile(options.jar_file, options.input_file, |
| 1369 options.output_dir) | 1387 options.output_dir) |
| 1370 elif options.input_file: | 1388 elif options.input_file: |
| 1371 input_file = options.input_file | 1389 input_file = options.input_file |
| 1372 else: | 1390 else: |
| 1373 option_parser.print_help() | 1391 option_parser.print_help() |
| 1374 print '\nError: Must specify --jar_file or --input_file.' | 1392 print '\nError: Must specify --jar_file or --input_file.' |
| 1375 return 1 | 1393 return 1 |
| 1376 output_file = None | 1394 output_file = None |
| 1377 if options.output_dir: | 1395 if options.output_dir: |
| 1378 root_name = os.path.splitext(os.path.basename(input_file))[0] | 1396 root_name = os.path.splitext(os.path.basename(input_file))[0] |
| 1379 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' | 1397 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' |
| 1380 GenerateJNIHeader(input_file, output_file, options) | 1398 GenerateJNIHeader(input_file, output_file, options) |
| 1381 | 1399 |
| 1382 if options.depfile: | 1400 if options.depfile: |
| 1383 build_utils.WriteDepfile(options.depfile, output_file) | 1401 build_utils.WriteDepfile(options.depfile, output_file) |
| 1384 | 1402 |
| 1385 | 1403 |
| 1386 if __name__ == '__main__': | 1404 if __name__ == '__main__': |
| 1387 sys.exit(main(sys.argv)) | 1405 sys.exit(main(sys.argv)) |
| OLD | NEW |