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 index
= signature_line
.find(prefix
)
214 prefix
= 'descriptor: '
215 index
= signature_line
.index(prefix
)
216 return '"%s"' % signature_line
[index
+ len(prefix
):]
219 def JavaToJni(param
):
220 """Converts a java param into a JNI signature type."""
232 object_param_list
= [
233 'Ljava/lang/Boolean',
234 'Ljava/lang/Integer',
243 while param
[-2:] == '[]':
248 param
= param
[:param
.index('<')]
249 if param
in pod_param_map
:
250 return prefix
+ pod_param_map
[param
]
252 # Coming from javap, use the fully qualified param directly.
253 return prefix
+ 'L' + JniParams
.RemapClassName(param
) + ';'
255 for qualified_name
in (object_param_list
+
256 [JniParams
._fully
_qualified
_class
] +
257 JniParams
._inner
_classes
):
258 if (qualified_name
.endswith('/' + param
) or
259 qualified_name
.endswith('$' + param
.replace('.', '$')) or
260 qualified_name
== 'L' + param
):
261 return prefix
+ JniParams
.RemapClassName(qualified_name
) + ';'
263 # Is it from an import? (e.g. referecing Class from import pkg.Class;
264 # note that referencing an inner class Inner from import pkg.Class.Inner
266 for qualified_name
in JniParams
._imports
:
267 if qualified_name
.endswith('/' + param
):
268 # Ensure it's not an inner class.
269 components
= qualified_name
.split('/')
270 if len(components
) > 2 and components
[-2][0].isupper():
271 raise SyntaxError('Inner class (%s) can not be imported '
272 'and used by JNI (%s). Please import the outer '
273 'class and use Outer.Inner instead.' %
274 (qualified_name
, param
))
275 return prefix
+ JniParams
.RemapClassName(qualified_name
) + ';'
277 # Is it an inner class from an outer class import? (e.g. referencing
278 # Class.Inner from import pkg.Class).
280 components
= param
.split('.')
281 outer
= '/'.join(components
[:-1])
282 inner
= components
[-1]
283 for qualified_name
in JniParams
._imports
:
284 if qualified_name
.endswith('/' + outer
):
285 return (prefix
+ JniParams
.RemapClassName(qualified_name
) +
287 raise SyntaxError('Inner class (%s) can not be '
288 'used directly by JNI. Please import the outer '
291 (param
, JniParams
._package
.replace('/', '.'),
292 outer
.replace('/', '.')))
294 JniParams
._CheckImplicitImports
(param
)
296 # Type not found, falling back to same package as this class.
297 return (prefix
+ 'L' +
298 JniParams
.RemapClassName(JniParams
._package
+ '/' + param
) + ';')
301 def _CheckImplicitImports(param
):
302 # Ensure implicit imports, such as java.lang.*, are not being treated
303 # as being in the same package.
304 if not JniParams
._implicit
_imports
:
305 # This file was generated from android.jar and lists
306 # all classes that are implicitly imported.
307 with
file(os
.path
.join(os
.path
.dirname(sys
.argv
[0]),
308 'android_jar.classes'), 'r') as f
:
309 JniParams
._implicit
_imports
= f
.readlines()
310 for implicit_import
in JniParams
._implicit
_imports
:
311 implicit_import
= implicit_import
.strip().replace('.class', '')
312 implicit_import
= implicit_import
.replace('/', '.')
313 if implicit_import
.endswith('.' + param
):
314 raise SyntaxError('Ambiguous class (%s) can not be used directly '
315 'by JNI.\nPlease import it, probably:\n\n'
317 (param
, implicit_import
))
321 def Signature(params
, returns
, wrap
):
322 """Returns the JNI signature for the given datatypes."""
324 items
+= [JniParams
.JavaToJni(param
.datatype
) for param
in params
]
326 items
+= [JniParams
.JavaToJni(returns
)]
328 return '\n' + '\n'.join(['"' + item
+ '"' for item
in items
])
330 return '"' + ''.join(items
) + '"'
334 """Parses the params into a list of Param objects."""
338 for p
in [p
.strip() for p
in params
.split(',')]:
341 items
.remove('final')
344 name
=(items
[1] if len(items
) > 1 else 'p%s' % len(ret
)),
350 def RemapClassName(class_name
):
351 """Remaps class names using the jarjar mapping table."""
352 for old
, new
in JniParams
._remappings
:
353 if old
.endswith('**') and old
[:-2] in class_name
:
354 return class_name
.replace(old
[:-2], new
, 1)
355 if '*' not in old
and class_name
.endswith(old
):
356 return class_name
.replace(old
, new
, 1)
361 def SetJarJarMappings(mappings
):
362 """Parse jarjar mappings from a string."""
363 JniParams
._remappings
= []
364 for line
in mappings
.splitlines():
366 if rule
[0] != 'rule':
369 src
= src
.replace('.', '/')
370 dest
= dest
.replace('.', '/')
371 if src
.endswith('**'):
372 src_real_name
= src
[:-2]
374 assert not '*' in src
377 if dest
.endswith('@0'):
378 JniParams
._remappings
.append((src
, dest
[:-2] + src_real_name
))
379 elif dest
.endswith('@1'):
381 JniParams
._remappings
.append((src
, dest
[:-2]))
383 assert not '@' in dest
384 JniParams
._remappings
.append((src
, dest
))
387 def ExtractJNINamespace(contents
):
388 re_jni_namespace
= re
.compile('.*?@JNINamespace\("(.*?)"\)')
389 m
= re
.findall(re_jni_namespace
, contents
)
395 def ExtractFullyQualifiedJavaClassName(java_file_name
, contents
):
396 re_package
= re
.compile('.*?package (.*?);')
397 matches
= re
.findall(re_package
, contents
)
399 raise SyntaxError('Unable to find "package" line in %s' % java_file_name
)
400 return (matches
[0].replace('.', '/') + '/' +
401 os
.path
.splitext(os
.path
.basename(java_file_name
))[0])
404 def ExtractNatives(contents
, ptr_type
):
405 """Returns a list of dict containing information about a native method."""
406 contents
= contents
.replace('\n', '')
408 re_native
= re
.compile(r
'(@NativeClassQualifiedName'
409 '\(\"(?P<native_class_name>.*?)\"\)\s+)?'
410 '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\))\s+)?'
411 '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*native '
412 '(?P<return_type>\S*) '
413 '(?P<name>native\w+)\((?P<params>.*?)\);')
414 for match
in re
.finditer(re_native
, contents
):
415 native
= NativeMethod(
416 static
='static' in match
.group('qualifiers'),
417 java_class_name
=match
.group('java_class_name'),
418 native_class_name
=match
.group('native_class_name'),
419 return_type
=match
.group('return_type'),
420 name
=match
.group('name').replace('native', ''),
421 params
=JniParams
.Parse(match
.group('params')),
427 def GetStaticCastForReturnType(return_type
):
428 type_map
= { 'String' : 'jstring',
429 'java/lang/String' : 'jstring',
430 'boolean[]': 'jbooleanArray',
431 'byte[]': 'jbyteArray',
432 'char[]': 'jcharArray',
433 'short[]': 'jshortArray',
434 'int[]': 'jintArray',
435 'long[]': 'jlongArray',
436 'float[]': 'jfloatArray',
437 'double[]': 'jdoubleArray' }
438 ret
= type_map
.get(return_type
, None)
441 if return_type
.endswith('[]'):
442 return 'jobjectArray'
446 def GetEnvCall(is_constructor
, is_static
, return_type
):
447 """Maps the types availabe via env->Call__Method."""
450 env_call_map
= {'boolean': 'Boolean',
461 call
= env_call_map
.get(return_type
, 'Object')
463 call
= 'Static' + call
464 return 'Call' + call
+ 'Method'
467 def GetMangledParam(datatype
):
468 """Returns a mangled identifier for the datatype."""
469 if len(datatype
) <= 2:
470 return datatype
.replace('[', 'A')
472 for i
in range(1, len(datatype
)):
476 elif c
.isupper() or datatype
[i
- 1] in ['/', 'L']:
481 def GetMangledMethodName(name
, params
, return_type
):
482 """Returns a mangled method name for the given signature.
484 The returned name can be used as a C identifier and will be unique for all
485 valid overloads of the same method.
489 params: list of Param.
496 for datatype
in [return_type
] + [x
.datatype
for x
in params
]:
497 mangled_items
+= [GetMangledParam(JniParams
.JavaToJni(datatype
))]
498 mangled_name
= name
+ '_'.join(mangled_items
)
499 assert re
.match(r
'[0-9a-zA-Z_]+', mangled_name
)
503 def MangleCalledByNatives(called_by_natives
):
504 """Mangles all the overloads from the call_by_natives list."""
505 method_counts
= collections
.defaultdict(
506 lambda: collections
.defaultdict(lambda: 0))
507 for called_by_native
in called_by_natives
:
508 java_class_name
= called_by_native
.java_class_name
509 name
= called_by_native
.name
510 method_counts
[java_class_name
][name
] += 1
511 for called_by_native
in called_by_natives
:
512 java_class_name
= called_by_native
.java_class_name
513 method_name
= called_by_native
.name
514 method_id_var_name
= method_name
515 if method_counts
[java_class_name
][method_name
] > 1:
516 method_id_var_name
= GetMangledMethodName(method_name
,
517 called_by_native
.params
,
518 called_by_native
.return_type
)
519 called_by_native
.method_id_var_name
= method_id_var_name
520 return called_by_natives
523 # Regex to match the JNI return types that should be included in a
524 # ScopedJavaLocalRef.
525 RE_SCOPED_JNI_RETURN_TYPES
= re
.compile('jobject|jclass|jstring|.*Array')
527 # Regex to match a string like "@CalledByNative public void foo(int bar)".
528 RE_CALLED_BY_NATIVE
= re
.compile(
529 '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
530 '\s+(?P<prefix>[\w ]*?)'
531 '\s*(?P<return_type>\S+?)'
533 '\s*\((?P<params>[^\)]*)\)')
536 def ExtractCalledByNatives(contents
):
537 """Parses all methods annotated with @CalledByNative.
540 contents: the contents of the java file.
543 A list of dict with information about the annotated methods.
544 TODO(bulach): return a CalledByNative object.
547 ParseError: if unable to parse.
549 called_by_natives
= []
550 for match
in re
.finditer(RE_CALLED_BY_NATIVE
, contents
):
551 called_by_natives
+= [CalledByNative(
553 unchecked
='Unchecked' in match
.group('Unchecked'),
554 static
='static' in match
.group('prefix'),
555 java_class_name
=match
.group('annotation') or '',
556 return_type
=match
.group('return_type'),
557 name
=match
.group('name'),
558 params
=JniParams
.Parse(match
.group('params')))]
559 # Check for any @CalledByNative occurrences that weren't matched.
560 unmatched_lines
= re
.sub(RE_CALLED_BY_NATIVE
, '', contents
).split('\n')
561 for line1
, line2
in zip(unmatched_lines
, unmatched_lines
[1:]):
562 if '@CalledByNative' in line1
:
563 raise ParseError('could not parse @CalledByNative method signature',
565 return MangleCalledByNatives(called_by_natives
)
568 class JNIFromJavaP(object):
569 """Uses 'javap' to parse a .class file and generate the JNI header file."""
571 def __init__(self
, contents
, options
):
572 self
.contents
= contents
573 self
.namespace
= options
.namespace
574 for line
in contents
:
575 class_name
= re
.match(
576 '.*?(public).*?(class|interface) (?P<class_name>\S+?)( |\Z)',
579 self
.fully_qualified_class
= class_name
.group('class_name')
581 self
.fully_qualified_class
= self
.fully_qualified_class
.replace('.', '/')
582 # Java 7's javap includes type parameters in output, like HashSet<T>. Strip
583 # away the <...> and use the raw class name that Java 6 would've given us.
584 self
.fully_qualified_class
= self
.fully_qualified_class
.split('<', 1)[0]
585 JniParams
.SetFullyQualifiedClass(self
.fully_qualified_class
)
586 self
.java_class_name
= self
.fully_qualified_class
.split('/')[-1]
587 if not self
.namespace
:
588 self
.namespace
= 'JNI_' + self
.java_class_name
589 re_method
= re
.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
590 '\((?P<params>.*?)\)')
591 self
.called_by_natives
= []
592 for lineno
, content
in enumerate(contents
[2:], 2):
593 match
= re
.match(re_method
, content
)
596 self
.called_by_natives
+= [CalledByNative(
599 static
='static' in match
.group('prefix'),
601 return_type
=match
.group('return_type').replace('.', '/'),
602 name
=match
.group('name'),
603 params
=JniParams
.Parse(match
.group('params').replace('.', '/')),
604 signature
=JniParams
.ParseJavaPSignature(contents
[lineno
+ 1]))]
605 re_constructor
= re
.compile('(.*?)public ' +
606 self
.fully_qualified_class
.replace('/', '.') +
607 '\((?P<params>.*?)\)')
608 for lineno
, content
in enumerate(contents
[2:], 2):
609 match
= re
.match(re_constructor
, content
)
612 self
.called_by_natives
+= [CalledByNative(
617 return_type
=self
.fully_qualified_class
,
619 params
=JniParams
.Parse(match
.group('params').replace('.', '/')),
620 signature
=JniParams
.ParseJavaPSignature(contents
[lineno
+ 1]),
621 is_constructor
=True)]
622 self
.called_by_natives
= MangleCalledByNatives(self
.called_by_natives
)
624 self
.constant_fields
= []
625 re_constant_field
= re
.compile('.*?public static final int (?P<name>.*?);')
626 re_constant_field_value
= re
.compile(
627 '.*?Constant(Value| value): int (?P<value>(-*[0-9]+)?)')
628 for lineno
, content
in enumerate(contents
[2:], 2):
629 match
= re
.match(re_constant_field
, content
)
632 value
= re
.match(re_constant_field_value
, contents
[lineno
+ 2])
634 value
= re
.match(re_constant_field_value
, contents
[lineno
+ 3])
636 self
.constant_fields
.append(
637 ConstantField(name
=match
.group('name'),
638 value
=value
.group('value')))
640 self
.inl_header_file_generator
= InlHeaderFileGenerator(
641 self
.namespace
, self
.fully_qualified_class
, [],
642 self
.called_by_natives
, self
.constant_fields
, options
)
644 def GetContent(self
):
645 return self
.inl_header_file_generator
.GetContent()
648 def CreateFromClass(class_file
, options
):
649 class_name
= os
.path
.splitext(os
.path
.basename(class_file
))[0]
650 p
= subprocess
.Popen(args
=[options
.javap
, '-c', '-verbose',
652 cwd
=os
.path
.dirname(class_file
),
653 stdout
=subprocess
.PIPE
,
654 stderr
=subprocess
.PIPE
)
655 stdout
, _
= p
.communicate()
656 jni_from_javap
= JNIFromJavaP(stdout
.split('\n'), options
)
657 return jni_from_javap
660 class JNIFromJavaSource(object):
661 """Uses the given java source file to generate the JNI header file."""
663 # Match single line comments, multiline comments, character literals, and
664 # double-quoted strings.
665 _comment_remover_regex
= re
.compile(
666 r
'//.*?$|/\*.*?\*/|\'(?
:\\.|
[^
\\\'])*\'|
"(?:\\.|[^\\"])*"',
667 re.DOTALL | re.MULTILINE)
669 def __init__(self, contents, fully_qualified_class, options):
670 contents = self._RemoveComments(contents)
671 JniParams.SetFullyQualifiedClass(fully_qualified_class)
672 JniParams.ExtractImportsAndInnerClasses(contents)
673 jni_namespace = ExtractJNINamespace(contents) or options.namespace
674 natives = ExtractNatives(contents, options.ptr_type)
675 called_by_natives = ExtractCalledByNatives(contents)
676 if len(natives) == 0 and len(called_by_natives) == 0:
677 raise SyntaxError('Unable to find any JNI methods for %s.' %
678 fully_qualified_class)
679 inl_header_file_generator = InlHeaderFileGenerator(
680 jni_namespace, fully_qualified_class, natives, called_by_natives,
682 self.content = inl_header_file_generator.GetContent()
685 def _RemoveComments(cls, contents):
686 # We need to support both inline and block comments, and we need to handle
687 # strings that contain '//' or '/*'.
688 # TODO(bulach): This is a bit hacky. It would be cleaner to use a real Java
689 # parser. Maybe we could ditch JNIFromJavaSource and just always use
690 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
691 # http://code.google.com/p/chromium/issues/detail?id=138941
693 # Replace matches that are comments with nothing; return literals/strings
696 if s.startswith('/'):
700 return cls._comment_remover_regex.sub(replacer, contents)
702 def GetContent(self):
706 def CreateFromFile(java_file_name, options):
707 contents = file(java_file_name).read()
708 fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
710 return JNIFromJavaSource(contents, fully_qualified_class, options)
713 class InlHeaderFileGenerator(object):
714 """Generates an inline header file for JNI integration."""
716 def __init__(self, namespace, fully_qualified_class, natives,
717 called_by_natives, constant_fields, options):
718 self.namespace = namespace
719 self.fully_qualified_class = fully_qualified_class
720 self.class_name = self.fully_qualified_class.split('/')[-1]
721 self.natives = natives
722 self.called_by_natives = called_by_natives
723 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
724 self.constant_fields = constant_fields
725 self.options = options
726 self.init_native = self.ExtractInitNative(options)
728 def ExtractInitNative(self, options):
729 for native in self.natives:
730 if options.jni_init_native_name == 'native' + native.name:
731 self.natives.remove(native)
735 def GetContent(self):
736 """Returns the content of the JNI binding file."""
737 template = Template("""\
738 // Copyright 2014 The Chromium Authors. All rights reserved.
739 // Use of this source code is governed by a BSD-style license that can be
740 // found in the LICENSE file.
743 // This file is autogenerated by
746 // ${FULLY_QUALIFIED_CLASS}
748 #ifndef ${HEADER_GUARD}
749 #define ${HEADER_GUARD}
755 #include "base
/android
/jni_int_wrapper
.h
"
757 // Step 1: forward declarations.
759 $CLASS_PATH_DEFINITIONS
760 $METHOD_ID_DEFINITIONS
764 $FORWARD_DECLARATIONS
768 // Step 2: method stubs.
771 // Step 3: RegisterNatives.
775 $JNI_REGISTER_NATIVES
776 #endif // ${HEADER_GUARD}
779 'SCRIPT_NAME': self.options.script_name,
780 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
781 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
782 'METHOD_ID_DEFINITIONS': self.GetMethodIDDefinitionsString(),
783 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
784 'CONSTANT_FIELDS': self.GetConstantFieldsString(),
785 'METHOD_STUBS': self.GetMethodStubsString(),
786 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
787 'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(),
788 'REGISTER_NATIVES': self.GetRegisterNativesString(),
789 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
790 'HEADER_GUARD': self.header_guard,
791 'INCLUDES': self.GetIncludesString(),
792 'JNI_REGISTER_NATIVES': self.GetJNIRegisterNativesString()
794 return WrapOutput(template.substitute(values))
796 def GetClassPathDefinitionsString(self):
798 ret += [self.GetClassPathDefinitions()]
799 return '\n'.join(ret)
801 def GetMethodIDDefinitionsString(self):
802 """Returns the definition of method ids for the called by native methods."""
803 if not self.options.eager_called_by_natives:
805 template = Template("""\
806 jmethodID g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = NULL;""")
808 for called_by_native in self.called_by_natives:
810 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
811 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
813 ret += [template.substitute(values)]
814 return '\n'.join(ret)
816 def GetForwardDeclarationsString(self):
818 for native in self.natives:
819 if native.type != 'method':
820 ret += [self.GetForwardDeclaration(native)]
821 if self.options.native_exports and ret:
822 return '\nextern "C
" {\n' + "\n".join(ret) + '\n}; // extern "C
"'
823 return '\n'.join(ret)
825 def GetConstantFieldsString(self):
826 if not self.constant_fields:
828 ret = ['enum Java_%s_constant_fields {' % self.class_name]
829 for c in self.constant_fields:
830 ret += [' %s = %s,' % (c.name, c.value)]
832 return '\n'.join(ret)
834 def GetMethodStubsString(self):
835 """Returns the code corresponding to method stubs."""
837 for native in self.natives:
838 if native.type == 'method':
839 ret += [self.GetNativeMethodStubString(native)]
840 if self.options.eager_called_by_natives:
841 ret += self.GetEagerCalledByNativeMethodStubs()
843 ret += self.GetLazyCalledByNativeMethodStubs()
845 if self.options.native_exports and ret:
846 return '\nextern "C
" {\n' + "\n".join(ret) + '\n}; // extern "C
"'
847 return '\n'.join(ret)
849 def GetLazyCalledByNativeMethodStubs(self):
850 return [self.GetLazyCalledByNativeMethodStub(called_by_native)
851 for called_by_native in self.called_by_natives]
853 def GetEagerCalledByNativeMethodStubs(self):
855 if self.called_by_natives:
856 ret += ['namespace {']
857 for called_by_native in self.called_by_natives:
858 ret += [self.GetEagerCalledByNativeMethodStub(called_by_native)]
859 ret += ['} // namespace']
862 def GetIncludesString(self):
863 if not self.options.includes:
865 includes = self.options.includes.split(',')
866 return '\n'.join('#include "%s"' % x for x in includes)
868 def GetKMethodsString(self, clazz):
870 for native in self.natives:
871 if (native.java_class_name == clazz or
872 (not native.java_class_name and clazz == self.class_name)):
873 ret += [self.GetKMethodArrayEntry(native)]
874 return '\n'.join(ret)
876 def SubstituteNativeMethods(self, template):
877 """Substitutes JAVA_CLASS and KMETHODS in the provided template."""
879 all_classes = self.GetUniqueClasses(self.natives)
880 all_classes[self.class_name] = self.fully_qualified_class
881 for clazz in all_classes:
882 kmethods = self.GetKMethodsString(clazz)
884 values = {'JAVA_CLASS': clazz,
885 'KMETHODS': kmethods}
886 ret += [template.substitute(values)]
887 if not ret: return ''
888 return '\n' + '\n'.join(ret)
890 def GetJNINativeMethodsString(self):
891 """Returns the implementation of the array of native methods."""
892 if self.options.native_exports and not self.options.native_exports_optional:
894 template = Template("""\
895 static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
899 return self.SubstituteNativeMethods(template)
901 def GetRegisterCalledByNativesImplString(self):
902 """Returns the code for registering the called by native methods."""
903 if not self.options.eager_called_by_natives:
905 template = Template("""\
906 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = ${GET_METHOD_ID_IMPL}
907 if (g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} == NULL) {
912 for called_by_native in self.called_by_natives:
914 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
915 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
916 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native),
918 ret += [template.substitute(values)]
919 return '\n'.join(ret)
921 def GetRegisterNativesString(self):
922 """Returns the code for RegisterNatives."""
923 template = Template("""\
924 ${REGISTER_NATIVES_SIGNATURE} {
932 signature = 'static bool RegisterNativesImpl(JNIEnv* env'
934 signature += ', jclass clazz)'
939 if self.options.native_exports_optional:
941 if (base::android::IsManualJniRegistrationDisabled()) return true;
944 natives = self.GetRegisterNativesImplString()
945 called_by_natives = self.GetRegisterCalledByNativesImplString()
946 values = {'REGISTER_NATIVES_SIGNATURE': signature,
947 'EARLY_EXIT': early_exit,
948 'CLASSES': self.GetFindClasses(),
950 'CALLED_BY_NATIVES': called_by_natives,
952 return template.substitute(values)
954 def GetRegisterNativesImplString(self):
955 """Returns the shared implementation for RegisterNatives."""
956 if self.options.native_exports and not self.options.native_exports_optional:
959 template = Template("""\
960 const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
962 if (env->RegisterNatives(${JAVA_CLASS}_clazz(env),
963 kMethods${JAVA_CLASS},
964 kMethods${JAVA_CLASS}Size) < 0) {
965 jni_generator::HandleRegistrationError(
966 env, ${JAVA_CLASS}_clazz(env), __FILE__);
970 return self.SubstituteNativeMethods(template)
972 def GetJNIRegisterNativesString(self):
973 """Returns the implementation for the JNI registration of native methods."""
974 if not self.init_native:
977 template = Template("""\
978 extern "C
" JNIEXPORT bool JNICALL
979 Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) {
980 return ${NAMESPACE}RegisterNativesImpl(env, clazz);
984 if self.options.native_exports:
985 java_name = JniParams.RemapClassName(self.fully_qualified_class)
986 java_name = java_name.replace('_', '_1').replace('/', '_')
988 java_name = self.fully_qualified_class.replace('/', '_')
992 namespace = self.namespace + '::'
993 values = {'FULLY_QUALIFIED_CLASS': java_name,
994 'INIT_NATIVE_NAME': 'native' + self.init_native.name,
995 'NAMESPACE': namespace,
996 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString()
998 return template.substitute(values)
1000 def GetOpenNamespaceString(self):
1002 all_namespaces = ['namespace %s {' % ns
1003 for ns in self.namespace.split('::')]
1004 return '\n'.join(all_namespaces)
1007 def GetCloseNamespaceString(self):
1009 all_namespaces = ['} // namespace %s' % ns
1010 for ns in self.namespace.split('::')]
1011 all_namespaces.reverse()
1012 return '\n'.join(all_namespaces) + '\n'
1015 def GetJNIFirstParam(self, native):
1017 if native.type == 'method':
1018 ret = ['jobject jcaller']
1019 elif native.type == 'function':
1021 ret = ['jclass jcaller']
1023 ret = ['jobject jcaller']
1026 def GetParamsInDeclaration(self, native):
1027 """Returns the params for the stub declaration.
1030 native: the native dictionary describing the method.
1033 A string containing the params.
1035 return ',\n '.join(self.GetJNIFirstParam(native) +
1036 [JavaDataTypeToC(param.datatype) + ' ' +
1038 for param in native.params])
1040 def GetCalledByNativeParamsInDeclaration(self, called_by_native):
1041 return ',\n '.join([
1042 JavaDataTypeToCForCalledByNativeParam(param.datatype) + ' ' +
1044 for param in called_by_native.params])
1046 def GetStubName(self, native):
1047 """Return the name of the stub function for this native method.
1050 native: the native dictionary describing the method.
1053 A string with the stub function name. For native exports mode this is the
1054 Java_* symbol name required by the JVM; otherwise it is just the name of
1055 the native method itself.
1057 if self.options.native_exports:
1058 template = Template("Java_${JAVA_NAME}_native${NAME}
")
1060 java_name = JniParams.RemapClassName(self.fully_qualified_class)
1061 java_name = java_name.replace('_', '_1').replace('/', '_')
1062 if native.java_class_name:
1063 java_name += '_00024' + native.java_class_name
1065 values = {'NAME': native.name,
1066 'JAVA_NAME': java_name}
1067 return template.substitute(values)
1071 def GetForwardDeclaration(self, native):
1073 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
1075 if self.options.native_exports:
1077 __attribute__((visibility("default
")))
1078 ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS}) {
1079 return ${NAME}(${PARAMS_IN_CALL});
1082 template = Template(template_str)
1084 if not self.options.pure_native_methods:
1085 params_in_call = ['env', 'jcaller']
1086 params_in_call = ', '.join(params_in_call + [p.name for p in native.params])
1088 values = {'RETURN': JavaDataTypeToC(native.return_type),
1089 'NAME': native.name,
1090 'PARAMS': self.GetParamsInDeclaration(native),
1091 'PARAMS_IN_CALL': params_in_call,
1092 'STUB_NAME': self.GetStubName(native)}
1093 return template.substitute(values)
1095 def GetNativeMethodStubString(self, native):
1096 """Returns stubs for native methods."""
1097 if self.options.native_exports:
1099 __attribute__((visibility("default
")))
1100 ${RETURN} ${STUB_NAME}(JNIEnv* env,
1101 ${PARAMS_IN_DECLARATION}) {"""
1104 static ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {"""
1106 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
1107 CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}
"${OPTIONAL_ERROR_RETURN});
1108 return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL};
1112 template = Template(template_str)
1114 if not self.options.pure_native_methods:
1115 params = ['env', 'jcaller']
1116 params_in_call = ', '.join(params + [p.name for p in native.params[1:]])
1118 return_type = JavaDataTypeToC(native.return_type)
1119 optional_error_return = JavaReturnValueToC(native.return_type)
1120 if optional_error_return:
1121 optional_error_return = ', ' + optional_error_return
1123 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
1124 post_call = '.Release()'
1127 'RETURN': return_type,
1128 'OPTIONAL_ERROR_RETURN': optional_error_return,
1129 'NAME': native.name,
1130 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
1131 'PARAM0_NAME': native.params[0].name,
1132 'P0_TYPE': native.p0_type,
1133 'PARAMS_IN_CALL': params_in_call,
1134 'POST_CALL': post_call,
1135 'STUB_NAME': self.GetStubName(native),
1137 return template.substitute(values)
1139 def GetArgument(self, param):
1140 return ('as_jint(' + param.name + ')'
1141 if param.datatype == 'int' else param.name)
1143 def GetArgumentsInCall(self, params):
1144 """Return a string of arguments to call from native into Java"""
1145 return [self.GetArgument(p) for p in params]
1147 def GetCalledByNativeValues(self, called_by_native):
1148 """Fills in necessary values for the CalledByNative methods."""
1149 java_class = called_by_native.java_class_name or self.class_name
1150 if called_by_native.static or called_by_native.is_constructor:
1151 first_param_in_declaration = ''
1152 first_param_in_call = ('%s_clazz(env)' % java_class)
1154 first_param_in_declaration = ', jobject obj'
1155 first_param_in_call = 'obj'
1156 params_in_declaration = self.GetCalledByNativeParamsInDeclaration(
1158 if params_in_declaration:
1159 params_in_declaration = ', ' + params_in_declaration
1160 params_in_call = ', '.join(self.GetArgumentsInCall(called_by_native.params))
1162 params_in_call = ', ' + params_in_call
1165 if called_by_native.static_cast:
1166 pre_call = 'static_cast<%s>(' % called_by_native.static_cast
1168 check_exception = ''
1169 if not called_by_native.unchecked:
1170 check_exception = 'jni_generator::CheckException(env);'
1171 return_type = JavaDataTypeToC(called_by_native.return_type)
1172 optional_error_return = JavaReturnValueToC(called_by_native.return_type)
1173 if optional_error_return:
1174 optional_error_return = ', ' + optional_error_return
1175 return_declaration = ''
1177 if return_type != 'void':
1178 pre_call = ' ' + pre_call
1179 return_declaration = return_type + ' ret ='
1180 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
1181 return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>'
1182 return_clause = 'return ' + return_type + '(env, ret);'
1184 return_clause = 'return ret;'
1186 'JAVA_CLASS': java_class,
1187 'RETURN_TYPE': return_type,
1188 'OPTIONAL_ERROR_RETURN': optional_error_return,
1189 'RETURN_DECLARATION': return_declaration,
1190 'RETURN_CLAUSE': return_clause,
1191 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration,
1192 'PARAMS_IN_DECLARATION': params_in_declaration,
1193 'PRE_CALL': pre_call,
1194 'POST_CALL': post_call,
1195 'ENV_CALL': called_by_native.env_call,
1196 'FIRST_PARAM_IN_CALL': first_param_in_call,
1197 'PARAMS_IN_CALL': params_in_call,
1198 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
1199 'CHECK_EXCEPTION': check_exception,
1200 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native)
1203 def GetEagerCalledByNativeMethodStub(self, called_by_native):
1204 """Returns the implementation of the called by native method."""
1205 template = Template("""
1206 static ${RETURN_TYPE} ${METHOD_ID_VAR_NAME}(\
1207 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION}) {
1208 ${RETURN_DECLARATION}${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
1209 g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}${PARAMS_IN_CALL})${POST_CALL};
1212 values = self.GetCalledByNativeValues(called_by_native)
1213 return template.substitute(values)
1215 def GetLazyCalledByNativeMethodStub(self, called_by_native):
1216 """Returns a string."""
1217 function_signature_template = Template("""\
1218 static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\
1219 JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""")
1220 function_header_template = Template("""\
1221 ${FUNCTION_SIGNATURE} {""")
1222 function_header_with_unused_template = Template("""\
1223 ${FUNCTION_SIGNATURE} __attribute__ ((unused));
1224 ${FUNCTION_SIGNATURE} {""")
1225 template = Template("""
1226 static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0;
1228 /* Must call RegisterNativesImpl() */
1229 CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL},
1230 ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN});
1231 jmethodID method_id =
1232 ${GET_METHOD_ID_IMPL}
1233 ${RETURN_DECLARATION}
1234 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
1235 method_id${PARAMS_IN_CALL})${POST_CALL};
1239 values = self.GetCalledByNativeValues(called_by_native)
1240 values['FUNCTION_SIGNATURE'] = (
1241 function_signature_template.substitute(values))
1242 if called_by_native.system_class:
1243 values['FUNCTION_HEADER'] = (
1244 function_header_with_unused_template.substitute(values))
1246 values['FUNCTION_HEADER'] = function_header_template.substitute(values)
1247 return template.substitute(values)
1249 def GetKMethodArrayEntry(self, native):
1250 template = Template(' { "native${NAME}
", ${JNI_SIGNATURE}, ' +
1251 'reinterpret_cast<void*>(${STUB_NAME}) },')
1252 values = {'NAME': native.name,
1253 'JNI_SIGNATURE': JniParams.Signature(native.params,
1256 'STUB_NAME': self.GetStubName(native)}
1257 return template.substitute(values)
1259 def GetUniqueClasses(self, origin):
1260 ret = {self.class_name: self.fully_qualified_class}
1261 for entry in origin:
1262 class_name = self.class_name
1263 jni_class_path = self.fully_qualified_class
1264 if entry.java_class_name:
1265 class_name = entry.java_class_name
1266 jni_class_path = self.fully_qualified_class + '$' + class_name
1267 ret[class_name] = jni_class_path
1270 def GetClassPathDefinitions(self):
1271 """Returns the ClassPath constants."""
1273 template = Template("""\
1274 const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}
";""")
1275 native_classes = self.GetUniqueClasses(self.natives)
1276 called_by_native_classes = self.GetUniqueClasses(self.called_by_natives)
1277 if self.options.native_exports:
1278 all_classes = called_by_native_classes
1280 all_classes = native_classes
1281 all_classes.update(called_by_native_classes)
1283 for clazz in all_classes:
1285 'JAVA_CLASS': clazz,
1286 'JNI_CLASS_PATH': JniParams.RemapClassName(all_classes[clazz]),
1288 ret += [template.substitute(values)]
1291 class_getter_methods = []
1292 if self.options.native_exports:
1293 template = Template("""\
1294 // Leaking this jclass as we cannot use LazyInstance from some threads.
1295 base::subtle::AtomicWord g_${JAVA_CLASS}_clazz __attribute__((unused)) = 0;
1296 #define ${JAVA_CLASS}_clazz(env) \
1297 base::android::LazyGetClass(env, k${JAVA_CLASS}ClassPath, \
1298 &g_${JAVA_CLASS}_clazz)""")
1300 template = Template("""\
1301 // Leaking this jclass as we cannot use LazyInstance from some threads.
1302 jclass g_${JAVA_CLASS}_clazz = NULL;
1303 #define ${JAVA_CLASS}_clazz(env) g_${JAVA_CLASS}_clazz""")
1305 for clazz in called_by_native_classes:
1307 'JAVA_CLASS': clazz,
1309 ret += [template.substitute(values)]
1311 return '\n'.join(ret)
1313 def GetFindClasses(self):
1314 """Returns the imlementation of FindClass for all known classes."""
1315 if self.init_native:
1316 if self.options.native_exports:
1317 template = Template("""\
1318 base::subtle::Release_Store(&g_${JAVA_CLASS}_clazz,
1319 static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));""")
1321 template = Template("""\
1322 g_${JAVA_CLASS}_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));""")
1324 if self.options.native_exports:
1326 template = Template("""\
1327 g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
1328 base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""")
1330 for clazz in self.GetUniqueClasses(self.called_by_natives):
1331 values = {'JAVA_CLASS': clazz}
1332 ret += [template.substitute(values)]
1333 return '\n'.join(ret)
1335 def GetMethodIDImpl(self, called_by_native):
1336 """Returns the implementation of GetMethodID."""
1337 if self.options.eager_called_by_natives:
1338 template = Template("""\
1339 env->Get${STATIC_METHOD_PART}MethodID(
1340 ${JAVA_CLASS}_clazz(env),
1341 "${JNI_NAME}
", ${JNI_SIGNATURE});""")
1343 template = Template("""\
1344 base::android::MethodID::LazyGet<
1345 base::android::MethodID::TYPE_${STATIC}>(
1346 env, ${JAVA_CLASS}_clazz(env),
1349 &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
1351 jni_name = called_by_native.name
1352 jni_return_type = called_by_native.return_type
1353 if called_by_native.is_constructor:
1355 jni_return_type = 'void'
1356 if called_by_native.signature:
1357 signature = called_by_native.signature
1359 signature = JniParams.Signature(called_by_native.params,
1363 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
1364 'JNI_NAME': jni_name,
1365 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
1366 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE',
1367 'STATIC_METHOD_PART': 'Static' if called_by_native.static else '',
1368 'JNI_SIGNATURE': signature,
1370 return template.substitute(values)
1373 def WrapOutput(output):
1375 for line in output.splitlines():
1376 # Do not wrap lines under 80 characters or preprocessor directives.
1377 if len(line) < 80 or line.lstrip()[:1] == '#':
1378 stripped = line.rstrip()
1379 if len(ret) == 0 or len(ret[-1]) or len(stripped):
1380 ret.append(stripped)
1382 first_line_indent = ' ' * (len(line) - len(line.lstrip()))
1383 subsequent_indent = first_line_indent + ' ' * 4
1384 if line.startswith('//'):
1385 subsequent_indent = '//' + subsequent_indent
1386 wrapper = textwrap.TextWrapper(width=80,
1387 subsequent_indent=subsequent_indent,
1388 break_long_words=False)
1389 ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)]
1391 return '\n'.join(ret)
1394 def ExtractJarInputFile(jar_file, input_file, out_dir):
1395 """Extracts input file from jar and returns the filename.
1397 The input file is extracted to the same directory that the generated jni
1398 headers will be placed in. This is passed as an argument to script.
1401 jar_file: the jar file containing the input files to extract.
1402 input_files: the list of files to extract from the jar file.
1403 out_dir: the name of the directories to extract to.
1406 the name of extracted input file.
1408 jar_file = zipfile.ZipFile(jar_file)
1410 out_dir = os.path.join(out_dir, os.path.dirname(input_file))
1412 os.makedirs(out_dir)
1413 except OSError as e:
1414 if e.errno != errno.EEXIST:
1416 extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
1417 with open(extracted_file_name, 'w') as outfile:
1418 outfile.write(jar_file.read(input_file))
1420 return extracted_file_name
1423 def GenerateJNIHeader(input_file, output_file, options):
1425 if os.path.splitext(input_file)[1] == '.class':
1426 jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options)
1427 content = jni_from_javap.GetContent()
1429 jni_from_java_source = JNIFromJavaSource.CreateFromFile(
1430 input_file, options)
1431 content = jni_from_java_source.GetContent()
1432 except ParseError, e:
1436 if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
1437 os.makedirs(os.path.dirname(os.path.abspath(output_file)))
1438 if options.optimize_generation and os.path.exists(output_file):
1439 with file(output_file, 'r') as f:
1440 existing_content = f.read()
1441 if existing_content == content:
1443 with file(output_file, 'w') as f:
1449 def GetScriptName():
1450 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
1452 for idx, value in enumerate(script_components):
1453 if value == 'base' or value == 'third_party':
1456 return os.sep.join(script_components[base_index:])
1460 usage = """usage: %prog [OPTIONS]
1461 This script will parse the given java source code extracting the native
1462 declarations and print the header file to stdout (or a file).
1463 See SampleForTests.java for more details.
1465 option_parser = optparse.OptionParser(usage=usage)
1466 build_utils.AddDepfileOption(option_parser)
1468 option_parser.add_option('-j', '--jar_file', dest='jar_file',
1469 help='Extract the list of input files from'
1470 ' a specified jar file.'
1471 ' Uses javap to extract the methods from a'
1472 ' pre-compiled class. --input should point'
1473 ' to pre-compiled Java .class files.')
1474 option_parser.add_option('-n', dest='namespace',
1475 help='Uses as a namespace in the generated header '
1476 'instead of the javap class name, or when there is '
1477 'no JNINamespace annotation in the java source.')
1478 option_parser.add_option('--input_file',
1479 help='Single input file name. The output file name '
1480 'will be derived from it. Must be used with '
1482 option_parser.add_option('--output_dir',
1483 help='The output directory. Must be used with '
1485 option_parser.add_option('--optimize_generation', type="int",
1486 default=0, help='Whether we should optimize JNI '
1487 'generation by not regenerating files if they have '
1489 option_parser.add_option('--jarjar',
1490 help='Path to optional jarjar rules file.')
1491 option_parser.add_option('--script_name', default=GetScriptName(),
1492 help='The name of this script in the generated '
1494 option_parser.add_option('--includes',
1495 help='The comma-separated list of header files to '
1496 'include in the generated header.')
1497 option_parser.add_option('--pure_native_methods',
1498 action='store_true', dest='pure_native_methods',
1499 help='When true, the native methods will be called '
1500 'without any JNI-specific arguments.')
1501 option_parser.add_option('--ptr_type', default='int',
1502 type='choice', choices=['int', 'long'],
1503 help='The type used to represent native pointers in '
1504 'Java code. For 32-bit, use int; '
1505 'for 64-bit, use long.')
1506 option_parser.add_option('--jni_init_native_name', default='',
1507 help='The name of the JNI registration method that '
1508 'is used to initialize all native methods. If a '
1509 'method with this name is not present in the Java '
1510 'source file, setting this option is a no-op. When '
1511 'a method with this name is found however, the '
1512 'naming convention Java_<packageName>_<className> '
1513 'will limit the initialization to only the '
1515 option_parser.add_option('--eager_called_by_natives',
1516 action='store_true', dest='eager_called_by_natives',
1517 help='When true, the called-by-native methods will '
1518 'be initialized in a non-atomic way.')
1519 option_parser.add_option('--cpp', default='cpp',
1520 help='The path to cpp command.')
1521 option_parser.add_option('--javap', default='javap',
1522 help='The path to javap command.')
1523 option_parser.add_option('--native_exports', action='store_true',
1524 help='Native method registration through .so '
1526 option_parser.add_option('--native_exports_optional', action='store_true',
1527 help='Support both explicit and native method'
1529 options, args = option_parser.parse_args(argv)
1530 if options.native_exports_optional:
1531 options.native_exports = True
1532 if options.jar_file:
1533 input_file = ExtractJarInputFile(options.jar_file, options.input_file,
1535 elif options.input_file:
1536 input_file = options.input_file
1538 option_parser.print_help()
1539 print '\nError: Must specify --jar_file or --input_file.'
1542 if options.output_dir:
1543 root_name = os.path.splitext(os.path.basename(input_file))[0]
1544 output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
1546 with open(options.jarjar) as f:
1547 JniParams.SetJarJarMappings(f.read())
1548 GenerateJNIHeader(input_file, output_file, options)
1551 build_utils.WriteDepfile(
1553 build_utils.GetPythonDependencies())
1556 if __name__ == '__main__':
1557 sys.exit(main(sys.argv))