diff options
author | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-07 19:06:51 +0000 |
---|---|---|
committer | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-07 19:06:51 +0000 |
commit | 2f8aa425134919f4573b8dd9ac85fd0b4310d8e2 (patch) | |
tree | bbf49efd48d56440814ed779028aa8ffceeeb398 /base/android | |
parent | 3e89600cff82761ccf4cad606b6ea6c12148fd65 (diff) | |
download | chromium_src-2f8aa425134919f4573b8dd9ac85fd0b4310d8e2.zip chromium_src-2f8aa425134919f4573b8dd9ac85fd0b4310d8e2.tar.gz chromium_src-2f8aa425134919f4573b8dd9ac85fd0b4310d8e2.tar.bz2 |
Android: uses "import" section and inner classes for obtaining qualified JNI parameters.
Rather than explicitly listing all the parameters
(see crbug.com/158722), infer almost all of them from the import section and inner classes.
Assumes other classes are in the same package as a fallback.
BUG=159397
TEST=base/android/jni_generator/jni_generator_tests.py
Review URL: https://chromiumcodereview.appspot.com/11363079
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166482 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/android')
-rw-r--r-- | base/android/jni_generator/SampleForTests.java | 2 | ||||
-rw-r--r-- | base/android/jni_generator/class_list.jni | 50 | ||||
-rwxr-xr-x | base/android/jni_generator/jni_generator.py | 112 | ||||
-rwxr-xr-x | base/android/jni_generator/jni_generator_tests.py | 94 |
4 files changed, 106 insertions, 152 deletions
diff --git a/base/android/jni_generator/SampleForTests.java b/base/android/jni_generator/SampleForTests.java index 6f8b1ca..341f0ea 100644 --- a/base/android/jni_generator/SampleForTests.java +++ b/base/android/jni_generator/SampleForTests.java @@ -4,6 +4,8 @@ package org.chromium.example.jni_generator; +import android.graphics.Rect; + // This class serves as a reference test for the bindings generator, and as example documentation // for how to use the jni generator. // The C++ counter-part is sample_for_tests.cc. diff --git a/base/android/jni_generator/class_list.jni b/base/android/jni_generator/class_list.jni deleted file mode 100644 index 3395a3d..0000000 --- a/base/android/jni_generator/class_list.jni +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2012 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. - -# This file contains a list of JNI parameters used to generate the -# bindings. -# TODO(bulach): this file should only contain classes from base/ all other -# classes should move to corresponding class_list.jni files closer to their own -# directories. See crbug.com/158722 -Lcom/google/android/apps/chrome/ChromeContextMenuInfo -Lcom/google/android/apps/chrome/ChromeWindow -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/component/navigation_interception/InterceptNavigationDelegate -Lorg/chromium/chrome/browser/ChromeHttpAuthHandler -Lorg/chromium/chrome/browser/ChromeWebContentsDelegateAndroid -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
\ No newline at end of file diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py index 20595dd..8616240 100755 --- a/base/android/jni_generator/jni_generator.py +++ b/base/android/jni_generator/jni_generator.py @@ -94,6 +94,7 @@ def JavaDataTypeToC(java_type): java_type_map = { 'void': 'void', 'String': 'jstring', + 'java/lang/String': 'jstring', } if java_type in java_pod_type_map: return java_pod_type_map[java_type] @@ -108,22 +109,29 @@ def JavaDataTypeToC(java_type): class JniParams(object): - _UNKNOWN_JAVA_TYPE_PREFIX = 'UNKNOWN_JAVA_TYPE: ' - _external_param_files = set() - _external_param_list = [] + _imports = [] + _fully_qualified_class = '' + _package = '' + _inner_classes = [] @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 SetFullyQualifiedClass(fully_qualified_class): + JniParams._fully_qualified_class = 'L' + fully_qualified_class + JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1]) + + @staticmethod + def ExtractImportsAndInnerClasses(contents): + contents = contents.replace('\n', '') + re_import = re.compile(r'import.*?(?P<class>\S*?);') + for match in re.finditer(re_import, contents): + JniParams._imports += ['L' + match.group('class').replace('.', '/')] + + re_inner = re.compile(r'(class|interface)\s+?(?P<name>\w+?)\W') + for match in re.finditer(re_inner, contents): + inner = match.group('name') + if not JniParams._fully_qualified_class.endswith(inner): + JniParams._inner_classes += [JniParams._fully_qualified_class + '$' + + inner] @staticmethod def JavaToJni(param): @@ -143,27 +151,6 @@ class JniParams(object): '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' @@ -180,13 +167,16 @@ class JniParams(object): 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: + for qualified_name in (object_param_list + + JniParams._imports + + [JniParams._fully_qualified_class] + + JniParams._inner_classes): 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 + ';' + # Type not found, falling back to same package as this class. + return prefix + 'L' + JniParams._package + '/' + param + ';' @staticmethod def Signature(params, returns, wrap): @@ -217,33 +207,6 @@ class JniParams(object): 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) - - @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): re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)') @@ -285,8 +248,10 @@ def ExtractNatives(contents): def GetStaticCastForReturnType(return_type): - if return_type == 'String': + if return_type in ['String', 'java/lang/String']: return 'jstring' + elif return_type.endswith('[]'): + return 'jobjectArray' return None @@ -421,10 +386,11 @@ class JNIFromJavaP(object): self.fully_qualified_class = re.match('.*?class (?P<class_name>.*?) ', contents[1]).group('class_name') self.fully_qualified_class = self.fully_qualified_class.replace('.', '/') + JniParams.SetFullyQualifiedClass(self.fully_qualified_class) self.java_class_name = self.fully_qualified_class.split('/')[-1] if not self.namespace: self.namespace = 'JNI_' + self.java_class_name - re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\w+?) (?P<name>\w+?)' + re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)' '\((?P<params>.*?)\)') self.called_by_natives = [] for content in contents[2:]: @@ -436,7 +402,7 @@ class JNIFromJavaP(object): unchecked=False, static='static' in match.group('prefix'), java_class_name='', - return_type=match.group('return_type'), + return_type=match.group('return_type').replace('.', '/'), name=match.group('name'), params=JniParams.Parse(match.group('params').replace('.', '/')))] re_constructor = re.compile('.*? public ' + @@ -479,6 +445,8 @@ class JNIFromJavaSource(object): def __init__(self, contents, fully_qualified_class): contents = self._RemoveComments(contents) + JniParams.SetFullyQualifiedClass(fully_qualified_class) + JniParams.ExtractImportsAndInnerClasses(contents) jni_namespace = ExtractJNINamespace(contents) natives = ExtractNatives(contents) called_by_natives = ExtractCalledByNatives(contents) @@ -529,8 +497,6 @@ class InlHeaderFileGenerator(object): self.natives = natives self.called_by_natives = called_by_natives self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' - JniParams.CheckUnknownDatatypes(self.fully_qualified_class, - self.natives + self.called_by_natives) def GetContent(self): """Returns the content of the JNI binding file.""" @@ -994,13 +960,7 @@ 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) diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py index 3dd9097..886b731 100755 --- a/base/android/jni_generator/jni_generator_tests.py +++ b/base/android/jni_generator/jni_generator_tests.py @@ -56,6 +56,7 @@ class TestGenerator(unittest.TestCase): def testNatives(self): test_data = """" + interface OnFrameAvailableListener {} private native int nativeInit(); private native void nativeDestroy(int nativeChromeBrowserProvider); private native long nativeAddBookmark( @@ -82,6 +83,7 @@ class TestGenerator(unittest.TestCase): int nativeDataFetcherImplAndroid, double alpha, double beta, double gamma); """ + jni_generator.JniParams.ExtractImportsAndInnerClasses(test_data) natives = jni_generator.ExtractNatives(test_data) golden_natives = [ NativeMethod(return_type='int', static=False, @@ -389,7 +391,7 @@ static bool RegisterNativesImpl(JNIEnv* env) { { "nativeGetInnerClass", "(" ")" -"Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;", +"Lorg/chromium/example/jni_generator/SampleForTests$OnFrameAvailableListener;", reinterpret_cast<void*>(GetInnerClass) }, { "nativeQueryBitmap", "(" @@ -718,8 +720,14 @@ static bool RegisterNativesImpl(JNIEnv* env) { def testCalledByNatives(self): test_data = """" + import android.graphics.Bitmap; + import android.view.View; + import java.io.InputStream; + + class InnerClass {} + @CalledByNative - OnFrameAvailableListener showConfirmInfoBar(int nativeInfoBar, + InnerClass showConfirmInfoBar(int nativeInfoBar, String buttonOk, String buttonCancel, String title, Bitmap icon) { InfoBar infobar = new ConfirmInfoBar(nativeInfoBar, mContext, buttonOk, buttonCancel, @@ -727,7 +735,7 @@ static bool RegisterNativesImpl(JNIEnv* env) { return infobar; } @CalledByNative - OnFrameAvailableListener showAutoLoginInfoBar(int nativeInfoBar, + InnerClass showAutoLoginInfoBar(int nativeInfoBar, String realm, String account, String args) { AutoLoginInfoBar infobar = new AutoLoginInfoBar(nativeInfoBar, mContext, realm, account, args); @@ -762,10 +770,12 @@ static bool RegisterNativesImpl(JNIEnv* env) { @CalledByNativeUnchecked private void uncheckedCall(int iParam); """ + jni_generator.JniParams.SetFullyQualifiedClass('org/chromium/Foo') + jni_generator.JniParams.ExtractImportsAndInnerClasses(test_data) called_by_natives = jni_generator.ExtractCalledByNatives(test_data) golden_called_by_natives = [ CalledByNative( - return_type='OnFrameAvailableListener', + return_type='InnerClass', system_class=False, static=False, name='showConfirmInfoBar', @@ -780,7 +790,7 @@ static bool RegisterNativesImpl(JNIEnv* env) { unchecked=False, ), CalledByNative( - return_type='OnFrameAvailableListener', + return_type='InnerClass', system_class=False, static=False, name='showAutoLoginInfoBar', @@ -916,7 +926,7 @@ static ScopedJavaLocalRef<jobject> Java_TestJni_showConfirmInfoBar(JNIEnv* env, "Ljava/lang/String;" "Landroid/graphics/Bitmap;" ")" -"Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;", +"Lorg/chromium/Foo$InnerClass;", &g_TestJni_showConfirmInfoBar); jobject ret = @@ -946,7 +956,7 @@ static ScopedJavaLocalRef<jobject> Java_TestJni_showAutoLoginInfoBar(JNIEnv* "Ljava/lang/String;" "Ljava/lang/String;" ")" -"Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;", +"Lorg/chromium/Foo$InnerClass;", &g_TestJni_showAutoLoginInfoBar); jobject ret = @@ -1480,16 +1490,6 @@ static bool RegisterNativesImpl(JNIEnv* env) { """ jni_from_java = jni_generator.JNIFromJavaSource(test_data, 'foo/bar') - def testRaisesOnUnknownDatatype(self): - test_data = """ - class MyInnerClass { - private native int nativeInit(AnUnknownDatatype p0); - } - """ - self.assertRaises(SyntaxError, - jni_generator.JNIFromJavaSource, - test_data, 'foo/bar') - def testRaisesOnNonJNIMethod(self): test_data = """ class MyInnerClass { @@ -1527,15 +1527,57 @@ static bool RegisterNativesImpl(JNIEnv* env) { self.assertTrue(len(line) > 80, ('Expected #ifndef line to be > 80 chars: ', line)) - def testExternalParamList(self): - script_dir = os.path.dirname(sys.argv[0]) - external_param_list = [os.path.join(script_dir, 'class_list.jni')] - jni_generator.JniParams.ReadExternalParamList(external_param_list) - self.assertTrue('Lorg/chromium/base/SystemMessageHandler' in - jni_generator.JniParams._external_param_list) - self.assertRaises(AssertionError, - jni_generator.JniParams.ReadExternalParamList, - external_param_list) + def testImports(self): + import_header = """ +// Copyright (c) 2012 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.app; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.graphics.SurfaceTexture; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; +import android.view.Surface; + +import java.util.ArrayList; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; +import org.chromium.content.app.ContentMain; +import org.chromium.content.browser.SandboxedProcessConnection; +import org.chromium.content.common.ISandboxedProcessCallback; +import org.chromium.content.common.ISandboxedProcessService; +import org.chromium.content.common.SurfaceCallback; + +import static org.chromium.Bar.Zoo; + +class Foo { + public static class BookmarkNode implements Parcelable { + } + public interface PasswordListObserver { + } +} + """ + jni_generator.JniParams.SetFullyQualifiedClass( + 'org/chromium/content/app/Foo') + jni_generator.JniParams.ExtractImportsAndInnerClasses(import_header) + self.assertTrue('Lorg/chromium/content/common/ISandboxedProcessService' in + jni_generator.JniParams._imports) + self.assertTrue('Lorg/chromium/Bar/Zoo' in + jni_generator.JniParams._imports) + self.assertTrue('Lorg/chromium/content/app/Foo$BookmarkNode' in + jni_generator.JniParams._inner_classes) + self.assertTrue('Lorg/chromium/content/app/Foo$PasswordListObserver' in + jni_generator.JniParams._inner_classes) + if __name__ == '__main__': unittest.main() |