Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(762)

Side by Side Diff: base/android/jni_generator/jni_generator.py

Issue 15715006: Android: allow JNI generator to be used with jarjar. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Change gyp variable to a pathname. Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | base/android/jni_generator/jni_generator_tests.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Extracts native methods from a Java file and generates the JNI bindings. 6 """Extracts native methods from a Java file and generates the JNI bindings.
7 If you change this, please run and update the tests.""" 7 If you change this, please run and update the tests."""
8 8
9 import collections 9 import collections
10 import errno 10 import errno
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 return 'jobjectArray' 112 return 'jobjectArray'
113 else: 113 else:
114 return 'jobject' 114 return 'jobject'
115 115
116 116
117 class JniParams(object): 117 class JniParams(object):
118 _imports = [] 118 _imports = []
119 _fully_qualified_class = '' 119 _fully_qualified_class = ''
120 _package = '' 120 _package = ''
121 _inner_classes = [] 121 _inner_classes = []
122 _remappings = []
122 123
123 @staticmethod 124 @staticmethod
124 def SetFullyQualifiedClass(fully_qualified_class): 125 def SetFullyQualifiedClass(fully_qualified_class):
125 JniParams._fully_qualified_class = 'L' + fully_qualified_class 126 JniParams._fully_qualified_class = 'L' + fully_qualified_class
126 JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1]) 127 JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1])
127 128
128 @staticmethod 129 @staticmethod
129 def ExtractImportsAndInnerClasses(contents): 130 def ExtractImportsAndInnerClasses(contents):
130 contents = contents.replace('\n', '') 131 contents = contents.replace('\n', '')
131 re_import = re.compile(r'import.*?(?P<class>\S*?);') 132 re_import = re.compile(r'import.*?(?P<class>\S*?);')
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 while param[-2:] == '[]': 167 while param[-2:] == '[]':
167 prefix += '[' 168 prefix += '['
168 param = param[:-2] 169 param = param[:-2]
169 # Generic? 170 # Generic?
170 if '<' in param: 171 if '<' in param:
171 param = param[:param.index('<')] 172 param = param[:param.index('<')]
172 if param in pod_param_map: 173 if param in pod_param_map:
173 return prefix + pod_param_map[param] 174 return prefix + pod_param_map[param]
174 if '/' in param: 175 if '/' in param:
175 # Coming from javap, use the fully qualified param directly. 176 # Coming from javap, use the fully qualified param directly.
176 return prefix + 'L' + param + ';' 177 return prefix + 'L' + JniParams.RemapClassName(param) + ';'
177 for qualified_name in (object_param_list + 178 for qualified_name in (object_param_list +
178 [JniParams._fully_qualified_class] + 179 [JniParams._fully_qualified_class] +
179 JniParams._inner_classes): 180 JniParams._inner_classes):
180 if (qualified_name.endswith('/' + param) or 181 if (qualified_name.endswith('/' + param) or
181 qualified_name.endswith('$' + param.replace('.', '$')) or 182 qualified_name.endswith('$' + param.replace('.', '$')) or
182 qualified_name == 'L' + param): 183 qualified_name == 'L' + param):
183 return prefix + qualified_name + ';' 184 return prefix + JniParams.RemapClassName(qualified_name) + ';'
184 185
185 # Is it from an import? (e.g. referecing Class from import pkg.Class; 186 # Is it from an import? (e.g. referecing Class from import pkg.Class;
186 # note that referencing an inner class Inner from import pkg.Class.Inner 187 # note that referencing an inner class Inner from import pkg.Class.Inner
187 # is not supported). 188 # is not supported).
188 for qualified_name in JniParams._imports: 189 for qualified_name in JniParams._imports:
189 if qualified_name.endswith('/' + param): 190 if qualified_name.endswith('/' + param):
190 # Ensure it's not an inner class. 191 # Ensure it's not an inner class.
191 components = qualified_name.split('/') 192 components = qualified_name.split('/')
192 if len(components) > 2 and components[-2][0].isupper(): 193 if len(components) > 2 and components[-2][0].isupper():
193 raise SyntaxError('Inner class (%s) can not be imported ' 194 raise SyntaxError('Inner class (%s) can not be imported '
194 'and used by JNI (%s). Please import the outer ' 195 'and used by JNI (%s). Please import the outer '
195 'class and use Outer.Inner instead.' % 196 'class and use Outer.Inner instead.' %
196 (qualified_name, param)) 197 (qualified_name, param))
197 return prefix + qualified_name + ';' 198 return prefix + JniParams.RemapClassName(qualified_name) + ';'
198 199
199 # Is it an inner class from an outer class import? (e.g. referencing 200 # Is it an inner class from an outer class import? (e.g. referencing
200 # Class.Inner from import pkg.Class). 201 # Class.Inner from import pkg.Class).
201 if '.' in param: 202 if '.' in param:
202 components = param.split('.') 203 components = param.split('.')
203 outer = '/'.join(components[:-1]) 204 outer = '/'.join(components[:-1])
204 inner = components[-1] 205 inner = components[-1]
205 for qualified_name in JniParams._imports: 206 for qualified_name in JniParams._imports:
206 if qualified_name.endswith('/' + outer): 207 if qualified_name.endswith('/' + outer):
207 return prefix + qualified_name + '$' + inner + ';' 208 return (prefix + JniParams.RemapClassName(qualified_name) +
209 '$' + inner + ';')
208 210
209 # Type not found, falling back to same package as this class. 211 # Type not found, falling back to same package as this class.
210 return prefix + 'L' + JniParams._package + '/' + param + ';' 212 return (prefix + 'L' +
213 JniParams.RemapClassName(JniParams._package + '/' + param) + ';')
211 214
212 @staticmethod 215 @staticmethod
213 def Signature(params, returns, wrap): 216 def Signature(params, returns, wrap):
214 """Returns the JNI signature for the given datatypes.""" 217 """Returns the JNI signature for the given datatypes."""
215 items = ['('] 218 items = ['(']
216 items += [JniParams.JavaToJni(param.datatype) for param in params] 219 items += [JniParams.JavaToJni(param.datatype) for param in params]
217 items += [')'] 220 items += [')']
218 items += [JniParams.JavaToJni(returns)] 221 items += [JniParams.JavaToJni(returns)]
219 if wrap: 222 if wrap:
220 return '\n' + '\n'.join(['"' + item + '"' for item in items]) 223 return '\n' + '\n'.join(['"' + item + '"' for item in items])
(...skipping 10 matching lines...) Expand all
231 items = p.split(' ') 234 items = p.split(' ')
232 if 'final' in items: 235 if 'final' in items:
233 items.remove('final') 236 items.remove('final')
234 param = Param( 237 param = Param(
235 datatype=items[0], 238 datatype=items[0],
236 name=(items[1] if len(items) > 1 else 'p%s' % len(ret)), 239 name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
237 ) 240 )
238 ret += [param] 241 ret += [param]
239 return ret 242 return ret
240 243
244 @staticmethod
245 def RemapClassName(class_name):
246 """Remaps class names using the jarjar mapping table."""
247 for old, new in JniParams._remappings:
248 if old in class_name:
249 return class_name.replace(old, new, 1)
250 return class_name
251
252 @staticmethod
253 def SetJarJarMappings(mappings):
254 """Parse jarjar mappings from a string."""
255 JniParams._remappings = []
256 for line in mappings.splitlines():
257 keyword, src, dest = line.split()
258 if keyword != 'rule':
259 continue
260 assert src.endswith('.**')
261 src = src[:-2].replace('.', '/')
262 dest = dest.replace('.', '/')
263 if dest.endswith('@0'):
264 JniParams._remappings.append((src, dest[:-2] + src))
265 else:
266 assert dest.endswith('@1')
267 JniParams._remappings.append((src, dest[:-2]))
268
241 269
242 def ExtractJNINamespace(contents): 270 def ExtractJNINamespace(contents):
243 re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)') 271 re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
244 m = re.findall(re_jni_namespace, contents) 272 m = re.findall(re_jni_namespace, contents)
245 if not m: 273 if not m:
246 return '' 274 return ''
247 return m[0] 275 return m[0]
248 276
249 277
250 def ExtractFullyQualifiedJavaClassName(java_file_name, contents): 278 def ExtractFullyQualifiedJavaClassName(java_file_name, contents):
(...skipping 602 matching lines...) Expand 10 before | Expand all | Expand 10 after
853 ret = [] 881 ret = []
854 template = Template("""\ 882 template = Template("""\
855 const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""") 883 const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""")
856 native_classes = self.GetUniqueClasses(self.natives) 884 native_classes = self.GetUniqueClasses(self.natives)
857 called_by_native_classes = self.GetUniqueClasses(self.called_by_natives) 885 called_by_native_classes = self.GetUniqueClasses(self.called_by_natives)
858 all_classes = native_classes 886 all_classes = native_classes
859 all_classes.update(called_by_native_classes) 887 all_classes.update(called_by_native_classes)
860 for clazz in all_classes: 888 for clazz in all_classes:
861 values = { 889 values = {
862 'JAVA_CLASS': clazz, 890 'JAVA_CLASS': clazz,
863 'JNI_CLASS_PATH': all_classes[clazz], 891 'JNI_CLASS_PATH': JniParams.RemapClassName(all_classes[clazz]),
864 } 892 }
865 ret += [template.substitute(values)] 893 ret += [template.substitute(values)]
866 ret += '' 894 ret += ''
867 for clazz in called_by_native_classes: 895 for clazz in called_by_native_classes:
868 template = Template("""\ 896 template = Template("""\
869 // Leaking this jclass as we cannot use LazyInstance from some threads. 897 // Leaking this jclass as we cannot use LazyInstance from some threads.
870 jclass g_${JAVA_CLASS}_clazz = NULL;""") 898 jclass g_${JAVA_CLASS}_clazz = NULL;""")
871 values = { 899 values = {
872 'JAVA_CLASS': clazz, 900 'JAVA_CLASS': clazz,
873 } 901 }
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
1007 help='Single input file name. The output file name ' 1035 help='Single input file name. The output file name '
1008 'will be derived from it. Must be used with ' 1036 'will be derived from it. Must be used with '
1009 '--output_dir.') 1037 '--output_dir.')
1010 option_parser.add_option('--output_dir', 1038 option_parser.add_option('--output_dir',
1011 help='The output directory. Must be used with ' 1039 help='The output directory. Must be used with '
1012 '--input') 1040 '--input')
1013 option_parser.add_option('--optimize_generation', type="int", 1041 option_parser.add_option('--optimize_generation', type="int",
1014 default=0, help='Whether we should optimize JNI ' 1042 default=0, help='Whether we should optimize JNI '
1015 'generation by not regenerating files if they have ' 1043 'generation by not regenerating files if they have '
1016 'not changed.') 1044 'not changed.')
1045 option_parser.add_option('--jarjar',
1046 help='Path to optional jarjar rules file.')
1017 options, args = option_parser.parse_args(argv) 1047 options, args = option_parser.parse_args(argv)
1018 if options.jar_file: 1048 if options.jar_file:
1019 input_file = ExtractJarInputFile(options.jar_file, options.input_file, 1049 input_file = ExtractJarInputFile(options.jar_file, options.input_file,
1020 options.output_dir) 1050 options.output_dir)
1021 else: 1051 else:
1022 input_file = options.input_file 1052 input_file = options.input_file
1023 output_file = None 1053 output_file = None
1024 if options.output_dir: 1054 if options.output_dir:
1025 root_name = os.path.splitext(os.path.basename(input_file))[0] 1055 root_name = os.path.splitext(os.path.basename(input_file))[0]
1026 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' 1056 output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
1057 if options.jarjar:
1058 with open(options.jarjar) as f:
1059 JniParams.SetJarJarMappings(f.read())
1027 GenerateJNIHeader(input_file, output_file, options.namespace, 1060 GenerateJNIHeader(input_file, output_file, options.namespace,
1028 options.optimize_generation) 1061 options.optimize_generation)
1029 1062
1030 1063
1031 if __name__ == '__main__': 1064 if __name__ == '__main__':
1032 sys.exit(main(sys.argv)) 1065 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | base/android/jni_generator/jni_generator_tests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698