OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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)) |
OLD | NEW |