Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(678)

Side by Side Diff: base/android/jni_generator/jni_generator.py

Issue 11363079: Android: uses "import" section and inner classes for obtaining qualified JNI parameters. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Yaron's comments Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 optparse 10 import optparse
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 'int': 'jint', 87 'int': 'jint',
88 'byte': 'jbyte', 88 'byte': 'jbyte',
89 'boolean': 'jboolean', 89 'boolean': 'jboolean',
90 'long': 'jlong', 90 'long': 'jlong',
91 'double': 'jdouble', 91 'double': 'jdouble',
92 'float': 'jfloat', 92 'float': 'jfloat',
93 } 93 }
94 java_type_map = { 94 java_type_map = {
95 'void': 'void', 95 'void': 'void',
96 'String': 'jstring', 96 'String': 'jstring',
97 'java/lang/String': 'jstring',
97 } 98 }
98 if java_type in java_pod_type_map: 99 if java_type in java_pod_type_map:
99 return java_pod_type_map[java_type] 100 return java_pod_type_map[java_type]
100 elif java_type in java_type_map: 101 elif java_type in java_type_map:
101 return java_type_map[java_type] 102 return java_type_map[java_type]
102 elif java_type.endswith('[]'): 103 elif java_type.endswith('[]'):
103 if java_type[:-2] in java_pod_type_map: 104 if java_type[:-2] in java_pod_type_map:
104 return java_pod_type_map[java_type[:-2]] + 'Array' 105 return java_pod_type_map[java_type[:-2]] + 'Array'
105 return 'jobjectArray' 106 return 'jobjectArray'
106 else: 107 else:
107 return 'jobject' 108 return 'jobject'
108 109
109 110
110 class JniParams(object): 111 class JniParams(object):
111 _UNKNOWN_JAVA_TYPE_PREFIX = 'UNKNOWN_JAVA_TYPE: ' 112 _imports = []
112 _external_param_files = set() 113 _fully_qualified_class = ''
113 _external_param_list = [] 114 _package = ''
115 _inner_classes = []
114 116
115 @staticmethod 117 @staticmethod
116 def ReadExternalParamList(external_param_files): 118 def SetFullyQualifiedClass(fully_qualified_class):
117 if not external_param_files: 119 JniParams._fully_qualified_class = 'L' + fully_qualified_class
118 return 120 JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1])
119 assert not JniParams._external_param_files 121
120 JniParams._external_param_files = set(external_param_files) 122 @staticmethod
121 for external_param_file in JniParams._external_param_files: 123 def ExtractImportsAndInnerClasses(contents):
122 with file(external_param_file, 'r') as f: 124 contents = contents.replace('\n', '')
123 contents = f.read() 125 re_import = re.compile(r'import.*?(?P<class>\S*?);')
124 JniParams._external_param_list += [x.strip() 126 for match in re.finditer(re_import, contents):
125 for x in contents.splitlines() 127 JniParams._imports += ['L' + match.group('class').replace('.', '/')]
126 if x and not x.startswith('#')] 128
129 re_inner = re.compile(r'(class|interface)\s+?(?P<name>\w+?)\W')
130 for match in re.finditer(re_inner, contents):
131 inner = match.group('name')
132 if not JniParams._fully_qualified_class.endswith(inner):
133 JniParams._inner_classes += [JniParams._fully_qualified_class + '$' +
134 inner]
127 135
128 @staticmethod 136 @staticmethod
129 def JavaToJni(param): 137 def JavaToJni(param):
130 """Converts a java param into a JNI signature type.""" 138 """Converts a java param into a JNI signature type."""
131 pod_param_map = { 139 pod_param_map = {
132 'int': 'I', 140 'int': 'I',
133 'boolean': 'Z', 141 'boolean': 'Z',
134 'long': 'J', 142 'long': 'J',
135 'double': 'D', 143 'double': 'D',
136 'float': 'F', 144 'float': 'F',
137 'byte': 'B', 145 'byte': 'B',
138 'void': 'V', 146 'void': 'V',
139 } 147 }
140 object_param_list = [ 148 object_param_list = [
141 'Ljava/lang/Boolean', 149 'Ljava/lang/Boolean',
142 'Ljava/lang/Integer', 150 'Ljava/lang/Integer',
143 'Ljava/lang/Long', 151 'Ljava/lang/Long',
144 'Ljava/lang/Object', 152 'Ljava/lang/Object',
145 'Ljava/lang/String', 153 'Ljava/lang/String',
146 'Ljava/util/ArrayList',
147 'Ljava/util/HashMap',
148 'Ljava/util/List',
149 'Landroid/content/Context',
150 'Landroid/graphics/Bitmap',
151 'Landroid/graphics/Canvas',
152 'Landroid/graphics/Rect',
153 'Landroid/graphics/RectF',
154 'Landroid/graphics/Matrix',
155 'Landroid/graphics/Point',
156 'Landroid/graphics/SurfaceTexture',
157 'Landroid/graphics/SurfaceTexture$OnFrameAvailableListener',
158 'Landroid/media/MediaPlayer',
159 'Landroid/os/Message',
160 'Landroid/view/KeyEvent',
161 'Landroid/view/Surface',
162 'Landroid/view/View',
163 'Landroid/webkit/ValueCallback',
164 'Ljava/io/InputStream',
165 'Ljava/nio/ByteBuffer',
166 'Ljava/util/Vector',
167 ] 154 ]
168 if param == 'byte[][]': 155 if param == 'byte[][]':
169 return '[[B' 156 return '[[B'
170 prefix = '' 157 prefix = ''
171 # Array? 158 # Array?
172 if param[-2:] == '[]': 159 if param[-2:] == '[]':
173 prefix = '[' 160 prefix = '['
174 param = param[:-2] 161 param = param[:-2]
175 # Generic? 162 # Generic?
176 if '<' in param: 163 if '<' in param:
177 param = param[:param.index('<')] 164 param = param[:param.index('<')]
178 if param in pod_param_map: 165 if param in pod_param_map:
179 return prefix + pod_param_map[param] 166 return prefix + pod_param_map[param]
180 if '/' in param: 167 if '/' in param:
181 # Coming from javap, use the fully qualified param directly. 168 # Coming from javap, use the fully qualified param directly.
182 return 'L' + param + ';' 169 return 'L' + param + ';'
183 for qualified_name in object_param_list + JniParams._external_param_list: 170 for qualified_name in (object_param_list +
171 JniParams._imports +
172 [JniParams._fully_qualified_class] +
173 JniParams._inner_classes):
184 if (qualified_name.endswith('/' + param) or 174 if (qualified_name.endswith('/' + param) or
185 qualified_name.endswith('$' + param.replace('.', '$')) or 175 qualified_name.endswith('$' + param.replace('.', '$')) or
186 qualified_name == 'L' + param): 176 qualified_name == 'L' + param):
187 return prefix + qualified_name + ';' 177 return prefix + qualified_name + ';'
188 else: 178 # Type not found, falling back to same package as this class.
189 return JniParams._UNKNOWN_JAVA_TYPE_PREFIX + prefix + param + ';' 179 return prefix + 'L' + JniParams._package + '/' + param + ';'
190 180
191 @staticmethod 181 @staticmethod
192 def Signature(params, returns, wrap): 182 def Signature(params, returns, wrap):
193 """Returns the JNI signature for the given datatypes.""" 183 """Returns the JNI signature for the given datatypes."""
194 items = ['('] 184 items = ['(']
195 items += [JniParams.JavaToJni(param.datatype) for param in params] 185 items += [JniParams.JavaToJni(param.datatype) for param in params]
196 items += [')'] 186 items += [')']
197 items += [JniParams.JavaToJni(returns)] 187 items += [JniParams.JavaToJni(returns)]
198 if wrap: 188 if wrap:
199 return '\n' + '\n'.join(['"' + item + '"' for item in items]) 189 return '\n' + '\n'.join(['"' + item + '"' for item in items])
(...skipping 10 matching lines...) Expand all
210 items = p.split(' ') 200 items = p.split(' ')
211 if 'final' in items: 201 if 'final' in items:
212 items.remove('final') 202 items.remove('final')
213 param = Param( 203 param = Param(
214 datatype=items[0], 204 datatype=items[0],
215 name=(items[1] if len(items) > 1 else 'p%s' % len(ret)), 205 name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
216 ) 206 )
217 ret += [param] 207 ret += [param]
218 return ret 208 return ret
219 209
220 @staticmethod
221 def CheckUnknownDatatypes(fully_qualified_class, items):
222 unknown_datatypes = JniParams._GetUnknownDatatypes(items)
223 if unknown_datatypes:
224 msg = ('There are a few unknown datatypes in %s' %
225 fully_qualified_class)
226 msg += '\nPlease, edit %s' % str(JniParams._external_param_files)
227 msg += '\nand add the JNI type(s):\n'
228 for unknown_datatype in unknown_datatypes:
229 msg += '\n%s in methods:\n' % unknown_datatype
230 msg += '\n '.join(unknown_datatypes[unknown_datatype])
231 raise SyntaxError(msg)
232
233 @staticmethod
234 def _GetUnknownDatatypes(items):
235 """Returns a list containing the unknown datatypes."""
236 unknown_types = {}
237 for item in items:
238 all_datatypes = ([JniParams.JavaToJni(param.datatype)
239 for param in item.params] +
240 [JniParams.JavaToJni(item.return_type)])
241 for d in all_datatypes:
242 if d.startswith(JniParams._UNKNOWN_JAVA_TYPE_PREFIX):
243 unknown_types[d] = (unknown_types.get(d, []) +
244 [item.name or 'Unable to parse'])
245 return unknown_types
246
247 210
248 def ExtractJNINamespace(contents): 211 def ExtractJNINamespace(contents):
249 re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)') 212 re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
250 m = re.findall(re_jni_namespace, contents) 213 m = re.findall(re_jni_namespace, contents)
251 if not m: 214 if not m:
252 return '' 215 return ''
253 return m[0] 216 return m[0]
254 217
255 218
256 def ExtractFullyQualifiedJavaClassName(java_file_name, contents): 219 def ExtractFullyQualifiedJavaClassName(java_file_name, contents):
(...skipping 21 matching lines...) Expand all
278 java_class_name=match.group('java_class_name'), 241 java_class_name=match.group('java_class_name'),
279 native_class_name=match.group('native_class_name'), 242 native_class_name=match.group('native_class_name'),
280 return_type=match.group('return'), 243 return_type=match.group('return'),
281 name=match.group('name').replace('native', ''), 244 name=match.group('name').replace('native', ''),
282 params=JniParams.Parse(match.group('params'))) 245 params=JniParams.Parse(match.group('params')))
283 natives += [native] 246 natives += [native]
284 return natives 247 return natives
285 248
286 249
287 def GetStaticCastForReturnType(return_type): 250 def GetStaticCastForReturnType(return_type):
288 if return_type == 'String': 251 if return_type in ['String', 'java/lang/String']:
289 return 'jstring' 252 return 'jstring'
253 elif return_type.endswith('[]'):
254 return 'jobjectArray'
290 return None 255 return None
291 256
292 257
293 def GetEnvCall(is_constructor, is_static, return_type): 258 def GetEnvCall(is_constructor, is_static, return_type):
294 """Maps the types availabe via env->Call__Method.""" 259 """Maps the types availabe via env->Call__Method."""
295 if is_constructor: 260 if is_constructor:
296 return 'NewObject' 261 return 'NewObject'
297 env_call_map = {'boolean': 'Boolean', 262 env_call_map = {'boolean': 'Boolean',
298 'byte': 'Byte', 263 'byte': 'Byte',
299 'char': 'Char', 264 'char': 'Char',
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 379
415 class JNIFromJavaP(object): 380 class JNIFromJavaP(object):
416 """Uses 'javap' to parse a .class file and generate the JNI header file.""" 381 """Uses 'javap' to parse a .class file and generate the JNI header file."""
417 382
418 def __init__(self, contents, namespace): 383 def __init__(self, contents, namespace):
419 self.contents = contents 384 self.contents = contents
420 self.namespace = namespace 385 self.namespace = namespace
421 self.fully_qualified_class = re.match('.*?class (?P<class_name>.*?) ', 386 self.fully_qualified_class = re.match('.*?class (?P<class_name>.*?) ',
422 contents[1]).group('class_name') 387 contents[1]).group('class_name')
423 self.fully_qualified_class = self.fully_qualified_class.replace('.', '/') 388 self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
389 JniParams.SetFullyQualifiedClass(self.fully_qualified_class)
424 self.java_class_name = self.fully_qualified_class.split('/')[-1] 390 self.java_class_name = self.fully_qualified_class.split('/')[-1]
425 if not self.namespace: 391 if not self.namespace:
426 self.namespace = 'JNI_' + self.java_class_name 392 self.namespace = 'JNI_' + self.java_class_name
427 re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\w+?) (?P<name>\w+?)' 393 re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
428 '\((?P<params>.*?)\)') 394 '\((?P<params>.*?)\)')
429 self.called_by_natives = [] 395 self.called_by_natives = []
430 for content in contents[2:]: 396 for content in contents[2:]:
431 match = re.match(re_method, content) 397 match = re.match(re_method, content)
432 if not match: 398 if not match:
433 continue 399 continue
434 self.called_by_natives += [CalledByNative( 400 self.called_by_natives += [CalledByNative(
435 system_class=True, 401 system_class=True,
436 unchecked=False, 402 unchecked=False,
437 static='static' in match.group('prefix'), 403 static='static' in match.group('prefix'),
438 java_class_name='', 404 java_class_name='',
439 return_type=match.group('return_type'), 405 return_type=match.group('return_type').replace('.', '/'),
440 name=match.group('name'), 406 name=match.group('name'),
441 params=JniParams.Parse(match.group('params').replace('.', '/')))] 407 params=JniParams.Parse(match.group('params').replace('.', '/')))]
442 re_constructor = re.compile('.*? public ' + 408 re_constructor = re.compile('.*? public ' +
443 self.fully_qualified_class.replace('/', '.') + 409 self.fully_qualified_class.replace('/', '.') +
444 '\((?P<params>.*?)\)') 410 '\((?P<params>.*?)\)')
445 for content in contents[2:]: 411 for content in contents[2:]:
446 match = re.match(re_constructor, content) 412 match = re.match(re_constructor, content)
447 if not match: 413 if not match:
448 continue 414 continue
449 self.called_by_natives += [CalledByNative( 415 self.called_by_natives += [CalledByNative(
(...skipping 22 matching lines...) Expand all
472 stdout, _ = p.communicate() 438 stdout, _ = p.communicate()
473 jni_from_javap = JNIFromJavaP(stdout.split('\n'), namespace) 439 jni_from_javap = JNIFromJavaP(stdout.split('\n'), namespace)
474 return jni_from_javap 440 return jni_from_javap
475 441
476 442
477 class JNIFromJavaSource(object): 443 class JNIFromJavaSource(object):
478 """Uses the given java source file to generate the JNI header file.""" 444 """Uses the given java source file to generate the JNI header file."""
479 445
480 def __init__(self, contents, fully_qualified_class): 446 def __init__(self, contents, fully_qualified_class):
481 contents = self._RemoveComments(contents) 447 contents = self._RemoveComments(contents)
448 JniParams.SetFullyQualifiedClass(fully_qualified_class)
449 JniParams.ExtractImportsAndInnerClasses(contents)
482 jni_namespace = ExtractJNINamespace(contents) 450 jni_namespace = ExtractJNINamespace(contents)
483 natives = ExtractNatives(contents) 451 natives = ExtractNatives(contents)
484 called_by_natives = ExtractCalledByNatives(contents) 452 called_by_natives = ExtractCalledByNatives(contents)
485 if len(natives) == 0 and len(called_by_natives) == 0: 453 if len(natives) == 0 and len(called_by_natives) == 0:
486 raise SyntaxError('Unable to find any JNI methods for %s.' % 454 raise SyntaxError('Unable to find any JNI methods for %s.' %
487 fully_qualified_class) 455 fully_qualified_class)
488 inl_header_file_generator = InlHeaderFileGenerator( 456 inl_header_file_generator = InlHeaderFileGenerator(
489 jni_namespace, fully_qualified_class, natives, called_by_natives) 457 jni_namespace, fully_qualified_class, natives, called_by_natives)
490 self.content = inl_header_file_generator.GetContent() 458 self.content = inl_header_file_generator.GetContent()
491 459
(...skipping 30 matching lines...) Expand all
522 """Generates an inline header file for JNI integration.""" 490 """Generates an inline header file for JNI integration."""
523 491
524 def __init__(self, namespace, fully_qualified_class, natives, 492 def __init__(self, namespace, fully_qualified_class, natives,
525 called_by_natives): 493 called_by_natives):
526 self.namespace = namespace 494 self.namespace = namespace
527 self.fully_qualified_class = fully_qualified_class 495 self.fully_qualified_class = fully_qualified_class
528 self.class_name = self.fully_qualified_class.split('/')[-1] 496 self.class_name = self.fully_qualified_class.split('/')[-1]
529 self.natives = natives 497 self.natives = natives
530 self.called_by_natives = called_by_natives 498 self.called_by_natives = called_by_natives
531 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' 499 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
532 JniParams.CheckUnknownDatatypes(self.fully_qualified_class,
533 self.natives + self.called_by_natives)
534 500
535 def GetContent(self): 501 def GetContent(self):
536 """Returns the content of the JNI binding file.""" 502 """Returns the content of the JNI binding file."""
537 template = Template("""\ 503 template = Template("""\
538 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 504 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
539 // Use of this source code is governed by a BSD-style license that can be 505 // Use of this source code is governed by a BSD-style license that can be
540 // found in the LICENSE file. 506 // found in the LICENSE file.
541 507
542 508
543 // This file is autogenerated by 509 // This file is autogenerated by
(...skipping 443 matching lines...) Expand 10 before | Expand all | Expand 10 after
987 option_parser.add_option('-n', dest='namespace', 953 option_parser.add_option('-n', dest='namespace',
988 help='Uses as a namespace in the generated header,' 954 help='Uses as a namespace in the generated header,'
989 ' instead of the javap class name.') 955 ' instead of the javap class name.')
990 option_parser.add_option('--input_file', 956 option_parser.add_option('--input_file',
991 help='Single input file name. The output file name ' 957 help='Single input file name. The output file name '
992 'will be derived from it. Must be used with ' 958 'will be derived from it. Must be used with '
993 '--output_dir.') 959 '--output_dir.')
994 option_parser.add_option('--output_dir', 960 option_parser.add_option('--output_dir',
995 help='The output directory. Must be used with ' 961 help='The output directory. Must be used with '
996 '--input') 962 '--input')
997 option_parser.add_option('--external_param_list',
998 help='A file name containing a list with extra '
999 'fully-qualified param names. Can be used multiple '
1000 'times.',
1001 action='append')
1002 options, args = option_parser.parse_args(argv) 963 options, args = option_parser.parse_args(argv)
1003 JniParams.ReadExternalParamList(options.external_param_list)
1004 if options.jar_file: 964 if options.jar_file:
1005 input_file = ExtractJarInputFile(options.jar_file, options.input_file, 965 input_file = ExtractJarInputFile(options.jar_file, options.input_file,
1006 options.output_dir) 966 options.output_dir)
1007 else: 967 else:
1008 input_file = options.input_file 968 input_file = options.input_file
1009 output_file = None 969 output_file = None
1010 if options.output_dir: 970 if options.output_dir:
1011 root_name = os.path.splitext(os.path.basename(input_file))[0] 971 root_name = os.path.splitext(os.path.basename(input_file))[0]
1012 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' 972 output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
1013 GenerateJNIHeader(input_file, output_file, options.namespace) 973 GenerateJNIHeader(input_file, output_file, options.namespace)
1014 974
1015 975
1016 if __name__ == '__main__': 976 if __name__ == '__main__':
1017 sys.exit(main(sys.argv)) 977 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « base/android/jni_generator/class_list.jni ('k') | base/android/jni_generator/jni_generator_tests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698