Index: base/android/jni_generator/jni_generator.py |
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py |
index 85d8ce8077e2404148b8e1aa9579346f5fd42df6..20595dd8cbe1e9923f45b10e8159e153fc43fb3f 100755 |
--- a/base/android/jni_generator/jni_generator.py |
+++ b/base/android/jni_generator/jni_generator.py |
@@ -3,10 +3,6 @@ |
# Use of this source code is governed by a BSD-style license that can be |
# found in the LICENSE file. |
-# TODO (qinmin): Need to refactor this file as base should not know about |
-# higher level concepts. Currently this file has knowledge about higher level |
-# java classes. |
- |
"""Extracts native methods from a Java file and generates the JNI bindings. |
If you change this, please run and update the tests.""" |
@@ -21,8 +17,6 @@ import sys |
import textwrap |
import zipfile |
-UNKNOWN_JAVA_TYPE_PREFIX = 'UNKNOWN_JAVA_TYPE: ' |
- |
class ParseError(Exception): |
"""Exception thrown when we can't parse the input file.""" |
@@ -113,168 +107,142 @@ def JavaDataTypeToC(java_type): |
return 'jobject' |
-def JavaParamToJni(param): |
- """Converts a java param into a JNI signature type.""" |
- pod_param_map = { |
- 'int': 'I', |
- 'boolean': 'Z', |
- 'long': 'J', |
- 'double': 'D', |
- 'float': 'F', |
- 'byte': 'B', |
- 'void': 'V', |
- } |
- object_param_list = [ |
- 'Ljava/lang/Boolean', |
- 'Ljava/lang/Integer', |
- 'Ljava/lang/Long', |
- 'Ljava/lang/Object', |
- 'Ljava/lang/String', |
- 'Ljava/util/ArrayList', |
- 'Ljava/util/HashMap', |
- 'Ljava/util/List', |
- 'Landroid/content/Context', |
- 'Landroid/graphics/Bitmap', |
- 'Landroid/graphics/Canvas', |
- 'Landroid/graphics/Rect', |
- 'Landroid/graphics/RectF', |
- 'Landroid/graphics/Matrix', |
- 'Landroid/graphics/Point', |
- 'Landroid/graphics/SurfaceTexture$OnFrameAvailableListener', |
- 'Landroid/media/MediaPlayer', |
- 'Landroid/os/Message', |
- 'Landroid/view/KeyEvent', |
- 'Landroid/view/Surface', |
- 'Landroid/view/View', |
- 'Landroid/webkit/ValueCallback', |
- 'Ljava/io/InputStream', |
- 'Ljava/nio/ByteBuffer', |
- 'Ljava/util/Vector', |
- ] |
- app_param_list = [ |
- 'Landroid/graphics/SurfaceTexture', |
- 'Lcom/google/android/apps/chrome/ChromeContextMenuInfo', |
- 'Lcom/google/android/apps/chrome/ChromeWindow', |
- 'Lcom/google/android/apps/chrome/GoogleLocationSettingsHelperImpl', |
- 'Lcom/google/android/apps/chrome/OmniboxSuggestion', |
- 'Lcom/google/android/apps/chrome/PageInfoViewer', |
- 'Lcom/google/android/apps/chrome/Tab', |
- 'Lcom/google/android/apps/chrome/infobar/AutoLogin', |
- 'Lcom/google/android/apps/chrome/infobar/InfoBarContainer', |
- 'Lcom/google/android/apps/chrome/infobar/InfoBarContainer$NativeInfoBar', |
- ('Lcom/google/android/apps/chrome/preferences/ChromeNativePreferences$' |
- 'PasswordListObserver'), |
- 'Lorg/chromium/android_webview/AwContents', |
- 'Lorg/chromium/android_webview/AwContentsClient', |
- 'Lorg/chromium/android_webview/AwHttpAuthHandler', |
- 'Lorg/chromium/android_webview/AwContentsIoThreadClient', |
- 'Lorg/chromium/android_webview/AwWebContentsDelegate', |
- 'Lorg/chromium/android_webview/InterceptedRequestData', |
- 'Lorg/chromium/android_webview/JsPromptResultReceiver', |
- 'Lorg/chromium/android_webview/JsResultHandler', |
- 'Lorg/chromium/android_webview/JsResultReceiver', |
- 'Lorg/chromium/base/SystemMessageHandler', |
- 'Lorg/chromium/chrome/browser/autofill/AutofillExternalDelegate', |
- 'Lorg/chromium/chrome/browser/autofill/AutofillSuggestion', |
- 'Lorg/chromium/chrome/browser/ChromeBrowserProvider$BookmarkNode', |
- 'Lorg/chromium/chrome/browser/ChromeHttpAuthHandler', |
- 'Lorg/chromium/chrome/browser/ChromeWebContentsDelegateAndroid', |
- 'Lorg/chromium/chrome/browser/FindMatchRectsDetails', |
- 'Lorg/chromium/chrome/browser/FindNotificationDetails', |
- 'Lorg/chromium/chrome/browser/GoogleLocationSettingsHelper', |
- 'Lorg/chromium/chrome/browser/GoogleLocationSettingsHelperStub', |
- 'Lorg/chromium/chrome/browser/JavascriptAppModalDialog', |
- 'Lorg/chromium/chrome/browser/ProcessUtils', |
- ('Lorg/chromium/chrome/browser/component/navigation_interception/' |
- 'InterceptNavigationDelegate'), |
- ('Lorg/chromium/chrome/browser/component/web_contents_delegate_android/' |
- 'WebContentsDelegateAndroid'), |
- 'Lorg/chromium/chrome/browser/database/SQLiteCursor', |
- 'Lorg/chromium/content/app/SandboxedProcessService', |
- 'Lorg/chromium/content/browser/ContainerViewDelegate', |
- 'Lorg/chromium/content/browser/ContentVideoView', |
- 'Lorg/chromium/content/browser/ContentViewCore', |
- 'Lorg/chromium/content/browser/DeviceOrientation', |
- 'Lorg/chromium/content/browser/JavaInputStream', |
- 'Lorg/chromium/content/browser/LocationProvider', |
- 'Lorg/chromium/content/browser/SandboxedProcessArgs', |
- 'Lorg/chromium/content/browser/SandboxedProcessConnection', |
- 'Lorg/chromium/content/browser/TouchPoint', |
- 'Lorg/chromium/content/browser/WaitableNativeEvent', |
- 'Lorg/chromium/content/browser/WebContentsObserverAndroid', |
- 'Lorg/chromium/content/common/DeviceInfo', |
- 'Lorg/chromium/content/common/SurfaceTextureListener', |
- 'Lorg/chromium/media/MediaPlayerListener', |
- 'Lorg/chromium/net/NetworkChangeNotifier', |
- 'Lorg/chromium/net/ProxyChangeListener', |
- 'Lorg/chromium/ui/gfx/NativeWindow', |
- 'Lorg/chromium/ui/SelectFileDialog', |
- ] |
- if param == 'byte[][]': |
- return '[[B' |
- prefix = '' |
- # Array? |
- if param[-2:] == '[]': |
- prefix = '[' |
- param = param[:-2] |
- # Generic? |
- if '<' in param: |
- param = param[:param.index('<')] |
- if param in pod_param_map: |
- return prefix + pod_param_map[param] |
- if '/' in param: |
- # Coming from javap, use the fully qualified param directly. |
- return 'L' + param + ';' |
- for qualified_name in object_param_list + app_param_list: |
- if (qualified_name.endswith('/' + param) or |
- qualified_name.endswith('$' + param.replace('.', '$')) or |
- qualified_name == 'L' + param): |
- return prefix + qualified_name + ';' |
- else: |
- return UNKNOWN_JAVA_TYPE_PREFIX + prefix + param + ';' |
+class JniParams(object): |
+ _UNKNOWN_JAVA_TYPE_PREFIX = 'UNKNOWN_JAVA_TYPE: ' |
+ _external_param_files = set() |
+ _external_param_list = [] |
Nico
2012/10/31 17:41:28
Meh, that's still global state. Why not make this
bulach
2012/10/31 17:53:10
I agree, but that's a much more complex change :)
|
+ @staticmethod |
+ def ReadExternalParamList(external_param_files): |
+ if not external_param_files: |
+ return |
+ assert not JniParams._external_param_files |
+ JniParams._external_param_files = set(external_param_files) |
+ for external_param_file in JniParams._external_param_files: |
+ with file(external_param_file, 'r') as f: |
+ contents = f.read() |
+ JniParams._external_param_list += [x.strip() |
+ for x in contents.splitlines() |
+ if x and not x.startswith('#')] |
-def JniSignature(params, returns, wrap): |
- """Returns the JNI signature for the given datatypes.""" |
- items = ['('] |
- items += [JavaParamToJni(param.datatype) for param in params] |
- items += [')'] |
- items += [JavaParamToJni(returns)] |
- if wrap: |
- return '\n' + '\n'.join(['"' + item + '"' for item in items]) |
- else: |
- return '"' + ''.join(items) + '"' |
+ @staticmethod |
+ def JavaToJni(param): |
+ """Converts a java param into a JNI signature type.""" |
+ pod_param_map = { |
+ 'int': 'I', |
+ 'boolean': 'Z', |
+ 'long': 'J', |
+ 'double': 'D', |
+ 'float': 'F', |
+ 'byte': 'B', |
+ 'void': 'V', |
+ } |
+ object_param_list = [ |
+ 'Ljava/lang/Boolean', |
+ 'Ljava/lang/Integer', |
+ 'Ljava/lang/Long', |
+ 'Ljava/lang/Object', |
+ 'Ljava/lang/String', |
+ 'Ljava/util/ArrayList', |
+ 'Ljava/util/HashMap', |
+ 'Ljava/util/List', |
+ 'Landroid/content/Context', |
+ 'Landroid/graphics/Bitmap', |
+ 'Landroid/graphics/Canvas', |
+ 'Landroid/graphics/Rect', |
+ 'Landroid/graphics/RectF', |
+ 'Landroid/graphics/Matrix', |
+ 'Landroid/graphics/Point', |
+ 'Landroid/graphics/SurfaceTexture', |
+ 'Landroid/graphics/SurfaceTexture$OnFrameAvailableListener', |
+ 'Landroid/media/MediaPlayer', |
+ 'Landroid/os/Message', |
+ 'Landroid/view/KeyEvent', |
+ 'Landroid/view/Surface', |
+ 'Landroid/view/View', |
+ 'Landroid/webkit/ValueCallback', |
+ 'Ljava/io/InputStream', |
+ 'Ljava/nio/ByteBuffer', |
+ 'Ljava/util/Vector', |
+ ] |
+ if param == 'byte[][]': |
+ return '[[B' |
+ prefix = '' |
+ # Array? |
+ if param[-2:] == '[]': |
+ prefix = '[' |
+ param = param[:-2] |
+ # Generic? |
+ if '<' in param: |
+ param = param[:param.index('<')] |
+ if param in pod_param_map: |
+ return prefix + pod_param_map[param] |
+ if '/' in param: |
+ # Coming from javap, use the fully qualified param directly. |
+ return 'L' + param + ';' |
+ for qualified_name in object_param_list + JniParams._external_param_list: |
+ if (qualified_name.endswith('/' + param) or |
+ qualified_name.endswith('$' + param.replace('.', '$')) or |
+ qualified_name == 'L' + param): |
+ return prefix + qualified_name + ';' |
+ else: |
+ return JniParams._UNKNOWN_JAVA_TYPE_PREFIX + prefix + param + ';' |
+ @staticmethod |
+ def Signature(params, returns, wrap): |
+ """Returns the JNI signature for the given datatypes.""" |
+ items = ['('] |
+ items += [JniParams.JavaToJni(param.datatype) for param in params] |
+ items += [')'] |
+ items += [JniParams.JavaToJni(returns)] |
+ if wrap: |
+ return '\n' + '\n'.join(['"' + item + '"' for item in items]) |
+ else: |
+ return '"' + ''.join(items) + '"' |
-def ParseParams(params): |
- """Parses the params into a list of Param objects.""" |
- if not params: |
- return [] |
- ret = [] |
- for p in [p.strip() for p in params.split(',')]: |
- items = p.split(' ') |
- if 'final' in items: |
- items.remove('final') |
- param = Param( |
- datatype=items[0], |
- name=(items[1] if len(items) > 1 else 'p%s' % len(ret)), |
- ) |
- ret += [param] |
- return ret |
+ @staticmethod |
+ def Parse(params): |
+ """Parses the params into a list of Param objects.""" |
+ if not params: |
+ return [] |
+ ret = [] |
+ for p in [p.strip() for p in params.split(',')]: |
+ items = p.split(' ') |
+ if 'final' in items: |
+ items.remove('final') |
+ param = Param( |
+ datatype=items[0], |
+ name=(items[1] if len(items) > 1 else 'p%s' % len(ret)), |
+ ) |
+ ret += [param] |
+ return ret |
+ @staticmethod |
+ def CheckUnknownDatatypes(fully_qualified_class, items): |
+ unknown_datatypes = JniParams._GetUnknownDatatypes(items) |
+ if unknown_datatypes: |
+ msg = ('There are a few unknown datatypes in %s' % |
+ fully_qualified_class) |
+ msg += '\nPlease, edit %s' % str(JniParams._external_param_files) |
+ msg += '\nand add the JNI type(s):\n' |
+ for unknown_datatype in unknown_datatypes: |
+ msg += '\n%s in methods:\n' % unknown_datatype |
+ msg += '\n '.join(unknown_datatypes[unknown_datatype]) |
+ raise SyntaxError(msg) |
-def GetUnknownDatatypes(items): |
- """Returns a list containing the unknown datatypes.""" |
- unknown_types = {} |
- for item in items: |
- all_datatypes = ([JavaParamToJni(param.datatype) |
- for param in item.params] + |
- [JavaParamToJni(item.return_type)]) |
- for d in all_datatypes: |
- if d.startswith(UNKNOWN_JAVA_TYPE_PREFIX): |
- unknown_types[d] = (unknown_types.get(d, []) + |
- [item.name or 'Unable to parse']) |
- return unknown_types |
+ @staticmethod |
+ def _GetUnknownDatatypes(items): |
+ """Returns a list containing the unknown datatypes.""" |
+ unknown_types = {} |
+ for item in items: |
+ all_datatypes = ([JniParams.JavaToJni(param.datatype) |
+ for param in item.params] + |
+ [JniParams.JavaToJni(item.return_type)]) |
+ for d in all_datatypes: |
+ if d.startswith(JniParams._UNKNOWN_JAVA_TYPE_PREFIX): |
+ unknown_types[d] = (unknown_types.get(d, []) + |
+ [item.name or 'Unable to parse']) |
+ return unknown_types |
def ExtractJNINamespace(contents): |
@@ -311,7 +279,7 @@ def ExtractNatives(contents): |
native_class_name=match.group('native_class_name'), |
return_type=match.group('return'), |
name=match.group('name').replace('native', ''), |
- params=ParseParams(match.group('params'))) |
+ params=JniParams.Parse(match.group('params'))) |
natives += [native] |
return natives |
@@ -373,7 +341,7 @@ def GetMangledMethodName(name, params, return_type): |
""" |
mangled_items = [] |
for datatype in [return_type] + [x.datatype for x in params]: |
- mangled_items += [GetMangledParam(JavaParamToJni(datatype))] |
+ mangled_items += [GetMangledParam(JniParams.JavaToJni(datatype))] |
mangled_name = name + '_'.join(mangled_items) |
assert re.match(r'[0-9a-zA-Z_]+', mangled_name) |
return mangled_name |
@@ -434,7 +402,7 @@ def ExtractCalledByNatives(contents): |
java_class_name=match.group('annotation') or '', |
return_type=match.group('return_type'), |
name=match.group('name'), |
- params=ParseParams(match.group('params')))] |
+ params=JniParams.Parse(match.group('params')))] |
# Check for any @CalledByNative occurrences that weren't matched. |
unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n') |
for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]): |
@@ -470,7 +438,7 @@ class JNIFromJavaP(object): |
java_class_name='', |
return_type=match.group('return_type'), |
name=match.group('name'), |
- params=ParseParams(match.group('params').replace('.', '/')))] |
+ params=JniParams.Parse(match.group('params').replace('.', '/')))] |
re_constructor = re.compile('.*? public ' + |
self.fully_qualified_class.replace('/', '.') + |
'\((?P<params>.*?)\)') |
@@ -485,7 +453,7 @@ class JNIFromJavaP(object): |
java_class_name='', |
return_type=self.fully_qualified_class, |
name='Constructor', |
- params=ParseParams(match.group('params').replace('.', '/')), |
+ params=JniParams.Parse(match.group('params').replace('.', '/')), |
is_constructor=True)] |
self.called_by_natives = MangleCalledByNatives(self.called_by_natives) |
self.inl_header_file_generator = InlHeaderFileGenerator( |
@@ -561,17 +529,8 @@ class InlHeaderFileGenerator(object): |
self.natives = natives |
self.called_by_natives = called_by_natives |
self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' |
- unknown_datatypes = GetUnknownDatatypes(self.natives + |
- self.called_by_natives) |
- if unknown_datatypes: |
- msg = ('There are a few unknown datatypes in %s' % |
- self.fully_qualified_class) |
- msg += '\nPlease, edit %s' % sys.argv[0] |
- msg += '\nand add the java type to JavaParamToJni()\n' |
- for unknown_datatype in unknown_datatypes: |
- msg += '\n%s in methods:\n' % unknown_datatype |
- msg += '\n '.join(unknown_datatypes[unknown_datatype]) |
- raise SyntaxError(msg) |
+ JniParams.CheckUnknownDatatypes(self.fully_qualified_class, |
+ self.natives + self.called_by_natives) |
def GetContent(self): |
"""Returns the content of the JNI binding file.""" |
@@ -865,8 +824,9 @@ ${FUNCTION_HEADER} |
template = Template("""\ |
{ "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""") |
values = {'NAME': native.name, |
- 'JNI_SIGNATURE': JniSignature(native.params, native.return_type, |
- True)} |
+ 'JNI_SIGNATURE': JniParams.Signature(native.params, |
+ native.return_type, |
+ True)} |
return template.substitute(values) |
def GetUniqueClasses(self, origin): |
@@ -937,9 +897,9 @@ jclass g_${JAVA_CLASS}_clazz = NULL;""") |
'JNI_NAME': jni_name, |
'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, |
'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE', |
- 'JNI_SIGNATURE': JniSignature(called_by_native.params, |
- jni_return_type, |
- True) |
+ 'JNI_SIGNATURE': JniParams.Signature(called_by_native.params, |
+ jni_return_type, |
+ True) |
} |
return template.substitute(values) |
@@ -1034,7 +994,13 @@ See SampleForTests.java for more details. |
option_parser.add_option('--output_dir', |
help='The output directory. Must be used with ' |
'--input') |
+ option_parser.add_option('--external_param_list', |
+ help='A file name containing a list with extra ' |
+ 'fully-qualified param names. Can be used multiple ' |
+ 'times.', |
+ action='append') |
options, args = option_parser.parse_args(argv) |
+ JniParams.ReadExternalParamList(options.external_param_list) |
if options.jar_file: |
input_file = ExtractJarInputFile(options.jar_file, options.input_file, |
options.output_dir) |