2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Extracts native methods from a Java file and generates the JNI bindings.
7 If you change this, please run and update the tests."""
15 from string
import Template
21 CHROMIUM_SRC
= os
.path
.join(
22 os
.path
.dirname(__file__
), os
.pardir
, os
.pardir
, os
.pardir
)
23 BUILD_ANDROID_GYP
= os
.path
.join(
24 CHROMIUM_SRC
, 'build', 'android', 'gyp')
26 sys
.path
.append(BUILD_ANDROID_GYP
)
28 from util
import build_utils
31 class ParseError(Exception):
32 """Exception thrown when we can't parse the input file."""
34 def __init__(self
, description
, *context_lines
):
35 Exception.__init
__(self
)
36 self
.description
= description
37 self
.context_lines
= context_lines
40 context
= '\n'.join(self
.context_lines
)
41 return '***\nERROR: %s\n\n%s\n***' % (self
.description
, context
)
45 """Describes a param for a method, either java or native."""
47 def __init__(self
, **kwargs
):
48 self
.datatype
= kwargs
['datatype']
49 self
.name
= kwargs
['name']
52 class NativeMethod(object):
53 """Describes a C/C++ method that is called by Java code"""
55 def __init__(self
, **kwargs
):
56 self
.static
= kwargs
['static']
57 self
.java_class_name
= kwargs
['java_class_name']
58 self
.return_type
= kwargs
['return_type']
59 self
.name
= kwargs
['name']
60 self
.params
= kwargs
['params']
62 assert type(self
.params
) is list
63 assert type(self
.params
[0]) is Param
65 self
.params
[0].datatype
== kwargs
.get('ptr_type', 'int') and
66 self
.params
[0].name
.startswith('native')):
68 self
.p0_type
= self
.params
[0].name
[len('native'):]
69 if kwargs
.get('native_class_name'):
70 self
.p0_type
= kwargs
['native_class_name']
72 self
.type = 'function'
73 self
.method_id_var_name
= kwargs
.get('method_id_var_name', None)
76 class CalledByNative(object):
77 """Describes a java method exported to c/c++"""
79 def __init__(self
, **kwargs
):
80 self
.system_class
= kwargs
['system_class']
81 self
.unchecked
= kwargs
['unchecked']
82 self
.static
= kwargs
['static']
83 self
.java_class_name
= kwargs
['java_class_name']
84 self
.return_type
= kwargs
['return_type']
85 self
.name
= kwargs
['name']
86 self
.params
= kwargs
['params']
87 self
.method_id_var_name
= kwargs
.get('method_id_var_name', None)
88 self
.signature
= kwargs
.get('signature')
89 self
.is_constructor
= kwargs
.get('is_constructor', False)
90 self
.env_call
= GetEnvCall(self
.is_constructor
, self
.static
,
92 self
.static_cast
= GetStaticCastForReturnType(self
.return_type
)
95 class ConstantField(object):
96 def __init__(self
, **kwargs
):
97 self
.name
= kwargs
['name']
98 self
.value
= kwargs
['value']
101 def JavaDataTypeToC(java_type
):
102 """Returns a C datatype for the given java type."""
103 java_pod_type_map
= {
108 'boolean': 'jboolean',
116 'java/lang/String': 'jstring',
117 'java/lang/Class': 'jclass',
120 if java_type
in java_pod_type_map
:
121 return java_pod_type_map
[java_type
]
122 elif java_type
in java_type_map
:
123 return java_type_map
[java_type
]
124 elif java_type
.endswith('[]'):
125 if java_type
[:-2] in java_pod_type_map
:
126 return java_pod_type_map
[java_type
[:-2]] + 'Array'
127 return 'jobjectArray'
128 elif java_type
.startswith('Class'):
129 # Checking just the start of the name, rather than a direct comparison,
130 # in order to handle generics.
136 def JavaDataTypeToCForCalledByNativeParam(java_type
):
137 """Returns a C datatype to be when calling from native."""
138 if java_type
== 'int':
139 return 'JniIntWrapper'
141 return JavaDataTypeToC(java_type
)
144 def JavaReturnValueToC(java_type
):
145 """Returns a valid C return value for the given java type."""
146 java_pod_type_map
= {
157 return java_pod_type_map
.get(java_type
, 'NULL')
160 class JniParams(object):
162 _fully_qualified_class
= ''
166 _implicit_imports
= []
169 def SetFullyQualifiedClass(fully_qualified_class
):
170 JniParams
._fully
_qualified
_class
= 'L' + fully_qualified_class
171 JniParams
._package
= '/'.join(fully_qualified_class
.split('/')[:-1])
174 def AddAdditionalImport(class_name
):
175 assert class_name
.endswith('.class')
176 raw_class_name
= class_name
[:-len('.class')]
177 if '.' in raw_class_name
:
178 raise SyntaxError('%s cannot be used in @JNIAdditionalImport. '
179 'Only import unqualified outer classes.' % class_name
)
180 new_import
= 'L%s/%s' % (JniParams
._package
, raw_class_name
)
181 if new_import
in JniParams
._imports
:
182 raise SyntaxError('Do not use JNIAdditionalImport on an already '
183 'imported class: %s' % (new_import
.replace('/', '.')))
184 JniParams
._imports
+= [new_import
]
187 def ExtractImportsAndInnerClasses(contents
):
188 if not JniParams
._package
:
189 raise RuntimeError('SetFullyQualifiedClass must be called before '
190 'ExtractImportsAndInnerClasses')
191 contents
= contents
.replace('\n', '')
192 re_import
= re
.compile(r
'import.*?(?P<class>\S*?);')
193 for match
in re
.finditer(re_import
, contents
):
194 JniParams
._imports
+= ['L' + match
.group('class').replace('.', '/')]
196 re_inner
= re
.compile(r
'(class|interface)\s+?(?P<name>\w+?)\W')
197 for match
in re
.finditer(re_inner
, contents
):
198 inner
= match
.group('name')
199 if not JniParams
._fully
_qualified
_class
.endswith(inner
):
200 JniParams
._inner
_classes
+= [JniParams
._fully
_qualified
_class
+ '$' +
203 re_additional_imports
= re
.compile(
204 r
'@JNIAdditionalImport\(\s*{?(?P<class_names>.*?)}?\s*\)')
205 for match
in re
.finditer(re_additional_imports
, contents
):
206 for class_name
in match
.group('class_names').split(','):
207 JniParams
.AddAdditionalImport(class_name
.strip())
210 def ParseJavaPSignature(signature_line
):
211 prefix
= 'Signature: '
212 return '"%s"' % signature_line
[signature_line
.index(prefix
) + len(prefix
):]
215 def JavaToJni(param
):
216 """Converts a java param into a JNI signature type."""
228 object_param_list
= [
229 'Ljava/lang/Boolean',
230 'Ljava/lang/Integer',
239 while param
[-2:] == '[]':
244 param
= param
[:param
.index('<')]
245 if param
in pod_param_map
:
246 return prefix
+ pod_param_map
[param
]
248 # Coming from javap, use the fully qualified param directly.
249 return prefix
+ 'L' + JniParams
.RemapClassName(param
) + ';'
251 for qualified_name
in (object_param_list
+
252 [JniParams
._fully
_qualified
_class
] +
253 JniParams
._inner
_classes
):
254 if (qualified_name
.endswith('/' + param
) or
255 qualified_name
.endswith('$' + param
.replace('.', '$')) or
256 qualified_name
== 'L' + param
):
257 return prefix
+ JniParams
.RemapClassName(qualified_name
) + ';'
259 # Is it from an import? (e.g. referecing Class from import pkg.Class;
260 # note that referencing an inner class Inner from import pkg.Class.Inner
262 for qualified_name
in JniParams
._imports
:
263 if qualified_name
.endswith('/' + param
):
264 # Ensure it's not an inner class.
265 components
= qualified_name
.split('/')
266 if len(components
) > 2 and components
[-2][0].isupper():
267 raise SyntaxError('Inner class (%s) can not be imported '
268 'and used by JNI (%s). Please import the outer '
269 'class and use Outer.Inner instead.' %
270 (qualified_name
, param
))
271 return prefix
+ JniParams
.RemapClassName(qualified_name
) + ';'
273 # Is it an inner class from an outer class import? (e.g. referencing
274 # Class.Inner from import pkg.Class).
276 components
= param
.split('.')
277 outer
= '/'.join(components
[:-1])
278 inner
= components
[-1]
279 for qualified_name
in JniParams
._imports
:
280 if qualified_name
.endswith('/' + outer
):
281 return (prefix
+ JniParams
.RemapClassName(qualified_name
) +
283 raise SyntaxError('Inner class (%s) can not be '
284 'used directly by JNI. Please import the outer '
287 (param
, JniParams
._package
.replace('/', '.'),
288 outer
.replace('/', '.')))
290 JniParams
._CheckImplicitImports
(param
)
292 # Type not found, falling back to same package as this class.
293 return (prefix
+ 'L' +
294 JniParams
.RemapClassName(JniParams
._package
+ '/' + param
) + ';')
297 def _CheckImplicitImports(param
):
298 # Ensure implicit imports, such as java.lang.*, are not being treated
299 # as being in the same package.
300 if not JniParams
._implicit
_imports
:
301 # This file was generated from android.jar and lists
302 # all classes that are implicitly imported.
303 with
file(os
.path
.join(os
.path
.dirname(sys
.argv
[0]),
304 'android_jar.classes'), 'r') as f
:
305 JniParams
._implicit
_imports
= f
.readlines()
306 for implicit_import
in JniParams
._implicit
_imports
:
307 implicit_import
= implicit_import
.strip().replace('.class', '')
308 implicit_import
= implicit_import
.replace('/', '.')
309 if implicit_import
.endswith('.' + param
):
310 raise SyntaxError('Ambiguous class (%s) can not be used directly '
311 'by JNI.\nPlease import it, probably:\n\n'
313 (param
, implicit_import
))
317 def Signature(params
, returns
, wrap
):
318 """Returns the JNI signature for the given datatypes."""
320 items
+= [JniParams
.JavaToJni(param
.datatype
) for param
in params
]
322 items
+= [JniParams
.JavaToJni(returns
)]
324 return '\n' + '\n'.join(['"' + item
+ '"' for item
in items
])
326 return '"' + ''.join(items
) + '"'
330 """Parses the params into a list of Param objects."""
334 for p
in [p
.strip() for p
in params
.split(',')]:
337 items
.remove('final')
340 name
=(items
[1] if len(items
) > 1 else 'p%s' % len(ret
)),
346 def RemapClassName(class_name
):
347 """Remaps class names using the jarjar mapping table."""
348 for old
, new
in JniParams
._remappings
:
349 if old
.endswith('**') and old
[:-2] in class_name
:
350 return class_name
.replace(old
[:-2], new
, 1)
351 if '*' not in old
and class_name
.endswith(old
):
352 return class_name
.replace(old
, new
, 1)
357 def SetJarJarMappings(mappings
):
358 """Parse jarjar mappings from a string."""
359 JniParams
._remappings
= []
360 for line
in mappings
.splitlines():
362 if rule
[0] != 'rule':
365 src
= src
.replace('.', '/')
366 dest
= dest
.replace('.', '/')
367 if src
.endswith('**'):
368 src_real_name
= src
[:-2]
370 assert not '*' in src
373 if dest
.endswith('@0'):
374 JniParams
._remappings
.append((src
, dest
[:-2] + src_real_name
))
375 elif dest
.endswith('@1'):
377 JniParams
._remappings
.append((src
, dest
[:-2]))
379 assert not '@' in dest
380 JniParams
._remappings
.append((src
, dest
))
383 def ExtractJNINamespace(contents
):
384 re_jni_namespace
= re
.compile('.*?@JNINamespace\("(.*?)"\)')
385 m
= re
.findall(re_jni_namespace
, contents
)
391 def ExtractFullyQualifiedJavaClassName(java_file_name
, contents
):
392 re_package
= re
.compile('.*?package (.*?);')
393 matches
= re
.findall(re_package
, contents
)
395 raise SyntaxError('Unable to find "package" line in %s' % java_file_name
)
396 return (matches
[0].replace('.', '/') + '/' +
397 os
.path
.splitext(os
.path
.basename(java_file_name
))[0])
400 def ExtractNatives(contents
, ptr_type
):
401 """Returns a list of dict containing information about a native method."""
402 contents
= contents
.replace('\n', '')
404 re_native
= re
.compile(r
'(@NativeClassQualifiedName'
405 '\(\"(?P<native_class_name>.*?)\"\)\s+)?'
406 '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\))\s+)?'
407 '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*native '
408 '(?P<return_type>\S*) '
409 '(?P<name>native\w+)\((?P<params>.*?)\);')
410 for match
in re
.finditer(re_native
, contents
):
411 native
= NativeMethod(
412 static
='static' in match
.group('qualifiers'),
413 java_class_name
=match
.group('java_class_name'),
414 native_class_name
=match
.group('native_class_name'),
415 return_type
=match
.group('return_type'),
416 name
=match
.group('name').replace('native', ''),
417 params
=JniParams
.Parse(match
.group('params')),
423 def GetStaticCastForReturnType(return_type
):
424 type_map
= { 'String' : 'jstring',
425 'java/lang/String' : 'jstring',
426 'boolean[]': 'jbooleanArray',
427 'byte[]': 'jbyteArray',
428 'char[]': 'jcharArray',
429 'short[]': 'jshortArray',
430 'int[]': 'jintArray',
431 'long[]': 'jlongArray',
432 'float[]': 'jfloatArray',
433 'double[]': 'jdoubleArray' }
434 ret
= type_map
.get(return_type
, None)
437 if return_type
.endswith('[]'):
438 return 'jobjectArray'
442 def GetEnvCall(is_constructor
, is_static
, return_type
):
443 """Maps the types availabe via env->Call__Method."""
446 env_call_map
= {'boolean': 'Boolean',
457 call
= env_call_map
.get(return_type
, 'Object')
459 call
= 'Static' + call
460 return 'Call' + call
+ 'Method'
463 def GetMangledParam(datatype
):
464 """Returns a mangled identifier for the datatype."""
465 if len(datatype
) <= 2:
466 return datatype
.replace('[', 'A')
468 for i
in range(1, len(datatype
)):
472 elif c
.isupper() or datatype
[i
- 1] in ['/', 'L']:
477 def GetMangledMethodName(name
, params
, return_type
):
478 """Returns a mangled method name for the given signature.
480 The returned name can be used as a C identifier and will be unique for all
481 valid overloads of the same method.
485 params: list of Param.
492 for datatype
in [return_type
] + [x
.datatype
for x
in params
]:
493 mangled_items
+= [GetMangledParam(JniParams
.JavaToJni(datatype
))]
494 mangled_name
= name
+ '_'.join(mangled_items
)
495 assert re
.match(r
'[0-9a-zA-Z_]+', mangled_name
)
499 def MangleCalledByNatives(called_by_natives
):
500 """Mangles all the overloads from the call_by_natives list."""
501 method_counts
= collections
.defaultdict(
502 lambda: collections
.defaultdict(lambda: 0))
503 for called_by_native
in called_by_natives
:
504 java_class_name
= called_by_native
.java_class_name
505 name
= called_by_native
.name
506 method_counts
[java_class_name
][name
] += 1
507 for called_by_native
in called_by_natives
:
508 java_class_name
= called_by_native
.java_class_name
509 method_name
= called_by_native
.name
510 method_id_var_name
= method_name
511 if method_counts
[java_class_name
][method_name
] > 1:
512 method_id_var_name
= GetMangledMethodName(method_name
,
513 called_by_native
.params
,
514 called_by_native
.return_type
)
515 called_by_native
.method_id_var_name
= method_id_var_name
516 return called_by_natives
519 # Regex to match the JNI return types that should be included in a
520 # ScopedJavaLocalRef.
521 RE_SCOPED_JNI_RETURN_TYPES
= re
.compile('jobject|jclass|jstring|.*Array')
523 # Regex to match a string like "@CalledByNative public void foo(int bar)".
524 RE_CALLED_BY_NATIVE
= re
.compile(
525 '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
526 '\s+(?P<prefix>[\w ]*?)'
527 '\s*(?P<return_type>\S+?)'
529 '\s*\((?P<params>[^\)]*)\)')
532 def ExtractCalledByNatives(contents
):
533 """Parses all methods annotated with @CalledByNative.
536 contents: the contents of the java file.
539 A list of dict with information about the annotated methods.
540 TODO(bulach): return a CalledByNative object.
543 ParseError: if unable to parse.
545 called_by_natives
= []
546 for match
in re
.finditer(RE_CALLED_BY_NATIVE
, contents
):
547 called_by_natives
+= [CalledByNative(
549 unchecked
='Unchecked' in match
.group('Unchecked'),
550 static
='static' in match
.group('prefix'),
551 java_class_name
=match
.group('annotation') or '',
552 return_type
=match
.group('return_type'),
553 name
=match
.group('name'),
554 params
=JniParams
.Parse(match
.group('params')))]
555 # Check for any @CalledByNative occurrences that weren't matched.
556 unmatched_lines
= re
.sub(RE_CALLED_BY_NATIVE
, '', contents
).split('\n')
557 for line1
, line2
in zip(unmatched_lines
, unmatched_lines
[1:]):
558 if '@CalledByNative' in line1
:
559 raise ParseError('could not parse @CalledByNative method signature',
561 return MangleCalledByNatives(called_by_natives
)
564 class JNIFromJavaP(object):
565 """Uses 'javap' to parse a .class file and generate the JNI header file."""
567 def __init__(self
, contents
, options
):
568 self
.contents
= contents
569 self
.namespace
= options
.namespace
570 for line
in contents
:
571 class_name
= re
.match(
572 '.*?(public).*?(class|interface) (?P<class_name>\S+?)( |\Z)',
575 self
.fully_qualified_class
= class_name
.group('class_name')
577 self
.fully_qualified_class
= self
.fully_qualified_class
.replace('.', '/')
578 # Java 7's javap includes type parameters in output, like HashSet<T>. Strip
579 # away the <...> and use the raw class name that Java 6 would've given us.
580 self
.fully_qualified_class
= self
.fully_qualified_class
.split('<', 1)[0]
581 JniParams
.SetFullyQualifiedClass(self
.fully_qualified_class
)
582 self
.java_class_name
= self
.fully_qualified_class
.split('/')[-1]
583 if not self
.namespace
:
584 self
.namespace
= 'JNI_' + self
.java_class_name
585 re_method
= re
.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
586 '\((?P<params>.*?)\)')
587 self
.called_by_natives
= []
588 for lineno
, content
in enumerate(contents
[2:], 2):
589 match
= re
.match(re_method
, content
)
592 self
.called_by_natives
+= [CalledByNative(
595 static
='static' in match
.group('prefix'),
597 return_type
=match
.group('return_type').replace('.', '/'),
598 name
=match
.group('name'),
599 params
=JniParams
.Parse(match
.group('params').replace('.', '/')),
600 signature
=JniParams
.ParseJavaPSignature(contents
[lineno
+ 1]))]
601 re_constructor
= re
.compile('(.*?)public ' +
602 self
.fully_qualified_class
.replace('/', '.') +
603 '\((?P<params>.*?)\)')
604 for lineno
, content
in enumerate(contents
[2:], 2):
605 match
= re
.match(re_constructor
, content
)
608 self
.called_by_natives
+= [CalledByNative(
613 return_type
=self
.fully_qualified_class
,
615 params
=JniParams
.Parse(match
.group('params').replace('.', '/')),
616 signature
=JniParams
.ParseJavaPSignature(contents
[lineno
+ 1]),
617 is_constructor
=True)]
618 self
.called_by_natives
= MangleCalledByNatives(self
.called_by_natives
)
620 self
.constant_fields
= []
621 re_constant_field
= re
.compile('.*?public static final int (?P<name>.*?);')
622 re_constant_field_value
= re
.compile(
623 '.*?Constant(Value| value): int (?P<value>(-*[0-9]+)?)')
624 for lineno
, content
in enumerate(contents
[2:], 2):
625 match
= re
.match(re_constant_field
, content
)
628 value
= re
.match(re_constant_field_value
, contents
[lineno
+ 2])
630 value
= re
.match(re_constant_field_value
, contents
[lineno
+ 3])
632 self
.constant_fields
.append(
633 ConstantField(name
=match
.group('name'),
634 value
=value
.group('value')))
636 self
.inl_header_file_generator
= InlHeaderFileGenerator(
637 self
.namespace
, self
.fully_qualified_class
, [],
638 self
.called_by_natives
, self
.constant_fields
, options
)
640 def GetContent(self
):
641 return self
.inl_header_file_generator
.GetContent()
644 def CreateFromClass(class_file
, options
):
645 class_name
= os
.path
.splitext(os
.path
.basename(class_file
))[0]
646 p
= subprocess
.Popen(args
=[options
.javap
, '-c', '-verbose',
648 cwd
=os
.path
.dirname(class_file
),
649 stdout
=subprocess
.PIPE
,
650 stderr
=subprocess
.PIPE
)
651 stdout
, _
= p
.communicate()
652 jni_from_javap
= JNIFromJavaP(stdout
.split('\n'), options
)
653 return jni_from_javap
656 class JNIFromJavaSource(object):
657 """Uses the given java source file to generate the JNI header file."""
659 # Match single line comments, multiline comments, character literals, and
660 # double-quoted strings.
661 _comment_remover_regex
= re
.compile(
662 r
'//.*?$|/\*.*?\*/|\'(?
:\\.|
[^
\\\'])*\'|
"(?:\\.|[^\\"])*"',
663 re.DOTALL | re.MULTILINE)
665 def __init__(self, contents, fully_qualified_class, options):
666 contents = self._RemoveComments(contents)
667 JniParams.SetFullyQualifiedClass(fully_qualified_class)
668 JniParams.ExtractImportsAndInnerClasses(contents)
669 jni_namespace = ExtractJNINamespace(contents) or options.namespace
670 natives = ExtractNatives(contents, options.ptr_type)
671 called_by_natives = ExtractCalledByNatives(contents)
672 if len(natives) == 0 and len(called_by_natives) == 0:
673 raise SyntaxError('Unable to find any JNI methods for %s.' %
674 fully_qualified_class)
675 inl_header_file_generator = InlHeaderFileGenerator(
676 jni_namespace, fully_qualified_class, natives, called_by_natives,
678 self.content = inl_header_file_generator.GetContent()
681 def _RemoveComments(cls, contents):
682 # We need to support both inline and block comments, and we need to handle
683 # strings that contain '//' or '/*'.
684 # TODO(bulach): This is a bit hacky. It would be cleaner to use a real Java
685 # parser. Maybe we could ditch JNIFromJavaSource and just always use
686 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
687 # http://code.google.com/p/chromium/issues/detail?id=138941
689 # Replace matches that are comments with nothing; return literals/strings
692 if s.startswith('/'):
696 return cls._comment_remover_regex.sub(replacer, contents)
698 def GetContent(self):
702 def CreateFromFile(java_file_name, options):
703 contents = file(java_file_name).read()
704 fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
706 return JNIFromJavaSource(contents, fully_qualified_class, options)
709 class InlHeaderFileGenerator(object):
710 """Generates an inline header file for JNI integration."""
712 def __init__(self, namespace, fully_qualified_class, natives,
713 called_by_natives, constant_fields, options):
714 self.namespace = namespace
715 self.fully_qualified_class = fully_qualified_class
716 self.class_name = self.fully_qualified_class.split('/')[-1]
717 self.natives = natives
718 self.called_by_natives = called_by_natives
719 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
720 self.constant_fields = constant_fields
721 self.options = options
722 self.init_native = self.ExtractInitNative(options)
724 def ExtractInitNative(self, options):
725 for native in self.natives:
726 if options.jni_init_native_name == 'native' + native.name:
727 self.natives.remove(native)
731 def GetContent(self):
732 """Returns the content of the JNI binding file."""
733 template = Template("""\
734 // Copyright 2014 The Chromium Authors. All rights reserved.
735 // Use of this source code is governed by a BSD-style license that can be
736 // found in the LICENSE file.
739 // This file is autogenerated by
742 // ${FULLY_QUALIFIED_CLASS}
744 #ifndef ${HEADER_GUARD}
745 #define ${HEADER_GUARD}
751 #include "base
/android
/jni_int_wrapper
.h
"
753 // Step 1: forward declarations.
755 $CLASS_PATH_DEFINITIONS
756 $METHOD_ID_DEFINITIONS
760 $FORWARD_DECLARATIONS
764 // Step 2: method stubs.
767 // Step 3: RegisterNatives.
771 $JNI_REGISTER_NATIVES
772 #endif // ${HEADER_GUARD}
775 'SCRIPT_NAME': self.options.script_name,
776 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
777 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
778 'METHOD_ID_DEFINITIONS': self.GetMethodIDDefinitionsString(),
779 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
780 'CONSTANT_FIELDS': self.GetConstantFieldsString(),
781 'METHOD_STUBS': self.GetMethodStubsString(),
782 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
783 'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(),
784 'REGISTER_NATIVES': self.GetRegisterNativesString(),
785 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
786 'HEADER_GUARD': self.header_guard,
787 'INCLUDES': self.GetIncludesString(),
788 'JNI_REGISTER_NATIVES': self.GetJNIRegisterNativesString()
790 return WrapOutput(template.substitute(values))
792 def GetClassPathDefinitionsString(self):
794 ret += [self.GetClassPathDefinitions()]
795 return '\n'.join(ret)
797 def GetMethodIDDefinitionsString(self):
798 """Returns the definition of method ids for the called by native methods."""
799 if not self.options.eager_called_by_natives:
801 template = Template("""\
802 jmethodID g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = NULL;""")
804 for called_by_native in self.called_by_natives:
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,
809 ret += [template.substitute(values)]
810 return '\n'.join(ret)
812 def GetForwardDeclarationsString(self):
814 for native in self.natives:
815 if native.type != 'method':
816 ret += [self.GetForwardDeclaration(native)]
817 if self.options.native_exports and ret:
818 return '\nextern "C
" {\n' + "\n".join(ret) + '\n}; // extern "C
"'
819 return '\n'.join(ret)
821 def GetConstantFieldsString(self):
822 if not self.constant_fields:
824 ret = ['enum Java_%s_constant_fields {' % self.class_name]
825 for c in self.constant_fields:
826 ret += [' %s = %s,' % (c.name, c.value)]
828 return '\n'.join(ret)
830 def GetMethodStubsString(self):
831 """Returns the code corresponding to method stubs."""
833 for native in self.natives:
834 if native.type == 'method':
835 ret += [self.GetNativeMethodStubString(native)]
836 if self.options.eager_called_by_natives:
837 ret += self.GetEagerCalledByNativeMethodStubs()
839 ret += self.GetLazyCalledByNativeMethodStubs()
841 if self.options.native_exports and ret:
842 return '\nextern "C
" {\n' + "\n".join(ret) + '\n}; // extern "C
"'
843 return '\n'.join(ret)
845 def GetLazyCalledByNativeMethodStubs(self):
846 return [self.GetLazyCalledByNativeMethodStub(called_by_native)
847 for called_by_native in self.called_by_natives]
849 def GetEagerCalledByNativeMethodStubs(self):
851 if self.called_by_natives:
852 ret += ['namespace {']
853 for called_by_native in self.called_by_natives:
854 ret += [self.GetEagerCalledByNativeMethodStub(called_by_native)]
855 ret += ['} // namespace']
858 def GetIncludesString(self):
859 if not self.options.includes:
861 includes = self.options.includes.split(',')
862 return '\n'.join('#include "%s"' % x for x in includes)
864 def GetKMethodsString(self, clazz):
866 for native in self.natives:
867 if (native.java_class_name == clazz or
868 (not native.java_class_name and clazz == self.class_name)):
869 ret += [self.GetKMethodArrayEntry(native)]
870 return '\n'.join(ret)
872 def SubstituteNativeMethods(self, template):
873 """Substitutes JAVA_CLASS and KMETHODS in the provided template."""
875 all_classes = self.GetUniqueClasses(self.natives)
876 all_classes[self.class_name] = self.fully_qualified_class
877 for clazz in all_classes:
878 kmethods = self.GetKMethodsString(clazz)
880 values = {'JAVA_CLASS': clazz,
881 'KMETHODS': kmethods}
882 ret += [template.substitute(values)]
883 if not ret: return ''
884 return '\n' + '\n'.join(ret)
886 def GetJNINativeMethodsString(self):
887 """Returns the implementation of the array of native methods."""
888 if self.options.native_exports:
890 template = Template("""\
891 static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
895 return self.SubstituteNativeMethods(template)
897 def GetRegisterCalledByNativesImplString(self):
898 """Returns the code for registering the called by native methods."""
899 if not self.options.eager_called_by_natives:
901 template = Template("""\
902 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = ${GET_METHOD_ID_IMPL}
903 if (g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} == NULL) {
908 for called_by_native in self.called_by_natives:
910 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
911 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
912 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native),
914 ret += [template.substitute(values)]
915 return '\n'.join(ret)
917 def GetRegisterNativesString(self):
918 """Returns the code for RegisterNatives."""
919 template = Template("""\
920 ${REGISTER_NATIVES_SIGNATURE} {
927 signature = 'static bool RegisterNativesImpl(JNIEnv* env'
929 signature += ', jclass clazz)'
933 natives = self.GetRegisterNativesImplString()
934 called_by_natives = self.GetRegisterCalledByNativesImplString()
935 values = {'REGISTER_NATIVES_SIGNATURE': signature,
936 'CLASSES': self.GetFindClasses(),
938 'CALLED_BY_NATIVES': called_by_natives,
940 return template.substitute(values)
942 def GetRegisterNativesImplString(self):
943 """Returns the shared implementation for RegisterNatives."""
944 if self.options.native_exports:
947 template = Template("""\
948 const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
950 if (env->RegisterNatives(${JAVA_CLASS}_clazz(env),
951 kMethods${JAVA_CLASS},
952 kMethods${JAVA_CLASS}Size) < 0) {
953 jni_generator::HandleRegistrationError(
954 env, ${JAVA_CLASS}_clazz(env), __FILE__);
958 return self.SubstituteNativeMethods(template)
960 def GetJNIRegisterNativesString(self):
961 """Returns the implementation for the JNI registration of native methods."""
962 if not self.init_native:
965 template = Template("""\
966 extern "C
" JNIEXPORT bool JNICALL
967 Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) {
968 return ${NAMESPACE}RegisterNativesImpl(env, clazz);
972 if self.options.native_exports:
973 java_name = JniParams.RemapClassName(self.fully_qualified_class)
974 java_name = java_name.replace('_', '_1').replace('/', '_')
976 java_name = self.fully_qualified_class.replace('/', '_')
980 namespace = self.namespace + '::'
981 values = {'FULLY_QUALIFIED_CLASS': java_name,
982 'INIT_NATIVE_NAME': 'native' + self.init_native.name,
983 'NAMESPACE': namespace,
984 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString()
986 return template.substitute(values)
988 def GetOpenNamespaceString(self):
990 all_namespaces = ['namespace %s {' % ns
991 for ns in self.namespace.split('::')]
992 return '\n'.join(all_namespaces)
995 def GetCloseNamespaceString(self):
997 all_namespaces = ['} // namespace %s' % ns
998 for ns in self.namespace.split('::')]
999 all_namespaces.reverse()
1000 return '\n'.join(all_namespaces) + '\n'
1003 def GetJNIFirstParam(self, native):
1005 if native.type == 'method':
1006 ret = ['jobject jcaller']
1007 elif native.type == 'function':
1009 ret = ['jclass jcaller']
1011 ret = ['jobject jcaller']
1014 def GetParamsInDeclaration(self, native):
1015 """Returns the params for the stub declaration.
1018 native: the native dictionary describing the method.
1021 A string containing the params.
1023 return ',\n '.join(self.GetJNIFirstParam(native) +
1024 [JavaDataTypeToC(param.datatype) + ' ' +
1026 for param in native.params])
1028 def GetCalledByNativeParamsInDeclaration(self, called_by_native):
1029 return ',\n '.join([
1030 JavaDataTypeToCForCalledByNativeParam(param.datatype) + ' ' +
1032 for param in called_by_native.params])
1034 def GetForwardDeclaration(self, native):
1036 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
1038 if self.options.native_exports:
1040 __attribute__((visibility("default
")))
1041 ${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env, ${PARAMS}) {
1042 return ${NAME}(${PARAMS_IN_CALL});
1045 template = Template(template_str)
1047 if not self.options.pure_native_methods:
1048 params_in_call = ['env', 'jcaller']
1049 params_in_call = ', '.join(params_in_call + [p.name for p in native.params])
1051 java_name = JniParams.RemapClassName(self.fully_qualified_class)
1052 java_name = java_name.replace('_', '_1').replace('/', '_')
1053 if native.java_class_name:
1054 java_name += '_00024' + native.java_class_name
1056 values = {'RETURN': JavaDataTypeToC(native.return_type),
1057 'NAME': native.name,
1058 'JAVA_NAME': java_name,
1059 'PARAMS': self.GetParamsInDeclaration(native),
1060 'PARAMS_IN_CALL': params_in_call}
1061 return template.substitute(values)
1063 def GetNativeMethodStubString(self, native):
1064 """Returns stubs for native methods."""
1065 if self.options.native_exports:
1067 __attribute__((visibility("default
")))
1068 ${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env,
1069 ${PARAMS_IN_DECLARATION}) {"""
1072 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {"""
1074 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
1075 CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}
"${OPTIONAL_ERROR_RETURN});
1076 return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL};
1080 template = Template(template_str)
1082 if not self.options.pure_native_methods:
1083 params = ['env', 'jcaller']
1084 params_in_call = ', '.join(params + [p.name for p in native.params[1:]])
1086 return_type = JavaDataTypeToC(native.return_type)
1087 optional_error_return = JavaReturnValueToC(native.return_type)
1088 if optional_error_return:
1089 optional_error_return = ', ' + optional_error_return
1091 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
1092 post_call = '.Release()'
1094 if self.options.native_exports:
1095 java_name = JniParams.RemapClassName(self.fully_qualified_class)
1096 java_name = java_name.replace('_', '_1').replace('/', '_')
1097 if native.java_class_name:
1098 java_name += '_00024' + native.java_class_name
1103 'RETURN': return_type,
1104 'OPTIONAL_ERROR_RETURN': optional_error_return,
1105 'JAVA_NAME': java_name,
1106 'NAME': native.name,
1107 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
1108 'PARAM0_NAME': native.params[0].name,
1109 'P0_TYPE': native.p0_type,
1110 'PARAMS_IN_CALL': params_in_call,
1111 'POST_CALL': post_call
1113 return template.substitute(values)
1115 def GetArgument(self, param):
1116 return ('as_jint(' + param.name + ')'
1117 if param.datatype == 'int' else param.name)
1119 def GetArgumentsInCall(self, params):
1120 """Return a string of arguments to call from native into Java"""
1121 return [self.GetArgument(p) for p in params]
1123 def GetCalledByNativeValues(self, called_by_native):
1124 """Fills in necessary values for the CalledByNative methods."""
1125 java_class = called_by_native.java_class_name or self.class_name
1126 if called_by_native.static or called_by_native.is_constructor:
1127 first_param_in_declaration = ''
1128 first_param_in_call = ('%s_clazz(env)' % java_class)
1130 first_param_in_declaration = ', jobject obj'
1131 first_param_in_call = 'obj'
1132 params_in_declaration = self.GetCalledByNativeParamsInDeclaration(
1134 if params_in_declaration:
1135 params_in_declaration = ', ' + params_in_declaration
1136 params_in_call = ', '.join(self.GetArgumentsInCall(called_by_native.params))
1138 params_in_call = ', ' + params_in_call
1141 if called_by_native.static_cast:
1142 pre_call = 'static_cast<%s>(' % called_by_native.static_cast
1144 check_exception = ''
1145 if not called_by_native.unchecked:
1146 check_exception = 'jni_generator::CheckException(env);'
1147 return_type = JavaDataTypeToC(called_by_native.return_type)
1148 optional_error_return = JavaReturnValueToC(called_by_native.return_type)
1149 if optional_error_return:
1150 optional_error_return = ', ' + optional_error_return
1151 return_declaration = ''
1153 if return_type != 'void':
1154 pre_call = ' ' + pre_call
1155 return_declaration = return_type + ' ret ='
1156 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
1157 return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>'
1158 return_clause = 'return ' + return_type + '(env, ret);'
1160 return_clause = 'return ret;'
1162 'JAVA_CLASS': java_class,
1163 'RETURN_TYPE': return_type,
1164 'OPTIONAL_ERROR_RETURN': optional_error_return,
1165 'RETURN_DECLARATION': return_declaration,
1166 'RETURN_CLAUSE': return_clause,
1167 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration,
1168 'PARAMS_IN_DECLARATION': params_in_declaration,
1169 'PRE_CALL': pre_call,
1170 'POST_CALL': post_call,
1171 'ENV_CALL': called_by_native.env_call,
1172 'FIRST_PARAM_IN_CALL': first_param_in_call,
1173 'PARAMS_IN_CALL': params_in_call,
1174 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
1175 'CHECK_EXCEPTION': check_exception,
1176 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native)
1179 def GetEagerCalledByNativeMethodStub(self, called_by_native):
1180 """Returns the implementation of the called by native method."""
1181 template = Template("""
1182 static ${RETURN_TYPE} ${METHOD_ID_VAR_NAME}(\
1183 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION}) {
1184 ${RETURN_DECLARATION}${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
1185 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}${PARAMS_IN_CALL})${POST_CALL};
1188 values = self.GetCalledByNativeValues(called_by_native)
1189 return template.substitute(values)
1191 def GetLazyCalledByNativeMethodStub(self, called_by_native):
1192 """Returns a string."""
1193 function_signature_template = Template("""\
1194 static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\
1195 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""")
1196 function_header_template = Template("""\
1197 ${FUNCTION_SIGNATURE} {""")
1198 function_header_with_unused_template = Template("""\
1199 ${FUNCTION_SIGNATURE} __attribute__ ((unused));
1200 ${FUNCTION_SIGNATURE} {""")
1201 template = Template("""
1202 static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0;
1204 /* Must call RegisterNativesImpl() */
1205 CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL},
1206 ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN});
1207 jmethodID method_id =
1208 ${GET_METHOD_ID_IMPL}
1209 ${RETURN_DECLARATION}
1210 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
1211 method_id${PARAMS_IN_CALL})${POST_CALL};
1215 values = self.GetCalledByNativeValues(called_by_native)
1216 values['FUNCTION_SIGNATURE'] = (
1217 function_signature_template.substitute(values))
1218 if called_by_native.system_class:
1219 values['FUNCTION_HEADER'] = (
1220 function_header_with_unused_template.substitute(values))
1222 values['FUNCTION_HEADER'] = function_header_template.substitute(values)
1223 return template.substitute(values)
1225 def GetKMethodArrayEntry(self, native):
1226 template = Template("""\
1227 { "native${NAME}
", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""")
1228 values = {'NAME': native.name,
1229 'JNI_SIGNATURE': JniParams.Signature(native.params,
1232 return template.substitute(values)
1234 def GetUniqueClasses(self, origin):
1235 ret = {self.class_name: self.fully_qualified_class}
1236 for entry in origin:
1237 class_name = self.class_name
1238 jni_class_path = self.fully_qualified_class
1239 if entry.java_class_name:
1240 class_name = entry.java_class_name
1241 jni_class_path = self.fully_qualified_class + '$' + class_name
1242 ret[class_name] = jni_class_path
1245 def GetClassPathDefinitions(self):
1246 """Returns the ClassPath constants."""
1248 template = Template("""\
1249 const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}
";""")
1250 native_classes = self.GetUniqueClasses(self.natives)
1251 called_by_native_classes = self.GetUniqueClasses(self.called_by_natives)
1252 if self.options.native_exports:
1253 all_classes = called_by_native_classes
1255 all_classes = native_classes
1256 all_classes.update(called_by_native_classes)
1258 for clazz in all_classes:
1260 'JAVA_CLASS': clazz,
1261 'JNI_CLASS_PATH': JniParams.RemapClassName(all_classes[clazz]),
1263 ret += [template.substitute(values)]
1266 class_getter_methods = []
1267 if self.options.native_exports:
1268 template = Template("""\
1269 // Leaking this jclass as we cannot use LazyInstance from some threads.
1270 base::subtle::AtomicWord g_${JAVA_CLASS}_clazz = 0;
1271 #define ${JAVA_CLASS}_clazz(env) \
1272 base::android::LazyGetClass(env, k${JAVA_CLASS}ClassPath, \
1273 &g_${JAVA_CLASS}_clazz)""")
1275 template = Template("""\
1276 // Leaking this jclass as we cannot use LazyInstance from some threads.
1277 jclass g_${JAVA_CLASS}_clazz = NULL;
1278 #define ${JAVA_CLASS}_clazz(env) g_${JAVA_CLASS}_clazz""")
1280 for clazz in called_by_native_classes:
1282 'JAVA_CLASS': clazz,
1284 ret += [template.substitute(values)]
1286 return '\n'.join(ret)
1288 def GetFindClasses(self):
1289 """Returns the imlementation of FindClass for all known classes."""
1290 if self.init_native:
1291 if self.options.native_exports:
1292 template = Template("""\
1293 base::subtle::Release_Store(&g_${JAVA_CLASS}_clazz,
1294 static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));""")
1296 template = Template("""\
1297 g_${JAVA_CLASS}_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));""")
1299 if self.options.native_exports:
1301 template = Template("""\
1302 g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
1303 base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""")
1305 for clazz in self.GetUniqueClasses(self.called_by_natives):
1306 values = {'JAVA_CLASS': clazz}
1307 ret += [template.substitute(values)]
1308 return '\n'.join(ret)
1310 def GetMethodIDImpl(self, called_by_native):
1311 """Returns the implementation of GetMethodID."""
1312 if self.options.eager_called_by_natives:
1313 template = Template("""\
1314 env->Get${STATIC_METHOD_PART}MethodID(
1315 ${JAVA_CLASS}_clazz(env),
1316 "${JNI_NAME}
", ${JNI_SIGNATURE});""")
1318 template = Template("""\
1319 base::android::MethodID::LazyGet<
1320 base::android::MethodID::TYPE_${STATIC}>(
1321 env, ${JAVA_CLASS}_clazz(env),
1324 &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
1326 jni_name = called_by_native.name
1327 jni_return_type = called_by_native.return_type
1328 if called_by_native.is_constructor:
1330 jni_return_type = 'void'
1331 if called_by_native.signature:
1332 signature = called_by_native.signature
1334 signature = JniParams.Signature(called_by_native.params,
1338 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
1339 'JNI_NAME': jni_name,
1340 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
1341 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE',
1342 'STATIC_METHOD_PART': 'Static' if called_by_native.static else '',
1343 'JNI_SIGNATURE': signature,
1345 return template.substitute(values)
1348 def WrapOutput(output):
1350 for line in output.splitlines():
1351 # Do not wrap lines under 80 characters or preprocessor directives.
1352 if len(line) < 80 or line.lstrip()[:1] == '#':
1353 stripped = line.rstrip()
1354 if len(ret) == 0 or len(ret[-1]) or len(stripped):
1355 ret.append(stripped)
1357 first_line_indent = ' ' * (len(line) - len(line.lstrip()))
1358 subsequent_indent = first_line_indent + ' ' * 4
1359 if line.startswith('//'):
1360 subsequent_indent = '//' + subsequent_indent
1361 wrapper = textwrap.TextWrapper(width=80,
1362 subsequent_indent=subsequent_indent,
1363 break_long_words=False)
1364 ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)]
1366 return '\n'.join(ret)
1369 def ExtractJarInputFile(jar_file, input_file, out_dir):
1370 """Extracts input file from jar and returns the filename.
1372 The input file is extracted to the same directory that the generated jni
1373 headers will be placed in. This is passed as an argument to script.
1376 jar_file: the jar file containing the input files to extract.
1377 input_files: the list of files to extract from the jar file.
1378 out_dir: the name of the directories to extract to.
1381 the name of extracted input file.
1383 jar_file = zipfile.ZipFile(jar_file)
1385 out_dir = os.path.join(out_dir, os.path.dirname(input_file))
1387 os.makedirs(out_dir)
1388 except OSError as e:
1389 if e.errno != errno.EEXIST:
1391 extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
1392 with open(extracted_file_name, 'w') as outfile:
1393 outfile.write(jar_file.read(input_file))
1395 return extracted_file_name
1398 def GenerateJNIHeader(input_file, output_file, options):
1400 if os.path.splitext(input_file)[1] == '.class':
1401 jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options)
1402 content = jni_from_javap.GetContent()
1404 jni_from_java_source = JNIFromJavaSource.CreateFromFile(
1405 input_file, options)
1406 content = jni_from_java_source.GetContent()
1407 except ParseError, e:
1411 if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
1412 os.makedirs(os.path.dirname(os.path.abspath(output_file)))
1413 if options.optimize_generation and os.path.exists(output_file):
1414 with file(output_file, 'r') as f:
1415 existing_content = f.read()
1416 if existing_content == content:
1418 with file(output_file, 'w') as f:
1424 def GetScriptName():
1425 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
1427 for idx, value in enumerate(script_components):
1428 if value == 'base' or value == 'third_party':
1431 return os.sep.join(script_components[base_index:])
1435 usage = """usage: %prog [OPTIONS]
1436 This script will parse the given java source code extracting the native
1437 declarations and print the header file to stdout (or a file).
1438 See SampleForTests.java for more details.
1440 option_parser = optparse.OptionParser(usage=usage)
1441 build_utils.AddDepfileOption(option_parser)
1443 option_parser.add_option('-j', '--jar_file', dest='jar_file',
1444 help='Extract the list of input files from'
1445 ' a specified jar file.'
1446 ' Uses javap to extract the methods from a'
1447 ' pre-compiled class. --input should point'
1448 ' to pre-compiled Java .class files.')
1449 option_parser.add_option('-n', dest='namespace',
1450 help='Uses as a namespace in the generated header '
1451 'instead of the javap class name, or when there is '
1452 'no JNINamespace annotation in the java source.')
1453 option_parser.add_option('--input_file',
1454 help='Single input file name. The output file name '
1455 'will be derived from it. Must be used with '
1457 option_parser.add_option('--output_dir',
1458 help='The output directory. Must be used with '
1460 option_parser.add_option('--optimize_generation', type="int",
1461 default=0, help='Whether we should optimize JNI '
1462 'generation by not regenerating files if they have '
1464 option_parser.add_option('--jarjar',
1465 help='Path to optional jarjar rules file.')
1466 option_parser.add_option('--script_name', default=GetScriptName(),
1467 help='The name of this script in the generated '
1469 option_parser.add_option('--includes',
1470 help='The comma-separated list of header files to '
1471 'include in the generated header.')
1472 option_parser.add_option('--pure_native_methods',
1473 action='store_true', dest='pure_native_methods',
1474 help='When true, the native methods will be called '
1475 'without any JNI-specific arguments.')
1476 option_parser.add_option('--ptr_type', default='int',
1477 type='choice', choices=['int', 'long'],
1478 help='The type used to represent native pointers in '
1479 'Java code. For 32-bit, use int; '
1480 'for 64-bit, use long.')
1481 option_parser.add_option('--jni_init_native_name', default='',
1482 help='The name of the JNI registration method that '
1483 'is used to initialize all native methods. If a '
1484 'method with this name is not present in the Java '
1485 'source file, setting this option is a no-op. When '
1486 'a method with this name is found however, the '
1487 'naming convention Java_<packageName>_<className> '
1488 'will limit the initialization to only the '
1490 option_parser.add_option('--eager_called_by_natives',
1491 action='store_true', dest='eager_called_by_natives',
1492 help='When true, the called-by-native methods will '
1493 'be initialized in a non-atomic way.')
1494 option_parser.add_option('--cpp', default='cpp',
1495 help='The path to cpp command.')
1496 option_parser.add_option('--javap', default='javap',
1497 help='The path to javap command.')
1498 option_parser.add_option('--native_exports', action='store_true',
1499 help='Native method registration through .so '
1501 options, args = option_parser.parse_args(argv)
1502 if options.jar_file:
1503 input_file = ExtractJarInputFile(options.jar_file, options.input_file,
1505 elif options.input_file:
1506 input_file = options.input_file
1508 option_parser.print_help()
1509 print '\nError: Must specify --jar_file or --input_file.'
1512 if options.output_dir:
1513 root_name = os.path.splitext(os.path.basename(input_file))[0]
1514 output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
1516 with open(options.jarjar) as f:
1517 JniParams.SetJarJarMappings(f.read())
1518 GenerateJNIHeader(input_file, output_file, options)
1521 build_utils.WriteDepfile(
1523 build_utils.GetPythonDependencies())
1526 if __name__ == '__main__':
1527 sys.exit(main(sys.argv))