blob: 8249fc9edff9b827a1a8a110d667eda21308196d [file] [log] [blame]
maruel@chromium.orgbe8446a2012-04-21 23:30:11 +09001#!/usr/bin/env python
bulach@chromium.org6079a072012-02-24 09:09:38 +09002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Extracts native methods from a Java file and generates the JNI bindings.
7If you change this, please run and update the tests."""
8
9import collections
torne@chromium.org108c37d2013-03-08 22:52:50 +090010import errno
bulach@chromium.org6079a072012-02-24 09:09:38 +090011import optparse
12import os
13import re
14import string
15from string import Template
16import subprocess
17import sys
18import textwrap
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +090019import zipfile
bulach@chromium.org6079a072012-02-24 09:09:38 +090020
cjhopman@chromium.orgfb98e332014-06-25 08:38:17 +090021CHROMIUM_SRC = os.path.join(
22 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)
23BUILD_ANDROID_GYP = os.path.join(
24 CHROMIUM_SRC, 'build', 'android', 'gyp')
25
26sys.path.append(BUILD_ANDROID_GYP)
27
28from util import build_utils
29
bulach@chromium.org6079a072012-02-24 09:09:38 +090030
31class ParseError(Exception):
32 """Exception thrown when we can't parse the input file."""
33
34 def __init__(self, description, *context_lines):
35 Exception.__init__(self)
36 self.description = description
37 self.context_lines = context_lines
38
39 def __str__(self):
40 context = '\n'.join(self.context_lines)
41 return '***\nERROR: %s\n\n%s\n***' % (self.description, context)
42
43
44class Param(object):
45 """Describes a param for a method, either java or native."""
46
47 def __init__(self, **kwargs):
48 self.datatype = kwargs['datatype']
49 self.name = kwargs['name']
bulach@chromium.org6079a072012-02-24 09:09:38 +090050
51
52class NativeMethod(object):
53 """Describes a C/C++ method that is called by Java code"""
54
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']
61 if self.params:
62 assert type(self.params) is list
63 assert type(self.params[0]) is Param
64 if (self.params and
bulach@chromium.orga022cf62013-11-05 09:54:22 +090065 self.params[0].datatype == kwargs.get('ptr_type', 'int') and
bulach@chromium.org6079a072012-02-24 09:09:38 +090066 self.params[0].name.startswith('native')):
67 self.type = 'method'
bulach@chromium.org1c775752012-06-22 19:03:16 +090068 self.p0_type = self.params[0].name[len('native'):]
69 if kwargs.get('native_class_name'):
70 self.p0_type = kwargs['native_class_name']
bulach@chromium.org6079a072012-02-24 09:09:38 +090071 else:
72 self.type = 'function'
73 self.method_id_var_name = kwargs.get('method_id_var_name', None)
74
75
76class CalledByNative(object):
77 """Describes a java method exported to c/c++"""
78
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']
bulach@chromium.org6079a072012-02-24 09:09:38 +090085 self.name = kwargs['name']
86 self.params = kwargs['params']
87 self.method_id_var_name = kwargs.get('method_id_var_name', None)
bulach@chromium.org37fc9112013-10-26 01:27:03 +090088 self.signature = kwargs.get('signature')
bulach@chromium.org31af7532012-09-24 20:01:41 +090089 self.is_constructor = kwargs.get('is_constructor', False)
90 self.env_call = GetEnvCall(self.is_constructor, self.static,
91 self.return_type)
92 self.static_cast = GetStaticCastForReturnType(self.return_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +090093
94
bulach@chromium.org658dbc02014-02-27 04:59:00 +090095class ConstantField(object):
96 def __init__(self, **kwargs):
97 self.name = kwargs['name']
98 self.value = kwargs['value']
99
100
bulach@chromium.org6079a072012-02-24 09:09:38 +0900101def JavaDataTypeToC(java_type):
102 """Returns a C datatype for the given java type."""
103 java_pod_type_map = {
104 'int': 'jint',
105 'byte': 'jbyte',
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900106 'char': 'jchar',
107 'short': 'jshort',
bulach@chromium.org6079a072012-02-24 09:09:38 +0900108 'boolean': 'jboolean',
109 'long': 'jlong',
110 'double': 'jdouble',
111 'float': 'jfloat',
112 }
113 java_type_map = {
114 'void': 'void',
115 'String': 'jstring',
tornec9124232015-08-27 23:57:06 +0900116 'Throwable': 'jthrowable',
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900117 'java/lang/String': 'jstring',
dtrainor@chromium.orgf7e99772012-12-06 08:27:41 +0900118 'java/lang/Class': 'jclass',
tornec9124232015-08-27 23:57:06 +0900119 'java/lang/Throwable': 'jthrowable',
bulach@chromium.org6079a072012-02-24 09:09:38 +0900120 }
dtrainor@chromium.orgf7e99772012-12-06 08:27:41 +0900121
bulach@chromium.org6079a072012-02-24 09:09:38 +0900122 if java_type in java_pod_type_map:
123 return java_pod_type_map[java_type]
124 elif java_type in java_type_map:
125 return java_type_map[java_type]
126 elif java_type.endswith('[]'):
127 if java_type[:-2] in java_pod_type_map:
128 return java_pod_type_map[java_type[:-2]] + 'Array'
129 return 'jobjectArray'
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900130 elif java_type.startswith('Class'):
131 # Checking just the start of the name, rather than a direct comparison,
132 # in order to handle generics.
133 return 'jclass'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900134 else:
135 return 'jobject'
136
137
tornec510c592015-09-04 20:16:35 +0900138def WrapCTypeForDeclaration(c_type):
139 """Wrap the C datatype in a JavaRef if required."""
140 if re.match(RE_SCOPED_JNI_TYPES, c_type):
torne9854c722016-08-05 00:44:06 +0900141 return 'const base::android::JavaParamRef<' + c_type + '>&'
tornec510c592015-09-04 20:16:35 +0900142 else:
143 return c_type
144
145
146def JavaDataTypeToCForDeclaration(java_type):
147 """Returns a JavaRef-wrapped C datatype for the given java type."""
148 return WrapCTypeForDeclaration(JavaDataTypeToC(java_type))
149
150
anton@chromium.org9d3b13b2014-05-07 23:14:10 +0900151def JavaDataTypeToCForCalledByNativeParam(java_type):
152 """Returns a C datatype to be when calling from native."""
153 if java_type == 'int':
154 return 'JniIntWrapper'
155 else:
torne28061e92016-08-09 01:29:18 +0900156 c_type = JavaDataTypeToC(java_type)
157 if re.match(RE_SCOPED_JNI_TYPES, c_type):
158 return 'const base::android::JavaRefOrBare<' + c_type + '>&'
159 else:
160 return c_type
anton@chromium.org9d3b13b2014-05-07 23:14:10 +0900161
162
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900163def JavaReturnValueToC(java_type):
164 """Returns a valid C return value for the given java type."""
165 java_pod_type_map = {
166 'int': '0',
167 'byte': '0',
168 'char': '0',
169 'short': '0',
170 'boolean': 'false',
171 'long': '0',
172 'double': '0',
173 'float': '0',
174 'void': ''
175 }
176 return java_pod_type_map.get(java_type, 'NULL')
177
178
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900179class JniParams(object):
Yipeng Wang60438ac2017-06-17 06:08:33 +0900180 """Get JNI related parameters."""
bulach@chromium.org6079a072012-02-24 09:09:38 +0900181
Yipeng Wang60438ac2017-06-17 06:08:33 +0900182 def __init__(self, fully_qualified_class):
183 self._fully_qualified_class = 'L' + fully_qualified_class
184 self._package = '/'.join(fully_qualified_class.split('/')[:-1])
185 self._imports = []
186 self._inner_classes = []
187 self._implicit_imports = []
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900188
Yipeng Wang60438ac2017-06-17 06:08:33 +0900189 def ExtractImportsAndInnerClasses(self, contents):
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900190 contents = contents.replace('\n', '')
191 re_import = re.compile(r'import.*?(?P<class>\S*?);')
192 for match in re.finditer(re_import, contents):
Yipeng Wang60438ac2017-06-17 06:08:33 +0900193 self._imports += ['L' + match.group('class').replace('.', '/')]
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900194
195 re_inner = re.compile(r'(class|interface)\s+?(?P<name>\w+?)\W')
196 for match in re.finditer(re_inner, contents):
197 inner = match.group('name')
Yipeng Wang60438ac2017-06-17 06:08:33 +0900198 if not self._fully_qualified_class.endswith(inner):
199 self._inner_classes += [self._fully_qualified_class + '$' +
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900200 inner]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900201
qsr@chromium.org47bd3462014-05-19 23:26:49 +0900202 re_additional_imports = re.compile(
qsr@chromium.org74f541e2014-05-23 23:44:37 +0900203 r'@JNIAdditionalImport\(\s*{?(?P<class_names>.*?)}?\s*\)')
qsr@chromium.org47bd3462014-05-19 23:26:49 +0900204 for match in re.finditer(re_additional_imports, contents):
qsr@chromium.org74f541e2014-05-23 23:44:37 +0900205 for class_name in match.group('class_names').split(','):
Yipeng Wang60438ac2017-06-17 06:08:33 +0900206 self._AddAdditionalImport(class_name.strip())
qsr@chromium.org47bd3462014-05-19 23:26:49 +0900207
Yipeng Wang60438ac2017-06-17 06:08:33 +0900208 def JavaToJni(self, param):
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900209 """Converts a java param into a JNI signature type."""
210 pod_param_map = {
211 'int': 'I',
212 'boolean': 'Z',
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900213 'char': 'C',
214 'short': 'S',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900215 'long': 'J',
216 'double': 'D',
217 'float': 'F',
218 'byte': 'B',
219 'void': 'V',
220 }
221 object_param_list = [
222 'Ljava/lang/Boolean',
223 'Ljava/lang/Integer',
224 'Ljava/lang/Long',
225 'Ljava/lang/Object',
226 'Ljava/lang/String',
dtrainor@chromium.orgf7e99772012-12-06 08:27:41 +0900227 'Ljava/lang/Class',
aurimas9e29f732015-06-25 07:53:10 +0900228 'Ljava/lang/CharSequence',
229 'Ljava/lang/Runnable',
230 'Ljava/lang/Throwable',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900231 ]
bulach@chromium.org69931302014-05-08 04:16:23 +0900232
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900233 prefix = ''
234 # Array?
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900235 while param[-2:] == '[]':
236 prefix += '['
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900237 param = param[:-2]
238 # Generic?
239 if '<' in param:
240 param = param[:param.index('<')]
241 if param in pod_param_map:
242 return prefix + pod_param_map[param]
243 if '/' in param:
244 # Coming from javap, use the fully qualified param directly.
torneb66773d2016-05-10 02:08:20 +0900245 return prefix + 'L' + param + ';'
bulach@chromium.org4a130cc2014-03-28 20:15:06 +0900246
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900247 for qualified_name in (object_param_list +
Yipeng Wang60438ac2017-06-17 06:08:33 +0900248 [self._fully_qualified_class] + self._inner_classes):
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900249 if (qualified_name.endswith('/' + param) or
250 qualified_name.endswith('$' + param.replace('.', '$')) or
251 qualified_name == 'L' + param):
torneb66773d2016-05-10 02:08:20 +0900252 return prefix + qualified_name + ';'
bulach@chromium.org7156c762012-11-13 23:35:00 +0900253
254 # Is it from an import? (e.g. referecing Class from import pkg.Class;
255 # note that referencing an inner class Inner from import pkg.Class.Inner
256 # is not supported).
Yipeng Wang60438ac2017-06-17 06:08:33 +0900257 for qualified_name in self._imports:
bulach@chromium.org7156c762012-11-13 23:35:00 +0900258 if qualified_name.endswith('/' + param):
259 # Ensure it's not an inner class.
260 components = qualified_name.split('/')
261 if len(components) > 2 and components[-2][0].isupper():
262 raise SyntaxError('Inner class (%s) can not be imported '
263 'and used by JNI (%s). Please import the outer '
264 'class and use Outer.Inner instead.' %
265 (qualified_name, param))
torneb66773d2016-05-10 02:08:20 +0900266 return prefix + qualified_name + ';'
bulach@chromium.org7156c762012-11-13 23:35:00 +0900267
268 # Is it an inner class from an outer class import? (e.g. referencing
269 # Class.Inner from import pkg.Class).
270 if '.' in param:
271 components = param.split('.')
272 outer = '/'.join(components[:-1])
273 inner = components[-1]
Yipeng Wang60438ac2017-06-17 06:08:33 +0900274 for qualified_name in self._imports:
bulach@chromium.org7156c762012-11-13 23:35:00 +0900275 if qualified_name.endswith('/' + outer):
torneb66773d2016-05-10 02:08:20 +0900276 return (prefix + qualified_name + '$' + inner + ';')
bulach@chromium.org4a130cc2014-03-28 20:15:06 +0900277 raise SyntaxError('Inner class (%s) can not be '
278 'used directly by JNI. Please import the outer '
279 'class, probably:\n'
280 'import %s.%s;' %
Yipeng Wang60438ac2017-06-17 06:08:33 +0900281 (param, self._package.replace('/', '.'),
bulach@chromium.org4a130cc2014-03-28 20:15:06 +0900282 outer.replace('/', '.')))
bulach@chromium.org7156c762012-11-13 23:35:00 +0900283
Yipeng Wang60438ac2017-06-17 06:08:33 +0900284 self._CheckImplicitImports(param)
bulach@chromium.org69931302014-05-08 04:16:23 +0900285
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900286 # Type not found, falling back to same package as this class.
Yipeng Wang60438ac2017-06-17 06:08:33 +0900287 return (prefix + 'L' + self._package + '/' + param + ';')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900288
Yipeng Wang60438ac2017-06-17 06:08:33 +0900289 def _AddAdditionalImport(self, class_name):
290 assert class_name.endswith('.class')
291 raw_class_name = class_name[:-len('.class')]
292 if '.' in raw_class_name:
293 raise SyntaxError('%s cannot be used in @JNIAdditionalImport. '
294 'Only import unqualified outer classes.' % class_name)
295 new_import = 'L%s/%s' % (self._package, raw_class_name)
296 if new_import in self._imports:
297 raise SyntaxError('Do not use JNIAdditionalImport on an already '
298 'imported class: %s' % (new_import.replace('/', '.')))
299 self._imports += [new_import]
300
301 def _CheckImplicitImports(self, param):
bulach@chromium.org69931302014-05-08 04:16:23 +0900302 # Ensure implicit imports, such as java.lang.*, are not being treated
303 # as being in the same package.
Yipeng Wang60438ac2017-06-17 06:08:33 +0900304 if not self._implicit_imports:
bulach@chromium.org69931302014-05-08 04:16:23 +0900305 # 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:
Yipeng Wang60438ac2017-06-17 06:08:33 +0900309 self._implicit_imports = f.readlines()
310 for implicit_import in self._implicit_imports:
bulach@chromium.org69931302014-05-08 04:16:23 +0900311 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'
316 'import %s;' %
317 (param, implicit_import))
318
Yipeng Wang60438ac2017-06-17 06:08:33 +0900319 def Signature(self, params, returns, wrap):
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900320 """Returns the JNI signature for the given datatypes."""
321 items = ['(']
Yipeng Wang60438ac2017-06-17 06:08:33 +0900322 items += [self.JavaToJni(param.datatype) for param in params]
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900323 items += [')']
Yipeng Wang60438ac2017-06-17 06:08:33 +0900324 items += [self.JavaToJni(returns)]
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900325 if wrap:
326 return '\n' + '\n'.join(['"' + item + '"' for item in items])
327 else:
328 return '"' + ''.join(items) + '"'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900329
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900330 @staticmethod
Yipeng Wang60438ac2017-06-17 06:08:33 +0900331 def ParseJavaPSignature(signature_line):
332 prefix = 'Signature: '
333 index = signature_line.find(prefix)
334 if index == -1:
335 prefix = 'descriptor: '
336 index = signature_line.index(prefix)
337 return '"%s"' % signature_line[index + len(prefix):]
338
339 @staticmethod
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900340 def Parse(params):
341 """Parses the params into a list of Param objects."""
342 if not params:
343 return []
344 ret = []
345 for p in [p.strip() for p in params.split(',')]:
346 items = p.split(' ')
bauerbb7c53862016-08-25 22:41:41 +0900347
348 # Remove @Annotations from parameters.
349 while items[0].startswith('@'):
350 del items[0]
351
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900352 if 'final' in items:
353 items.remove('final')
bauerbb7c53862016-08-25 22:41:41 +0900354
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900355 param = Param(
356 datatype=items[0],
357 name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
358 )
359 ret += [param]
360 return ret
bulach@chromium.org6079a072012-02-24 09:09:38 +0900361
bulach@chromium.org6079a072012-02-24 09:09:38 +0900362
bulach@chromium.org1c775752012-06-22 19:03:16 +0900363def ExtractJNINamespace(contents):
364 re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
365 m = re.findall(re_jni_namespace, contents)
366 if not m:
367 return ''
368 return m[0]
369
370
bulach@chromium.org6079a072012-02-24 09:09:38 +0900371def ExtractFullyQualifiedJavaClassName(java_file_name, contents):
372 re_package = re.compile('.*?package (.*?);')
373 matches = re.findall(re_package, contents)
374 if not matches:
375 raise SyntaxError('Unable to find "package" line in %s' % java_file_name)
376 return (matches[0].replace('.', '/') + '/' +
377 os.path.splitext(os.path.basename(java_file_name))[0])
378
379
bulach@chromium.orga022cf62013-11-05 09:54:22 +0900380def ExtractNatives(contents, ptr_type):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900381 """Returns a list of dict containing information about a native method."""
382 contents = contents.replace('\n', '')
383 natives = []
bulach@chromium.org1c775752012-06-22 19:03:16 +0900384 re_native = re.compile(r'(@NativeClassQualifiedName'
bulach@chromium.org86eae282014-05-10 21:15:07 +0900385 '\(\"(?P<native_class_name>.*?)\"\)\s+)?'
386 '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\))\s+)?'
387 '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*native '
388 '(?P<return_type>\S*) '
389 '(?P<name>native\w+)\((?P<params>.*?)\);')
bulach@chromium.org1c775752012-06-22 19:03:16 +0900390 for match in re.finditer(re_native, contents):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900391 native = NativeMethod(
bulach@chromium.org1c775752012-06-22 19:03:16 +0900392 static='static' in match.group('qualifiers'),
393 java_class_name=match.group('java_class_name'),
394 native_class_name=match.group('native_class_name'),
bulach@chromium.orga6e185e2013-03-26 16:32:39 +0900395 return_type=match.group('return_type'),
bulach@chromium.org1c775752012-06-22 19:03:16 +0900396 name=match.group('name').replace('native', ''),
bulach@chromium.orga022cf62013-11-05 09:54:22 +0900397 params=JniParams.Parse(match.group('params')),
398 ptr_type=ptr_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900399 natives += [native]
400 return natives
401
402
estevenson2498e5d2017-02-01 05:34:56 +0900403def IsMainDexJavaClass(contents):
Yipeng Wangfba87a32017-07-01 03:16:41 +0900404 """Returns True if the class is annotated with "@MainDex", False if not.
estevenson2498e5d2017-02-01 05:34:56 +0900405
406 JNI registration doesn't always need to be completed for non-browser processes
407 since most Java code is only used by the browser process. Classes that are
408 needed by non-browser processes must explicitly be annotated with @MainDex
409 to force JNI registration.
410 """
Jay Civelli76455192017-06-10 06:57:19 +0900411 re_maindex = re.compile(r'@MainDex[\s\S]*class({|[\s\S]*{)')
Yipeng Wangfba87a32017-07-01 03:16:41 +0900412 return bool(re.search(re_maindex, contents))
413
414
415def GetBinaryClassName(fully_qualified_class):
416 """Returns a string concatenating the Java package and class."""
417 return fully_qualified_class.replace('_', '_1').replace('/', '_')
418
419
420def GetRegistrationFunctionName(fully_qualified_class):
421 """Returns the register name with a given class."""
422 return 'RegisterNative_' + GetBinaryClassName(fully_qualified_class)
estevenson2498e5d2017-02-01 05:34:56 +0900423
424
bulach@chromium.org31af7532012-09-24 20:01:41 +0900425def GetStaticCastForReturnType(return_type):
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900426 type_map = { 'String' : 'jstring',
427 'java/lang/String' : 'jstring',
tornec9124232015-08-27 23:57:06 +0900428 'Throwable': 'jthrowable',
429 'java/lang/Throwable': 'jthrowable',
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900430 'boolean[]': 'jbooleanArray',
431 'byte[]': 'jbyteArray',
432 'char[]': 'jcharArray',
433 'short[]': 'jshortArray',
434 'int[]': 'jintArray',
435 'long[]': 'jlongArray',
skhatri@nvidia.comf5bf8c42014-06-02 21:23:12 +0900436 'float[]': 'jfloatArray',
digit@chromium.org9d7eab02012-12-21 05:33:03 +0900437 'double[]': 'jdoubleArray' }
438 ret = type_map.get(return_type, None)
439 if ret:
440 return ret
441 if return_type.endswith('[]'):
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900442 return 'jobjectArray'
bulach@chromium.org31af7532012-09-24 20:01:41 +0900443 return None
444
445
446def GetEnvCall(is_constructor, is_static, return_type):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900447 """Maps the types availabe via env->Call__Method."""
bulach@chromium.org31af7532012-09-24 20:01:41 +0900448 if is_constructor:
449 return 'NewObject'
450 env_call_map = {'boolean': 'Boolean',
451 'byte': 'Byte',
452 'char': 'Char',
453 'short': 'Short',
454 'int': 'Int',
455 'long': 'Long',
456 'float': 'Float',
457 'void': 'Void',
458 'double': 'Double',
459 'Object': 'Object',
bulach@chromium.org6079a072012-02-24 09:09:38 +0900460 }
bulach@chromium.org31af7532012-09-24 20:01:41 +0900461 call = env_call_map.get(return_type, 'Object')
462 if is_static:
463 call = 'Static' + call
464 return 'Call' + call + 'Method'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900465
466
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900467def GetMangledParam(datatype):
468 """Returns a mangled identifier for the datatype."""
469 if len(datatype) <= 2:
470 return datatype.replace('[', 'A')
471 ret = ''
472 for i in range(1, len(datatype)):
473 c = datatype[i]
474 if c == '[':
475 ret += 'A'
476 elif c.isupper() or datatype[i - 1] in ['/', 'L']:
477 ret += c.upper()
478 return ret
479
480
Yipeng Wang60438ac2017-06-17 06:08:33 +0900481def GetMangledMethodName(jni_params, name, params, return_type):
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900482 """Returns a mangled method name for the given signature.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900483
484 The returned name can be used as a C identifier and will be unique for all
485 valid overloads of the same method.
486
487 Args:
Yipeng Wang60438ac2017-06-17 06:08:33 +0900488 jni_params: JniParams object.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900489 name: string.
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900490 params: list of Param.
491 return_type: string.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900492
493 Returns:
494 A mangled name.
495 """
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900496 mangled_items = []
497 for datatype in [return_type] + [x.datatype for x in params]:
Yipeng Wang60438ac2017-06-17 06:08:33 +0900498 mangled_items += [GetMangledParam(jni_params.JavaToJni(datatype))]
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900499 mangled_name = name + '_'.join(mangled_items)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900500 assert re.match(r'[0-9a-zA-Z_]+', mangled_name)
501 return mangled_name
502
503
Yipeng Wang60438ac2017-06-17 06:08:33 +0900504def MangleCalledByNatives(jni_params, called_by_natives):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900505 """Mangles all the overloads from the call_by_natives list."""
506 method_counts = collections.defaultdict(
507 lambda: collections.defaultdict(lambda: 0))
508 for called_by_native in called_by_natives:
509 java_class_name = called_by_native.java_class_name
510 name = called_by_native.name
511 method_counts[java_class_name][name] += 1
512 for called_by_native in called_by_natives:
513 java_class_name = called_by_native.java_class_name
514 method_name = called_by_native.name
515 method_id_var_name = method_name
516 if method_counts[java_class_name][method_name] > 1:
Yipeng Wang60438ac2017-06-17 06:08:33 +0900517 method_id_var_name = GetMangledMethodName(jni_params, method_name,
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900518 called_by_native.params,
519 called_by_native.return_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900520 called_by_native.method_id_var_name = method_id_var_name
521 return called_by_natives
522
523
tornec510c592015-09-04 20:16:35 +0900524# Regex to match the JNI types that should be wrapped in a JavaRef.
525RE_SCOPED_JNI_TYPES = re.compile('jobject|jclass|jstring|jthrowable|.*Array')
526
bulach@chromium.org6079a072012-02-24 09:09:38 +0900527
528# Regex to match a string like "@CalledByNative public void foo(int bar)".
529RE_CALLED_BY_NATIVE = re.compile(
530 '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
531 '\s+(?P<prefix>[\w ]*?)'
estevenson0621dbe2016-10-01 05:52:49 +0900532 '(:?\s*@\w+)?' # Ignore annotations in return types.
bulach@chromium.orga6e185e2013-03-26 16:32:39 +0900533 '\s*(?P<return_type>\S+?)'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900534 '\s+(?P<name>\w+)'
535 '\s*\((?P<params>[^\)]*)\)')
536
537
dskibab7cdc892016-10-26 08:39:11 +0900538# Removes empty lines that are indented (i.e. start with 2x spaces).
539def RemoveIndentedEmptyLines(string):
540 return re.sub('^(?: {2})+$\n', '', string, flags=re.MULTILINE)
541
542
Yipeng Wang60438ac2017-06-17 06:08:33 +0900543def ExtractCalledByNatives(jni_params, contents):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900544 """Parses all methods annotated with @CalledByNative.
545
546 Args:
Yipeng Wang60438ac2017-06-17 06:08:33 +0900547 jni_params: JniParams object.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900548 contents: the contents of the java file.
549
550 Returns:
551 A list of dict with information about the annotated methods.
552 TODO(bulach): return a CalledByNative object.
553
554 Raises:
555 ParseError: if unable to parse.
556 """
557 called_by_natives = []
558 for match in re.finditer(RE_CALLED_BY_NATIVE, contents):
559 called_by_natives += [CalledByNative(
560 system_class=False,
561 unchecked='Unchecked' in match.group('Unchecked'),
562 static='static' in match.group('prefix'),
563 java_class_name=match.group('annotation') or '',
564 return_type=match.group('return_type'),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900565 name=match.group('name'),
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900566 params=JniParams.Parse(match.group('params')))]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900567 # Check for any @CalledByNative occurrences that weren't matched.
568 unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
569 for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]):
570 if '@CalledByNative' in line1:
571 raise ParseError('could not parse @CalledByNative method signature',
572 line1, line2)
Yipeng Wang60438ac2017-06-17 06:08:33 +0900573 return MangleCalledByNatives(jni_params, called_by_natives)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900574
575
576class JNIFromJavaP(object):
577 """Uses 'javap' to parse a .class file and generate the JNI header file."""
578
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900579 def __init__(self, contents, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900580 self.contents = contents
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900581 self.namespace = options.namespace
bulach@chromium.org07a9ba42014-02-28 03:23:11 +0900582 for line in contents:
583 class_name = re.match(
584 '.*?(public).*?(class|interface) (?P<class_name>\S+?)( |\Z)',
585 line)
586 if class_name:
587 self.fully_qualified_class = class_name.group('class_name')
588 break
bulach@chromium.org6079a072012-02-24 09:09:38 +0900589 self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
simonb@opera.com521714e2013-09-04 06:22:48 +0900590 # Java 7's javap includes type parameters in output, like HashSet<T>. Strip
591 # away the <...> and use the raw class name that Java 6 would've given us.
592 self.fully_qualified_class = self.fully_qualified_class.split('<', 1)[0]
Yipeng Wang60438ac2017-06-17 06:08:33 +0900593 self.jni_params = JniParams(self.fully_qualified_class)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900594 self.java_class_name = self.fully_qualified_class.split('/')[-1]
595 if not self.namespace:
596 self.namespace = 'JNI_' + self.java_class_name
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900597 re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
bulach@chromium.org31af7532012-09-24 20:01:41 +0900598 '\((?P<params>.*?)\)')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900599 self.called_by_natives = []
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900600 for lineno, content in enumerate(contents[2:], 2):
bulach@chromium.org31af7532012-09-24 20:01:41 +0900601 match = re.match(re_method, content)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900602 if not match:
603 continue
604 self.called_by_natives += [CalledByNative(
605 system_class=True,
606 unchecked=False,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900607 static='static' in match.group('prefix'),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900608 java_class_name='',
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900609 return_type=match.group('return_type').replace('.', '/'),
bulach@chromium.org31af7532012-09-24 20:01:41 +0900610 name=match.group('name'),
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900611 params=JniParams.Parse(match.group('params').replace('.', '/')),
612 signature=JniParams.ParseJavaPSignature(contents[lineno + 1]))]
613 re_constructor = re.compile('(.*?)public ' +
bulach@chromium.org31af7532012-09-24 20:01:41 +0900614 self.fully_qualified_class.replace('/', '.') +
615 '\((?P<params>.*?)\)')
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900616 for lineno, content in enumerate(contents[2:], 2):
bulach@chromium.org31af7532012-09-24 20:01:41 +0900617 match = re.match(re_constructor, content)
618 if not match:
619 continue
620 self.called_by_natives += [CalledByNative(
bulach@chromium.org208f05b2012-10-03 06:13:55 +0900621 system_class=True,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900622 unchecked=False,
623 static=False,
624 java_class_name='',
625 return_type=self.fully_qualified_class,
626 name='Constructor',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900627 params=JniParams.Parse(match.group('params').replace('.', '/')),
bulach@chromium.org37fc9112013-10-26 01:27:03 +0900628 signature=JniParams.ParseJavaPSignature(contents[lineno + 1]),
bulach@chromium.org31af7532012-09-24 20:01:41 +0900629 is_constructor=True)]
Yipeng Wang60438ac2017-06-17 06:08:33 +0900630 self.called_by_natives = MangleCalledByNatives(self.jni_params,
631 self.called_by_natives)
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900632 self.constant_fields = []
bulach@chromium.org07a9ba42014-02-28 03:23:11 +0900633 re_constant_field = re.compile('.*?public static final int (?P<name>.*?);')
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900634 re_constant_field_value = re.compile(
bulach@chromium.org07a9ba42014-02-28 03:23:11 +0900635 '.*?Constant(Value| value): int (?P<value>(-*[0-9]+)?)')
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900636 for lineno, content in enumerate(contents[2:], 2):
637 match = re.match(re_constant_field, content)
638 if not match:
639 continue
640 value = re.match(re_constant_field_value, contents[lineno + 2])
bulach@chromium.org07a9ba42014-02-28 03:23:11 +0900641 if not value:
642 value = re.match(re_constant_field_value, contents[lineno + 3])
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900643 if value:
644 self.constant_fields.append(
645 ConstantField(name=match.group('name'),
646 value=value.group('value')))
647
bulach@chromium.org6079a072012-02-24 09:09:38 +0900648 self.inl_header_file_generator = InlHeaderFileGenerator(
estevenson2498e5d2017-02-01 05:34:56 +0900649 self.namespace, self.fully_qualified_class, [], self.called_by_natives,
Yipeng Wang60438ac2017-06-17 06:08:33 +0900650 self.constant_fields, self.jni_params, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900651
652 def GetContent(self):
653 return self.inl_header_file_generator.GetContent()
654
655 @staticmethod
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900656 def CreateFromClass(class_file, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900657 class_name = os.path.splitext(os.path.basename(class_file))[0]
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900658 p = subprocess.Popen(args=[options.javap, '-c', '-verbose',
659 '-s', class_name],
bulach@chromium.org6079a072012-02-24 09:09:38 +0900660 cwd=os.path.dirname(class_file),
661 stdout=subprocess.PIPE,
662 stderr=subprocess.PIPE)
663 stdout, _ = p.communicate()
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900664 jni_from_javap = JNIFromJavaP(stdout.split('\n'), options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900665 return jni_from_javap
666
667
668class JNIFromJavaSource(object):
669 """Uses the given java source file to generate the JNI header file."""
670
torne@chromium.org202d2672014-04-15 02:29:59 +0900671 # Match single line comments, multiline comments, character literals, and
672 # double-quoted strings.
673 _comment_remover_regex = re.compile(
674 r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
675 re.DOTALL | re.MULTILINE)
676
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900677 def __init__(self, contents, fully_qualified_class, options):
torne@chromium.org202d2672014-04-15 02:29:59 +0900678 contents = self._RemoveComments(contents)
Yipeng Wang60438ac2017-06-17 06:08:33 +0900679 self.jni_params = JniParams(fully_qualified_class)
680 self.jni_params.ExtractImportsAndInnerClasses(contents)
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900681 jni_namespace = ExtractJNINamespace(contents) or options.namespace
bulach@chromium.orga022cf62013-11-05 09:54:22 +0900682 natives = ExtractNatives(contents, options.ptr_type)
Yipeng Wang60438ac2017-06-17 06:08:33 +0900683 called_by_natives = ExtractCalledByNatives(self.jni_params, contents)
bulach@chromium.orge2530d02012-07-03 01:23:35 +0900684 if len(natives) == 0 and len(called_by_natives) == 0:
685 raise SyntaxError('Unable to find any JNI methods for %s.' %
686 fully_qualified_class)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900687 inl_header_file_generator = InlHeaderFileGenerator(
estevenson2498e5d2017-02-01 05:34:56 +0900688 jni_namespace, fully_qualified_class, natives, called_by_natives, [],
Yipeng Wangfba87a32017-07-01 03:16:41 +0900689 self.jni_params, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900690 self.content = inl_header_file_generator.GetContent()
691
torne@chromium.org202d2672014-04-15 02:29:59 +0900692 @classmethod
693 def _RemoveComments(cls, contents):
husky@chromium.org8c438d72012-07-31 02:27:48 +0900694 # We need to support both inline and block comments, and we need to handle
torne@chromium.org202d2672014-04-15 02:29:59 +0900695 # strings that contain '//' or '/*'.
696 # TODO(bulach): This is a bit hacky. It would be cleaner to use a real Java
husky@chromium.org8c438d72012-07-31 02:27:48 +0900697 # parser. Maybe we could ditch JNIFromJavaSource and just always use
698 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
699 # http://code.google.com/p/chromium/issues/detail?id=138941
torne@chromium.org202d2672014-04-15 02:29:59 +0900700 def replacer(match):
701 # Replace matches that are comments with nothing; return literals/strings
702 # unchanged.
703 s = match.group(0)
704 if s.startswith('/'):
705 return ''
706 else:
707 return s
708 return cls._comment_remover_regex.sub(replacer, contents)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900709
710 def GetContent(self):
711 return self.content
712
713 @staticmethod
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900714 def CreateFromFile(java_file_name, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900715 contents = file(java_file_name).read()
716 fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
717 contents)
bulach@chromium.org7cd403c2013-10-03 03:11:24 +0900718 return JNIFromJavaSource(contents, fully_qualified_class, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900719
720
721class InlHeaderFileGenerator(object):
722 """Generates an inline header file for JNI integration."""
723
724 def __init__(self, namespace, fully_qualified_class, natives,
Yipeng Wangfba87a32017-07-01 03:16:41 +0900725 called_by_natives, constant_fields, jni_params, options):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900726 self.namespace = namespace
727 self.fully_qualified_class = fully_qualified_class
728 self.class_name = self.fully_qualified_class.split('/')[-1]
729 self.natives = natives
730 self.called_by_natives = called_by_natives
731 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900732 self.constant_fields = constant_fields
Yipeng Wang60438ac2017-06-17 06:08:33 +0900733 self.jni_params = jni_params
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900734 self.options = options
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900735
bulach@chromium.org6079a072012-02-24 09:09:38 +0900736
737 def GetContent(self):
738 """Returns the content of the JNI binding file."""
739 template = Template("""\
mkosiba@chromium.org33075172014-08-22 01:37:32 +0900740// Copyright 2014 The Chromium Authors. All rights reserved.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900741// Use of this source code is governed by a BSD-style license that can be
742// found in the LICENSE file.
743
744
745// This file is autogenerated by
746// ${SCRIPT_NAME}
747// For
748// ${FULLY_QUALIFIED_CLASS}
749
750#ifndef ${HEADER_GUARD}
751#define ${HEADER_GUARD}
752
753#include <jni.h>
754
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900755${INCLUDES}
bulach@chromium.org6079a072012-02-24 09:09:38 +0900756
anton@chromium.org9d3b13b2014-05-07 23:14:10 +0900757#include "base/android/jni_int_wrapper.h"
758
bulach@chromium.org6079a072012-02-24 09:09:38 +0900759// Step 1: forward declarations.
760namespace {
761$CLASS_PATH_DEFINITIONS
torneb66773d2016-05-10 02:08:20 +0900762
bulach@chromium.org6079a072012-02-24 09:09:38 +0900763} // namespace
bulach@chromium.org1c775752012-06-22 19:03:16 +0900764
765$OPEN_NAMESPACE
bulach@chromium.org6079a072012-02-24 09:09:38 +0900766
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900767$CONSTANT_FIELDS
768
bulach@chromium.org6079a072012-02-24 09:09:38 +0900769// Step 2: method stubs.
770$METHOD_STUBS
771
bulach@chromium.org231cff62012-10-17 03:35:10 +0900772// Step 3: RegisterNatives.
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900773$JNI_NATIVE_METHODS
Yipeng Wangfba87a32017-07-01 03:16:41 +0900774$REGISTER_NATIVES_EMPTY
Yipeng Wang97484ae2017-07-01 00:27:54 +0900775$CLOSE_NAMESPACE
Yipeng Wangfba87a32017-07-01 03:16:41 +0900776$REGISTER_NATIVES
torneb66773d2016-05-10 02:08:20 +0900777
bulach@chromium.org6079a072012-02-24 09:09:38 +0900778#endif // ${HEADER_GUARD}
779""")
bulach@chromium.org6079a072012-02-24 09:09:38 +0900780 values = {
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900781 'SCRIPT_NAME': self.options.script_name,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900782 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
783 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900784 'CONSTANT_FIELDS': self.GetConstantFieldsString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900785 'METHOD_STUBS': self.GetMethodStubsString(),
786 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900787 'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(),
Yipeng Wangfba87a32017-07-01 03:16:41 +0900788 'REGISTER_NATIVES_EMPTY': self.GetOriginalRegisterNativesString(),
Yipeng Wang97484ae2017-07-01 00:27:54 +0900789 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
Yipeng Wangfba87a32017-07-01 03:16:41 +0900790 'REGISTER_NATIVES': self.GetRegisterNativesString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900791 'HEADER_GUARD': self.header_guard,
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900792 'INCLUDES': self.GetIncludesString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900793 }
sievers2c26d4b2016-08-04 08:20:40 +0900794 assert ((values['JNI_NATIVE_METHODS'] == '') ==
795 (values['REGISTER_NATIVES'] == ''))
bulach@chromium.org6079a072012-02-24 09:09:38 +0900796 return WrapOutput(template.substitute(values))
797
798 def GetClassPathDefinitionsString(self):
799 ret = []
800 ret += [self.GetClassPathDefinitions()]
801 return '\n'.join(ret)
802
bulach@chromium.org658dbc02014-02-27 04:59:00 +0900803 def GetConstantFieldsString(self):
804 if not self.constant_fields:
805 return ''
806 ret = ['enum Java_%s_constant_fields {' % self.class_name]
807 for c in self.constant_fields:
808 ret += [' %s = %s,' % (c.name, c.value)]
809 ret += ['};']
810 return '\n'.join(ret)
811
bulach@chromium.org6079a072012-02-24 09:09:38 +0900812 def GetMethodStubsString(self):
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900813 """Returns the code corresponding to method stubs."""
bulach@chromium.org6079a072012-02-24 09:09:38 +0900814 ret = []
815 for native in self.natives:
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +0900816 ret += [self.GetNativeStub(native)]
torneb66773d2016-05-10 02:08:20 +0900817 ret += self.GetLazyCalledByNativeMethodStubs()
bulach@chromium.org6079a072012-02-24 09:09:38 +0900818 return '\n'.join(ret)
819
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900820 def GetLazyCalledByNativeMethodStubs(self):
821 return [self.GetLazyCalledByNativeMethodStub(called_by_native)
822 for called_by_native in self.called_by_natives]
823
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900824 def GetIncludesString(self):
825 if not self.options.includes:
826 return ''
827 includes = self.options.includes.split(',')
828 return '\n'.join('#include "%s"' % x for x in includes)
829
bulach@chromium.org6079a072012-02-24 09:09:38 +0900830 def GetKMethodsString(self, clazz):
831 ret = []
832 for native in self.natives:
833 if (native.java_class_name == clazz or
834 (not native.java_class_name and clazz == self.class_name)):
835 ret += [self.GetKMethodArrayEntry(native)]
836 return '\n'.join(ret)
837
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900838 def SubstituteNativeMethods(self, template):
Yipeng Wangfba87a32017-07-01 03:16:41 +0900839 """Substitutes NAMESPACE, JAVA_CLASS and KMETHODS in the provided
840 template."""
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900841 ret = []
bulach@chromium.org6079a072012-02-24 09:09:38 +0900842 all_classes = self.GetUniqueClasses(self.natives)
843 all_classes[self.class_name] = self.fully_qualified_class
844 for clazz in all_classes:
845 kmethods = self.GetKMethodsString(clazz)
Yipeng Wangfba87a32017-07-01 03:16:41 +0900846 namespace_str = ''
847 if self.namespace:
848 namespace_str = self.namespace + '::'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900849 if kmethods:
Yipeng Wangfba87a32017-07-01 03:16:41 +0900850 values = {'NAMESPACE': namespace_str,
851 'JAVA_CLASS': clazz,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900852 'KMETHODS': kmethods}
853 ret += [template.substitute(values)]
854 if not ret: return ''
855 return '\n' + '\n'.join(ret)
856
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900857 def GetJNINativeMethodsString(self):
858 """Returns the implementation of the array of native methods."""
tornefc32b002016-07-21 19:41:58 +0900859 if not self.options.native_exports_optional:
mkosiba@chromium.org56092f02014-08-11 20:27:12 +0900860 return ''
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900861 template = Template("""\
862static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
863${KMETHODS}
864};
865""")
866 return self.SubstituteNativeMethods(template)
867
Yipeng Wangfba87a32017-07-01 03:16:41 +0900868 # TODO(agrieve): Remove this function when deleting original registers.
869 # https://crbug.com/683256.
870 def GetOriginalRegisterNativesString(self):
871 """Return the code for original RegisterNatives"""
872 natives = self.GetRegisterNativesImplString()
873 if not natives:
874 return ''
875
876 return """
877// TODO(agrieve): Remove these empty registration functions and functions
878// calling them. https://crbug.com/683256.
879static bool RegisterNativesImpl(JNIEnv* env) {
880 return true;
881}
882"""
883
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900884 def GetRegisterNativesString(self):
885 """Returns the code for RegisterNatives."""
sievers2c26d4b2016-08-04 08:20:40 +0900886 natives = self.GetRegisterNativesImplString()
887 if not natives:
888 return ''
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900889 template = Template("""\
Yipeng Wangfba87a32017-07-01 03:16:41 +0900890JNI_REGISTRATION_EXPORT bool ${REGISTER_NAME}(JNIEnv* env) {
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900891${NATIVES}
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900892 return true;
893}
894""")
Yipeng Wangfba87a32017-07-01 03:16:41 +0900895 values = {
896 'REGISTER_NAME':
897 GetRegistrationFunctionName(self.fully_qualified_class),
898 'NATIVES': natives
899 }
sievers2c26d4b2016-08-04 08:20:40 +0900900 return template.substitute(values)
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900901
902 def GetRegisterNativesImplString(self):
903 """Returns the shared implementation for RegisterNatives."""
tornefc32b002016-07-21 19:41:58 +0900904 if not self.options.native_exports_optional:
mkosiba@chromium.org56092f02014-08-11 20:27:12 +0900905 return ''
906
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900907 template = Template("""\
Yipeng Wangfba87a32017-07-01 03:16:41 +0900908 const int kMethods${JAVA_CLASS}Size =
909 arraysize(${NAMESPACE}kMethods${JAVA_CLASS});
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900910
mkosiba@chromium.org33075172014-08-22 01:37:32 +0900911 if (env->RegisterNatives(${JAVA_CLASS}_clazz(env),
Yipeng Wangfba87a32017-07-01 03:16:41 +0900912 ${NAMESPACE}kMethods${JAVA_CLASS},
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900913 kMethods${JAVA_CLASS}Size) < 0) {
914 jni_generator::HandleRegistrationError(
mkosiba@chromium.org33075172014-08-22 01:37:32 +0900915 env, ${JAVA_CLASS}_clazz(env), __FILE__);
bulach@chromium.org51dd7492014-01-08 23:41:13 +0900916 return false;
917 }
918""")
919 return self.SubstituteNativeMethods(template)
920
bulach@chromium.org6079a072012-02-24 09:09:38 +0900921 def GetOpenNamespaceString(self):
922 if self.namespace:
bulach@chromium.org16dde202012-07-24 00:31:35 +0900923 all_namespaces = ['namespace %s {' % ns
924 for ns in self.namespace.split('::')]
925 return '\n'.join(all_namespaces)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900926 return ''
927
928 def GetCloseNamespaceString(self):
929 if self.namespace:
bulach@chromium.org16dde202012-07-24 00:31:35 +0900930 all_namespaces = ['} // namespace %s' % ns
931 for ns in self.namespace.split('::')]
932 all_namespaces.reverse()
933 return '\n'.join(all_namespaces) + '\n'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900934 return ''
935
tornec510c592015-09-04 20:16:35 +0900936 def GetJNIFirstParamType(self, native):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900937 if native.type == 'method':
tornec510c592015-09-04 20:16:35 +0900938 return 'jobject'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900939 elif native.type == 'function':
940 if native.static:
tornec510c592015-09-04 20:16:35 +0900941 return 'jclass'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900942 else:
tornec510c592015-09-04 20:16:35 +0900943 return 'jobject'
944
945 def GetJNIFirstParam(self, native, for_declaration):
946 c_type = self.GetJNIFirstParamType(native)
947 if for_declaration:
948 c_type = WrapCTypeForDeclaration(c_type)
949 return [c_type + ' jcaller']
bulach@chromium.org6079a072012-02-24 09:09:38 +0900950
951 def GetParamsInDeclaration(self, native):
tornec510c592015-09-04 20:16:35 +0900952 """Returns the params for the forward declaration.
953
954 Args:
955 native: the native dictionary describing the method.
956
957 Returns:
958 A string containing the params.
959 """
960 return ',\n '.join(self.GetJNIFirstParam(native, True) +
961 [JavaDataTypeToCForDeclaration(param.datatype) + ' ' +
962 param.name
963 for param in native.params])
964
965 def GetParamsInStub(self, native):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900966 """Returns the params for the stub declaration.
967
968 Args:
969 native: the native dictionary describing the method.
970
971 Returns:
972 A string containing the params.
973 """
tornec510c592015-09-04 20:16:35 +0900974 return ',\n '.join(self.GetJNIFirstParam(native, False) +
bulach@chromium.org6079a072012-02-24 09:09:38 +0900975 [JavaDataTypeToC(param.datatype) + ' ' +
976 param.name
977 for param in native.params])
978
979 def GetCalledByNativeParamsInDeclaration(self, called_by_native):
anton@chromium.org9d3b13b2014-05-07 23:14:10 +0900980 return ',\n '.join([
981 JavaDataTypeToCForCalledByNativeParam(param.datatype) + ' ' +
982 param.name
983 for param in called_by_native.params])
bulach@chromium.org6079a072012-02-24 09:09:38 +0900984
torne94bda722015-02-20 21:37:52 +0900985 def GetStubName(self, native):
986 """Return the name of the stub function for this native method.
987
988 Args:
989 native: the native dictionary describing the method.
990
991 Returns:
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +0900992 A string with the stub function name (used by the JVM).
torne94bda722015-02-20 21:37:52 +0900993 """
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +0900994 template = Template("Java_${JAVA_NAME}_native${NAME}")
torne94bda722015-02-20 21:37:52 +0900995
Yipeng Wangfba87a32017-07-01 03:16:41 +0900996 java_name = GetBinaryClassName(self.fully_qualified_class)
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +0900997 if native.java_class_name:
998 java_name += '_00024' + native.java_class_name
torne94bda722015-02-20 21:37:52 +0900999
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001000 values = {'NAME': native.name,
1001 'JAVA_NAME': java_name}
1002 return template.substitute(values)
1003
tornec510c592015-09-04 20:16:35 +09001004 def GetJavaParamRefForCall(self, c_type, name):
torne9854c722016-08-05 00:44:06 +09001005 return Template(
1006 'base::android::JavaParamRef<${TYPE}>(env, ${NAME})').substitute({
tornec510c592015-09-04 20:16:35 +09001007 'TYPE': c_type,
1008 'NAME': name,
1009 })
1010
1011 def GetJNIFirstParamForCall(self, native):
1012 c_type = self.GetJNIFirstParamType(native)
1013 return [self.GetJavaParamRefForCall(c_type, 'jcaller')]
1014
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001015 def GetNativeStub(self, native):
1016 is_method = native.type == 'method'
1017
1018 if is_method:
1019 params = native.params[1:]
torne94bda722015-02-20 21:37:52 +09001020 else:
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001021 params = native.params
torneb66773d2016-05-10 02:08:20 +09001022 params_in_call = ['env'] + self.GetJNIFirstParamForCall(native)
tornec510c592015-09-04 20:16:35 +09001023 for p in params:
1024 c_type = JavaDataTypeToC(p.datatype)
1025 if re.match(RE_SCOPED_JNI_TYPES, c_type):
1026 params_in_call.append(self.GetJavaParamRefForCall(c_type, p.name))
1027 else:
1028 params_in_call.append(p.name)
1029 params_in_call = ', '.join(params_in_call)
mkosiba@chromium.org56092f02014-08-11 20:27:12 +09001030
torne41495262015-08-26 23:07:32 +09001031 return_type = return_declaration = JavaDataTypeToC(native.return_type)
1032 post_call = ''
tornec510c592015-09-04 20:16:35 +09001033 if re.match(RE_SCOPED_JNI_TYPES, return_type):
torne41495262015-08-26 23:07:32 +09001034 post_call = '.Release()'
torne9854c722016-08-05 00:44:06 +09001035 return_declaration = ('base::android::ScopedJavaLocalRef<' + return_type +
1036 '>')
dskibab7cdc892016-10-26 08:39:11 +09001037 profiling_entered_native = ''
1038 if self.options.enable_profiling:
1039 profiling_entered_native = 'JNI_LINK_SAVED_FRAME_POINTER;'
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001040 values = {
1041 'RETURN': return_type,
torne41495262015-08-26 23:07:32 +09001042 'RETURN_DECLARATION': return_declaration,
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001043 'NAME': native.name,
1044 'PARAMS': self.GetParamsInDeclaration(native),
tornec510c592015-09-04 20:16:35 +09001045 'PARAMS_IN_STUB': self.GetParamsInStub(native),
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001046 'PARAMS_IN_CALL': params_in_call,
torne41495262015-08-26 23:07:32 +09001047 'POST_CALL': post_call,
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001048 'STUB_NAME': self.GetStubName(native),
dskibab7cdc892016-10-26 08:39:11 +09001049 'PROFILING_ENTERED_NATIVE': profiling_entered_native,
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001050 }
1051
1052 if is_method:
1053 optional_error_return = JavaReturnValueToC(native.return_type)
1054 if optional_error_return:
1055 optional_error_return = ', ' + optional_error_return
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001056 values.update({
1057 'OPTIONAL_ERROR_RETURN': optional_error_return,
1058 'PARAM0_NAME': native.params[0].name,
1059 'P0_TYPE': native.p0_type,
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001060 })
1061 template = Template("""\
torne17bc9642016-12-02 02:11:54 +09001062JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) {
dskibab7cdc892016-10-26 08:39:11 +09001063 ${PROFILING_ENTERED_NATIVE}
bulach@chromium.org6079a072012-02-24 09:09:38 +09001064 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001065 CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN});
1066 return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL};
bulach@chromium.org6079a072012-02-24 09:09:38 +09001067}
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001068""")
1069 else:
1070 template = Template("""
torne41495262015-08-26 23:07:32 +09001071static ${RETURN_DECLARATION} ${NAME}(JNIEnv* env, ${PARAMS});
mkosiba@chromium.org56092f02014-08-11 20:27:12 +09001072
torne17bc9642016-12-02 02:11:54 +09001073JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) {
dskibab7cdc892016-10-26 08:39:11 +09001074 ${PROFILING_ENTERED_NATIVE}
torne41495262015-08-26 23:07:32 +09001075 return ${NAME}(${PARAMS_IN_CALL})${POST_CALL};
Torne (Richard Coles)0715a9e2015-08-14 21:41:14 +09001076}
1077""")
bulach@chromium.org6079a072012-02-24 09:09:38 +09001078
dskibab7cdc892016-10-26 08:39:11 +09001079 return RemoveIndentedEmptyLines(template.substitute(values))
bulach@chromium.org6079a072012-02-24 09:09:38 +09001080
anton@chromium.org9d3b13b2014-05-07 23:14:10 +09001081 def GetArgument(self, param):
torne28061e92016-08-09 01:29:18 +09001082 if param.datatype == 'int':
1083 return 'as_jint(' + param.name + ')'
1084 elif re.match(RE_SCOPED_JNI_TYPES, JavaDataTypeToC(param.datatype)):
1085 return param.name + '.obj()'
1086 else:
1087 return param.name
anton@chromium.org9d3b13b2014-05-07 23:14:10 +09001088
1089 def GetArgumentsInCall(self, params):
1090 """Return a string of arguments to call from native into Java"""
1091 return [self.GetArgument(p) for p in params]
1092
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001093 def GetCalledByNativeValues(self, called_by_native):
1094 """Fills in necessary values for the CalledByNative methods."""
mkosiba@chromium.org33075172014-08-22 01:37:32 +09001095 java_class = called_by_native.java_class_name or self.class_name
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001096 if called_by_native.static or called_by_native.is_constructor:
1097 first_param_in_declaration = ''
mkosiba@chromium.org33075172014-08-22 01:37:32 +09001098 first_param_in_call = ('%s_clazz(env)' % java_class)
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001099 else:
torne28061e92016-08-09 01:29:18 +09001100 first_param_in_declaration = (
1101 ', const base::android::JavaRefOrBare<jobject>& obj')
1102 first_param_in_call = 'obj.obj()'
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001103 params_in_declaration = self.GetCalledByNativeParamsInDeclaration(
1104 called_by_native)
1105 if params_in_declaration:
1106 params_in_declaration = ', ' + params_in_declaration
anton@chromium.org9d3b13b2014-05-07 23:14:10 +09001107 params_in_call = ', '.join(self.GetArgumentsInCall(called_by_native.params))
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001108 if params_in_call:
1109 params_in_call = ', ' + params_in_call
1110 pre_call = ''
1111 post_call = ''
1112 if called_by_native.static_cast:
1113 pre_call = 'static_cast<%s>(' % called_by_native.static_cast
1114 post_call = ')'
1115 check_exception = ''
1116 if not called_by_native.unchecked:
1117 check_exception = 'jni_generator::CheckException(env);'
1118 return_type = JavaDataTypeToC(called_by_native.return_type)
1119 optional_error_return = JavaReturnValueToC(called_by_native.return_type)
1120 if optional_error_return:
1121 optional_error_return = ', ' + optional_error_return
1122 return_declaration = ''
1123 return_clause = ''
1124 if return_type != 'void':
1125 pre_call = ' ' + pre_call
1126 return_declaration = return_type + ' ret ='
tornec510c592015-09-04 20:16:35 +09001127 if re.match(RE_SCOPED_JNI_TYPES, return_type):
torne9854c722016-08-05 00:44:06 +09001128 return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>'
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001129 return_clause = 'return ' + return_type + '(env, ret);'
1130 else:
1131 return_clause = 'return ret;'
dskibab7cdc892016-10-26 08:39:11 +09001132 profiling_leaving_native = ''
1133 if self.options.enable_profiling:
1134 profiling_leaving_native = 'JNI_SAVE_FRAME_POINTER;'
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001135 return {
mkosiba@chromium.org33075172014-08-22 01:37:32 +09001136 'JAVA_CLASS': java_class,
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001137 'RETURN_TYPE': return_type,
1138 'OPTIONAL_ERROR_RETURN': optional_error_return,
1139 'RETURN_DECLARATION': return_declaration,
1140 'RETURN_CLAUSE': return_clause,
1141 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration,
1142 'PARAMS_IN_DECLARATION': params_in_declaration,
1143 'PRE_CALL': pre_call,
1144 'POST_CALL': post_call,
1145 'ENV_CALL': called_by_native.env_call,
1146 'FIRST_PARAM_IN_CALL': first_param_in_call,
1147 'PARAMS_IN_CALL': params_in_call,
1148 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
1149 'CHECK_EXCEPTION': check_exception,
dskibab7cdc892016-10-26 08:39:11 +09001150 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native),
1151 'PROFILING_LEAVING_NATIVE': profiling_leaving_native,
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001152 }
1153
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001154
1155 def GetLazyCalledByNativeMethodStub(self, called_by_native):
bulach@chromium.org6079a072012-02-24 09:09:38 +09001156 """Returns a string."""
1157 function_signature_template = Template("""\
bulach@chromium.org0c6805b2012-09-28 21:34:33 +09001158static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\
bulach@chromium.org6079a072012-02-24 09:09:38 +09001159JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""")
1160 function_header_template = Template("""\
1161${FUNCTION_SIGNATURE} {""")
1162 function_header_with_unused_template = Template("""\
1163${FUNCTION_SIGNATURE} __attribute__ ((unused));
1164${FUNCTION_SIGNATURE} {""")
1165 template = Template("""
bulach@chromium.org231cff62012-10-17 03:35:10 +09001166static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0;
bulach@chromium.org6079a072012-02-24 09:09:38 +09001167${FUNCTION_HEADER}
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001168 CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL},
mkosiba@chromium.org33075172014-08-22 01:37:32 +09001169 ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN});
bulach@chromium.org231cff62012-10-17 03:35:10 +09001170 jmethodID method_id =
1171 ${GET_METHOD_ID_IMPL}
dskibab7cdc892016-10-26 08:39:11 +09001172 ${PROFILING_LEAVING_NATIVE}
bulach@chromium.org6079a072012-02-24 09:09:38 +09001173 ${RETURN_DECLARATION}
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001174 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
1175 method_id${PARAMS_IN_CALL})${POST_CALL};
bulach@chromium.org6079a072012-02-24 09:09:38 +09001176 ${CHECK_EXCEPTION}
1177 ${RETURN_CLAUSE}
1178}""")
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001179 values = self.GetCalledByNativeValues(called_by_native)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001180 values['FUNCTION_SIGNATURE'] = (
1181 function_signature_template.substitute(values))
1182 if called_by_native.system_class:
1183 values['FUNCTION_HEADER'] = (
1184 function_header_with_unused_template.substitute(values))
1185 else:
1186 values['FUNCTION_HEADER'] = function_header_template.substitute(values)
dskibab7cdc892016-10-26 08:39:11 +09001187 return RemoveIndentedEmptyLines(template.substitute(values))
bulach@chromium.org6079a072012-02-24 09:09:38 +09001188
1189 def GetKMethodArrayEntry(self, native):
torne94bda722015-02-20 21:37:52 +09001190 template = Template(' { "native${NAME}", ${JNI_SIGNATURE}, ' +
1191 'reinterpret_cast<void*>(${STUB_NAME}) },')
Yipeng Wang60438ac2017-06-17 06:08:33 +09001192 values = {
1193 'NAME': native.name,
1194 'JNI_SIGNATURE': self.jni_params.Signature(native.params,
1195 native.return_type, True),
1196 'STUB_NAME': self.GetStubName(native)
1197 }
bulach@chromium.org6079a072012-02-24 09:09:38 +09001198 return template.substitute(values)
1199
1200 def GetUniqueClasses(self, origin):
1201 ret = {self.class_name: self.fully_qualified_class}
1202 for entry in origin:
1203 class_name = self.class_name
1204 jni_class_path = self.fully_qualified_class
1205 if entry.java_class_name:
1206 class_name = entry.java_class_name
1207 jni_class_path = self.fully_qualified_class + '$' + class_name
1208 ret[class_name] = jni_class_path
1209 return ret
1210
1211 def GetClassPathDefinitions(self):
1212 """Returns the ClassPath constants."""
1213 ret = []
1214 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +09001215const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""")
tornefc32b002016-07-21 19:41:58 +09001216 all_classes = self.GetUniqueClasses(self.called_by_natives)
1217 if self.options.native_exports_optional:
1218 all_classes.update(self.GetUniqueClasses(self.natives))
mkosiba@chromium.org56092f02014-08-11 20:27:12 +09001219
bulach@chromium.org6079a072012-02-24 09:09:38 +09001220 for clazz in all_classes:
1221 values = {
1222 'JAVA_CLASS': clazz,
torneb66773d2016-05-10 02:08:20 +09001223 'JNI_CLASS_PATH': all_classes[clazz],
bulach@chromium.org6079a072012-02-24 09:09:38 +09001224 }
1225 ret += [template.substitute(values)]
1226 ret += ''
mkosiba@chromium.org33075172014-08-22 01:37:32 +09001227
tornefc32b002016-07-21 19:41:58 +09001228 template = Template("""\
ygorshenin@chromium.org25171e52014-08-20 20:44:05 +09001229// Leaking this jclass as we cannot use LazyInstance from some threads.
torne09434e52015-01-09 04:48:31 +09001230base::subtle::AtomicWord g_${JAVA_CLASS}_clazz __attribute__((unused)) = 0;
mkosiba@chromium.org33075172014-08-22 01:37:32 +09001231#define ${JAVA_CLASS}_clazz(env) \
1232base::android::LazyGetClass(env, k${JAVA_CLASS}ClassPath, \
1233&g_${JAVA_CLASS}_clazz)""")
mkosiba@chromium.org33075172014-08-22 01:37:32 +09001234
Torne (Richard Coles)42ef7042016-07-19 20:36:09 +09001235 for clazz in all_classes:
bulach@chromium.org6079a072012-02-24 09:09:38 +09001236 values = {
1237 'JAVA_CLASS': clazz,
1238 }
ygorshenin@chromium.org25171e52014-08-20 20:44:05 +09001239 ret += [template.substitute(values)]
mkosiba@chromium.org33075172014-08-22 01:37:32 +09001240
bulach@chromium.org6079a072012-02-24 09:09:38 +09001241 return '\n'.join(ret)
1242
bulach@chromium.org6079a072012-02-24 09:09:38 +09001243 def GetMethodIDImpl(self, called_by_native):
1244 """Returns the implementation of GetMethodID."""
torneb66773d2016-05-10 02:08:20 +09001245 template = Template("""\
bulach@chromium.org231cff62012-10-17 03:35:10 +09001246 base::android::MethodID::LazyGet<
1247 base::android::MethodID::TYPE_${STATIC}>(
mkosiba@chromium.org33075172014-08-22 01:37:32 +09001248 env, ${JAVA_CLASS}_clazz(env),
bulach@chromium.org231cff62012-10-17 03:35:10 +09001249 "${JNI_NAME}",
1250 ${JNI_SIGNATURE},
1251 &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
bulach@chromium.org6079a072012-02-24 09:09:38 +09001252""")
bulach@chromium.org31af7532012-09-24 20:01:41 +09001253 jni_name = called_by_native.name
1254 jni_return_type = called_by_native.return_type
1255 if called_by_native.is_constructor:
1256 jni_name = '<init>'
1257 jni_return_type = 'void'
bulach@chromium.org37fc9112013-10-26 01:27:03 +09001258 if called_by_native.signature:
1259 signature = called_by_native.signature
1260 else:
Yipeng Wang60438ac2017-06-17 06:08:33 +09001261 signature = self.jni_params.Signature(called_by_native.params,
1262 jni_return_type, True)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001263 values = {
1264 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
bulach@chromium.org31af7532012-09-24 20:01:41 +09001265 'JNI_NAME': jni_name,
bulach@chromium.org6079a072012-02-24 09:09:38 +09001266 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
bulach@chromium.org231cff62012-10-17 03:35:10 +09001267 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE',
bulach@chromium.org37fc9112013-10-26 01:27:03 +09001268 'JNI_SIGNATURE': signature,
bulach@chromium.org6079a072012-02-24 09:09:38 +09001269 }
1270 return template.substitute(values)
1271
1272
1273def WrapOutput(output):
1274 ret = []
1275 for line in output.splitlines():
tedchoc@chromium.org72992182012-08-03 11:00:17 +09001276 # Do not wrap lines under 80 characters or preprocessor directives.
1277 if len(line) < 80 or line.lstrip()[:1] == '#':
bulach@chromium.org1c775752012-06-22 19:03:16 +09001278 stripped = line.rstrip()
1279 if len(ret) == 0 or len(ret[-1]) or len(stripped):
1280 ret.append(stripped)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001281 else:
1282 first_line_indent = ' ' * (len(line) - len(line.lstrip()))
1283 subsequent_indent = first_line_indent + ' ' * 4
1284 if line.startswith('//'):
1285 subsequent_indent = '//' + subsequent_indent
1286 wrapper = textwrap.TextWrapper(width=80,
1287 subsequent_indent=subsequent_indent,
1288 break_long_words=False)
1289 ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)]
1290 ret += ['']
1291 return '\n'.join(ret)
1292
1293
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001294def ExtractJarInputFile(jar_file, input_file, out_dir):
1295 """Extracts input file from jar and returns the filename.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001296
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001297 The input file is extracted to the same directory that the generated jni
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001298 headers will be placed in. This is passed as an argument to script.
1299
1300 Args:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001301 jar_file: the jar file containing the input files to extract.
1302 input_files: the list of files to extract from the jar file.
1303 out_dir: the name of the directories to extract to.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001304
1305 Returns:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001306 the name of extracted input file.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001307 """
1308 jar_file = zipfile.ZipFile(jar_file)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001309
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001310 out_dir = os.path.join(out_dir, os.path.dirname(input_file))
torne@chromium.org108c37d2013-03-08 22:52:50 +09001311 try:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001312 os.makedirs(out_dir)
torne@chromium.org108c37d2013-03-08 22:52:50 +09001313 except OSError as e:
1314 if e.errno != errno.EEXIST:
1315 raise
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001316 extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
1317 with open(extracted_file_name, 'w') as outfile:
1318 outfile.write(jar_file.read(input_file))
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001319
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001320 return extracted_file_name
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001321
1322
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001323def GenerateJNIHeader(input_file, output_file, options):
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001324 try:
1325 if os.path.splitext(input_file)[1] == '.class':
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001326 jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options)
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001327 content = jni_from_javap.GetContent()
bulach@chromium.org6079a072012-02-24 09:09:38 +09001328 else:
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001329 jni_from_java_source = JNIFromJavaSource.CreateFromFile(
1330 input_file, options)
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001331 content = jni_from_java_source.GetContent()
1332 except ParseError, e:
1333 print e
1334 sys.exit(1)
1335 if output_file:
Yipeng Wangfba87a32017-07-01 03:16:41 +09001336 WriteOutput(output_file, content)
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001337 else:
paulmiller4d5b53e2015-05-30 02:24:37 +09001338 print content
bulach@chromium.org6079a072012-02-24 09:09:38 +09001339
1340
Yipeng Wangfba87a32017-07-01 03:16:41 +09001341def WriteOutput(output_file, content):
1342 if os.path.exists(output_file):
1343 with open(output_file) as f:
1344 existing_content = f.read()
1345 if existing_content == content:
1346 return
1347 with open(output_file, 'w') as f:
1348 f.write(content)
1349
1350
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001351def GetScriptName():
1352 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
1353 base_index = 0
1354 for idx, value in enumerate(script_components):
1355 if value == 'base' or value == 'third_party':
1356 base_index = idx
1357 break
1358 return os.sep.join(script_components[base_index:])
1359
1360
bulach@chromium.org6079a072012-02-24 09:09:38 +09001361def main(argv):
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001362 usage = """usage: %prog [OPTIONS]
bulach@chromium.org6079a072012-02-24 09:09:38 +09001363This script will parse the given java source code extracting the native
1364declarations and print the header file to stdout (or a file).
1365See SampleForTests.java for more details.
1366 """
1367 option_parser = optparse.OptionParser(usage=usage)
cjhopman@chromium.orgfb98e332014-06-25 08:38:17 +09001368 build_utils.AddDepfileOption(option_parser)
1369
cjhopman@chromium.orge607d252014-06-06 04:47:30 +09001370 option_parser.add_option('-j', '--jar_file', dest='jar_file',
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001371 help='Extract the list of input files from'
bulach@chromium.org7f85d462012-05-30 18:56:02 +09001372 ' a specified jar file.'
1373 ' Uses javap to extract the methods from a'
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001374 ' pre-compiled class. --input should point'
bulach@chromium.org6079a072012-02-24 09:09:38 +09001375 ' to pre-compiled Java .class files.')
1376 option_parser.add_option('-n', dest='namespace',
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001377 help='Uses as a namespace in the generated header '
1378 'instead of the javap class name, or when there is '
1379 'no JNINamespace annotation in the java source.')
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001380 option_parser.add_option('--input_file',
1381 help='Single input file name. The output file name '
1382 'will be derived from it. Must be used with '
1383 '--output_dir.')
1384 option_parser.add_option('--output_dir',
1385 help='The output directory. Must be used with '
1386 '--input')
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001387 option_parser.add_option('--script_name', default=GetScriptName(),
1388 help='The name of this script in the generated '
1389 'header.')
bulach@chromium.org51dd7492014-01-08 23:41:13 +09001390 option_parser.add_option('--includes',
1391 help='The comma-separated list of header files to '
1392 'include in the generated header.')
bulach@chromium.orga022cf62013-11-05 09:54:22 +09001393 option_parser.add_option('--ptr_type', default='int',
1394 type='choice', choices=['int', 'long'],
1395 help='The type used to represent native pointers in '
1396 'Java code. For 32-bit, use int; '
1397 'for 64-bit, use long.')
bulach@chromium.org92d0dac2014-01-17 01:08:05 +09001398 option_parser.add_option('--cpp', default='cpp',
1399 help='The path to cpp command.')
1400 option_parser.add_option('--javap', default='javap',
1401 help='The path to javap command.')
torne0dd0b0d2015-02-21 08:06:33 +09001402 option_parser.add_option('--native_exports_optional', action='store_true',
1403 help='Support both explicit and native method'
1404 'registration.')
dskibab7cdc892016-10-26 08:39:11 +09001405 option_parser.add_option('--enable_profiling', action='store_true',
1406 help='Add additional profiling instrumentation.')
bulach@chromium.org6079a072012-02-24 09:09:38 +09001407 options, args = option_parser.parse_args(argv)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +09001408 if options.jar_file:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001409 input_file = ExtractJarInputFile(options.jar_file, options.input_file,
1410 options.output_dir)
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001411 elif options.input_file:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001412 input_file = options.input_file
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001413 else:
1414 option_parser.print_help()
1415 print '\nError: Must specify --jar_file or --input_file.'
1416 return 1
bulach@chromium.org4c248ed2012-07-20 05:02:55 +09001417 output_file = None
1418 if options.output_dir:
1419 root_name = os.path.splitext(os.path.basename(input_file))[0]
1420 output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
bulach@chromium.org7cd403c2013-10-03 03:11:24 +09001421 GenerateJNIHeader(input_file, output_file, options)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001422
cjhopman@chromium.orgfb98e332014-06-25 08:38:17 +09001423 if options.depfile:
agrieve92aed192016-09-14 11:04:21 +09001424 build_utils.WriteDepfile(options.depfile, output_file)
cjhopman@chromium.orgfb98e332014-06-25 08:38:17 +09001425
bulach@chromium.org6079a072012-02-24 09:09:38 +09001426
1427if __name__ == '__main__':
1428 sys.exit(main(sys.argv))