def _DictForPath(path, use_proxy_hash=False): with open(path) as f: contents = jni_generator.RemoveComments(f.read()) if '@JniIgnoreNatives' in contents: return None fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName( path, contents) natives = jni_generator.ExtractNatives(contents, 'long') natives += jni_generator.ProxyHelpers.ExtractStaticProxyNatives( fully_qualified_class=fully_qualified_class, contents=contents, ptr_type='long', use_hash=use_proxy_hash) if len(natives) == 0: return None namespace = jni_generator.ExtractJNINamespace(contents) jni_params = jni_generator.JniParams(fully_qualified_class) jni_params.ExtractImportsAndInnerClasses(contents) is_main_dex = jni_generator.IsMainDexJavaClass(contents) header_generator = HeaderGenerator(namespace, fully_qualified_class, natives, jni_params, is_main_dex, use_proxy_hash) return header_generator.Generate()
def testFullyQualifiedClassName(self): contents = """ // Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.content.browser; import org.chromium.base.BuildInfo; """ self.assertEquals('org/chromium/content/browser/Foo', jni_generator.ExtractFullyQualifiedJavaClassName( 'org/chromium/content/browser/Foo.java', contents)) self.assertEquals('org/chromium/content/browser/Foo', jni_generator.ExtractFullyQualifiedJavaClassName( 'frameworks/Foo.java', contents)) self.assertRaises(SyntaxError, jni_generator.ExtractFullyQualifiedJavaClassName, 'com/foo/Bar', 'no PACKAGE line')
def _DictForPath(path): with open(path) as f: contents = jni_generator.RemoveComments(f.read()) natives = jni_generator.ExtractNatives(contents, 'long') if len(natives) == 0: return None namespace = jni_generator.ExtractJNINamespace(contents) fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName( path, contents) jni_params = jni_generator.JniParams(fully_qualified_class) jni_params.ExtractImportsAndInnerClasses(contents) main_dex = jni_generator.IsMainDexJavaClass(contents) header_generator = HeaderGenerator(namespace, fully_qualified_class, natives, jni_params, main_dex) return header_generator.Generate()
def GenerateJNIHeader(java_file_paths, output_file, args): """Generate a header file including two registration functions. Forward declares all JNI registration functions created by jni_generator.py. Calls the functions in RegisterMainDexNatives() if they are main dex. And calls them in RegisterNonMainDexNatives() if they are non-main dex. Args: java_file_paths: A list of java file paths. output_file: A relative path to output file. args: All input arguments. """ registration_dict = {} # Sort the file list to make sure the order is deterministic. java_file_paths.sort() for path in java_file_paths: if path in args.no_register_java: continue with open(path) as f: contents = jni_generator.RemoveComments(f.read()) natives = jni_generator.ExtractNatives(contents, 'long') if len(natives) == 0: continue namespace = jni_generator.ExtractJNINamespace(contents) fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName( path, contents) jni_params = jni_generator.JniParams(fully_qualified_class) jni_params.ExtractImportsAndInnerClasses(contents) main_dex = jni_generator.IsMainDexJavaClass(contents) header_generator = HeaderGenerator(namespace, fully_qualified_class, natives, jni_params, registration_dict, main_dex) header_generator.AddContent() header_content = CreateFromDict(registration_dict) if output_file: jni_generator.WriteOutput(output_file, header_content) else: print header_content
def GenerateJNIHeader(java_file_paths, output_file, args): """Generate a header file including two registration functions. Forward declares all JNI registration functions created by jni_generator.py. Calls the functions in RegisterMainDexNatives() if they are main dex. And calls them in RegisterNonMainDexNatives() if they are non-main dex. Args: java_file_paths: A list of java file paths. output_file: A relative path to output file. args: All input arguments. """ registration_dict = { 'FORWARD_DECLARATIONS': '', 'REGISTER_MAIN_DEX_NATIVES': '', 'REGISTER_NON_MAIN_DEX_NATIVES': '' } # Sort the file list to make sure the order is deterministic. java_file_paths.sort() for path in java_file_paths: if path in args.no_register_java: continue with open(path) as f: contents = f.read() natives = jni_generator.ExtractNatives(contents, 'long') if len(natives) == 0: continue fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName( path, contents) main_dex = jni_generator.IsMainDexJavaClass(contents) header_generator = HeaderGenerator( fully_qualified_class, registration_dict, main_dex) registration_dict = header_generator.GetContent() header_content = CreateFromDict(registration_dict) if output_file: jni_generator.WriteOutput(output_file, header_content) else: print header_content
def convert_file_to_proxy_natives(java_file_name, dry=False, verbose=True): if not os.path.isfile(java_file_name): if verbose: print('%s does not exist', java_file_name) return with open(java_file_name, 'r') as f: contents = f.read() no_comment_content = jni_generator.RemoveComments(contents) natives = jni_generator.ExtractNatives(no_comment_content, 'long') static_natives = [n for n in natives if n.static] if not static_natives: if verbose: print('%s has no static natives.', java_file_name) return contents = add_chromium_import_to_java_file(contents, JNI_IMPORT_STRING) # Extract comments and annotations above native methods. native_map = {} for itr in re.finditer(_NATIVES_REGEX, contents): n_dict = {} n_dict['annotations'] = itr.group('annotations').strip() n_dict['comments'] = itr.group('comments').strip() n_dict['params'] = itr.group('params').strip() native_map[itr.group('name')] = n_dict # Using static natives here ensures all the methods that are picked up by # the JNI generator are also caught by our own regex. methods = [] for n in static_natives: new_name = n.name[0].lower() + n.name[1:] n_dict = native_map['native' + n.name] params = n_dict['params'] comments = n_dict['comments'] annotations = n_dict['annotations'] methods.append( build_method_declaration(n.return_type, new_name, params, annotations, comments)) fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName( java_file_name, contents) class_name = fully_qualified_class.split('/')[-1] jni_class_name = class_name + 'Jni' # Remove all old declarations. for n in static_natives: pattern = _NATIVES_REGEX contents = re.sub(pattern, '', contents) # Replace occurences with new signature. for n in static_natives: # Okay not to match first (. # Since even if natives share a prefix, the replacement is the same. # E.g. if nativeFoo() and nativeFooBar() are both statics # and in nativeFooBar() we replace nativeFoo -> AJni.get().foo # the result is the same as replacing nativeFooBar() -> AJni.get().fooBar pattern = r'native%s' % n.name lower_name = n.name[0].lower() + n.name[1:] contents = re.sub(pattern, '%s.get().%s' % (jni_class_name, lower_name), contents) # Build and insert the @NativeMethods interface. interface = _JNI_INTERFACE_TEMPLATES.substitute({ 'INTERFACE_NAME': 'Natives', 'METHODS': ''.join(methods) }) # Insert the interface at the bottom of the top level class. # Most of the time this will be before the last }. insertion_point = contents.rfind('}') contents = contents[:insertion_point] + '\n' + interface + contents[ insertion_point:] if not dry: with open(java_file_name, 'w') as f: f.write(contents) else: print(contents) return contents
def convert_nonstatic_to_static(java_file_name, dry=False, verbose=True): if java_file_name is None: return if not os.path.isfile(java_file_name): if verbose: print('%s does not exist', java_file_name) return with open(java_file_name, 'r') as f: contents = f.read() no_comment_content = jni_generator.RemoveComments(contents) parsed_natives = jni_generator.ExtractNatives(no_comment_content, 'long') non_static_natives = [n for n in parsed_natives if not n.static] if not non_static_natives: if verbose: print('no natives found') return class_name = jni_generator.ExtractFullyQualifiedJavaClassName( java_file_name, no_comment_content).split('/')[-1] replace_patterns = [] should_add_comma = [] new_contents = contents # 1. Change non-static -> static. insertion_offset = 0 matches = [] for match in _NON_STATIC_NATIVES_REGEX.finditer(contents): if not 'static' in match.group('qualifiers'): matches.append(match) # Insert static as a keyword. qual_end = match.end('qualifiers') + insertion_offset insert_str = ' static ' new_contents = new_contents[:qual_end] + insert_str + new_contents[ qual_end:] insertion_offset += len(insert_str) # Insert an object param. start = insertion_offset + match.end('params') insert_str = '%s caller' % class_name if match.group('params'): insert_str = ', ' + insert_str # Match lines that don't have a native keyword. replace_patterns.append(r'(^\s*' + match.group('name') + r'\(.*?(?=\)))') replace_patterns.append(r'(return ' + match.group('name') + r'\(.*?(?=\)))') replace_patterns.append(r'([\:\)\(\+\*\?\&\|,\.\-\=\!\/][ \t]*' + match.group('name') + r'\(.*?(?=\)))') add_comma = bool(match.group('params')) should_add_comma.extend([add_comma] * 3) new_contents = new_contents[:start] + insert_str + new_contents[ start:] insertion_offset += len(insert_str) assert len(matches) == len(non_static_natives), ( 'Regex missed a native ' 'method that was found by ' 'the jni_generator.') # 2. Add a this param to all calls. for i, r in enumerate(replace_patterns): if should_add_comma[i]: new_contents = re.sub(r, '\g<1>, %s.this' % class_name, new_contents, flags=re.MULTILINE) else: new_contents = re.sub(r, '\g<1>%s.this' % class_name, new_contents, flags=re.MULTILINE) if dry: print(new_contents) else: with open(java_file_name, 'w') as f: f.write(new_contents)
def convert_nonstatic_to_static(java_file_name, skip_caller=False, dry=False, verbose=True): if java_file_name is None: return if not os.path.isfile(java_file_name): if verbose: print('%s does not exist', java_file_name) return with open(java_file_name, 'r') as f: contents = f.read() no_comment_content = jni_generator.RemoveComments(contents) parsed_natives = jni_generator.ExtractNatives(no_comment_content, 'long') non_static_natives = [n for n in parsed_natives if not n.static] if not non_static_natives: if verbose: print('no natives found') return class_name = jni_generator.ExtractFullyQualifiedJavaClassName( java_file_name, no_comment_content).split('/')[-1] # For fixing call sites. replace_patterns = [] should_append_comma = [] should_prepend_comma = [] new_contents = contents # 1. Change non-static -> static. insertion_offset = 0 matches = [] for match in _NON_STATIC_NATIVES_REGEX.finditer(contents): if not 'static' in match.group('qualifiers'): matches.append(match) # Insert static as a keyword. qual_end = match.end('qualifiers') + insertion_offset insert_str = ' static ' new_contents = new_contents[:qual_end] + insert_str + new_contents[ qual_end:] insertion_offset += len(insert_str) if skip_caller: continue # Insert an object param. insert_str = '%s caller' % class_name # No params. if not match.group('params'): start = insertion_offset + match.start('params') append_comma = False prepend_comma = False # One or more params. else: # Has mNativePtr. if _NATIVE_PTR_REGEX.match(match.group('params')): # Only 1 param, append to end of params. if match.group('params').count(',') == 0: start = insertion_offset + match.end('params') append_comma = False prepend_comma = True # Multiple params, insert after first param. else: comma_pos = match.group('params').find(',') start = insertion_offset + match.start( 'params') + comma_pos + 1 append_comma = True prepend_comma = False else: # No mNativePtr, insert as first param. start = insertion_offset + match.start('params') append_comma = True prepend_comma = False if prepend_comma: insert_str = ', ' + insert_str if append_comma: insert_str = insert_str + ', ' new_contents = new_contents[:start] + insert_str + new_contents[ start:] # Match lines that don't have a native keyword. native_match = r'\((\s*?(([ms]Native\w+)|([ms]\w+Android(Ptr)?)),?)?)' replace_patterns.append(r'(^\s*' + match.group('name') + native_match) replace_patterns.append(r'(return ' + match.group('name') + native_match) replace_patterns.append(r'([\:\)\(\+\*\?\&\|,\.\-\=\!\/][ \t]*' + match.group('name') + native_match) should_append_comma.extend([append_comma] * 3) should_prepend_comma.extend([prepend_comma] * 3) insertion_offset += len(insert_str) assert len(matches) == len(non_static_natives), ( 'Regex missed a native ' 'method that was found by ' 'the jni_generator.') # 2. Add a this param to all calls. for i, r in enumerate(replace_patterns): prepend_comma = ', ' if should_prepend_comma[i] else '' append_comma = ', ' if should_append_comma[i] else '' repl_str = '\g<1>' + prepend_comma + ' %s.this' + append_comma new_contents = re.sub(r, repl_str % class_name, new_contents, flags=re.MULTILINE) if dry: print(new_contents) else: with open(java_file_name, 'w') as f: f.write(new_contents)