blob: 9984c2a2866901f16fcfee565c18a625b94dfea2 [file] [log] [blame]
maruel@chromium.orgbe8446a2012-04-21 23:30:11 +09001#!/usr/bin/env python
bulach@chromium.org6079a072012-02-24 09:09:38 +09002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Extracts native methods from a Java file and generates the JNI bindings.
7If you change this, please run and update the tests."""
8
9import collections
torne@chromium.org108c37d2013-03-08 22:52:50 +090010import errno
bulach@chromium.org6079a072012-02-24 09:09:38 +090011import optparse
12import os
13import re
14import string
15from string import Template
16import subprocess
17import sys
18import textwrap
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +090019import zipfile
bulach@chromium.org6079a072012-02-24 09:09:38 +090020
bulach@chromium.org6079a072012-02-24 09:09:38 +090021
22class ParseError(Exception):
23 """Exception thrown when we can't parse the input file."""
24
25 def __init__(self, description, *context_lines):
26 Exception.__init__(self)
27 self.description = description
28 self.context_lines = context_lines
29
30 def __str__(self):
31 context = '\n'.join(self.context_lines)
32 return '***\nERROR: %s\n\n%s\n***' % (self.description, context)
33
34
35class Param(object):
36 """Describes a param for a method, either java or native."""
37
38 def __init__(self, **kwargs):
39 self.datatype = kwargs['datatype']
40 self.name = kwargs['name']
bulach@chromium.org6079a072012-02-24 09:09:38 +090041
42
43class NativeMethod(object):
44 """Describes a C/C++ method that is called by Java code"""
45
46 def __init__(self, **kwargs):
47 self.static = kwargs['static']
48 self.java_class_name = kwargs['java_class_name']
49 self.return_type = kwargs['return_type']
50 self.name = kwargs['name']
51 self.params = kwargs['params']
52 if self.params:
53 assert type(self.params) is list
54 assert type(self.params[0]) is Param
55 if (self.params and
bulach@chromium.orga022cf62013-11-05 09:54:22 +090056 self.params[0].datatype == kwargs.get('ptr_type', 'int') and
bulach@chromium.org6079a072012-02-24 09:09:38 +090057 self.params[0].name.startswith('native')):
58 self.type = 'method'
bulach@chromium.org1c775752012-06-22 19:03:16 +090059 self.p0_type = self.params[0].name[len('native'):]
60 if kwargs.get('native_class_name'):
61 self.p0_type = kwargs['native_class_name']
bulach@chromium.org6079a072012-02-24 09:09:38 +090062 else:
63 self.type = 'function'
64 self.method_id_var_name = kwargs.get('method_id_var_name', None)
65
66
67class CalledByNative(object):
68 """Describes a java method exported to c/c++"""
69
70 def __init__(self, **kwargs):
71 self.system_class = kwargs['system_class']
72 self.unchecked = kwargs['unchecked']
73 self.static = kwargs['static']
74 self.java_class_name = kwargs['java_class_name']
75 self.return_type = kwargs['return_type']
bulach@chromium.org6079a072012-02-24 09:09:38 +090076 self.name = kwargs['name']
77 self.params = kwargs['params']
78 self.method_id_var_name = kwargs.get('method_id_var_name', None)
bulach@chromium.org37fc9112013-10-26 01:27:03 +090079 self.signature = kwargs.get('signature')
bulach@chromium.org31af7532012-09-24 20:01:41 +090080 self.is_constructor = kwargs.get('is_constructor', False)
81 self.env_call = GetEnvCall(self.is_constructor, self.static,
82 self.return_type)
83 self.static_cast = GetStaticCastForReturnType(self.return_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +090084
85
bulach@chromium.org658dbc02014-02-27 04:59:00 +090086class ConstantField(object):
87 def __init__(self, **kwargs):
88 self.name = kwargs['name']
89 self.value = kwargs['value']
90
91
bulach@chromium.org6079a072012-02-24 09:09:38 +090092def JavaDataTypeToC(java_type):
93 """Returns a C datatype for the given java type."""
94 java_pod_type_map = {
95 'int': 'jint',
96 'byte': 'jbyte',
digit@chromium.org9d7eab02012-12-21 05:33:03 +090097 'char': 'jchar',
98 'short': 'jshort',
bulach@chromium.org6079a072012-02-24 09:09:38 +090099 'boolean': 'jboolean',
100 'long': 'jlong',
101 'double': 'jdouble',
102 'float': 'jfloat',
103 }
104 java_type_map = {
105 'void': 'void',
106 'String': 'jstring',
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900107 'java/lang/String': 'jstring',
dtrainor@chromium.orgf7e99772012-12-06 08:27:41 +0900108 'java/lang/Class': 'jclass',
bulach@chromium.org6079a072012-02-24 09:09:38 +0900109 }
dtrainor@chromium.orgf7e99772012-12-06 08:27:41 +0900110
bulach@chromium.org6079a072012-02-24 09:09:38 +0900111 if java_type in java_pod_type_map:
112 return java_pod_type_map[java_type]
113 elif java_type in java_type_map:
114 return java_type_map[java_type]
115 elif java_type.endswith('[]'):
116 if java_type[:-2] in java_pod_type_map:
117 return java_pod_type_map[java_type[:-2]] + 'Array'
118 return 'jobjectArray'
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900119 elif java_type.startswith('Class'):
120 # Checking just the start of the name, rather than a direct comparison,
121 # in order to handle generics.
122 return 'jclass'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900123 else:
124 return 'jobject'
125
126
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900127def JavaReturnValueToC(java_type):
128 """Returns a valid C return value for the given java type."""
129 java_pod_type_map = {
130 'int': '0',
131 'byte': '0',
132 'char': '0',
133 'short': '0',
134 'boolean': 'false',
135 'long': '0',
136 'double': '0',
137 'float': '0',
138 'void': ''
139 }
140 return java_pod_type_map.get(java_type, 'NULL')
141
142
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900143class JniParams(object):
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900144 _imports = []
145 _fully_qualified_class = ''
146 _package = ''
147 _inner_classes = []
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900148 _remappings = []
bulach@chromium.org6079a072012-02-24 09:09:38 +0900149
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900150 @staticmethod
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900151 def SetFullyQualifiedClass(fully_qualified_class):
152 JniParams._fully_qualified_class = 'L' + fully_qualified_class
153 JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1])
154
155 @staticmethod
156 def ExtractImportsAndInnerClasses(contents):
157 contents = contents.replace('\n', '')
158 re_import = re.compile(r'import.*?(?P<class>\S*?);')
159 for match in re.finditer(re_import, contents):
160 JniParams._imports += ['L' + match.group('class').replace('.', '/')]
161
162 re_inner = re.compile(r'(class|interface)\s+?(?P<name>\w+?)\W')
163 for match in re.finditer(re_inner, contents):
164 inner = match.group('name')
165 if not JniParams._fully_qualified_class.endswith(inner):
166 JniParams._inner_classes += [JniParams._fully_qualified_class + '$' +
167 inner]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900168
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900169 @staticmethod
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900170 def ParseJavaPSignature(signature_line):
171 prefix = 'Signature: '
172 return '"%s"' % signature_line[signature_line.index(prefix) + len(prefix):]
173
174 @staticmethod
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900175 def JavaToJni(param):
176 """Converts a java param into a JNI signature type."""
177 pod_param_map = {
178 'int': 'I',
179 'boolean': 'Z',
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900180 'char': 'C',
181 'short': 'S',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900182 'long': 'J',
183 'double': 'D',
184 'float': 'F',
185 'byte': 'B',
186 'void': 'V',
187 }
188 object_param_list = [
189 'Ljava/lang/Boolean',
190 'Ljava/lang/Integer',
191 'Ljava/lang/Long',
192 'Ljava/lang/Object',
193 'Ljava/lang/String',
dtrainor@chromium.orgf7e99772012-12-06 08:27:41 +0900194 'Ljava/lang/Class',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900195 ]
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900196 prefix = ''
197 # Array?
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900198 while param[-2:] == '[]':
199 prefix += '['
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900200 param = param[:-2]
201 # Generic?
202 if '<' in param:
203 param = param[:param.index('<')]
204 if param in pod_param_map:
205 return prefix + pod_param_map[param]
206 if '/' in param:
207 # Coming from javap, use the fully qualified param directly.
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900208 return prefix + 'L' + JniParams.RemapClassName(param) + ';'
bulach@chromium.org4a130cc2014-03-28 20:15:06 +0900209
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900210 for qualified_name in (object_param_list +
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900211 [JniParams._fully_qualified_class] +
212 JniParams._inner_classes):
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900213 if (qualified_name.endswith('/' + param) or
214 qualified_name.endswith('$' + param.replace('.', '$')) or
215 qualified_name == 'L' + param):
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900216 return prefix + JniParams.RemapClassName(qualified_name) + ';'
bulach@chromium.org7156c762012-11-13 23:35:00 +0900217
218 # Is it from an import? (e.g. referecing Class from import pkg.Class;
219 # note that referencing an inner class Inner from import pkg.Class.Inner
220 # is not supported).
221 for qualified_name in JniParams._imports:
222 if qualified_name.endswith('/' + param):
223 # Ensure it's not an inner class.
224 components = qualified_name.split('/')
225 if len(components) > 2 and components[-2][0].isupper():
226 raise SyntaxError('Inner class (%s) can not be imported '
227 'and used by JNI (%s). Please import the outer '
228 'class and use Outer.Inner instead.' %
229 (qualified_name, param))
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900230 return prefix + JniParams.RemapClassName(qualified_name) + ';'
bulach@chromium.org7156c762012-11-13 23:35:00 +0900231
232 # Is it an inner class from an outer class import? (e.g. referencing
233 # Class.Inner from import pkg.Class).
234 if '.' in param:
235 components = param.split('.')
236 outer = '/'.join(components[:-1])
237 inner = components[-1]
238 for qualified_name in JniParams._imports:
239 if qualified_name.endswith('/' + outer):
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900240 return (prefix + JniParams.RemapClassName(qualified_name) +
241 '$' + inner + ';')
bulach@chromium.org4a130cc2014-03-28 20:15:06 +0900242 raise SyntaxError('Inner class (%s) can not be '
243 'used directly by JNI. Please import the outer '
244 'class, probably:\n'
245 'import %s.%s;' %
246 (param, JniParams._package.replace('/', '.'),
247 outer.replace('/', '.')))
bulach@chromium.org7156c762012-11-13 23:35:00 +0900248
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900249 # Type not found, falling back to same package as this class.
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900250 return (prefix + 'L' +
251 JniParams.RemapClassName(JniParams._package + '/' + param) + ';')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900252
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900253 @staticmethod
254 def Signature(params, returns, wrap):
255 """Returns the JNI signature for the given datatypes."""
256 items = ['(']
257 items += [JniParams.JavaToJni(param.datatype) for param in params]
258 items += [')']
259 items += [JniParams.JavaToJni(returns)]
260 if wrap:
261 return '\n' + '\n'.join(['"' + item + '"' for item in items])
262 else:
263 return '"' + ''.join(items) + '"'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900264
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900265 @staticmethod
266 def Parse(params):
267 """Parses the params into a list of Param objects."""
268 if not params:
269 return []
270 ret = []
271 for p in [p.strip() for p in params.split(',')]:
272 items = p.split(' ')
273 if 'final' in items:
274 items.remove('final')
275 param = Param(
276 datatype=items[0],
277 name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
278 )
279 ret += [param]
280 return ret
bulach@chromium.org6079a072012-02-24 09:09:38 +0900281
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900282 @staticmethod
283 def RemapClassName(class_name):
284 """Remaps class names using the jarjar mapping table."""
285 for old, new in JniParams._remappings:
286 if old in class_name:
287 return class_name.replace(old, new, 1)
288 return class_name
289
290 @staticmethod
291 def SetJarJarMappings(mappings):
292 """Parse jarjar mappings from a string."""
293 JniParams._remappings = []
294 for line in mappings.splitlines():
295 keyword, src, dest = line.split()
296 if keyword != 'rule':
297 continue
298 assert src.endswith('.**')
299 src = src[:-2].replace('.', '/')
300 dest = dest.replace('.', '/')
301 if dest.endswith('@0'):
302 JniParams._remappings.append((src, dest[:-2] + src))
303 else:
304 assert dest.endswith('@1')
305 JniParams._remappings.append((src, dest[:-2]))
306
bulach@chromium.org6079a072012-02-24 09:09:38 +0900307
bulach@chromium.org1c775752012-06-22 19:03:16 +0900308def ExtractJNINamespace(contents):
309 re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
310 m = re.findall(re_jni_namespace, contents)
311 if not m:
312 return ''
313 return m[0]
314
315
bulach@chromium.org6079a072012-02-24 09:09:38 +0900316def ExtractFullyQualifiedJavaClassName(java_file_name, contents):
317 re_package = re.compile('.*?package (.*?);')
318 matches = re.findall(re_package, contents)
319 if not matches:
320 raise SyntaxError('Unable to find "package" line in %s' % java_file_name)
321 return (matches[0].replace('.', '/') + '/' +
322 os.path.splitext(os.path.basename(java_file_name))[0])
323
324
bulach@chromium.orga022cf62013-11-05 09:54:22 +0900325def ExtractNatives(contents, ptr_type):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900326 """Returns a list of dict containing information about a native method."""
327 contents = contents.replace('\n', '')
328 natives = []
bulach@chromium.org1c775752012-06-22 19:03:16 +0900329 re_native = re.compile(r'(@NativeClassQualifiedName'
330 '\(\"(?P<native_class_name>.*?)\"\))?\s*'
331 '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\)))?\s*'
332 '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*?native '
bulach@chromium.orga6e185e2013-03-26 16:32:39 +0900333 '(?P<return_type>\S*?) '
bulach@chromium.org92d0dac2014-01-17 01:08:05 +0900334 '(?P<name>native\w+?)\((?P<params>.*?)\);')
bulach@chromium.org1c775752012-06-22 19:03:16 +0900335 for match in re.finditer(re_native, contents):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900336 native = NativeMethod(
bulach@chromium.org1c775752012-06-22 19:03:16 +0900337 static='static' in match.group('qualifiers'),
338 java_class_name=match.group('java_class_name'),
339 native_class_name=match.group('native_class_name'),
bulach@chromium.orga6e185e2013-03-26 16:32:39 +0900340 return_type=match.group('return_type'),
bulach@chromium.org1c775752012-06-22 19:03:16 +0900341 name=match.group('name').replace('native', ''),
bulach@chromium.orga022cf62013-11-05 09:54:22 +0900342 params=JniParams.Parse(match.group('params')),
343 ptr_type=ptr_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900344 natives += [native]
345 return natives
346
347
bulach@chromium.org31af7532012-09-24 20:01:41 +0900348def GetStaticCastForReturnType(return_type):
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900349 type_map = { 'String' : 'jstring',
350 'java/lang/String' : 'jstring',
351 'boolean[]': 'jbooleanArray',
352 'byte[]': 'jbyteArray',
353 'char[]': 'jcharArray',
354 'short[]': 'jshortArray',
355 'int[]': 'jintArray',
356 'long[]': 'jlongArray',
357 'double[]': 'jdoubleArray' }
358 ret = type_map.get(return_type, None)
359 if ret:
360 return ret
361 if return_type.endswith('[]'):
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900362 return 'jobjectArray'
bulach@chromium.org31af7532012-09-24 20:01:41 +0900363 return None
364
365
366def GetEnvCall(is_constructor, is_static, return_type):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900367 """Maps the types availabe via env->Call__Method."""
bulach@chromium.org31af7532012-09-24 20:01:41 +0900368 if is_constructor:
369 return 'NewObject'
370 env_call_map = {'boolean': 'Boolean',
371 'byte': 'Byte',
372 'char': 'Char',
373 'short': 'Short',
374 'int': 'Int',
375 'long': 'Long',
376 'float': 'Float',
377 'void': 'Void',
378 'double': 'Double',
379 'Object': 'Object',
bulach@chromium.org6079a072012-02-24 09:09:38 +0900380 }
bulach@chromium.org31af7532012-09-24 20:01:41 +0900381 call = env_call_map.get(return_type, 'Object')
382 if is_static:
383 call = 'Static' + call
384 return 'Call' + call + 'Method'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900385
386
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900387def GetMangledParam(datatype):
388 """Returns a mangled identifier for the datatype."""
389 if len(datatype) <= 2:
390 return datatype.replace('[', 'A')
391 ret = ''
392 for i in range(1, len(datatype)):
393 c = datatype[i]
394 if c == '[':
395 ret += 'A'
396 elif c.isupper() or datatype[i - 1] in ['/', 'L']:
397 ret += c.upper()
398 return ret
399
400
401def GetMangledMethodName(name, params, return_type):
402 """Returns a mangled method name for the given signature.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900403
404 The returned name can be used as a C identifier and will be unique for all
405 valid overloads of the same method.
406
407 Args:
408 name: string.
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900409 params: list of Param.
410 return_type: string.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900411
412 Returns:
413 A mangled name.
414 """
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900415 mangled_items = []
416 for datatype in [return_type] + [x.datatype for x in params]:
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900417 mangled_items += [GetMangledParam(JniParams.JavaToJni(datatype))]
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900418 mangled_name = name + '_'.join(mangled_items)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900419 assert re.match(r'[0-9a-zA-Z_]+', mangled_name)
420 return mangled_name
421
422
423def MangleCalledByNatives(called_by_natives):
424 """Mangles all the overloads from the call_by_natives list."""
425 method_counts = collections.defaultdict(
426 lambda: collections.defaultdict(lambda: 0))
427 for called_by_native in called_by_natives:
428 java_class_name = called_by_native.java_class_name
429 name = called_by_native.name
430 method_counts[java_class_name][name] += 1
431 for called_by_native in called_by_natives:
432 java_class_name = called_by_native.java_class_name
433 method_name = called_by_native.name
434 method_id_var_name = method_name
435 if method_counts[java_class_name][method_name] > 1:
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900436 method_id_var_name = GetMangledMethodName(method_name,
437 called_by_native.params,
438 called_by_native.return_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900439 called_by_native.method_id_var_name = method_id_var_name
440 return called_by_natives
441
442
443# Regex to match the JNI return types that should be included in a
444# ScopedJavaLocalRef.
445RE_SCOPED_JNI_RETURN_TYPES = re.compile('jobject|jclass|jstring|.*Array')
446
447# Regex to match a string like "@CalledByNative public void foo(int bar)".
448RE_CALLED_BY_NATIVE = re.compile(
449 '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
450 '\s+(?P<prefix>[\w ]*?)'
bulach@chromium.orga6e185e2013-03-26 16:32:39 +0900451 '\s*(?P<return_type>\S+?)'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900452 '\s+(?P<name>\w+)'
453 '\s*\((?P<params>[^\)]*)\)')
454
455
456def ExtractCalledByNatives(contents):
457 """Parses all methods annotated with @CalledByNative.
458
459 Args:
460 contents: the contents of the java file.
461
462 Returns:
463 A list of dict with information about the annotated methods.
464 TODO(bulach): return a CalledByNative object.
465
466 Raises:
467 ParseError: if unable to parse.
468 """
469 called_by_natives = []
470 for match in re.finditer(RE_CALLED_BY_NATIVE, contents):
471 called_by_natives += [CalledByNative(
472 system_class=False,
473 unchecked='Unchecked' in match.group('Unchecked'),
474 static='static' in match.group('prefix'),
475 java_class_name=match.group('annotation') or '',
476 return_type=match.group('return_type'),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900477 name=match.group('name'),
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900478 params=JniParams.Parse(match.group('params')))]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900479 # Check for any @CalledByNative occurrences that weren't matched.
480 unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
481 for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]):
482 if '@CalledByNative' in line1:
483 raise ParseError('could not parse @CalledByNative method signature',
484 line1, line2)
485 return MangleCalledByNatives(called_by_natives)
486
487
488class JNIFromJavaP(object):
489 """Uses 'javap' to parse a .class file and generate the JNI header file."""
490
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900491 def __init__(self, contents, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900492 self.contents = contents
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900493 self.namespace = options.namespace
bulach@chromium.org07a9ba42014-02-28 03:23:11 +0900494 for line in contents:
495 class_name = re.match(
496 '.*?(public).*?(class|interface) (?P<class_name>\S+?)( |\Z)',
497 line)
498 if class_name:
499 self.fully_qualified_class = class_name.group('class_name')
500 break
bulach@chromium.org6079a072012-02-24 09:09:38 +0900501 self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
simonb@opera.com521714e2013-09-04 06:22:48 +0900502 # Java 7's javap includes type parameters in output, like HashSet<T>. Strip
503 # away the <...> and use the raw class name that Java 6 would've given us.
504 self.fully_qualified_class = self.fully_qualified_class.split('<', 1)[0]
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900505 JniParams.SetFullyQualifiedClass(self.fully_qualified_class)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900506 self.java_class_name = self.fully_qualified_class.split('/')[-1]
507 if not self.namespace:
508 self.namespace = 'JNI_' + self.java_class_name
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900509 re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
bulach@chromium.org31af7532012-09-24 20:01:41 +0900510 '\((?P<params>.*?)\)')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900511 self.called_by_natives = []
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900512 for lineno, content in enumerate(contents[2:], 2):
bulach@chromium.org31af7532012-09-24 20:01:41 +0900513 match = re.match(re_method, content)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900514 if not match:
515 continue
516 self.called_by_natives += [CalledByNative(
517 system_class=True,
518 unchecked=False,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900519 static='static' in match.group('prefix'),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900520 java_class_name='',
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900521 return_type=match.group('return_type').replace('.', '/'),
bulach@chromium.org31af7532012-09-24 20:01:41 +0900522 name=match.group('name'),
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900523 params=JniParams.Parse(match.group('params').replace('.', '/')),
524 signature=JniParams.ParseJavaPSignature(contents[lineno + 1]))]
525 re_constructor = re.compile('(.*?)public ' +
bulach@chromium.org31af7532012-09-24 20:01:41 +0900526 self.fully_qualified_class.replace('/', '.') +
527 '\((?P<params>.*?)\)')
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900528 for lineno, content in enumerate(contents[2:], 2):
bulach@chromium.org31af7532012-09-24 20:01:41 +0900529 match = re.match(re_constructor, content)
530 if not match:
531 continue
532 self.called_by_natives += [CalledByNative(
bulach@chromium.org208f05b2012-10-03 06:13:55 +0900533 system_class=True,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900534 unchecked=False,
535 static=False,
536 java_class_name='',
537 return_type=self.fully_qualified_class,
538 name='Constructor',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900539 params=JniParams.Parse(match.group('params').replace('.', '/')),
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900540 signature=JniParams.ParseJavaPSignature(contents[lineno + 1]),
bulach@chromium.org31af7532012-09-24 20:01:41 +0900541 is_constructor=True)]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900542 self.called_by_natives = MangleCalledByNatives(self.called_by_natives)
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900543
544 self.constant_fields = []
bulach@chromium.org07a9ba42014-02-28 03:23:11 +0900545 re_constant_field = re.compile('.*?public static final int (?P<name>.*?);')
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900546 re_constant_field_value = re.compile(
bulach@chromium.org07a9ba42014-02-28 03:23:11 +0900547 '.*?Constant(Value| value): int (?P<value>(-*[0-9]+)?)')
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900548 for lineno, content in enumerate(contents[2:], 2):
549 match = re.match(re_constant_field, content)
550 if not match:
551 continue
552 value = re.match(re_constant_field_value, contents[lineno + 2])
bulach@chromium.org07a9ba42014-02-28 03:23:11 +0900553 if not value:
554 value = re.match(re_constant_field_value, contents[lineno + 3])
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900555 if value:
556 self.constant_fields.append(
557 ConstantField(name=match.group('name'),
558 value=value.group('value')))
559
bulach@chromium.org6079a072012-02-24 09:09:38 +0900560 self.inl_header_file_generator = InlHeaderFileGenerator(
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900561 self.namespace, self.fully_qualified_class, [],
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900562 self.called_by_natives, self.constant_fields, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900563
564 def GetContent(self):
565 return self.inl_header_file_generator.GetContent()
566
567 @staticmethod
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900568 def CreateFromClass(class_file, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900569 class_name = os.path.splitext(os.path.basename(class_file))[0]
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900570 p = subprocess.Popen(args=[options.javap, '-c', '-verbose',
571 '-s', class_name],
bulach@chromium.org6079a072012-02-24 09:09:38 +0900572 cwd=os.path.dirname(class_file),
573 stdout=subprocess.PIPE,
574 stderr=subprocess.PIPE)
575 stdout, _ = p.communicate()
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900576 jni_from_javap = JNIFromJavaP(stdout.split('\n'), options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900577 return jni_from_javap
578
579
580class JNIFromJavaSource(object):
581 """Uses the given java source file to generate the JNI header file."""
582
torne@chromium.org202d2672014-04-15 02:29:59 +0900583 # Match single line comments, multiline comments, character literals, and
584 # double-quoted strings.
585 _comment_remover_regex = re.compile(
586 r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
587 re.DOTALL | re.MULTILINE)
588
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900589 def __init__(self, contents, fully_qualified_class, options):
torne@chromium.org202d2672014-04-15 02:29:59 +0900590 contents = self._RemoveComments(contents)
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900591 JniParams.SetFullyQualifiedClass(fully_qualified_class)
592 JniParams.ExtractImportsAndInnerClasses(contents)
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900593 jni_namespace = ExtractJNINamespace(contents) or options.namespace
bulach@chromium.orga022cf62013-11-05 09:54:22 +0900594 natives = ExtractNatives(contents, options.ptr_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900595 called_by_natives = ExtractCalledByNatives(contents)
bulach@chromium.orge2530d02012-07-03 01:23:35 +0900596 if len(natives) == 0 and len(called_by_natives) == 0:
597 raise SyntaxError('Unable to find any JNI methods for %s.' %
598 fully_qualified_class)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900599 inl_header_file_generator = InlHeaderFileGenerator(
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900600 jni_namespace, fully_qualified_class, natives, called_by_natives,
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900601 [], options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900602 self.content = inl_header_file_generator.GetContent()
603
torne@chromium.org202d2672014-04-15 02:29:59 +0900604 @classmethod
605 def _RemoveComments(cls, contents):
husky@chromium.org8c438d72012-07-31 02:27:48 +0900606 # We need to support both inline and block comments, and we need to handle
torne@chromium.org202d2672014-04-15 02:29:59 +0900607 # strings that contain '//' or '/*'.
608 # TODO(bulach): This is a bit hacky. It would be cleaner to use a real Java
husky@chromium.org8c438d72012-07-31 02:27:48 +0900609 # parser. Maybe we could ditch JNIFromJavaSource and just always use
610 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
611 # http://code.google.com/p/chromium/issues/detail?id=138941
torne@chromium.org202d2672014-04-15 02:29:59 +0900612 def replacer(match):
613 # Replace matches that are comments with nothing; return literals/strings
614 # unchanged.
615 s = match.group(0)
616 if s.startswith('/'):
617 return ''
618 else:
619 return s
620 return cls._comment_remover_regex.sub(replacer, contents)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900621
622 def GetContent(self):
623 return self.content
624
625 @staticmethod
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900626 def CreateFromFile(java_file_name, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900627 contents = file(java_file_name).read()
628 fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
629 contents)
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900630 return JNIFromJavaSource(contents, fully_qualified_class, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900631
632
633class InlHeaderFileGenerator(object):
634 """Generates an inline header file for JNI integration."""
635
636 def __init__(self, namespace, fully_qualified_class, natives,
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900637 called_by_natives, constant_fields, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900638 self.namespace = namespace
639 self.fully_qualified_class = fully_qualified_class
640 self.class_name = self.fully_qualified_class.split('/')[-1]
641 self.natives = natives
642 self.called_by_natives = called_by_natives
643 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900644 self.constant_fields = constant_fields
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900645 self.options = options
646 self.init_native = self.ExtractInitNative(options)
647
648 def ExtractInitNative(self, options):
649 for native in self.natives:
bulach@chromium.org92d0dac2014-01-17 01:08:05 +0900650 if options.jni_init_native_name == 'native' + native.name:
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900651 self.natives.remove(native)
652 return native
653 return None
bulach@chromium.org6079a072012-02-24 09:09:38 +0900654
655 def GetContent(self):
656 """Returns the content of the JNI binding file."""
657 template = Template("""\
658// Copyright (c) 2012 The Chromium Authors. All rights reserved.
659// Use of this source code is governed by a BSD-style license that can be
660// found in the LICENSE file.
661
662
663// This file is autogenerated by
664// ${SCRIPT_NAME}
665// For
666// ${FULLY_QUALIFIED_CLASS}
667
668#ifndef ${HEADER_GUARD}
669#define ${HEADER_GUARD}
670
671#include <jni.h>
672
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900673${INCLUDES}
bulach@chromium.org6079a072012-02-24 09:09:38 +0900674
675// Step 1: forward declarations.
676namespace {
677$CLASS_PATH_DEFINITIONS
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900678$METHOD_ID_DEFINITIONS
bulach@chromium.org6079a072012-02-24 09:09:38 +0900679} // namespace
bulach@chromium.org1c775752012-06-22 19:03:16 +0900680
681$OPEN_NAMESPACE
bulach@chromium.org6079a072012-02-24 09:09:38 +0900682$FORWARD_DECLARATIONS
683
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900684$CONSTANT_FIELDS
685
bulach@chromium.org6079a072012-02-24 09:09:38 +0900686// Step 2: method stubs.
687$METHOD_STUBS
688
bulach@chromium.org231cff62012-10-17 03:35:10 +0900689// Step 3: RegisterNatives.
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900690$JNI_NATIVE_METHODS
691$REGISTER_NATIVES
bulach@chromium.org6079a072012-02-24 09:09:38 +0900692$CLOSE_NAMESPACE
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900693$JNI_REGISTER_NATIVES
bulach@chromium.org6079a072012-02-24 09:09:38 +0900694#endif // ${HEADER_GUARD}
695""")
bulach@chromium.org6079a072012-02-24 09:09:38 +0900696 values = {
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900697 'SCRIPT_NAME': self.options.script_name,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900698 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
699 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900700 'METHOD_ID_DEFINITIONS': self.GetMethodIDDefinitionsString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900701 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900702 'CONSTANT_FIELDS': self.GetConstantFieldsString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900703 'METHOD_STUBS': self.GetMethodStubsString(),
704 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900705 'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(),
706 'REGISTER_NATIVES': self.GetRegisterNativesString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900707 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
708 'HEADER_GUARD': self.header_guard,
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900709 'INCLUDES': self.GetIncludesString(),
710 'JNI_REGISTER_NATIVES': self.GetJNIRegisterNativesString()
bulach@chromium.org6079a072012-02-24 09:09:38 +0900711 }
712 return WrapOutput(template.substitute(values))
713
714 def GetClassPathDefinitionsString(self):
715 ret = []
716 ret += [self.GetClassPathDefinitions()]
717 return '\n'.join(ret)
718
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900719 def GetMethodIDDefinitionsString(self):
720 """Returns the definition of method ids for the called by native methods."""
721 if not self.options.eager_called_by_natives:
722 return ''
723 template = Template("""\
724jmethodID g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = NULL;""")
725 ret = []
726 for called_by_native in self.called_by_natives:
727 values = {
728 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
729 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
730 }
731 ret += [template.substitute(values)]
732 return '\n'.join(ret)
733
bulach@chromium.org6079a072012-02-24 09:09:38 +0900734 def GetForwardDeclarationsString(self):
735 ret = []
736 for native in self.natives:
737 if native.type != 'method':
738 ret += [self.GetForwardDeclaration(native)]
739 return '\n'.join(ret)
740
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900741 def GetConstantFieldsString(self):
742 if not self.constant_fields:
743 return ''
744 ret = ['enum Java_%s_constant_fields {' % self.class_name]
745 for c in self.constant_fields:
746 ret += [' %s = %s,' % (c.name, c.value)]
747 ret += ['};']
748 return '\n'.join(ret)
749
bulach@chromium.org6079a072012-02-24 09:09:38 +0900750 def GetMethodStubsString(self):
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900751 """Returns the code corresponding to method stubs."""
bulach@chromium.org6079a072012-02-24 09:09:38 +0900752 ret = []
753 for native in self.natives:
754 if native.type == 'method':
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900755 ret += [self.GetNativeMethodStubString(native)]
756 if self.options.eager_called_by_natives:
757 ret += self.GetEagerCalledByNativeMethodStubs()
758 else:
759 ret += self.GetLazyCalledByNativeMethodStubs()
bulach@chromium.org6079a072012-02-24 09:09:38 +0900760 return '\n'.join(ret)
761
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900762 def GetLazyCalledByNativeMethodStubs(self):
763 return [self.GetLazyCalledByNativeMethodStub(called_by_native)
764 for called_by_native in self.called_by_natives]
765
766 def GetEagerCalledByNativeMethodStubs(self):
767 ret = []
768 if self.called_by_natives:
769 ret += ['namespace {']
770 for called_by_native in self.called_by_natives:
771 ret += [self.GetEagerCalledByNativeMethodStub(called_by_native)]
772 ret += ['} // namespace']
773 return ret
774
775 def GetIncludesString(self):
776 if not self.options.includes:
777 return ''
778 includes = self.options.includes.split(',')
779 return '\n'.join('#include "%s"' % x for x in includes)
780
bulach@chromium.org6079a072012-02-24 09:09:38 +0900781 def GetKMethodsString(self, clazz):
782 ret = []
783 for native in self.natives:
784 if (native.java_class_name == clazz or
785 (not native.java_class_name and clazz == self.class_name)):
786 ret += [self.GetKMethodArrayEntry(native)]
787 return '\n'.join(ret)
788
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900789 def SubstituteNativeMethods(self, template):
790 """Substitutes JAVA_CLASS and KMETHODS in the provided template."""
791 ret = []
bulach@chromium.org6079a072012-02-24 09:09:38 +0900792 all_classes = self.GetUniqueClasses(self.natives)
793 all_classes[self.class_name] = self.fully_qualified_class
794 for clazz in all_classes:
795 kmethods = self.GetKMethodsString(clazz)
796 if kmethods:
797 values = {'JAVA_CLASS': clazz,
798 'KMETHODS': kmethods}
799 ret += [template.substitute(values)]
800 if not ret: return ''
801 return '\n' + '\n'.join(ret)
802
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900803 def GetJNINativeMethodsString(self):
804 """Returns the implementation of the array of native methods."""
805 template = Template("""\
806static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
807${KMETHODS}
808};
809""")
810 return self.SubstituteNativeMethods(template)
811
812 def GetRegisterCalledByNativesImplString(self):
813 """Returns the code for registering the called by native methods."""
814 if not self.options.eager_called_by_natives:
815 return ''
816 template = Template("""\
817 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = ${GET_METHOD_ID_IMPL}
818 if (g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} == NULL) {
819 return false;
820 }
821 """)
822 ret = []
823 for called_by_native in self.called_by_natives:
824 values = {
825 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
826 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
827 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native),
828 }
829 ret += [template.substitute(values)]
830 return '\n'.join(ret)
831
832 def GetRegisterNativesString(self):
833 """Returns the code for RegisterNatives."""
834 template = Template("""\
835${REGISTER_NATIVES_SIGNATURE} {
836${CLASSES}
837${NATIVES}
838${CALLED_BY_NATIVES}
839 return true;
840}
841""")
842 signature = 'static bool RegisterNativesImpl(JNIEnv* env'
843 if self.init_native:
844 signature += ', jclass clazz)'
845 else:
846 signature += ')'
847
848 natives = self.GetRegisterNativesImplString()
849 called_by_natives = self.GetRegisterCalledByNativesImplString()
850 values = {'REGISTER_NATIVES_SIGNATURE': signature,
851 'CLASSES': self.GetFindClasses(),
852 'NATIVES': natives,
853 'CALLED_BY_NATIVES': called_by_natives,
854 }
855 return template.substitute(values)
856
857 def GetRegisterNativesImplString(self):
858 """Returns the shared implementation for RegisterNatives."""
859 template = Template("""\
860 const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
861
862 if (env->RegisterNatives(g_${JAVA_CLASS}_clazz,
863 kMethods${JAVA_CLASS},
864 kMethods${JAVA_CLASS}Size) < 0) {
865 jni_generator::HandleRegistrationError(
866 env, g_${JAVA_CLASS}_clazz, __FILE__);
867 return false;
868 }
869""")
870 return self.SubstituteNativeMethods(template)
871
872 def GetJNIRegisterNativesString(self):
873 """Returns the implementation for the JNI registration of native methods."""
874 if not self.init_native:
875 return ''
876
877 template = Template("""\
878extern "C" JNIEXPORT bool JNICALL
879Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) {
880 return ${NAMESPACE}RegisterNativesImpl(env, clazz);
881}
882""")
883 fully_qualified_class = self.fully_qualified_class.replace('/', '_')
884 namespace = ''
885 if self.namespace:
886 namespace = self.namespace + '::'
887 values = {'FULLY_QUALIFIED_CLASS': fully_qualified_class,
bulach@chromium.org92d0dac2014-01-17 01:08:05 +0900888 'INIT_NATIVE_NAME': 'native' + self.init_native.name,
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900889 'NAMESPACE': namespace,
890 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString()
891 }
892 return template.substitute(values)
893
bulach@chromium.org6079a072012-02-24 09:09:38 +0900894 def GetOpenNamespaceString(self):
895 if self.namespace:
bulach@chromium.org16dde202012-07-24 00:31:35 +0900896 all_namespaces = ['namespace %s {' % ns
897 for ns in self.namespace.split('::')]
898 return '\n'.join(all_namespaces)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900899 return ''
900
901 def GetCloseNamespaceString(self):
902 if self.namespace:
bulach@chromium.org16dde202012-07-24 00:31:35 +0900903 all_namespaces = ['} // namespace %s' % ns
904 for ns in self.namespace.split('::')]
905 all_namespaces.reverse()
906 return '\n'.join(all_namespaces) + '\n'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900907 return ''
908
909 def GetJNIFirstParam(self, native):
910 ret = []
911 if native.type == 'method':
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900912 ret = ['jobject jcaller']
bulach@chromium.org6079a072012-02-24 09:09:38 +0900913 elif native.type == 'function':
914 if native.static:
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900915 ret = ['jclass jcaller']
bulach@chromium.org6079a072012-02-24 09:09:38 +0900916 else:
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900917 ret = ['jobject jcaller']
bulach@chromium.org6079a072012-02-24 09:09:38 +0900918 return ret
919
920 def GetParamsInDeclaration(self, native):
921 """Returns the params for the stub declaration.
922
923 Args:
924 native: the native dictionary describing the method.
925
926 Returns:
927 A string containing the params.
928 """
929 return ',\n '.join(self.GetJNIFirstParam(native) +
930 [JavaDataTypeToC(param.datatype) + ' ' +
931 param.name
932 for param in native.params])
933
934 def GetCalledByNativeParamsInDeclaration(self, called_by_native):
935 return ',\n '.join([JavaDataTypeToC(param.datatype) + ' ' +
936 param.name
937 for param in called_by_native.params])
938
939 def GetForwardDeclaration(self, native):
940 template = Template("""
pliard@chromium.org5fa58ac2012-07-02 19:31:31 +0900941static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
bulach@chromium.org6079a072012-02-24 09:09:38 +0900942""")
bulach@chromium.org16dde202012-07-24 00:31:35 +0900943 values = {'RETURN': JavaDataTypeToC(native.return_type),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900944 'NAME': native.name,
945 'PARAMS': self.GetParamsInDeclaration(native)}
946 return template.substitute(values)
947
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900948 def GetNativeMethodStubString(self, native):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900949 """Returns stubs for native methods."""
950 template = Template("""\
951static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {
bulach@chromium.org6079a072012-02-24 09:09:38 +0900952 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900953 CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN});
954 return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL};
bulach@chromium.org6079a072012-02-24 09:09:38 +0900955}
956""")
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900957 params = []
958 if not self.options.pure_native_methods:
959 params = ['env', 'jcaller']
960 params_in_call = ', '.join(params + [p.name for p in native.params[1:]])
bulach@chromium.org6079a072012-02-24 09:09:38 +0900961
962 return_type = JavaDataTypeToC(native.return_type)
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900963 optional_error_return = JavaReturnValueToC(native.return_type)
964 if optional_error_return:
965 optional_error_return = ', ' + optional_error_return
966 post_call = ''
bulach@chromium.org6079a072012-02-24 09:09:38 +0900967 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900968 post_call = '.Release()'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900969 values = {
970 'RETURN': return_type,
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900971 'OPTIONAL_ERROR_RETURN': optional_error_return,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900972 'NAME': native.name,
973 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
974 'PARAM0_NAME': native.params[0].name,
975 'P0_TYPE': native.p0_type,
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900976 'PARAMS_IN_CALL': params_in_call,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900977 'POST_CALL': post_call
978 }
979 return template.substitute(values)
980
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900981 def GetCalledByNativeValues(self, called_by_native):
982 """Fills in necessary values for the CalledByNative methods."""
983 if called_by_native.static or called_by_native.is_constructor:
984 first_param_in_declaration = ''
985 first_param_in_call = ('g_%s_clazz' %
986 (called_by_native.java_class_name or
987 self.class_name))
988 else:
989 first_param_in_declaration = ', jobject obj'
990 first_param_in_call = 'obj'
991 params_in_declaration = self.GetCalledByNativeParamsInDeclaration(
992 called_by_native)
993 if params_in_declaration:
994 params_in_declaration = ', ' + params_in_declaration
995 params_in_call = ', '.join(param.name for param in called_by_native.params)
996 if params_in_call:
997 params_in_call = ', ' + params_in_call
998 pre_call = ''
999 post_call = ''
1000 if called_by_native.static_cast:
1001 pre_call = 'static_cast<%s>(' % called_by_native.static_cast
1002 post_call = ')'
1003 check_exception = ''
1004 if not called_by_native.unchecked:
1005 check_exception = 'jni_generator::CheckException(env);'
1006 return_type = JavaDataTypeToC(called_by_native.return_type)
1007 optional_error_return = JavaReturnValueToC(called_by_native.return_type)
1008 if optional_error_return:
1009 optional_error_return = ', ' + optional_error_return
1010 return_declaration = ''
1011 return_clause = ''
1012 if return_type != 'void':
1013 pre_call = ' ' + pre_call
1014 return_declaration = return_type + ' ret ='
1015 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
1016 return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>'
1017 return_clause = 'return ' + return_type + '(env, ret);'
1018 else:
1019 return_clause = 'return ret;'
1020 return {
1021 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
1022 'RETURN_TYPE': return_type,
1023 'OPTIONAL_ERROR_RETURN': optional_error_return,
1024 'RETURN_DECLARATION': return_declaration,
1025 'RETURN_CLAUSE': return_clause,
1026 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration,
1027 'PARAMS_IN_DECLARATION': params_in_declaration,
1028 'PRE_CALL': pre_call,
1029 'POST_CALL': post_call,
1030 'ENV_CALL': called_by_native.env_call,
1031 'FIRST_PARAM_IN_CALL': first_param_in_call,
1032 'PARAMS_IN_CALL': params_in_call,
1033 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
1034 'CHECK_EXCEPTION': check_exception,
1035 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native)
1036 }
1037
1038 def GetEagerCalledByNativeMethodStub(self, called_by_native):
1039 """Returns the implementation of the called by native method."""
1040 template = Template("""
1041static ${RETURN_TYPE} ${METHOD_ID_VAR_NAME}(\
1042JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION}) {
1043 ${RETURN_DECLARATION}${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
1044 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}${PARAMS_IN_CALL})${POST_CALL};
1045 ${RETURN_CLAUSE}
1046}""")
1047 values = self.GetCalledByNativeValues(called_by_native)
1048 return template.substitute(values)
1049
1050 def GetLazyCalledByNativeMethodStub(self, called_by_native):
bulach@chromium.org6079a072012-02-24 09:09:38 +09001051 """Returns a string."""
1052 function_signature_template = Template("""\
bulach@chromium.org0c6805b2012-09-28 21:34:33 +09001053static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\
bulach@chromium.org6079a072012-02-24 09:09:38 +09001054JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""")
1055 function_header_template = Template("""\
1056${FUNCTION_SIGNATURE} {""")
1057 function_header_with_unused_template = Template("""\
1058${FUNCTION_SIGNATURE} __attribute__ ((unused));
1059${FUNCTION_SIGNATURE} {""")
1060 template = Template("""
bulach@chromium.org231cff62012-10-17 03:35:10 +09001061static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0;
bulach@chromium.org6079a072012-02-24 09:09:38 +09001062${FUNCTION_HEADER}
1063 /* Must call RegisterNativesImpl() */
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001064 CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL},
1065 g_${JAVA_CLASS}_clazz${OPTIONAL_ERROR_RETURN});
bulach@chromium.org231cff62012-10-17 03:35:10 +09001066 jmethodID method_id =
1067 ${GET_METHOD_ID_IMPL}
bulach@chromium.org6079a072012-02-24 09:09:38 +09001068 ${RETURN_DECLARATION}
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001069 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
1070 method_id${PARAMS_IN_CALL})${POST_CALL};
bulach@chromium.org6079a072012-02-24 09:09:38 +09001071 ${CHECK_EXCEPTION}
1072 ${RETURN_CLAUSE}
1073}""")
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001074 values = self.GetCalledByNativeValues(called_by_native)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001075 values['FUNCTION_SIGNATURE'] = (
1076 function_signature_template.substitute(values))
1077 if called_by_native.system_class:
1078 values['FUNCTION_HEADER'] = (
1079 function_header_with_unused_template.substitute(values))
1080 else:
1081 values['FUNCTION_HEADER'] = function_header_template.substitute(values)
1082 return template.substitute(values)
1083
1084 def GetKMethodArrayEntry(self, native):
1085 template = Template("""\
1086 { "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""")
1087 values = {'NAME': native.name,
bulach@chromium.orgc6b75842012-11-01 07:58:15 +09001088 'JNI_SIGNATURE': JniParams.Signature(native.params,
1089 native.return_type,
1090 True)}
bulach@chromium.org6079a072012-02-24 09:09:38 +09001091 return template.substitute(values)
1092
1093 def GetUniqueClasses(self, origin):
1094 ret = {self.class_name: self.fully_qualified_class}
1095 for entry in origin:
1096 class_name = self.class_name
1097 jni_class_path = self.fully_qualified_class
1098 if entry.java_class_name:
1099 class_name = entry.java_class_name
1100 jni_class_path = self.fully_qualified_class + '$' + class_name
1101 ret[class_name] = jni_class_path
1102 return ret
1103
1104 def GetClassPathDefinitions(self):
1105 """Returns the ClassPath constants."""
1106 ret = []
1107 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +09001108const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""")
bulach@chromium.org6079a072012-02-24 09:09:38 +09001109 native_classes = self.GetUniqueClasses(self.natives)
1110 called_by_native_classes = self.GetUniqueClasses(self.called_by_natives)
1111 all_classes = native_classes
1112 all_classes.update(called_by_native_classes)
1113 for clazz in all_classes:
1114 values = {
1115 'JAVA_CLASS': clazz,
torne@chromium.org6cbf5b92013-05-29 22:51:23 +09001116 'JNI_CLASS_PATH': JniParams.RemapClassName(all_classes[clazz]),
bulach@chromium.org6079a072012-02-24 09:09:38 +09001117 }
1118 ret += [template.substitute(values)]
1119 ret += ''
1120 for clazz in called_by_native_classes:
1121 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +09001122// Leaking this jclass as we cannot use LazyInstance from some threads.
1123jclass g_${JAVA_CLASS}_clazz = NULL;""")
bulach@chromium.org6079a072012-02-24 09:09:38 +09001124 values = {
1125 'JAVA_CLASS': clazz,
1126 }
1127 ret += [template.substitute(values)]
1128 return '\n'.join(ret)
1129
1130 def GetFindClasses(self):
1131 """Returns the imlementation of FindClass for all known classes."""
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001132 if self.init_native:
1133 template = Template("""\
1134 g_${JAVA_CLASS}_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));""")
1135 else:
1136 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +09001137 g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
nileshagrawal@chromium.org118e59a2013-04-09 18:07:13 +09001138 base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""")
bulach@chromium.org6079a072012-02-24 09:09:38 +09001139 ret = []
1140 for clazz in self.GetUniqueClasses(self.called_by_natives):
1141 values = {'JAVA_CLASS': clazz}
1142 ret += [template.substitute(values)]
1143 return '\n'.join(ret)
1144
1145 def GetMethodIDImpl(self, called_by_native):
1146 """Returns the implementation of GetMethodID."""
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001147 if self.options.eager_called_by_natives:
1148 template = Template("""\
1149env->Get${STATIC_METHOD_PART}MethodID(
1150 g_${JAVA_CLASS}_clazz,
1151 "${JNI_NAME}", ${JNI_SIGNATURE});""")
1152 else:
1153 template = Template("""\
bulach@chromium.org231cff62012-10-17 03:35:10 +09001154 base::android::MethodID::LazyGet<
1155 base::android::MethodID::TYPE_${STATIC}>(
1156 env, g_${JAVA_CLASS}_clazz,
1157 "${JNI_NAME}",
1158 ${JNI_SIGNATURE},
1159 &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
bulach@chromium.org6079a072012-02-24 09:09:38 +09001160""")
bulach@chromium.org31af7532012-09-24 20:01:41 +09001161 jni_name = called_by_native.name
1162 jni_return_type = called_by_native.return_type
1163 if called_by_native.is_constructor:
1164 jni_name = '<init>'
1165 jni_return_type = 'void'
bulach@chromium.org37fc9112013-10-26 01:27:03 +09001166 if called_by_native.signature:
1167 signature = called_by_native.signature
1168 else:
1169 signature = JniParams.Signature(called_by_native.params,
1170 jni_return_type,
1171 True)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001172 values = {
1173 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
bulach@chromium.org31af7532012-09-24 20:01:41 +09001174 'JNI_NAME': jni_name,
bulach@chromium.org6079a072012-02-24 09:09:38 +09001175 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
bulach@chromium.org231cff62012-10-17 03:35:10 +09001176 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE',
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001177 'STATIC_METHOD_PART': 'Static' if called_by_native.static else '',
bulach@chromium.org37fc9112013-10-26 01:27:03 +09001178 'JNI_SIGNATURE': signature,
bulach@chromium.org6079a072012-02-24 09:09:38 +09001179 }
1180 return template.substitute(values)
1181
1182
1183def WrapOutput(output):
1184 ret = []
1185 for line in output.splitlines():
tedchoc@chromium.org72992182012-08-03 11:00:17 +09001186 # Do not wrap lines under 80 characters or preprocessor directives.
1187 if len(line) < 80 or line.lstrip()[:1] == '#':
bulach@chromium.org1c775752012-06-22 19:03:16 +09001188 stripped = line.rstrip()
1189 if len(ret) == 0 or len(ret[-1]) or len(stripped):
1190 ret.append(stripped)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001191 else:
1192 first_line_indent = ' ' * (len(line) - len(line.lstrip()))
1193 subsequent_indent = first_line_indent + ' ' * 4
1194 if line.startswith('//'):
1195 subsequent_indent = '//' + subsequent_indent
1196 wrapper = textwrap.TextWrapper(width=80,
1197 subsequent_indent=subsequent_indent,
1198 break_long_words=False)
1199 ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)]
1200 ret += ['']
1201 return '\n'.join(ret)
1202
1203
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001204def ExtractJarInputFile(jar_file, input_file, out_dir):
1205 """Extracts input file from jar and returns the filename.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001206
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001207 The input file is extracted to the same directory that the generated jni
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001208 headers will be placed in. This is passed as an argument to script.
1209
1210 Args:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001211 jar_file: the jar file containing the input files to extract.
1212 input_files: the list of files to extract from the jar file.
1213 out_dir: the name of the directories to extract to.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001214
1215 Returns:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001216 the name of extracted input file.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001217 """
1218 jar_file = zipfile.ZipFile(jar_file)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001219
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001220 out_dir = os.path.join(out_dir, os.path.dirname(input_file))
torne@chromium.org108c37d2013-03-08 22:52:50 +09001221 try:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001222 os.makedirs(out_dir)
torne@chromium.org108c37d2013-03-08 22:52:50 +09001223 except OSError as e:
1224 if e.errno != errno.EEXIST:
1225 raise
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001226 extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
1227 with open(extracted_file_name, 'w') as outfile:
1228 outfile.write(jar_file.read(input_file))
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001229
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001230 return extracted_file_name
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001231
1232
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001233def GenerateJNIHeader(input_file, output_file, options):
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001234 try:
1235 if os.path.splitext(input_file)[1] == '.class':
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001236 jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options)
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001237 content = jni_from_javap.GetContent()
bulach@chromium.org6079a072012-02-24 09:09:38 +09001238 else:
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001239 jni_from_java_source = JNIFromJavaSource.CreateFromFile(
1240 input_file, options)
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001241 content = jni_from_java_source.GetContent()
1242 except ParseError, e:
1243 print e
1244 sys.exit(1)
1245 if output_file:
1246 if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
1247 os.makedirs(os.path.dirname(os.path.abspath(output_file)))
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001248 if options.optimize_generation and os.path.exists(output_file):
tedchoc@chromium.org91b355f2013-02-22 09:37:33 +09001249 with file(output_file, 'r') as f:
1250 existing_content = f.read()
1251 if existing_content == content:
1252 return
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001253 with file(output_file, 'w') as f:
1254 f.write(content)
1255 else:
1256 print output
bulach@chromium.org6079a072012-02-24 09:09:38 +09001257
1258
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001259def GetScriptName():
1260 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
1261 base_index = 0
1262 for idx, value in enumerate(script_components):
1263 if value == 'base' or value == 'third_party':
1264 base_index = idx
1265 break
1266 return os.sep.join(script_components[base_index:])
1267
1268
bulach@chromium.org6079a072012-02-24 09:09:38 +09001269def main(argv):
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001270 usage = """usage: %prog [OPTIONS]
bulach@chromium.org6079a072012-02-24 09:09:38 +09001271This script will parse the given java source code extracting the native
1272declarations and print the header file to stdout (or a file).
1273See SampleForTests.java for more details.
1274 """
1275 option_parser = optparse.OptionParser(usage=usage)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001276 option_parser.add_option('-j', dest='jar_file',
1277 help='Extract the list of input files from'
bulach@chromium.org7f85d462012-05-30 18:56:02 +09001278 ' a specified jar file.'
1279 ' Uses javap to extract the methods from a'
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001280 ' pre-compiled class. --input should point'
bulach@chromium.org6079a072012-02-24 09:09:38 +09001281 ' to pre-compiled Java .class files.')
1282 option_parser.add_option('-n', dest='namespace',
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001283 help='Uses as a namespace in the generated header '
1284 'instead of the javap class name, or when there is '
1285 'no JNINamespace annotation in the java source.')
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001286 option_parser.add_option('--input_file',
1287 help='Single input file name. The output file name '
1288 'will be derived from it. Must be used with '
1289 '--output_dir.')
1290 option_parser.add_option('--output_dir',
1291 help='The output directory. Must be used with '
1292 '--input')
tedchoc@chromium.org91b355f2013-02-22 09:37:33 +09001293 option_parser.add_option('--optimize_generation', type="int",
1294 default=0, help='Whether we should optimize JNI '
1295 'generation by not regenerating files if they have '
1296 'not changed.')
torne@chromium.org6cbf5b92013-05-29 22:51:23 +09001297 option_parser.add_option('--jarjar',
1298 help='Path to optional jarjar rules file.')
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001299 option_parser.add_option('--script_name', default=GetScriptName(),
1300 help='The name of this script in the generated '
1301 'header.')
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001302 option_parser.add_option('--includes',
1303 help='The comma-separated list of header files to '
1304 'include in the generated header.')
1305 option_parser.add_option('--pure_native_methods',
1306 action='store_true', dest='pure_native_methods',
1307 help='When true, the native methods will be called '
1308 'without any JNI-specific arguments.')
bulach@chromium.orga022cf62013-11-05 09:54:22 +09001309 option_parser.add_option('--ptr_type', default='int',
1310 type='choice', choices=['int', 'long'],
1311 help='The type used to represent native pointers in '
1312 'Java code. For 32-bit, use int; '
1313 'for 64-bit, use long.')
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001314 option_parser.add_option('--jni_init_native_name', default='',
1315 help='The name of the JNI registration method that '
1316 'is used to initialize all native methods. If a '
1317 'method with this name is not present in the Java '
1318 'source file, setting this option is a no-op. When '
1319 'a method with this name is found however, the '
1320 'naming convention Java_<packageName>_<className> '
1321 'will limit the initialization to only the '
1322 'top-level class.')
1323 option_parser.add_option('--eager_called_by_natives',
1324 action='store_true', dest='eager_called_by_natives',
1325 help='When true, the called-by-native methods will '
1326 'be initialized in a non-atomic way.')
bulach@chromium.org92d0dac2014-01-17 01:08:05 +09001327 option_parser.add_option('--cpp', default='cpp',
1328 help='The path to cpp command.')
1329 option_parser.add_option('--javap', default='javap',
1330 help='The path to javap command.')
bulach@chromium.org6079a072012-02-24 09:09:38 +09001331 options, args = option_parser.parse_args(argv)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001332 if options.jar_file:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001333 input_file = ExtractJarInputFile(options.jar_file, options.input_file,
1334 options.output_dir)
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001335 elif options.input_file:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001336 input_file = options.input_file
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001337 else:
1338 option_parser.print_help()
1339 print '\nError: Must specify --jar_file or --input_file.'
1340 return 1
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001341 output_file = None
1342 if options.output_dir:
1343 root_name = os.path.splitext(os.path.basename(input_file))[0]
1344 output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
torne@chromium.org6cbf5b92013-05-29 22:51:23 +09001345 if options.jarjar:
1346 with open(options.jarjar) as f:
1347 JniParams.SetJarJarMappings(f.read())
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001348 GenerateJNIHeader(input_file, output_file, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001349
1350
1351if __name__ == '__main__':
1352 sys.exit(main(sys.argv))