blob: 6b4a5f679672e439b0d0578e19942f415baa7e8c [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.org0bafda72012-11-08 04:06:51 +0900209 for qualified_name in (object_param_list +
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900210 [JniParams._fully_qualified_class] +
211 JniParams._inner_classes):
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900212 if (qualified_name.endswith('/' + param) or
213 qualified_name.endswith('$' + param.replace('.', '$')) or
214 qualified_name == 'L' + param):
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900215 return prefix + JniParams.RemapClassName(qualified_name) + ';'
bulach@chromium.org7156c762012-11-13 23:35:00 +0900216
217 # Is it from an import? (e.g. referecing Class from import pkg.Class;
218 # note that referencing an inner class Inner from import pkg.Class.Inner
219 # is not supported).
220 for qualified_name in JniParams._imports:
221 if qualified_name.endswith('/' + param):
222 # Ensure it's not an inner class.
223 components = qualified_name.split('/')
224 if len(components) > 2 and components[-2][0].isupper():
225 raise SyntaxError('Inner class (%s) can not be imported '
226 'and used by JNI (%s). Please import the outer '
227 'class and use Outer.Inner instead.' %
228 (qualified_name, param))
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900229 return prefix + JniParams.RemapClassName(qualified_name) + ';'
bulach@chromium.org7156c762012-11-13 23:35:00 +0900230
231 # Is it an inner class from an outer class import? (e.g. referencing
232 # Class.Inner from import pkg.Class).
233 if '.' in param:
234 components = param.split('.')
235 outer = '/'.join(components[:-1])
236 inner = components[-1]
237 for qualified_name in JniParams._imports:
238 if qualified_name.endswith('/' + outer):
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900239 return (prefix + JniParams.RemapClassName(qualified_name) +
240 '$' + inner + ';')
bulach@chromium.org7156c762012-11-13 23:35:00 +0900241
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900242 # Type not found, falling back to same package as this class.
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900243 return (prefix + 'L' +
244 JniParams.RemapClassName(JniParams._package + '/' + param) + ';')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900245
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900246 @staticmethod
247 def Signature(params, returns, wrap):
248 """Returns the JNI signature for the given datatypes."""
249 items = ['(']
250 items += [JniParams.JavaToJni(param.datatype) for param in params]
251 items += [')']
252 items += [JniParams.JavaToJni(returns)]
253 if wrap:
254 return '\n' + '\n'.join(['"' + item + '"' for item in items])
255 else:
256 return '"' + ''.join(items) + '"'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900257
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900258 @staticmethod
259 def Parse(params):
260 """Parses the params into a list of Param objects."""
261 if not params:
262 return []
263 ret = []
264 for p in [p.strip() for p in params.split(',')]:
265 items = p.split(' ')
266 if 'final' in items:
267 items.remove('final')
268 param = Param(
269 datatype=items[0],
270 name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
271 )
272 ret += [param]
273 return ret
bulach@chromium.org6079a072012-02-24 09:09:38 +0900274
torne@chromium.org6cbf5b92013-05-29 22:51:23 +0900275 @staticmethod
276 def RemapClassName(class_name):
277 """Remaps class names using the jarjar mapping table."""
278 for old, new in JniParams._remappings:
279 if old in class_name:
280 return class_name.replace(old, new, 1)
281 return class_name
282
283 @staticmethod
284 def SetJarJarMappings(mappings):
285 """Parse jarjar mappings from a string."""
286 JniParams._remappings = []
287 for line in mappings.splitlines():
288 keyword, src, dest = line.split()
289 if keyword != 'rule':
290 continue
291 assert src.endswith('.**')
292 src = src[:-2].replace('.', '/')
293 dest = dest.replace('.', '/')
294 if dest.endswith('@0'):
295 JniParams._remappings.append((src, dest[:-2] + src))
296 else:
297 assert dest.endswith('@1')
298 JniParams._remappings.append((src, dest[:-2]))
299
bulach@chromium.org6079a072012-02-24 09:09:38 +0900300
bulach@chromium.org1c775752012-06-22 19:03:16 +0900301def ExtractJNINamespace(contents):
302 re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
303 m = re.findall(re_jni_namespace, contents)
304 if not m:
305 return ''
306 return m[0]
307
308
bulach@chromium.org6079a072012-02-24 09:09:38 +0900309def ExtractFullyQualifiedJavaClassName(java_file_name, contents):
310 re_package = re.compile('.*?package (.*?);')
311 matches = re.findall(re_package, contents)
312 if not matches:
313 raise SyntaxError('Unable to find "package" line in %s' % java_file_name)
314 return (matches[0].replace('.', '/') + '/' +
315 os.path.splitext(os.path.basename(java_file_name))[0])
316
317
bulach@chromium.orga022cf62013-11-05 09:54:22 +0900318def ExtractNatives(contents, ptr_type):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900319 """Returns a list of dict containing information about a native method."""
320 contents = contents.replace('\n', '')
321 natives = []
bulach@chromium.org1c775752012-06-22 19:03:16 +0900322 re_native = re.compile(r'(@NativeClassQualifiedName'
323 '\(\"(?P<native_class_name>.*?)\"\))?\s*'
324 '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\)))?\s*'
325 '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*?native '
bulach@chromium.orga6e185e2013-03-26 16:32:39 +0900326 '(?P<return_type>\S*?) '
bulach@chromium.org92d0dac2014-01-17 01:08:05 +0900327 '(?P<name>native\w+?)\((?P<params>.*?)\);')
bulach@chromium.org1c775752012-06-22 19:03:16 +0900328 for match in re.finditer(re_native, contents):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900329 native = NativeMethod(
bulach@chromium.org1c775752012-06-22 19:03:16 +0900330 static='static' in match.group('qualifiers'),
331 java_class_name=match.group('java_class_name'),
332 native_class_name=match.group('native_class_name'),
bulach@chromium.orga6e185e2013-03-26 16:32:39 +0900333 return_type=match.group('return_type'),
bulach@chromium.org1c775752012-06-22 19:03:16 +0900334 name=match.group('name').replace('native', ''),
bulach@chromium.orga022cf62013-11-05 09:54:22 +0900335 params=JniParams.Parse(match.group('params')),
336 ptr_type=ptr_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900337 natives += [native]
338 return natives
339
340
bulach@chromium.org31af7532012-09-24 20:01:41 +0900341def GetStaticCastForReturnType(return_type):
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900342 type_map = { 'String' : 'jstring',
343 'java/lang/String' : 'jstring',
344 'boolean[]': 'jbooleanArray',
345 'byte[]': 'jbyteArray',
346 'char[]': 'jcharArray',
347 'short[]': 'jshortArray',
348 'int[]': 'jintArray',
349 'long[]': 'jlongArray',
350 'double[]': 'jdoubleArray' }
351 ret = type_map.get(return_type, None)
352 if ret:
353 return ret
354 if return_type.endswith('[]'):
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900355 return 'jobjectArray'
bulach@chromium.org31af7532012-09-24 20:01:41 +0900356 return None
357
358
359def GetEnvCall(is_constructor, is_static, return_type):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900360 """Maps the types availabe via env->Call__Method."""
bulach@chromium.org31af7532012-09-24 20:01:41 +0900361 if is_constructor:
362 return 'NewObject'
363 env_call_map = {'boolean': 'Boolean',
364 'byte': 'Byte',
365 'char': 'Char',
366 'short': 'Short',
367 'int': 'Int',
368 'long': 'Long',
369 'float': 'Float',
370 'void': 'Void',
371 'double': 'Double',
372 'Object': 'Object',
bulach@chromium.org6079a072012-02-24 09:09:38 +0900373 }
bulach@chromium.org31af7532012-09-24 20:01:41 +0900374 call = env_call_map.get(return_type, 'Object')
375 if is_static:
376 call = 'Static' + call
377 return 'Call' + call + 'Method'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900378
379
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900380def GetMangledParam(datatype):
381 """Returns a mangled identifier for the datatype."""
382 if len(datatype) <= 2:
383 return datatype.replace('[', 'A')
384 ret = ''
385 for i in range(1, len(datatype)):
386 c = datatype[i]
387 if c == '[':
388 ret += 'A'
389 elif c.isupper() or datatype[i - 1] in ['/', 'L']:
390 ret += c.upper()
391 return ret
392
393
394def GetMangledMethodName(name, params, return_type):
395 """Returns a mangled method name for the given signature.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900396
397 The returned name can be used as a C identifier and will be unique for all
398 valid overloads of the same method.
399
400 Args:
401 name: string.
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900402 params: list of Param.
403 return_type: string.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900404
405 Returns:
406 A mangled name.
407 """
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900408 mangled_items = []
409 for datatype in [return_type] + [x.datatype for x in params]:
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900410 mangled_items += [GetMangledParam(JniParams.JavaToJni(datatype))]
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900411 mangled_name = name + '_'.join(mangled_items)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900412 assert re.match(r'[0-9a-zA-Z_]+', mangled_name)
413 return mangled_name
414
415
416def MangleCalledByNatives(called_by_natives):
417 """Mangles all the overloads from the call_by_natives list."""
418 method_counts = collections.defaultdict(
419 lambda: collections.defaultdict(lambda: 0))
420 for called_by_native in called_by_natives:
421 java_class_name = called_by_native.java_class_name
422 name = called_by_native.name
423 method_counts[java_class_name][name] += 1
424 for called_by_native in called_by_natives:
425 java_class_name = called_by_native.java_class_name
426 method_name = called_by_native.name
427 method_id_var_name = method_name
428 if method_counts[java_class_name][method_name] > 1:
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900429 method_id_var_name = GetMangledMethodName(method_name,
430 called_by_native.params,
431 called_by_native.return_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900432 called_by_native.method_id_var_name = method_id_var_name
433 return called_by_natives
434
435
436# Regex to match the JNI return types that should be included in a
437# ScopedJavaLocalRef.
438RE_SCOPED_JNI_RETURN_TYPES = re.compile('jobject|jclass|jstring|.*Array')
439
440# Regex to match a string like "@CalledByNative public void foo(int bar)".
441RE_CALLED_BY_NATIVE = re.compile(
442 '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
443 '\s+(?P<prefix>[\w ]*?)'
bulach@chromium.orga6e185e2013-03-26 16:32:39 +0900444 '\s*(?P<return_type>\S+?)'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900445 '\s+(?P<name>\w+)'
446 '\s*\((?P<params>[^\)]*)\)')
447
448
449def ExtractCalledByNatives(contents):
450 """Parses all methods annotated with @CalledByNative.
451
452 Args:
453 contents: the contents of the java file.
454
455 Returns:
456 A list of dict with information about the annotated methods.
457 TODO(bulach): return a CalledByNative object.
458
459 Raises:
460 ParseError: if unable to parse.
461 """
462 called_by_natives = []
463 for match in re.finditer(RE_CALLED_BY_NATIVE, contents):
464 called_by_natives += [CalledByNative(
465 system_class=False,
466 unchecked='Unchecked' in match.group('Unchecked'),
467 static='static' in match.group('prefix'),
468 java_class_name=match.group('annotation') or '',
469 return_type=match.group('return_type'),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900470 name=match.group('name'),
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900471 params=JniParams.Parse(match.group('params')))]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900472 # Check for any @CalledByNative occurrences that weren't matched.
473 unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
474 for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]):
475 if '@CalledByNative' in line1:
476 raise ParseError('could not parse @CalledByNative method signature',
477 line1, line2)
478 return MangleCalledByNatives(called_by_natives)
479
480
481class JNIFromJavaP(object):
482 """Uses 'javap' to parse a .class file and generate the JNI header file."""
483
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900484 def __init__(self, contents, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900485 self.contents = contents
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900486 self.namespace = options.namespace
yfriedman@chromium.org287bc392013-02-27 05:50:50 +0900487 self.fully_qualified_class = re.match(
488 '.*?(class|interface) (?P<class_name>.*?)( |{)',
489 contents[1]).group('class_name')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900490 self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
simonb@opera.com521714e2013-09-04 06:22:48 +0900491 # Java 7's javap includes type parameters in output, like HashSet<T>. Strip
492 # away the <...> and use the raw class name that Java 6 would've given us.
493 self.fully_qualified_class = self.fully_qualified_class.split('<', 1)[0]
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900494 JniParams.SetFullyQualifiedClass(self.fully_qualified_class)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900495 self.java_class_name = self.fully_qualified_class.split('/')[-1]
496 if not self.namespace:
497 self.namespace = 'JNI_' + self.java_class_name
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900498 re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
bulach@chromium.org31af7532012-09-24 20:01:41 +0900499 '\((?P<params>.*?)\)')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900500 self.called_by_natives = []
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900501 for lineno, content in enumerate(contents[2:], 2):
bulach@chromium.org31af7532012-09-24 20:01:41 +0900502 match = re.match(re_method, content)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900503 if not match:
504 continue
505 self.called_by_natives += [CalledByNative(
506 system_class=True,
507 unchecked=False,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900508 static='static' in match.group('prefix'),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900509 java_class_name='',
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900510 return_type=match.group('return_type').replace('.', '/'),
bulach@chromium.org31af7532012-09-24 20:01:41 +0900511 name=match.group('name'),
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900512 params=JniParams.Parse(match.group('params').replace('.', '/')),
513 signature=JniParams.ParseJavaPSignature(contents[lineno + 1]))]
514 re_constructor = re.compile('(.*?)public ' +
bulach@chromium.org31af7532012-09-24 20:01:41 +0900515 self.fully_qualified_class.replace('/', '.') +
516 '\((?P<params>.*?)\)')
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900517 for lineno, content in enumerate(contents[2:], 2):
bulach@chromium.org31af7532012-09-24 20:01:41 +0900518 match = re.match(re_constructor, content)
519 if not match:
520 continue
521 self.called_by_natives += [CalledByNative(
bulach@chromium.org208f05b2012-10-03 06:13:55 +0900522 system_class=True,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900523 unchecked=False,
524 static=False,
525 java_class_name='',
526 return_type=self.fully_qualified_class,
527 name='Constructor',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900528 params=JniParams.Parse(match.group('params').replace('.', '/')),
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900529 signature=JniParams.ParseJavaPSignature(contents[lineno + 1]),
bulach@chromium.org31af7532012-09-24 20:01:41 +0900530 is_constructor=True)]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900531 self.called_by_natives = MangleCalledByNatives(self.called_by_natives)
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900532
533 self.constant_fields = []
534 re_constant_field = re.compile('public static final int (?P<name>.*?);')
535 re_constant_field_value = re.compile(
536 '.*?Constant value: int (?P<value>(-*[0-9]+)?)')
537 for lineno, content in enumerate(contents[2:], 2):
538 match = re.match(re_constant_field, content)
539 if not match:
540 continue
541 value = re.match(re_constant_field_value, contents[lineno + 2])
542 if value:
543 self.constant_fields.append(
544 ConstantField(name=match.group('name'),
545 value=value.group('value')))
546
bulach@chromium.org6079a072012-02-24 09:09:38 +0900547 self.inl_header_file_generator = InlHeaderFileGenerator(
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900548 self.namespace, self.fully_qualified_class, [],
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900549 self.called_by_natives, self.constant_fields, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900550
551 def GetContent(self):
552 return self.inl_header_file_generator.GetContent()
553
554 @staticmethod
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900555 def CreateFromClass(class_file, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900556 class_name = os.path.splitext(os.path.basename(class_file))[0]
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900557 p = subprocess.Popen(args=[options.javap, '-c', '-verbose',
558 '-s', class_name],
bulach@chromium.org6079a072012-02-24 09:09:38 +0900559 cwd=os.path.dirname(class_file),
560 stdout=subprocess.PIPE,
561 stderr=subprocess.PIPE)
562 stdout, _ = p.communicate()
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900563 jni_from_javap = JNIFromJavaP(stdout.split('\n'), options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900564 return jni_from_javap
565
566
567class JNIFromJavaSource(object):
568 """Uses the given java source file to generate the JNI header file."""
569
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900570 def __init__(self, contents, fully_qualified_class, options):
bulach@chromium.org92d0dac2014-01-17 01:08:05 +0900571 contents = self._RemoveComments(contents, options)
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900572 JniParams.SetFullyQualifiedClass(fully_qualified_class)
573 JniParams.ExtractImportsAndInnerClasses(contents)
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900574 jni_namespace = ExtractJNINamespace(contents) or options.namespace
bulach@chromium.orga022cf62013-11-05 09:54:22 +0900575 natives = ExtractNatives(contents, options.ptr_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900576 called_by_natives = ExtractCalledByNatives(contents)
bulach@chromium.orge2530d02012-07-03 01:23:35 +0900577 if len(natives) == 0 and len(called_by_natives) == 0:
578 raise SyntaxError('Unable to find any JNI methods for %s.' %
579 fully_qualified_class)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900580 inl_header_file_generator = InlHeaderFileGenerator(
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900581 jni_namespace, fully_qualified_class, natives, called_by_natives,
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900582 [], options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900583 self.content = inl_header_file_generator.GetContent()
584
bulach@chromium.org92d0dac2014-01-17 01:08:05 +0900585 def _RemoveComments(self, contents, options):
husky@chromium.org8c438d72012-07-31 02:27:48 +0900586 # We need to support both inline and block comments, and we need to handle
587 # strings that contain '//' or '/*'. Rather than trying to do all that with
588 # regexps, we just pipe the contents through the C preprocessor. We tell cpp
589 # the file has already been preprocessed, so it just removes comments and
590 # doesn't try to parse #include, #pragma etc.
591 #
592 # TODO(husky): This is a bit hacky. It would be cleaner to use a real Java
593 # parser. Maybe we could ditch JNIFromJavaSource and just always use
594 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
595 # http://code.google.com/p/chromium/issues/detail?id=138941
bulach@chromium.org92d0dac2014-01-17 01:08:05 +0900596 p = subprocess.Popen(args=[options.cpp, '-fpreprocessed'],
husky@chromium.org8c438d72012-07-31 02:27:48 +0900597 stdin=subprocess.PIPE,
598 stdout=subprocess.PIPE,
599 stderr=subprocess.PIPE)
600 stdout, _ = p.communicate(contents)
601 return stdout
bulach@chromium.org6079a072012-02-24 09:09:38 +0900602
603 def GetContent(self):
604 return self.content
605
606 @staticmethod
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900607 def CreateFromFile(java_file_name, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900608 contents = file(java_file_name).read()
609 fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
610 contents)
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900611 return JNIFromJavaSource(contents, fully_qualified_class, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900612
613
614class InlHeaderFileGenerator(object):
615 """Generates an inline header file for JNI integration."""
616
617 def __init__(self, namespace, fully_qualified_class, natives,
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900618 called_by_natives, constant_fields, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900619 self.namespace = namespace
620 self.fully_qualified_class = fully_qualified_class
621 self.class_name = self.fully_qualified_class.split('/')[-1]
622 self.natives = natives
623 self.called_by_natives = called_by_natives
624 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900625 self.constant_fields = constant_fields
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900626 self.options = options
627 self.init_native = self.ExtractInitNative(options)
628
629 def ExtractInitNative(self, options):
630 for native in self.natives:
bulach@chromium.org92d0dac2014-01-17 01:08:05 +0900631 if options.jni_init_native_name == 'native' + native.name:
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900632 self.natives.remove(native)
633 return native
634 return None
bulach@chromium.org6079a072012-02-24 09:09:38 +0900635
636 def GetContent(self):
637 """Returns the content of the JNI binding file."""
638 template = Template("""\
639// Copyright (c) 2012 The Chromium Authors. All rights reserved.
640// Use of this source code is governed by a BSD-style license that can be
641// found in the LICENSE file.
642
643
644// This file is autogenerated by
645// ${SCRIPT_NAME}
646// For
647// ${FULLY_QUALIFIED_CLASS}
648
649#ifndef ${HEADER_GUARD}
650#define ${HEADER_GUARD}
651
652#include <jni.h>
653
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900654${INCLUDES}
bulach@chromium.org6079a072012-02-24 09:09:38 +0900655
656// Step 1: forward declarations.
657namespace {
658$CLASS_PATH_DEFINITIONS
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900659$METHOD_ID_DEFINITIONS
bulach@chromium.org6079a072012-02-24 09:09:38 +0900660} // namespace
bulach@chromium.org1c775752012-06-22 19:03:16 +0900661
662$OPEN_NAMESPACE
bulach@chromium.org6079a072012-02-24 09:09:38 +0900663$FORWARD_DECLARATIONS
664
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900665$CONSTANT_FIELDS
666
bulach@chromium.org6079a072012-02-24 09:09:38 +0900667// Step 2: method stubs.
668$METHOD_STUBS
669
bulach@chromium.org231cff62012-10-17 03:35:10 +0900670// Step 3: RegisterNatives.
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900671$JNI_NATIVE_METHODS
672$REGISTER_NATIVES
bulach@chromium.org6079a072012-02-24 09:09:38 +0900673$CLOSE_NAMESPACE
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900674$JNI_REGISTER_NATIVES
bulach@chromium.org6079a072012-02-24 09:09:38 +0900675#endif // ${HEADER_GUARD}
676""")
bulach@chromium.org6079a072012-02-24 09:09:38 +0900677 values = {
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900678 'SCRIPT_NAME': self.options.script_name,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900679 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
680 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900681 'METHOD_ID_DEFINITIONS': self.GetMethodIDDefinitionsString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900682 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900683 'CONSTANT_FIELDS': self.GetConstantFieldsString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900684 'METHOD_STUBS': self.GetMethodStubsString(),
685 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900686 'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(),
687 'REGISTER_NATIVES': self.GetRegisterNativesString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900688 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
689 'HEADER_GUARD': self.header_guard,
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900690 'INCLUDES': self.GetIncludesString(),
691 'JNI_REGISTER_NATIVES': self.GetJNIRegisterNativesString()
bulach@chromium.org6079a072012-02-24 09:09:38 +0900692 }
693 return WrapOutput(template.substitute(values))
694
695 def GetClassPathDefinitionsString(self):
696 ret = []
697 ret += [self.GetClassPathDefinitions()]
698 return '\n'.join(ret)
699
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900700 def GetMethodIDDefinitionsString(self):
701 """Returns the definition of method ids for the called by native methods."""
702 if not self.options.eager_called_by_natives:
703 return ''
704 template = Template("""\
705jmethodID g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = NULL;""")
706 ret = []
707 for called_by_native in self.called_by_natives:
708 values = {
709 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
710 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
711 }
712 ret += [template.substitute(values)]
713 return '\n'.join(ret)
714
bulach@chromium.org6079a072012-02-24 09:09:38 +0900715 def GetForwardDeclarationsString(self):
716 ret = []
717 for native in self.natives:
718 if native.type != 'method':
719 ret += [self.GetForwardDeclaration(native)]
720 return '\n'.join(ret)
721
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900722 def GetConstantFieldsString(self):
723 if not self.constant_fields:
724 return ''
725 ret = ['enum Java_%s_constant_fields {' % self.class_name]
726 for c in self.constant_fields:
727 ret += [' %s = %s,' % (c.name, c.value)]
728 ret += ['};']
729 return '\n'.join(ret)
730
bulach@chromium.org6079a072012-02-24 09:09:38 +0900731 def GetMethodStubsString(self):
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900732 """Returns the code corresponding to method stubs."""
bulach@chromium.org6079a072012-02-24 09:09:38 +0900733 ret = []
734 for native in self.natives:
735 if native.type == 'method':
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900736 ret += [self.GetNativeMethodStubString(native)]
737 if self.options.eager_called_by_natives:
738 ret += self.GetEagerCalledByNativeMethodStubs()
739 else:
740 ret += self.GetLazyCalledByNativeMethodStubs()
bulach@chromium.org6079a072012-02-24 09:09:38 +0900741 return '\n'.join(ret)
742
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900743 def GetLazyCalledByNativeMethodStubs(self):
744 return [self.GetLazyCalledByNativeMethodStub(called_by_native)
745 for called_by_native in self.called_by_natives]
746
747 def GetEagerCalledByNativeMethodStubs(self):
748 ret = []
749 if self.called_by_natives:
750 ret += ['namespace {']
751 for called_by_native in self.called_by_natives:
752 ret += [self.GetEagerCalledByNativeMethodStub(called_by_native)]
753 ret += ['} // namespace']
754 return ret
755
756 def GetIncludesString(self):
757 if not self.options.includes:
758 return ''
759 includes = self.options.includes.split(',')
760 return '\n'.join('#include "%s"' % x for x in includes)
761
bulach@chromium.org6079a072012-02-24 09:09:38 +0900762 def GetKMethodsString(self, clazz):
763 ret = []
764 for native in self.natives:
765 if (native.java_class_name == clazz or
766 (not native.java_class_name and clazz == self.class_name)):
767 ret += [self.GetKMethodArrayEntry(native)]
768 return '\n'.join(ret)
769
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900770 def SubstituteNativeMethods(self, template):
771 """Substitutes JAVA_CLASS and KMETHODS in the provided template."""
772 ret = []
bulach@chromium.org6079a072012-02-24 09:09:38 +0900773 all_classes = self.GetUniqueClasses(self.natives)
774 all_classes[self.class_name] = self.fully_qualified_class
775 for clazz in all_classes:
776 kmethods = self.GetKMethodsString(clazz)
777 if kmethods:
778 values = {'JAVA_CLASS': clazz,
779 'KMETHODS': kmethods}
780 ret += [template.substitute(values)]
781 if not ret: return ''
782 return '\n' + '\n'.join(ret)
783
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900784 def GetJNINativeMethodsString(self):
785 """Returns the implementation of the array of native methods."""
786 template = Template("""\
787static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
788${KMETHODS}
789};
790""")
791 return self.SubstituteNativeMethods(template)
792
793 def GetRegisterCalledByNativesImplString(self):
794 """Returns the code for registering the called by native methods."""
795 if not self.options.eager_called_by_natives:
796 return ''
797 template = Template("""\
798 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = ${GET_METHOD_ID_IMPL}
799 if (g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} == NULL) {
800 return false;
801 }
802 """)
803 ret = []
804 for called_by_native in self.called_by_natives:
805 values = {
806 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
807 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
808 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native),
809 }
810 ret += [template.substitute(values)]
811 return '\n'.join(ret)
812
813 def GetRegisterNativesString(self):
814 """Returns the code for RegisterNatives."""
815 template = Template("""\
816${REGISTER_NATIVES_SIGNATURE} {
817${CLASSES}
818${NATIVES}
819${CALLED_BY_NATIVES}
820 return true;
821}
822""")
823 signature = 'static bool RegisterNativesImpl(JNIEnv* env'
824 if self.init_native:
825 signature += ', jclass clazz)'
826 else:
827 signature += ')'
828
829 natives = self.GetRegisterNativesImplString()
830 called_by_natives = self.GetRegisterCalledByNativesImplString()
831 values = {'REGISTER_NATIVES_SIGNATURE': signature,
832 'CLASSES': self.GetFindClasses(),
833 'NATIVES': natives,
834 'CALLED_BY_NATIVES': called_by_natives,
835 }
836 return template.substitute(values)
837
838 def GetRegisterNativesImplString(self):
839 """Returns the shared implementation for RegisterNatives."""
840 template = Template("""\
841 const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
842
843 if (env->RegisterNatives(g_${JAVA_CLASS}_clazz,
844 kMethods${JAVA_CLASS},
845 kMethods${JAVA_CLASS}Size) < 0) {
846 jni_generator::HandleRegistrationError(
847 env, g_${JAVA_CLASS}_clazz, __FILE__);
848 return false;
849 }
850""")
851 return self.SubstituteNativeMethods(template)
852
853 def GetJNIRegisterNativesString(self):
854 """Returns the implementation for the JNI registration of native methods."""
855 if not self.init_native:
856 return ''
857
858 template = Template("""\
859extern "C" JNIEXPORT bool JNICALL
860Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) {
861 return ${NAMESPACE}RegisterNativesImpl(env, clazz);
862}
863""")
864 fully_qualified_class = self.fully_qualified_class.replace('/', '_')
865 namespace = ''
866 if self.namespace:
867 namespace = self.namespace + '::'
868 values = {'FULLY_QUALIFIED_CLASS': fully_qualified_class,
bulach@chromium.org92d0dac2014-01-17 01:08:05 +0900869 'INIT_NATIVE_NAME': 'native' + self.init_native.name,
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900870 'NAMESPACE': namespace,
871 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString()
872 }
873 return template.substitute(values)
874
bulach@chromium.org6079a072012-02-24 09:09:38 +0900875 def GetOpenNamespaceString(self):
876 if self.namespace:
bulach@chromium.org16dde202012-07-24 00:31:35 +0900877 all_namespaces = ['namespace %s {' % ns
878 for ns in self.namespace.split('::')]
879 return '\n'.join(all_namespaces)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900880 return ''
881
882 def GetCloseNamespaceString(self):
883 if self.namespace:
bulach@chromium.org16dde202012-07-24 00:31:35 +0900884 all_namespaces = ['} // namespace %s' % ns
885 for ns in self.namespace.split('::')]
886 all_namespaces.reverse()
887 return '\n'.join(all_namespaces) + '\n'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900888 return ''
889
890 def GetJNIFirstParam(self, native):
891 ret = []
892 if native.type == 'method':
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900893 ret = ['jobject jcaller']
bulach@chromium.org6079a072012-02-24 09:09:38 +0900894 elif native.type == 'function':
895 if native.static:
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900896 ret = ['jclass jcaller']
bulach@chromium.org6079a072012-02-24 09:09:38 +0900897 else:
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900898 ret = ['jobject jcaller']
bulach@chromium.org6079a072012-02-24 09:09:38 +0900899 return ret
900
901 def GetParamsInDeclaration(self, native):
902 """Returns the params for the stub declaration.
903
904 Args:
905 native: the native dictionary describing the method.
906
907 Returns:
908 A string containing the params.
909 """
910 return ',\n '.join(self.GetJNIFirstParam(native) +
911 [JavaDataTypeToC(param.datatype) + ' ' +
912 param.name
913 for param in native.params])
914
915 def GetCalledByNativeParamsInDeclaration(self, called_by_native):
916 return ',\n '.join([JavaDataTypeToC(param.datatype) + ' ' +
917 param.name
918 for param in called_by_native.params])
919
920 def GetForwardDeclaration(self, native):
921 template = Template("""
pliard@chromium.org5fa58ac2012-07-02 19:31:31 +0900922static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
bulach@chromium.org6079a072012-02-24 09:09:38 +0900923""")
bulach@chromium.org16dde202012-07-24 00:31:35 +0900924 values = {'RETURN': JavaDataTypeToC(native.return_type),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900925 'NAME': native.name,
926 'PARAMS': self.GetParamsInDeclaration(native)}
927 return template.substitute(values)
928
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900929 def GetNativeMethodStubString(self, native):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900930 """Returns stubs for native methods."""
931 template = Template("""\
932static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {
bulach@chromium.org6079a072012-02-24 09:09:38 +0900933 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900934 CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN});
935 return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL};
bulach@chromium.org6079a072012-02-24 09:09:38 +0900936}
937""")
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900938 params = []
939 if not self.options.pure_native_methods:
940 params = ['env', 'jcaller']
941 params_in_call = ', '.join(params + [p.name for p in native.params[1:]])
bulach@chromium.org6079a072012-02-24 09:09:38 +0900942
943 return_type = JavaDataTypeToC(native.return_type)
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900944 optional_error_return = JavaReturnValueToC(native.return_type)
945 if optional_error_return:
946 optional_error_return = ', ' + optional_error_return
947 post_call = ''
bulach@chromium.org6079a072012-02-24 09:09:38 +0900948 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900949 post_call = '.Release()'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900950 values = {
951 'RETURN': return_type,
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900952 'OPTIONAL_ERROR_RETURN': optional_error_return,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900953 'NAME': native.name,
954 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
955 'PARAM0_NAME': native.params[0].name,
956 'P0_TYPE': native.p0_type,
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900957 'PARAMS_IN_CALL': params_in_call,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900958 'POST_CALL': post_call
959 }
960 return template.substitute(values)
961
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900962 def GetCalledByNativeValues(self, called_by_native):
963 """Fills in necessary values for the CalledByNative methods."""
964 if called_by_native.static or called_by_native.is_constructor:
965 first_param_in_declaration = ''
966 first_param_in_call = ('g_%s_clazz' %
967 (called_by_native.java_class_name or
968 self.class_name))
969 else:
970 first_param_in_declaration = ', jobject obj'
971 first_param_in_call = 'obj'
972 params_in_declaration = self.GetCalledByNativeParamsInDeclaration(
973 called_by_native)
974 if params_in_declaration:
975 params_in_declaration = ', ' + params_in_declaration
976 params_in_call = ', '.join(param.name for param in called_by_native.params)
977 if params_in_call:
978 params_in_call = ', ' + params_in_call
979 pre_call = ''
980 post_call = ''
981 if called_by_native.static_cast:
982 pre_call = 'static_cast<%s>(' % called_by_native.static_cast
983 post_call = ')'
984 check_exception = ''
985 if not called_by_native.unchecked:
986 check_exception = 'jni_generator::CheckException(env);'
987 return_type = JavaDataTypeToC(called_by_native.return_type)
988 optional_error_return = JavaReturnValueToC(called_by_native.return_type)
989 if optional_error_return:
990 optional_error_return = ', ' + optional_error_return
991 return_declaration = ''
992 return_clause = ''
993 if return_type != 'void':
994 pre_call = ' ' + pre_call
995 return_declaration = return_type + ' ret ='
996 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
997 return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>'
998 return_clause = 'return ' + return_type + '(env, ret);'
999 else:
1000 return_clause = 'return ret;'
1001 return {
1002 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
1003 'RETURN_TYPE': return_type,
1004 'OPTIONAL_ERROR_RETURN': optional_error_return,
1005 'RETURN_DECLARATION': return_declaration,
1006 'RETURN_CLAUSE': return_clause,
1007 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration,
1008 'PARAMS_IN_DECLARATION': params_in_declaration,
1009 'PRE_CALL': pre_call,
1010 'POST_CALL': post_call,
1011 'ENV_CALL': called_by_native.env_call,
1012 'FIRST_PARAM_IN_CALL': first_param_in_call,
1013 'PARAMS_IN_CALL': params_in_call,
1014 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
1015 'CHECK_EXCEPTION': check_exception,
1016 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native)
1017 }
1018
1019 def GetEagerCalledByNativeMethodStub(self, called_by_native):
1020 """Returns the implementation of the called by native method."""
1021 template = Template("""
1022static ${RETURN_TYPE} ${METHOD_ID_VAR_NAME}(\
1023JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION}) {
1024 ${RETURN_DECLARATION}${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
1025 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}${PARAMS_IN_CALL})${POST_CALL};
1026 ${RETURN_CLAUSE}
1027}""")
1028 values = self.GetCalledByNativeValues(called_by_native)
1029 return template.substitute(values)
1030
1031 def GetLazyCalledByNativeMethodStub(self, called_by_native):
bulach@chromium.org6079a072012-02-24 09:09:38 +09001032 """Returns a string."""
1033 function_signature_template = Template("""\
bulach@chromium.org0c6805b2012-09-28 21:34:33 +09001034static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\
bulach@chromium.org6079a072012-02-24 09:09:38 +09001035JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""")
1036 function_header_template = Template("""\
1037${FUNCTION_SIGNATURE} {""")
1038 function_header_with_unused_template = Template("""\
1039${FUNCTION_SIGNATURE} __attribute__ ((unused));
1040${FUNCTION_SIGNATURE} {""")
1041 template = Template("""
bulach@chromium.org231cff62012-10-17 03:35:10 +09001042static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0;
bulach@chromium.org6079a072012-02-24 09:09:38 +09001043${FUNCTION_HEADER}
1044 /* Must call RegisterNativesImpl() */
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001045 CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL},
1046 g_${JAVA_CLASS}_clazz${OPTIONAL_ERROR_RETURN});
bulach@chromium.org231cff62012-10-17 03:35:10 +09001047 jmethodID method_id =
1048 ${GET_METHOD_ID_IMPL}
bulach@chromium.org6079a072012-02-24 09:09:38 +09001049 ${RETURN_DECLARATION}
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001050 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
1051 method_id${PARAMS_IN_CALL})${POST_CALL};
bulach@chromium.org6079a072012-02-24 09:09:38 +09001052 ${CHECK_EXCEPTION}
1053 ${RETURN_CLAUSE}
1054}""")
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001055 values = self.GetCalledByNativeValues(called_by_native)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001056 values['FUNCTION_SIGNATURE'] = (
1057 function_signature_template.substitute(values))
1058 if called_by_native.system_class:
1059 values['FUNCTION_HEADER'] = (
1060 function_header_with_unused_template.substitute(values))
1061 else:
1062 values['FUNCTION_HEADER'] = function_header_template.substitute(values)
1063 return template.substitute(values)
1064
1065 def GetKMethodArrayEntry(self, native):
1066 template = Template("""\
1067 { "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""")
1068 values = {'NAME': native.name,
bulach@chromium.orgc6b75842012-11-01 07:58:15 +09001069 'JNI_SIGNATURE': JniParams.Signature(native.params,
1070 native.return_type,
1071 True)}
bulach@chromium.org6079a072012-02-24 09:09:38 +09001072 return template.substitute(values)
1073
1074 def GetUniqueClasses(self, origin):
1075 ret = {self.class_name: self.fully_qualified_class}
1076 for entry in origin:
1077 class_name = self.class_name
1078 jni_class_path = self.fully_qualified_class
1079 if entry.java_class_name:
1080 class_name = entry.java_class_name
1081 jni_class_path = self.fully_qualified_class + '$' + class_name
1082 ret[class_name] = jni_class_path
1083 return ret
1084
1085 def GetClassPathDefinitions(self):
1086 """Returns the ClassPath constants."""
1087 ret = []
1088 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +09001089const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""")
bulach@chromium.org6079a072012-02-24 09:09:38 +09001090 native_classes = self.GetUniqueClasses(self.natives)
1091 called_by_native_classes = self.GetUniqueClasses(self.called_by_natives)
1092 all_classes = native_classes
1093 all_classes.update(called_by_native_classes)
1094 for clazz in all_classes:
1095 values = {
1096 'JAVA_CLASS': clazz,
torne@chromium.org6cbf5b92013-05-29 22:51:23 +09001097 'JNI_CLASS_PATH': JniParams.RemapClassName(all_classes[clazz]),
bulach@chromium.org6079a072012-02-24 09:09:38 +09001098 }
1099 ret += [template.substitute(values)]
1100 ret += ''
1101 for clazz in called_by_native_classes:
1102 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +09001103// Leaking this jclass as we cannot use LazyInstance from some threads.
1104jclass g_${JAVA_CLASS}_clazz = NULL;""")
bulach@chromium.org6079a072012-02-24 09:09:38 +09001105 values = {
1106 'JAVA_CLASS': clazz,
1107 }
1108 ret += [template.substitute(values)]
1109 return '\n'.join(ret)
1110
1111 def GetFindClasses(self):
1112 """Returns the imlementation of FindClass for all known classes."""
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001113 if self.init_native:
1114 template = Template("""\
1115 g_${JAVA_CLASS}_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));""")
1116 else:
1117 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +09001118 g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
nileshagrawal@chromium.org118e59a2013-04-09 18:07:13 +09001119 base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""")
bulach@chromium.org6079a072012-02-24 09:09:38 +09001120 ret = []
1121 for clazz in self.GetUniqueClasses(self.called_by_natives):
1122 values = {'JAVA_CLASS': clazz}
1123 ret += [template.substitute(values)]
1124 return '\n'.join(ret)
1125
1126 def GetMethodIDImpl(self, called_by_native):
1127 """Returns the implementation of GetMethodID."""
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001128 if self.options.eager_called_by_natives:
1129 template = Template("""\
1130env->Get${STATIC_METHOD_PART}MethodID(
1131 g_${JAVA_CLASS}_clazz,
1132 "${JNI_NAME}", ${JNI_SIGNATURE});""")
1133 else:
1134 template = Template("""\
bulach@chromium.org231cff62012-10-17 03:35:10 +09001135 base::android::MethodID::LazyGet<
1136 base::android::MethodID::TYPE_${STATIC}>(
1137 env, g_${JAVA_CLASS}_clazz,
1138 "${JNI_NAME}",
1139 ${JNI_SIGNATURE},
1140 &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
bulach@chromium.org6079a072012-02-24 09:09:38 +09001141""")
bulach@chromium.org31af7532012-09-24 20:01:41 +09001142 jni_name = called_by_native.name
1143 jni_return_type = called_by_native.return_type
1144 if called_by_native.is_constructor:
1145 jni_name = '<init>'
1146 jni_return_type = 'void'
bulach@chromium.org37fc9112013-10-26 01:27:03 +09001147 if called_by_native.signature:
1148 signature = called_by_native.signature
1149 else:
1150 signature = JniParams.Signature(called_by_native.params,
1151 jni_return_type,
1152 True)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001153 values = {
1154 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
bulach@chromium.org31af7532012-09-24 20:01:41 +09001155 'JNI_NAME': jni_name,
bulach@chromium.org6079a072012-02-24 09:09:38 +09001156 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
bulach@chromium.org231cff62012-10-17 03:35:10 +09001157 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE',
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001158 'STATIC_METHOD_PART': 'Static' if called_by_native.static else '',
bulach@chromium.org37fc9112013-10-26 01:27:03 +09001159 'JNI_SIGNATURE': signature,
bulach@chromium.org6079a072012-02-24 09:09:38 +09001160 }
1161 return template.substitute(values)
1162
1163
1164def WrapOutput(output):
1165 ret = []
1166 for line in output.splitlines():
tedchoc@chromium.org72992182012-08-03 11:00:17 +09001167 # Do not wrap lines under 80 characters or preprocessor directives.
1168 if len(line) < 80 or line.lstrip()[:1] == '#':
bulach@chromium.org1c775752012-06-22 19:03:16 +09001169 stripped = line.rstrip()
1170 if len(ret) == 0 or len(ret[-1]) or len(stripped):
1171 ret.append(stripped)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001172 else:
1173 first_line_indent = ' ' * (len(line) - len(line.lstrip()))
1174 subsequent_indent = first_line_indent + ' ' * 4
1175 if line.startswith('//'):
1176 subsequent_indent = '//' + subsequent_indent
1177 wrapper = textwrap.TextWrapper(width=80,
1178 subsequent_indent=subsequent_indent,
1179 break_long_words=False)
1180 ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)]
1181 ret += ['']
1182 return '\n'.join(ret)
1183
1184
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001185def ExtractJarInputFile(jar_file, input_file, out_dir):
1186 """Extracts input file from jar and returns the filename.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001187
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001188 The input file is extracted to the same directory that the generated jni
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001189 headers will be placed in. This is passed as an argument to script.
1190
1191 Args:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001192 jar_file: the jar file containing the input files to extract.
1193 input_files: the list of files to extract from the jar file.
1194 out_dir: the name of the directories to extract to.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001195
1196 Returns:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001197 the name of extracted input file.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001198 """
1199 jar_file = zipfile.ZipFile(jar_file)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001200
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001201 out_dir = os.path.join(out_dir, os.path.dirname(input_file))
torne@chromium.org108c37d2013-03-08 22:52:50 +09001202 try:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001203 os.makedirs(out_dir)
torne@chromium.org108c37d2013-03-08 22:52:50 +09001204 except OSError as e:
1205 if e.errno != errno.EEXIST:
1206 raise
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001207 extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
1208 with open(extracted_file_name, 'w') as outfile:
1209 outfile.write(jar_file.read(input_file))
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001210
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001211 return extracted_file_name
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001212
1213
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001214def GenerateJNIHeader(input_file, output_file, options):
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001215 try:
1216 if os.path.splitext(input_file)[1] == '.class':
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001217 jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options)
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001218 content = jni_from_javap.GetContent()
bulach@chromium.org6079a072012-02-24 09:09:38 +09001219 else:
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001220 jni_from_java_source = JNIFromJavaSource.CreateFromFile(
1221 input_file, options)
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001222 content = jni_from_java_source.GetContent()
1223 except ParseError, e:
1224 print e
1225 sys.exit(1)
1226 if output_file:
1227 if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
1228 os.makedirs(os.path.dirname(os.path.abspath(output_file)))
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001229 if options.optimize_generation and os.path.exists(output_file):
tedchoc@chromium.org91b355f2013-02-22 09:37:33 +09001230 with file(output_file, 'r') as f:
1231 existing_content = f.read()
1232 if existing_content == content:
1233 return
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001234 with file(output_file, 'w') as f:
1235 f.write(content)
1236 else:
1237 print output
bulach@chromium.org6079a072012-02-24 09:09:38 +09001238
1239
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001240def GetScriptName():
1241 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
1242 base_index = 0
1243 for idx, value in enumerate(script_components):
1244 if value == 'base' or value == 'third_party':
1245 base_index = idx
1246 break
1247 return os.sep.join(script_components[base_index:])
1248
1249
bulach@chromium.org6079a072012-02-24 09:09:38 +09001250def main(argv):
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001251 usage = """usage: %prog [OPTIONS]
bulach@chromium.org6079a072012-02-24 09:09:38 +09001252This script will parse the given java source code extracting the native
1253declarations and print the header file to stdout (or a file).
1254See SampleForTests.java for more details.
1255 """
1256 option_parser = optparse.OptionParser(usage=usage)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001257 option_parser.add_option('-j', dest='jar_file',
1258 help='Extract the list of input files from'
bulach@chromium.org7f85d462012-05-30 18:56:02 +09001259 ' a specified jar file.'
1260 ' Uses javap to extract the methods from a'
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001261 ' pre-compiled class. --input should point'
bulach@chromium.org6079a072012-02-24 09:09:38 +09001262 ' to pre-compiled Java .class files.')
1263 option_parser.add_option('-n', dest='namespace',
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001264 help='Uses as a namespace in the generated header '
1265 'instead of the javap class name, or when there is '
1266 'no JNINamespace annotation in the java source.')
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001267 option_parser.add_option('--input_file',
1268 help='Single input file name. The output file name '
1269 'will be derived from it. Must be used with '
1270 '--output_dir.')
1271 option_parser.add_option('--output_dir',
1272 help='The output directory. Must be used with '
1273 '--input')
tedchoc@chromium.org91b355f2013-02-22 09:37:33 +09001274 option_parser.add_option('--optimize_generation', type="int",
1275 default=0, help='Whether we should optimize JNI '
1276 'generation by not regenerating files if they have '
1277 'not changed.')
torne@chromium.org6cbf5b92013-05-29 22:51:23 +09001278 option_parser.add_option('--jarjar',
1279 help='Path to optional jarjar rules file.')
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001280 option_parser.add_option('--script_name', default=GetScriptName(),
1281 help='The name of this script in the generated '
1282 'header.')
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001283 option_parser.add_option('--includes',
1284 help='The comma-separated list of header files to '
1285 'include in the generated header.')
1286 option_parser.add_option('--pure_native_methods',
1287 action='store_true', dest='pure_native_methods',
1288 help='When true, the native methods will be called '
1289 'without any JNI-specific arguments.')
bulach@chromium.orga022cf62013-11-05 09:54:22 +09001290 option_parser.add_option('--ptr_type', default='int',
1291 type='choice', choices=['int', 'long'],
1292 help='The type used to represent native pointers in '
1293 'Java code. For 32-bit, use int; '
1294 'for 64-bit, use long.')
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001295 option_parser.add_option('--jni_init_native_name', default='',
1296 help='The name of the JNI registration method that '
1297 'is used to initialize all native methods. If a '
1298 'method with this name is not present in the Java '
1299 'source file, setting this option is a no-op. When '
1300 'a method with this name is found however, the '
1301 'naming convention Java_<packageName>_<className> '
1302 'will limit the initialization to only the '
1303 'top-level class.')
1304 option_parser.add_option('--eager_called_by_natives',
1305 action='store_true', dest='eager_called_by_natives',
1306 help='When true, the called-by-native methods will '
1307 'be initialized in a non-atomic way.')
bulach@chromium.org92d0dac2014-01-17 01:08:05 +09001308 option_parser.add_option('--cpp', default='cpp',
1309 help='The path to cpp command.')
1310 option_parser.add_option('--javap', default='javap',
1311 help='The path to javap command.')
bulach@chromium.org6079a072012-02-24 09:09:38 +09001312 options, args = option_parser.parse_args(argv)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001313 if options.jar_file:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001314 input_file = ExtractJarInputFile(options.jar_file, options.input_file,
1315 options.output_dir)
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001316 elif options.input_file:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001317 input_file = options.input_file
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001318 else:
1319 option_parser.print_help()
1320 print '\nError: Must specify --jar_file or --input_file.'
1321 return 1
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001322 output_file = None
1323 if options.output_dir:
1324 root_name = os.path.splitext(os.path.basename(input_file))[0]
1325 output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
torne@chromium.org6cbf5b92013-05-29 22:51:23 +09001326 if options.jarjar:
1327 with open(options.jarjar) as f:
1328 JniParams.SetJarJarMappings(f.read())
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001329 GenerateJNIHeader(input_file, output_file, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001330
1331
1332if __name__ == '__main__':
1333 sys.exit(main(sys.argv))