diff options
author | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-24 00:09:38 +0000 |
---|---|---|
committer | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-24 00:09:38 +0000 |
commit | cef04bcfafed700743e5c74f37ebda7411efa420 (patch) | |
tree | 01e370446126ec06c221224c7f580e2c7dcc3ee6 /base/android | |
parent | a6cc6200839c21d51ea845976d5e6c9621efcf7f (diff) | |
download | chromium_src-cef04bcfafed700743e5c74f37ebda7411efa420.zip chromium_src-cef04bcfafed700743e5c74f37ebda7411efa420.tar.gz chromium_src-cef04bcfafed700743e5c74f37ebda7411efa420.tar.bz2 |
Chrome on Android: adds jni_generator.
On Android, we use JNI to call into the system API.
This patch adds the generator for the bindings and all its tests.
We'll soon start adding the API wrappers themselves.
BUG=
TEST=base/android/jni_generator/jni_generator_tests.py
Review URL: http://codereview.chromium.org/9384011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@123374 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/android')
-rw-r--r-- | base/android/jni_generator/SampleForTests.java | 150 | ||||
-rw-r--r-- | base/android/jni_generator/golden_sample_for_tests_jni.h | 297 | ||||
-rw-r--r-- | base/android/jni_generator/jni_generator.gyp | 67 | ||||
-rw-r--r-- | base/android/jni_generator/jni_generator.py | 972 | ||||
-rw-r--r-- | base/android/jni_generator/jni_generator_tests.py | 1527 | ||||
-rw-r--r-- | base/android/jni_generator/sample_for_tests.cc | 56 |
6 files changed, 3069 insertions, 0 deletions
diff --git a/base/android/jni_generator/SampleForTests.java b/base/android/jni_generator/SampleForTests.java new file mode 100644 index 0000000..a081921 --- /dev/null +++ b/base/android/jni_generator/SampleForTests.java @@ -0,0 +1,150 @@ +// 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.example.jni_generator; + +// 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. +// jni_generator.gyp has a jni_generator_tests target that will: +// * Generate a header file for the JNI bindings based on this file. +// * Compile sample_for_tests.cc using the generated header file. +// * link a native executable to prove the generated header + cc file are self-contained. +// All comments are informational only, and are ignored by the jni generator. +class SampleForTests { + // Classes can store their C++ pointer counter part as an int that is normally initialized by + // calling out a nativeInit() function. + int nativePtr; + + // You can define methods and attributes on the java class just like any other. + // Methods without the @CalledByNative annotation won't be exposed to JNI. + public SampleForTests() { + } + + public void startExample() { + // Calls native code and holds a pointer to the C++ class. + nativePtr = nativeInit("myParam"); + } + + public void doStuff() { + // This will call CPPClass::Method() using nativePtr as a pointer to the object. This must be + // done to: + // * avoid leaks. + // * using finalizers are not allowed to destroy the cpp class. + nativeMethod(nativePtr); + } + + public void finishExample() { + // We're done, so let's destroy nativePtr object. + nativeDestroy(nativePtr); + } + + // ----------------------------------------------------------------------------------------------- + // The following methods demonstrate exporting Java methods for invocation from C++ code. + // Java functions are mapping into C global functions by prefixing the method name with + // "Java_<Class>_" + // This is triggered by the @CalledByNative annotation; the methods may be named as you wish. + + // Exported to C++ as: + // Java_Example_javaMethod(JNIEnv* env, jobject obj, jint foo, jint bar) + // Typically the C++ code would have obtained the jobject via the Init() call described above. + @CalledByNative + public int javaMethod(int foo, + int bar) { + return 0; + } + + // Exported to C++ as Java_Example_staticJavaMethod(JNIEnv* env) + // Note no jobject argument, as it is static. + @CalledByNative + public static boolean staticJavaMethod() { + return true; + } + + // No prefix, so this method is package private. It will still be exported. + @CalledByNative + void packagePrivateJavaMethod() {} + + // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that + // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to + // call ClearException() and act as appropriate. + // See more details at the "@CalledByNativeUnchecked" annotation. + @CalledByNativeUnchecked + void methodThatThrowsException() throws Exception {} + + //------------------------------------------------------------------------------------------------ + // Java fields which are accessed from C++ code must be annotated with @AccessedByNative to + // prevent them being eliminated when unreferenced code is stripped. + @AccessedByNative + private int javaField; + + //------------------------------------------------------------------------------------------------ + // The following methods demonstrate declaring methods to call into C++ from Java. + // The generator detects the "native" and "static" keywords, the type and name of the first + // parameter, and the "native" prefix to the function name to determine the C++ function + // signatures. Besides these constraints the methods can be freely named. + + // This declares a C++ function which the application code must implement: + // static jint Init(JNIEnv* env, jobject obj); + // The jobject parameter refers back to this java side object instance. + // The implementation must return the pointer to the C++ object cast to jint. + // The caller of this method should store it, and supply it as a the nativeCPPClass param to + // subsequent native method calls (see the methods below that take an "int native..." as first + // param). + private native int nativeInit(); + + // This defines a function binding to the associated C++ class member function. The name is + // derived from |nativeDestroy| and |nativeCPPClass| to arrive at CPPClass::Destroy() (i.e. native + // prefixes stripped). + // The |nativeCPPClass| is automatically cast to type CPPClass* in order to obtain the object on + // which to invoke the member function. + private native void nativeDestroy(int nativeCPPClass); + + // This declares a C++ function which the application code must implement: + // static jdouble GetDoubleFunction(JNIEnv* env, jobject obj); + // The jobject parameter refers back to this java side object instance. + private native double nativeGetDoubleFunction(); + + // Similar to nativeGetDoubleFunction(), but here the C++ side will receive a jclass rather than + // jobject param, as the function is declared static. + private static native float nativeGetFloatFunction(); + + // This function takes a non-POD datatype. We have a list mapping them to their full classpath in + // jni_generator.py JavaParamToJni. If you require a new datatype, make sure you add to that + // function. + private native void nativeSetNonPODDatatype(Rect rect); + + // This declares a C++ function which the application code must implement: + // static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject obj); + // The jobject parameter refers back to this java side object instance. + // Note that it returns a ScopedJavaLocalRef<jobject> so that you don' have to worry about + // deleting the JNI local reference. This is similar with Strings and arrays. + private native Object nativeGetNonPODDatatype(); + + // Similar to nativeDestroy above, this will cast nativeCPPClass into pointer of CPPClass type and + // call its Method member function. + private native int nativeMethod(int nativeCPPClass); + + // Similar to nativeMethod above, but here the C++ fully qualified class name is taken from the + // comment rather than parameter name, which can thus be chosen freely. + private native double nativeMethodOtherP0(int nativeCPPClass /* cpp_namespace::CPPClass */); + + // An inner class has some special attributes for annotation. + class InnerClass { + @CalledByNative("InnerClass") + public float JavaInnerMethod() { + } + + @CalledByNative("InnerClass") + public static void javaInnerFunction() { + } + + @NativeCall("InnerClass") + private static native int nativeInnerFunction(); + + @NativeCall("InnerClass") + private static native String nativeInnerMethod(int nativeCPPClass); + + } +} diff --git a/base/android/jni_generator/golden_sample_for_tests_jni.h b/base/android/jni_generator/golden_sample_for_tests_jni.h new file mode 100644 index 0000000..10bc633 --- /dev/null +++ b/base/android/jni_generator/golden_sample_for_tests_jni.h @@ -0,0 +1,297 @@ +// 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 is autogenerated by +// base/android/jni_generator/jni_generator_tests.py +// For +// org/chromium/example/jni_generator/SampleForTests + +#ifndef org_chromium_example_jni_generator_SampleForTests_JNI +#define org_chromium_example_jni_generator_SampleForTests_JNI + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" +#include "base/logging.h" + +using base::android::ScopedJavaLocalRef; + +// Step 1: forward declarations. +namespace { +const char* const kInnerClassClassPath = + "org/chromium/example/jni_generator/SampleForTests$InnerClass"; +const char* const kSampleForTestsClassPath = + "org/chromium/example/jni_generator/SampleForTests"; +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_InnerClass_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>()); +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_SampleForTests_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>()); +} // namespace + +static jint Init(JNIEnv* env, jobject obj); + + +static jdouble GetDoubleFunction(JNIEnv* env, jobject obj); + + +static jfloat GetFloatFunction(JNIEnv* env, jclass clazz); + + +static void SetNonPODDatatype(JNIEnv* env, jobject obj, + jobject rect); + + +static jobject GetNonPODDatatype(JNIEnv* env, jobject obj); + + +static jint InnerFunction(JNIEnv* env, jclass clazz); + + +// Step 2: method stubs. +static void Destroy(JNIEnv* env, jobject obj, + jint nativeCPPClass) { + DCHECK(nativeCPPClass) << "Destroy"; + CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); + return native->Destroy(env, obj); +} + +static jint Method(JNIEnv* env, jobject obj, + jint nativeCPPClass) { + DCHECK(nativeCPPClass) << "Method"; + CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); + return native->Method(env, obj); +} + +static jdouble MethodOtherP0(JNIEnv* env, jobject obj, + jint nativeCPPClass) { + DCHECK(nativeCPPClass) << "MethodOtherP0"; + cpp_namespace::CPPClass* native = + reinterpret_cast<cpp_namespace::CPPClass*>(nativeCPPClass); + return native->MethodOtherP0(env, obj); +} + +static jstring InnerMethod(JNIEnv* env, jobject obj, + jint nativeCPPClass) { + DCHECK(nativeCPPClass) << "InnerMethod"; + CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); + return native->InnerMethod(env, obj).Release(); +} + + +static jmethodID g_SampleForTests_javaMethod = 0; +static jint Java_SampleForTests_javaMethod(JNIEnv* env, jobject obj, jint foo, + jint bar) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_SampleForTests_clazz.is_null()); + DCHECK(g_SampleForTests_javaMethod); + jint ret = + env->CallIntMethod(obj, + g_SampleForTests_javaMethod, foo, bar); + base::android::CheckException(env); + return ret; +} + +static jmethodID g_SampleForTests_staticJavaMethod = 0; +static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_SampleForTests_clazz.is_null()); + DCHECK(g_SampleForTests_staticJavaMethod); + jboolean ret = + env->CallStaticBooleanMethod(g_SampleForTests_clazz.obj(), + g_SampleForTests_staticJavaMethod); + base::android::CheckException(env); + return ret; +} + +static jmethodID g_SampleForTests_packagePrivateJavaMethod = 0; +static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, jobject + obj) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_SampleForTests_clazz.is_null()); + DCHECK(g_SampleForTests_packagePrivateJavaMethod); + + env->CallVoidMethod(obj, + g_SampleForTests_packagePrivateJavaMethod); + base::android::CheckException(env); + +} + +static jmethodID g_SampleForTests_methodThatThrowsException = 0; +static void Java_SampleForTests_methodThatThrowsException(JNIEnv* env, jobject + obj) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_SampleForTests_clazz.is_null()); + DCHECK(g_SampleForTests_methodThatThrowsException); + + env->CallVoidMethod(obj, + g_SampleForTests_methodThatThrowsException); + + +} + +static jmethodID g_InnerClass_JavaInnerMethod = 0; +static jfloat Java_InnerClass_JavaInnerMethod(JNIEnv* env, jobject obj) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InnerClass_clazz.is_null()); + DCHECK(g_InnerClass_JavaInnerMethod); + jfloat ret = + env->CallFloatMethod(obj, + g_InnerClass_JavaInnerMethod); + base::android::CheckException(env); + return ret; +} + +static jmethodID g_InnerClass_javaInnerFunction = 0; +static void Java_InnerClass_javaInnerFunction(JNIEnv* env) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InnerClass_clazz.is_null()); + DCHECK(g_InnerClass_javaInnerFunction); + + env->CallStaticVoidMethod(g_InnerClass_clazz.obj(), + g_InnerClass_javaInnerFunction); + base::android::CheckException(env); + +} + +// Step 3: GetMethodIDs and RegisterNatives. + + +static void GetMethodIDsImpl(JNIEnv* env) { + g_InnerClass_clazz.Reset( + base::android::GetClass(env, kInnerClassClassPath)); + g_SampleForTests_clazz.Reset( + base::android::GetClass(env, kSampleForTestsClassPath)); + g_SampleForTests_javaMethod = base::android::GetMethodID( + env, g_SampleForTests_clazz, + "javaMethod", + +"(" +"I" +"I" +")" +"I"); + + g_SampleForTests_staticJavaMethod = base::android::GetStaticMethodID( + env, g_SampleForTests_clazz, + "staticJavaMethod", + +"(" +")" +"Z"); + + g_SampleForTests_packagePrivateJavaMethod = base::android::GetMethodID( + env, g_SampleForTests_clazz, + "packagePrivateJavaMethod", + +"(" +")" +"V"); + + g_SampleForTests_methodThatThrowsException = base::android::GetMethodID( + env, g_SampleForTests_clazz, + "methodThatThrowsException", + +"(" +")" +"V"); + + g_InnerClass_JavaInnerMethod = base::android::GetMethodID( + env, g_InnerClass_clazz, + "JavaInnerMethod", + +"(" +")" +"F"); + + g_InnerClass_javaInnerFunction = base::android::GetStaticMethodID( + env, g_InnerClass_clazz, + "javaInnerFunction", + +"(" +")" +"V"); + +} + +static bool RegisterNativesImpl(JNIEnv* env) { + GetMethodIDsImpl(env); + + static const JNINativeMethod kMethodsInnerClass[] = { + { "nativeInnerFunction", +"(" +")" +"I", reinterpret_cast<void*>(InnerFunction) }, + { "nativeInnerMethod", +"(" +"I" +")" +"Ljava/lang/String;", reinterpret_cast<void*>(InnerMethod) }, + }; + const int kMethodsInnerClassSize = arraysize(kMethodsInnerClass); + + if (env->RegisterNatives(g_InnerClass_clazz.obj(), + kMethodsInnerClass, + kMethodsInnerClassSize) < 0) { + LOG(ERROR) << "RegisterNatives failed in " << __FILE__; + return false; + } + + static const JNINativeMethod kMethodsSampleForTests[] = { + { "nativeInit", +"(" +")" +"I", reinterpret_cast<void*>(Init) }, + { "nativeDestroy", +"(" +"I" +")" +"V", reinterpret_cast<void*>(Destroy) }, + { "nativeGetDoubleFunction", +"(" +")" +"D", reinterpret_cast<void*>(GetDoubleFunction) }, + { "nativeGetFloatFunction", +"(" +")" +"F", reinterpret_cast<void*>(GetFloatFunction) }, + { "nativeSetNonPODDatatype", +"(" +"Landroid/graphics/Rect;" +")" +"V", reinterpret_cast<void*>(SetNonPODDatatype) }, + { "nativeGetNonPODDatatype", +"(" +")" +"Ljava/lang/Object;", reinterpret_cast<void*>(GetNonPODDatatype) }, + { "nativeMethod", +"(" +"I" +")" +"I", reinterpret_cast<void*>(Method) }, + { "nativeMethodOtherP0", +"(" +"I" +")" +"D", reinterpret_cast<void*>(MethodOtherP0) }, + }; + const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests); + + if (env->RegisterNatives(g_SampleForTests_clazz.obj(), + kMethodsSampleForTests, + kMethodsSampleForTestsSize) < 0) { + LOG(ERROR) << "RegisterNatives failed in " << __FILE__; + return false; + } + + return true; +} + +#endif // org_chromium_example_jni_generator_SampleForTests_JNI diff --git a/base/android/jni_generator/jni_generator.gyp b/base/android/jni_generator/jni_generator.gyp new file mode 100644 index 0000000..0bd3d46 --- /dev/null +++ b/base/android/jni_generator/jni_generator.gyp @@ -0,0 +1,67 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'jni_generator_py_tests', + 'type': 'none', + 'actions': [ + { + 'action_name': 'run_jni_generator_py_tests', + 'inputs': [ + 'jni_generator.py', + 'jni_generator_tests.py', + 'SampleForTests.java', + 'golden_sample_for_tests_jni.h', + ], + 'outputs': [ + '', + ], + 'action': [ + 'python', 'jni_generator_tests.py', + ], + }, + ], + }, + { + 'target_name': 'jni_sample_header', + 'type': 'none', + 'actions': [ + { + 'action_name': 'generate_jni_sample_header', + 'inputs': [ + 'jni_generator.py', + 'SampleForTests.java', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/base/jni/sample_for_tests_jni.h', + ], + 'action': [ + 'python', + 'jni_generator.py', + '-o', + '<@(_inputs)', + '<@(_outputs)', + ], + }, + ], + }, + { + 'target_name': 'jni_generator_tests', + 'type': 'executable', + 'dependencies': [ + '../../base.gyp:test_support_base', + 'jni_generator_py_tests', + 'jni_sample_header', + ], + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)/base', + ], + 'sources': [ + 'sample_for_tests.cc', + ], + }, + ], +} diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py new file mode 100644 index 0000000..0a42f4c --- /dev/null +++ b/base/android/jni_generator/jni_generator.py @@ -0,0 +1,972 @@ +#!/usr/bin/python +# +# 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. + +"""Extracts native methods from a Java file and generates the JNI bindings. +If you change this, please run and update the tests.""" + +import collections +import optparse +import os +import re +import string +from string import Template +import subprocess +import sys +import textwrap + + +UNKNOWN_JAVA_TYPE_PREFIX = 'UNKNOWN_JAVA_TYPE: ' + + +class ParseError(Exception): + """Exception thrown when we can't parse the input file.""" + + def __init__(self, description, *context_lines): + Exception.__init__(self) + self.description = description + self.context_lines = context_lines + + def __str__(self): + context = '\n'.join(self.context_lines) + return '***\nERROR: %s\n\n%s\n***' % (self.description, context) + + +class Param(object): + """Describes a param for a method, either java or native.""" + + def __init__(self, **kwargs): + self.datatype = kwargs['datatype'] + self.name = kwargs['name'] + self.cpp_class_name = kwargs.get('cpp_class_name', None) + + +class NativeMethod(object): + """Describes a C/C++ method that is called by Java code""" + + def __init__(self, **kwargs): + self.static = kwargs['static'] + self.java_class_name = kwargs['java_class_name'] + self.return_type = kwargs['return_type'] + self.name = kwargs['name'] + self.params = kwargs['params'] + if self.params: + assert type(self.params) is list + assert type(self.params[0]) is Param + if (self.params and + self.params[0].datatype == 'int' and + self.params[0].name.startswith('native')): + self.type = 'method' + if self.params[0].cpp_class_name: + self.p0_type = self.params[0].cpp_class_name + else: + self.p0_type = self.params[0].name[len('native'):] + elif self.static: + self.type = 'function' + else: + self.type = 'function' + self.method_id_var_name = kwargs.get('method_id_var_name', None) + + +class CalledByNative(object): + """Describes a java method exported to c/c++""" + + def __init__(self, **kwargs): + self.system_class = kwargs['system_class'] + self.unchecked = kwargs['unchecked'] + self.static = kwargs['static'] + self.java_class_name = kwargs['java_class_name'] + self.return_type = kwargs['return_type'] + self.env_call = kwargs['env_call'] + self.name = kwargs['name'] + self.params = kwargs['params'] + self.method_id_var_name = kwargs.get('method_id_var_name', None) + + +def JavaDataTypeToC(java_type): + """Returns a C datatype for the given java type.""" + java_pod_type_map = { + 'int': 'jint', + 'byte': 'jbyte', + 'boolean': 'jboolean', + 'long': 'jlong', + 'double': 'jdouble', + 'float': 'jfloat', + } + java_type_map = { + 'void': 'void', + 'String': 'jstring', + } + if java_type in java_pod_type_map: + return java_pod_type_map[java_type] + elif java_type in java_type_map: + return java_type_map[java_type] + elif java_type.endswith('[]'): + if java_type[:-2] in java_pod_type_map: + return java_pod_type_map[java_type[:-2]] + 'Array' + return 'jobjectArray' + else: + 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_map = { + 'String': 'Ljava/lang/String', + 'Boolean': 'Ljava/lang/Boolean', + 'Integer': 'Ljava/lang/Integer', + 'Long': 'Ljava/lang/Long', + 'Object': 'Ljava/lang/Object', + 'List': 'Ljava/util/List', + 'ArrayList': 'Ljava/util/ArrayList', + 'HashMap': 'Ljava/util/HashMap', + 'Bitmap': 'Landroid/graphics/Bitmap', + 'Context': 'Landroid/content/Context', + 'Canvas': 'Landroid/graphics/Canvas', + 'Surface': 'Landroid/view/Surface', + 'KeyEvent': 'Landroid/view/KeyEvent', + 'Rect': 'Landroid/graphics/Rect', + 'RectF': 'Landroid/graphics/RectF', + 'View': 'Landroid/view/View', + 'Matrix': 'Landroid/graphics/Matrix', + 'Point': 'Landroid/graphics/Point', + 'ByteBuffer': 'Ljava/nio/ByteBuffer', + 'InputStream': 'Ljava/io/InputStream', + } + app_param_map = { + 'ChromeView': + 'Lorg/chromium/chromeview/ChromeView', + + 'Tab': + 'Lcom/android/chrome/Tab', + + 'TouchPoint': + 'Lorg/chromium/chromeview/TouchPoint', + + 'SurfaceTexture': + 'Landroid/graphics/SurfaceTexture', + + 'ChromeViewClient': + 'Lorg/chromium/chromeview/ChromeViewClient', + + 'JSModalDialog': + 'Lcom/android/chrome/JSModalDialog', + + 'NativeInfoBar': + 'Lcom/android/chrome/infobar/InfoBarContainer$NativeInfoBar', + + 'OmniboxSuggestion': + 'Lcom/android/chrome/OmniboxSuggestion', + + 'PasswordListObserver': + 'Lorg/chromium/chromeview/ChromePreferences$PasswordListObserver', + + 'SandboxedProcessArgs': 'Lorg/chromium/chromeview/SandboxedProcessArgs', + + 'SandboxedProcessConnection': + 'Lorg/chromium/chromeview/SandboxedProcessConnection', + + 'SandboxedProcessService': + 'Lorg/chromium/chromeview/SandboxedProcessService', + + 'BookmarkNode': 'Lcom/android/chrome/ChromeBrowserProvider$BookmarkNode', + + 'SQLiteCursor': 'Lcom/android/chrome/database/SQLiteCursor', + + 'FindResultReceivedListener.FindNotificationDetails': + ('Lorg/chromium/chromeview/ChromeView$' + 'FindResultReceivedListener$FindNotificationDetails'), + + 'ChromeViewContextMenuInfo': + 'Lorg/chromium/chromeview/ChromeView$ChromeViewContextMenuInfo', + + 'AutofillData': 'Lorg/chromium/chromeview/AutofillData', + + 'JavaInputStream': 'Lorg/chromium/chromeview/JavaInputStream', + + 'ChromeVideoView': 'Lorg/chromium/chromeview/ChromeVideoView', + + 'ChromeHttpAuthHandler': 'Lorg/chromium/chromeview/ChromeHttpAuthHandler', + } + 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] + elif param in object_param_map: + return prefix + object_param_map[param] + ';' + elif param in app_param_map: + return prefix + app_param_map[param] + ';' + else: + return UNKNOWN_JAVA_TYPE_PREFIX + prefix + param + ';' + + +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) + '"' + + +def ParseParams(params): + """Parses the params into a list of Param objects.""" + if not params: + return [] + ret = [] + re_comment = re.compile(r'.*?\/\* (.*) \*\/') + for p in [p.strip() for p in params.split(',')]: + items = p.split(' ') + if 'final' in items: + items.remove('final') + comment = re.match(re_comment, p) + param = Param( + datatype=items[0], + name=(items[1] if len(items) > 1 else 'p%s' % len(ret)), + cpp_class_name=comment.group(1) if comment else None + ) + ret += [param] + return ret + + +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 + + +def ExtractFullyQualifiedJavaClassName(java_file_name, contents): + re_package = re.compile('.*?package (.*?);') + matches = re.findall(re_package, contents) + if not matches: + raise SyntaxError('Unable to find "package" line in %s' % java_file_name) + return (matches[0].replace('.', '/') + '/' + + os.path.splitext(os.path.basename(java_file_name))[0]) + + +def ExtractNatives(contents): + """Returns a list of dict containing information about a native method.""" + contents = contents.replace('\n', '') + natives = [] + re_native = re.compile(r'(@NativeCall(\(\"(.*?)\"\)))?\s*' + '(\w+\s\w+|\w+|\s+)\s*?native (\S*?) (\w+?)\((.*?)\);') + matches = re.findall(re_native, contents) + for match in matches: + native = NativeMethod( + static='static' in match[3], + java_class_name=match[2], + return_type=match[4], + name=match[5].replace('native', ''), + params=ParseParams(match[6])) + natives += [native] + return natives + + +def GetEnvCallForReturnType(return_type): + """Maps the types availabe via env->Call__Method.""" + env_call_map = {'boolean': ('Boolean', ''), + 'byte': ('Byte', ''), + 'char': ('Char', ''), + 'short': ('Short', ''), + 'int': ('Int', ''), + 'long': ('Long', ''), + 'float': ('Float', ''), + 'void': ('Void', ''), + 'double': ('Double', ''), + 'String': ('Object', 'jstring'), + 'Object': ('Object', ''), + } + return env_call_map.get(return_type, ('Object', '')) + + +def GetMangledMethodName(name, jni_signature): + """Returns a mangled method name for a (name, jni_signature) pair. + + The returned name can be used as a C identifier and will be unique for all + valid overloads of the same method. + + Args: + name: string. + jni_signature: string. + + Returns: + A mangled name. + """ + sig_translation = string.maketrans('[()/;$', 'apq_xs') + mangled_name = name + '_' + string.translate(jni_signature, sig_translation, + '"') + assert re.match(r'[0-9a-zA-Z_]+', mangled_name) + return mangled_name + + +def MangleCalledByNatives(called_by_natives): + """Mangles all the overloads from the call_by_natives list.""" + method_counts = collections.defaultdict( + lambda: collections.defaultdict(lambda: 0)) + for called_by_native in called_by_natives: + java_class_name = called_by_native.java_class_name + name = called_by_native.name + method_counts[java_class_name][name] += 1 + for called_by_native in called_by_natives: + java_class_name = called_by_native.java_class_name + method_name = called_by_native.name + method_id_var_name = method_name + if method_counts[java_class_name][method_name] > 1: + jni_signature = JniSignature(called_by_native.params, + called_by_native.return_type, + False) + method_id_var_name = GetMangledMethodName(method_name, jni_signature) + called_by_native.method_id_var_name = method_id_var_name + return called_by_natives + + +# Regex to match the JNI return types that should be included in a +# ScopedJavaLocalRef. +RE_SCOPED_JNI_RETURN_TYPES = re.compile('jobject|jclass|jstring|.*Array') + +# Regex to match a string like "@CalledByNative public void foo(int bar)". +RE_CALLED_BY_NATIVE = re.compile( + '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?' + '\s+(?P<prefix>[\w ]*?)' + '\s*(?P<return_type>\w+)' + '\s+(?P<name>\w+)' + '\s*\((?P<params>[^\)]*)\)') + + +def ExtractCalledByNatives(contents): + """Parses all methods annotated with @CalledByNative. + + Args: + contents: the contents of the java file. + + Returns: + A list of dict with information about the annotated methods. + TODO(bulach): return a CalledByNative object. + + Raises: + ParseError: if unable to parse. + """ + called_by_natives = [] + for match in re.finditer(RE_CALLED_BY_NATIVE, contents): + called_by_natives += [CalledByNative( + system_class=False, + unchecked='Unchecked' in match.group('Unchecked'), + static='static' in match.group('prefix'), + java_class_name=match.group('annotation') or '', + return_type=match.group('return_type'), + env_call=GetEnvCallForReturnType(match.group('return_type')), + name=match.group('name'), + params=ParseParams(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:]): + if '@CalledByNative' in line1: + raise ParseError('could not parse @CalledByNative method signature', + line1, line2) + return MangleCalledByNatives(called_by_natives) + + +class JNIFromJavaP(object): + """Uses 'javap' to parse a .class file and generate the JNI header file.""" + + def __init__(self, contents, namespace): + self.contents = contents + self.namespace = namespace + self.fully_qualified_class = re.match('.*?class (.*?) ', + contents[1]).group(1) + self.fully_qualified_class = self.fully_qualified_class.replace('.', '/') + 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('(.*?)(\w+?) (\w+?)\((.*?)\)') + self.called_by_natives = [] + for method in contents[2:]: + match = re.match(re_method, method) + if not match: + continue + self.called_by_natives += [CalledByNative( + system_class=True, + unchecked=False, + static='static' in match.group(1), + java_class_name='', + return_type=match.group(2), + name=match.group(3), + params=ParseParams(match.group(4)), + env_call=GetEnvCallForReturnType(match.group(2)))] + self.called_by_natives = MangleCalledByNatives(self.called_by_natives) + self.inl_header_file_generator = InlHeaderFileGenerator( + self.namespace, self.fully_qualified_class, [], self.called_by_natives) + + def GetContent(self): + return self.inl_header_file_generator.GetContent() + + @staticmethod + def CreateFromClass(class_file, namespace): + class_name = os.path.splitext(os.path.basename(class_file))[0] + p = subprocess.Popen(args=['javap', class_name], + cwd=os.path.dirname(class_file), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, _ = p.communicate() + jni_from_javap = JNIFromJavaP(stdout.split('\n'), namespace) + return jni_from_javap + + +class JNIFromJavaSource(object): + """Uses the given java source file to generate the JNI header file.""" + + def __init__(self, contents, fully_qualified_class): + contents = self._RemoveComments(contents) + natives = ExtractNatives(contents) + called_by_natives = ExtractCalledByNatives(contents) + inl_header_file_generator = InlHeaderFileGenerator( + '', fully_qualified_class, natives, called_by_natives) + self.content = inl_header_file_generator.GetContent() + + def _RemoveComments(self, contents): + ret = [] + for c in [c.strip() for c in contents.split('\n')]: + if not c.startswith('//'): + ret += [c] + return '\n'.join(ret) + + def GetContent(self): + return self.content + + @staticmethod + def CreateFromFile(java_file_name): + contents = file(java_file_name).read() + fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name, + contents) + return JNIFromJavaSource(contents, fully_qualified_class) + + +class InlHeaderFileGenerator(object): + """Generates an inline header file for JNI integration.""" + + def __init__(self, namespace, fully_qualified_class, natives, + called_by_natives): + self.namespace = namespace + self.fully_qualified_class = fully_qualified_class + self.class_name = self.fully_qualified_class.split('/')[-1] + 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) + + def GetContent(self): + """Returns the content of the JNI binding file.""" + template = Template("""\ +// 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 is autogenerated by +// ${SCRIPT_NAME} +// For +// ${FULLY_QUALIFIED_CLASS} + +#ifndef ${HEADER_GUARD} +#define ${HEADER_GUARD} + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" +#include "base/logging.h" + +using base::android::ScopedJavaLocalRef; + +// Step 1: forward declarations. +namespace { +$CLASS_PATH_DEFINITIONS +} // namespace +$FORWARD_DECLARATIONS + +// Step 2: method stubs. +$METHOD_STUBS + +// Step 3: GetMethodIDs and RegisterNatives. +$OPEN_NAMESPACE + +static void GetMethodIDsImpl(JNIEnv* env) { +$GET_METHOD_IDS_IMPL +} + +static bool RegisterNativesImpl(JNIEnv* env) { + ${NAMESPACE}GetMethodIDsImpl(env); +$REGISTER_NATIVES_IMPL + return true; +} +$CLOSE_NAMESPACE +#endif // ${HEADER_GUARD} +""") + script_components = os.path.abspath(sys.argv[0]).split(os.path.sep) + base_index = script_components.index('base') + script_name = os.sep.join(script_components[base_index:]) + values = { + 'SCRIPT_NAME': script_name, + 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class, + 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(), + 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(), + 'METHOD_STUBS': self.GetMethodStubsString(), + 'OPEN_NAMESPACE': self.GetOpenNamespaceString(), + 'NAMESPACE': self.GetNamespaceString(), + 'GET_METHOD_IDS_IMPL': self.GetMethodIDsImplString(), + 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString(), + 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(), + 'HEADER_GUARD': self.header_guard, + } + return WrapOutput(template.substitute(values)) + + def GetClassPathDefinitionsString(self): + ret = [] + ret += [self.GetClassPathDefinitions()] + return '\n'.join(ret) + + def GetForwardDeclarationsString(self): + ret = [] + for native in self.natives: + if native.type != 'method': + ret += [self.GetForwardDeclaration(native)] + return '\n'.join(ret) + + def GetMethodStubsString(self): + ret = [] + for native in self.natives: + if native.type == 'method': + ret += [self.GetNativeMethodStub(native)] + for called_by_native in self.called_by_natives: + ret += [self.GetCalledByNativeMethodStub(called_by_native)] + return '\n'.join(ret) + + def GetKMethodsString(self, clazz): + ret = [] + for native in self.natives: + if (native.java_class_name == clazz or + (not native.java_class_name and clazz == self.class_name)): + ret += [self.GetKMethodArrayEntry(native)] + return '\n'.join(ret) + + def GetMethodIDsImplString(self): + ret = [] + ret += [self.GetFindClasses()] + for called_by_native in self.called_by_natives: + ret += [self.GetMethodIDImpl(called_by_native)] + return '\n'.join(ret) + + def GetRegisterNativesImplString(self): + """Returns the implementation for RegisterNatives.""" + template = Template("""\ + static const JNINativeMethod kMethods${JAVA_CLASS}[] = { +${KMETHODS} + }; + const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS}); + + if (env->RegisterNatives(g_${JAVA_CLASS}_clazz.obj(), + kMethods${JAVA_CLASS}, + kMethods${JAVA_CLASS}Size) < 0) { + LOG(ERROR) << "RegisterNatives failed in " << __FILE__; + return false; + } +""") + ret = [] + all_classes = self.GetUniqueClasses(self.natives) + all_classes[self.class_name] = self.fully_qualified_class + for clazz in all_classes: + kmethods = self.GetKMethodsString(clazz) + if kmethods: + values = {'JAVA_CLASS': clazz, + 'KMETHODS': kmethods} + ret += [template.substitute(values)] + if not ret: return '' + return '\n' + '\n'.join(ret) + + def GetOpenNamespaceString(self): + if self.namespace: + return 'namespace %s {' % self.namespace + return '' + + def GetNamespaceString(self): + if self.namespace: + return '%s::' % self.namespace + return '' + + def GetCloseNamespaceString(self): + if self.namespace: + return '} // namespace %s\n' % self.namespace + return '' + + def GetJNIFirstParam(self, native): + ret = [] + if native.type == 'method': + ret = ['jobject obj'] + elif native.type == 'function': + if native.static: + ret = ['jclass clazz'] + else: + ret = ['jobject obj'] + return ret + + def GetParamsInDeclaration(self, native): + """Returns the params for the stub declaration. + + Args: + native: the native dictionary describing the method. + + Returns: + A string containing the params. + """ + return ',\n '.join(self.GetJNIFirstParam(native) + + [JavaDataTypeToC(param.datatype) + ' ' + + param.name + for param in native.params]) + + def GetCalledByNativeParamsInDeclaration(self, called_by_native): + return ',\n '.join([JavaDataTypeToC(param.datatype) + ' ' + + param.name + for param in called_by_native.params]) + + def GetForwardDeclaration(self, native): + template = Template(""" +static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS}); +""") + values = {'RETURN': JavaDataTypeToC(native.return_type), + 'NAME': native.name, + 'PARAMS': self.GetParamsInDeclaration(native)} + return template.substitute(values) + + def GetNativeMethodStub(self, native): + """Returns stubs for native methods.""" + template = Template("""\ +static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) { + DCHECK(${PARAM0_NAME}) << "${NAME}"; + ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); + return native->${NAME}(env, obj${PARAMS_IN_CALL})${POST_CALL}; +} +""") + params_for_call = ', '.join(p.name for p in native.params[1:]) + if params_for_call: + params_for_call = ', ' + params_for_call + + return_type = JavaDataTypeToC(native.return_type) + if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): + scoped_return_type = 'ScopedJavaLocalRef<' + return_type + '>' + post_call = '.Release()' + else: + scoped_return_type = return_type + post_call = '' + values = { + 'RETURN': return_type, + 'SCOPED_RETURN': scoped_return_type, + 'NAME': native.name, + 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native), + 'PARAM0_NAME': native.params[0].name, + 'P0_TYPE': native.p0_type, + 'PARAMS_IN_CALL': params_for_call, + 'POST_CALL': post_call + } + return template.substitute(values) + + def GetCalledByNativeMethodStub(self, called_by_native): + """Returns a string.""" + function_signature_template = Template("""\ +static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD}(\ +JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""") + function_header_template = Template("""\ +${FUNCTION_SIGNATURE} {""") + function_header_with_unused_template = Template("""\ +${FUNCTION_SIGNATURE} __attribute__ ((unused)); +${FUNCTION_SIGNATURE} {""") + template = Template(""" +static jmethodID g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0; +${FUNCTION_HEADER} + /* Must call RegisterNativesImpl() */ + DCHECK(!g_${JAVA_CLASS}_clazz.is_null()); + DCHECK(g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}); + ${RETURN_DECLARATION} + ${PRE_CALL}env->Call${STATIC}${ENV_CALL}Method(${FIRST_PARAM_IN_CALL}, + g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}${PARAMS_IN_CALL})${POST_CALL}; + ${CHECK_EXCEPTION} + ${RETURN_CLAUSE} +}""") + if called_by_native.static: + first_param_in_declaration = '' + first_param_in_call = ('g_%s_clazz.obj()' % + (called_by_native.java_class_name or + self.class_name)) + else: + first_param_in_declaration = ', jobject obj' + first_param_in_call = 'obj' + params_in_declaration = self.GetCalledByNativeParamsInDeclaration( + called_by_native) + if params_in_declaration: + params_in_declaration = ', ' + params_in_declaration + params_for_call = ', '.join(param.name + for param in called_by_native.params) + if params_for_call: + params_for_call = ', ' + params_for_call + pre_call = '' + post_call = '' + if called_by_native.env_call[1]: + pre_call = 'static_cast<%s>(' % called_by_native.env_call[1] + post_call = ')' + check_exception = '' + if not called_by_native.unchecked: + check_exception = 'base::android::CheckException(env);' + return_type = JavaDataTypeToC(called_by_native.return_type) + return_declaration = '' + return_clause = '' + if return_type != 'void': + pre_call = ' ' + pre_call + return_declaration = return_type + ' ret =' + if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): + return_type = 'ScopedJavaLocalRef<' + return_type + '>' + return_clause = 'return ' + return_type + '(env, ret);' + else: + return_clause = 'return ret;' + values = { + 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, + 'METHOD': called_by_native.name, + 'RETURN_TYPE': return_type, + 'RETURN_DECLARATION': return_declaration, + 'RETURN_CLAUSE': return_clause, + 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration, + 'PARAMS_IN_DECLARATION': params_in_declaration, + 'STATIC': 'Static' if called_by_native.static else '', + 'PRE_CALL': pre_call, + 'POST_CALL': post_call, + 'ENV_CALL': called_by_native.env_call[0], + 'FIRST_PARAM_IN_CALL': first_param_in_call, + 'PARAMS_IN_CALL': params_for_call, + 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, + 'CHECK_EXCEPTION': check_exception, + } + values['FUNCTION_SIGNATURE'] = ( + function_signature_template.substitute(values)) + if called_by_native.system_class: + values['FUNCTION_HEADER'] = ( + function_header_with_unused_template.substitute(values)) + else: + values['FUNCTION_HEADER'] = function_header_template.substitute(values) + return template.substitute(values) + + def GetKMethodArrayEntry(self, native): + template = Template("""\ + { "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""") + values = {'NAME': native.name, + 'JNI_SIGNATURE': JniSignature(native.params, native.return_type, + True)} + return template.substitute(values) + + def GetUniqueClasses(self, origin): + ret = {self.class_name: self.fully_qualified_class} + for entry in origin: + class_name = self.class_name + jni_class_path = self.fully_qualified_class + if entry.java_class_name: + class_name = entry.java_class_name + jni_class_path = self.fully_qualified_class + '$' + class_name + ret[class_name] = jni_class_path + return ret + + def GetClassPathDefinitions(self): + """Returns the ClassPath constants.""" + ret = [] + template = Template("""\ +const char* const k${JAVA_CLASS}ClassPath = "${JNI_CLASS_PATH}";""") + native_classes = self.GetUniqueClasses(self.natives) + called_by_native_classes = self.GetUniqueClasses(self.called_by_natives) + all_classes = native_classes + all_classes.update(called_by_native_classes) + for clazz in all_classes: + values = { + 'JAVA_CLASS': clazz, + 'JNI_CLASS_PATH': all_classes[clazz], + } + ret += [template.substitute(values)] + ret += '' + for clazz in called_by_native_classes: + template = Template("""\ +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_${JAVA_CLASS}_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>());""") + values = { + 'JAVA_CLASS': clazz, + } + ret += [template.substitute(values)] + return '\n'.join(ret) + + def GetFindClasses(self): + """Returns the imlementation of FindClass for all known classes.""" + template = Template("""\ + g_${JAVA_CLASS}_clazz.Reset( + base::android::GetClass(env, k${JAVA_CLASS}ClassPath));""") + ret = [] + for clazz in self.GetUniqueClasses(self.called_by_natives): + values = {'JAVA_CLASS': clazz} + ret += [template.substitute(values)] + return '\n'.join(ret) + + def GetMethodIDImpl(self, called_by_native): + """Returns the implementation of GetMethodID.""" + template = Template("""\ + g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = base::android::Get${STATIC}MethodID( + env, g_${JAVA_CLASS}_clazz, + "${NAME}", + ${JNI_SIGNATURE}); +""") + values = { + 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, + 'NAME': called_by_native.name, + 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, + 'STATIC': 'Static' if called_by_native.static else '', + 'JNI_SIGNATURE': JniSignature(called_by_native.params, + called_by_native.return_type, + True) + } + return template.substitute(values) + + +def WrapOutput(output): + ret = [] + for line in output.splitlines(): + if len(line) < 80: + ret.append(line.rstrip()) + else: + first_line_indent = ' ' * (len(line) - len(line.lstrip())) + subsequent_indent = first_line_indent + ' ' * 4 + if line.startswith('//'): + subsequent_indent = '//' + subsequent_indent + wrapper = textwrap.TextWrapper(width=80, + subsequent_indent=subsequent_indent, + break_long_words=False) + ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)] + ret += [''] + return '\n'.join(ret) + + +def GenerateJNIHeaders(input_files, output_files, use_javap, namespace): + for i in xrange(len(input_files)): + try: + if use_javap: + jni_from_javap = JNIFromJavaP.CreateFromClass(input_files[i], namespace) + output = jni_from_javap.GetContent() + else: + jni_from_java_source = JNIFromJavaSource.CreateFromFile(input_files[i]) + output = jni_from_java_source.GetContent() + except ParseError, e: + print e + sys.exit(1) + if output_files: + header_name = output_files[i] + if not os.path.exists(os.path.dirname(os.path.abspath(header_name))): + os.makedirs(os.path.dirname(os.path.abspath(header_name))) + if (not os.path.exists(header_name) or + file(header_name).read() != output): + print 'Generating ', header_name + output_file = file(header_name, 'w') + output_file.write(output) + output_file.close() + else: + print output + + +def CheckFilenames(input_files, output_files): + """Make sure the input and output have consistent names.""" + if len(input_files) != len(output_files): + sys.exit('Input files length %d must match output length %d' % + (len(input_files), len(output_files))) + for i in xrange(len(input_files)): + input_prefix = os.path.splitext(os.path.basename(input_files[i]))[0] + output_prefix = os.path.splitext(os.path.basename(output_files[i]))[0] + if input_prefix.lower() + 'jni' != output_prefix.replace('_', '').lower(): + sys.exit('\n'.join([ + '*** Error ***', + 'Input and output files have inconsistent names:', + '\t' + os.path.basename(input_files[i]), + '\t' + os.path.basename(output_files[i]), + '', + 'Input "FooBar.java" must be converted to output "foo_bar_jni.h"', + '', + ])) + + +def main(argv): + usage = """usage: %prog [OPTION] file1[ file2...] [output1[ output2...]] +This script will parse the given java source code extracting the native +declarations and print the header file to stdout (or a file). +See SampleForTests.java for more details. + """ + option_parser = optparse.OptionParser(usage=usage) + option_parser.add_option('-o', dest='output_files', + action='store_true', + default=False, + help='Saves the output to file(s) (the first half of' + ' args specify the java input files, the second' + ' half specify the header output files.') + option_parser.add_option('-p', dest='javap_class', + action='store_true', + default=False, + help='Uses javap to extract the methods from a' + ' pre-compiled class. Input files should point' + ' to pre-compiled Java .class files.') + option_parser.add_option('-n', dest='namespace', + help='Uses as a namespace in the generated header,' + ' instead of the javap class name.') + options, args = option_parser.parse_args(argv) + input_files = args[1:] + output_files = [] + if options.output_files: + output_files = input_files[len(input_files) / 2:] + input_files = input_files[:len(input_files) / 2] + CheckFilenames(input_files, output_files) + GenerateJNIHeaders(input_files, output_files, options.javap_class, + options.namespace) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py new file mode 100644 index 0000000..0c5c0ce --- /dev/null +++ b/base/android/jni_generator/jni_generator_tests.py @@ -0,0 +1,1527 @@ +#!/usr/bin/python +# +# 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. + +"""Tests for jni_generator.py. + +This test suite contains various tests for the JNI generator. +It exercises the low-level parser all the way up to the +code generator and ensures the output matches a golden +file. +""" + +import difflib +import os +import sys +import unittest +import jni_generator +from jni_generator import CalledByNative, NativeMethod, Param + + +class TestGenerator(unittest.TestCase): + def assertObjEquals(self, first, second): + dict_first = first.__dict__ + dict_second = second.__dict__ + self.assertEquals(dict_first.keys(), dict_second.keys()) + for key, value in dict_first.iteritems(): + if (type(value) is list and len(value) and + isinstance(type(value[0]), object)): + self.assertListEquals(value, second.__getattribute__(key)) + else: + self.assertEquals(value, second.__getattribute__(key)) + + def assertListEquals(self, first, second): + self.assertEquals(len(first), len(second)) + for i in xrange(len(first)): + if isinstance(first[i], object): + self.assertObjEquals(first[i], second[i]) + else: + self.assertEquals(first[i], second[i]) + + def assertTextEquals(self, golden_text, generated_text): + stripped_golden = [l.strip() for l in golden_text.split('\n')] + stripped_generated = [l.strip() for l in generated_text.split('\n')] + if stripped_golden != stripped_generated: + print self.id() + for line in difflib.context_diff(stripped_golden, stripped_generated): + print line + self.fail('Golden text mismatch') + + def testNatives(self): + test_data = """" + private native int nativeInit(); + private native void nativeDestroy(int nativeChromeBrowserProvider); + private native long nativeAddBookmark( + int nativeChromeBrowserProvider, + String url, String title, boolean isFolder, long parentId); + private static native String nativeGetDomainAndRegistry(String url); + private static native void nativeCreateHistoricalTabFromState( + byte[] state, int tab_index); + private native byte[] nativeGetStateAsByteArray(ChromeView view); + private static native String[] nativeGetAutofillProfileGUIDs(); + private native void nativeSetRecognitionResults( + int sessionId, String[] results); + private native long nativeAddBookmarkFromAPI( + int nativeChromeBrowserProvider, + String url, Long created, Boolean isBookmark, + Long date, byte[] favicon, String title, Integer visits); + native int nativeFindAll(String find); + private static native BookmarkNode nativeGetDefaultBookmarkFolder(); + private native SQLiteCursor nativeQueryBookmarkFromAPI( + int nativeChromeBrowserProvider, + String[] projection, String selection, + String[] selectionArgs, String sortOrder); + private native void nativeGotOrientation( + int nativePtr /* device_orientation::DeviceOrientationAndroid */, + double alpha, double beta, double gamma); + """ + natives = jni_generator.ExtractNatives(test_data) + golden_natives = [ + NativeMethod(return_type='int', static=False, + name='Init', + params=[], + java_class_name='', + type='function'), + NativeMethod(return_type='void', static=False, name='Destroy', + params=[Param(datatype='int', + name='nativeChromeBrowserProvider')], + java_class_name='', + type='method', + p0_type='ChromeBrowserProvider'), + NativeMethod(return_type='long', static=False, name='AddBookmark', + params=[Param(datatype='int', + name='nativeChromeBrowserProvider'), + Param(datatype='String', + name='url'), + Param(datatype='String', + name='title'), + Param(datatype='boolean', + name='isFolder'), + Param(datatype='long', + name='parentId')], + java_class_name='', + type='method', + p0_type='ChromeBrowserProvider'), + NativeMethod(return_type='String', static=True, + name='GetDomainAndRegistry', + params=[Param(datatype='String', + name='url')], + java_class_name='', + type='function'), + NativeMethod(return_type='void', static=True, + name='CreateHistoricalTabFromState', + params=[Param(datatype='byte[]', + name='state'), + Param(datatype='int', + name='tab_index')], + java_class_name='', + type='function'), + NativeMethod(return_type='byte[]', static=False, + name='GetStateAsByteArray', + params=[Param(datatype='ChromeView', name='view')], + java_class_name='', + type='function'), + NativeMethod(return_type='String[]', static=True, + name='GetAutofillProfileGUIDs', params=[], + java_class_name='', + type='function'), + NativeMethod(return_type='void', static=False, + name='SetRecognitionResults', + params=[Param(datatype='int', name='sessionId'), + Param(datatype='String[]', name='results')], + java_class_name='', + type='function'), + NativeMethod(return_type='long', static=False, + name='AddBookmarkFromAPI', + params=[Param(datatype='int', + name='nativeChromeBrowserProvider'), + Param(datatype='String', + name='url'), + Param(datatype='Long', + name='created'), + Param(datatype='Boolean', + name='isBookmark'), + Param(datatype='Long', + name='date'), + Param(datatype='byte[]', + name='favicon'), + Param(datatype='String', + name='title'), + Param(datatype='Integer', + name='visits')], + java_class_name='', + type='method', + p0_type='ChromeBrowserProvider'), + NativeMethod(return_type='int', static=False, + name='FindAll', + params=[Param(datatype='String', + name='find')], + java_class_name='', + type='function'), + NativeMethod(return_type='BookmarkNode', static=True, + name='GetDefaultBookmarkFolder', + params=[], + java_class_name='', + type='function'), + NativeMethod(return_type='SQLiteCursor', + static=False, + name='QueryBookmarkFromAPI', + params=[Param(datatype='int', + name='nativeChromeBrowserProvider'), + Param(datatype='String[]', + name='projection'), + Param(datatype='String', + name='selection'), + Param(datatype='String[]', + name='selectionArgs'), + Param(datatype='String', + name='sortOrder'), + ], + java_class_name='', + type='method', + p0_type='ChromeBrowserProvider'), + NativeMethod(return_type='void', static=False, + name='GotOrientation', + params=[Param(datatype='int', + cpp_class_name= + 'device_orientation::DeviceOrientationAndroid', + name='nativePtr'), + Param(datatype='double', + name='alpha'), + Param(datatype='double', + name='beta'), + Param(datatype='double', + name='gamma'), + ], + java_class_name='', + type='method', + p0_type='device_orientation::DeviceOrientationAndroid'), + ] + self.assertListEquals(golden_natives, natives) + h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', + natives, []) + golden_content = """\ +// 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 is autogenerated by +// base/android/jni_generator/jni_generator_tests.py +// For +// org/chromium/TestJni + +#ifndef org_chromium_TestJni_JNI +#define org_chromium_TestJni_JNI + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" +#include "base/logging.h" + +using base::android::ScopedJavaLocalRef; + +// Step 1: forward declarations. +namespace { +const char* const kTestJniClassPath = "org/chromium/TestJni"; +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_TestJni_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>()); +} // namespace + +static jint Init(JNIEnv* env, jobject obj); + + +static jstring GetDomainAndRegistry(JNIEnv* env, jclass clazz, + jstring url); + + +static void CreateHistoricalTabFromState(JNIEnv* env, jclass clazz, + jbyteArray state, + jint tab_index); + + +static jbyteArray GetStateAsByteArray(JNIEnv* env, jobject obj, + jobject view); + + +static jobjectArray GetAutofillProfileGUIDs(JNIEnv* env, jclass clazz); + + +static void SetRecognitionResults(JNIEnv* env, jobject obj, + jint sessionId, + jobjectArray results); + + +static jint FindAll(JNIEnv* env, jobject obj, + jstring find); + + +static jobject GetDefaultBookmarkFolder(JNIEnv* env, jclass clazz); + + +// Step 2: method stubs. +static void Destroy(JNIEnv* env, jobject obj, + jint nativeChromeBrowserProvider) { + DCHECK(nativeChromeBrowserProvider) << "Destroy"; + ChromeBrowserProvider* native = + reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); + return native->Destroy(env, obj); +} + +static jlong AddBookmark(JNIEnv* env, jobject obj, + jint nativeChromeBrowserProvider, + jstring url, + jstring title, + jboolean isFolder, + jlong parentId) { + DCHECK(nativeChromeBrowserProvider) << "AddBookmark"; + ChromeBrowserProvider* native = + reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); + return native->AddBookmark(env, obj, url, title, isFolder, parentId); +} + +static jlong AddBookmarkFromAPI(JNIEnv* env, jobject obj, + jint nativeChromeBrowserProvider, + jstring url, + jobject created, + jobject isBookmark, + jobject date, + jbyteArray favicon, + jstring title, + jobject visits) { + DCHECK(nativeChromeBrowserProvider) << "AddBookmarkFromAPI"; + ChromeBrowserProvider* native = + reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); + return native->AddBookmarkFromAPI(env, obj, url, created, isBookmark, date, + favicon, title, visits); +} + +static jobject QueryBookmarkFromAPI(JNIEnv* env, jobject obj, + jint nativeChromeBrowserProvider, + jobjectArray projection, + jstring selection, + jobjectArray selectionArgs, + jstring sortOrder) { + DCHECK(nativeChromeBrowserProvider) << "QueryBookmarkFromAPI"; + ChromeBrowserProvider* native = + reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); + return native->QueryBookmarkFromAPI(env, obj, projection, selection, + selectionArgs, sortOrder).Release(); +} + +static void GotOrientation(JNIEnv* env, jobject obj, + jint nativePtr, + jdouble alpha, + jdouble beta, + jdouble gamma) { + DCHECK(nativePtr) << "GotOrientation"; + device_orientation::DeviceOrientationAndroid* native = + reinterpret_cast<device_orientation::DeviceOrientationAndroid*>(nativePtr); + return native->GotOrientation(env, obj, alpha, beta, gamma); +} + + +// Step 3: GetMethodIDs and RegisterNatives. + + +static void GetMethodIDsImpl(JNIEnv* env) { + g_TestJni_clazz.Reset( + base::android::GetClass(env, kTestJniClassPath)); +} + +static bool RegisterNativesImpl(JNIEnv* env) { + GetMethodIDsImpl(env); + + static const JNINativeMethod kMethodsTestJni[] = { + { "nativeInit", +"(" +")" +"I", reinterpret_cast<void*>(Init) }, + { "nativeDestroy", +"(" +"I" +")" +"V", reinterpret_cast<void*>(Destroy) }, + { "nativeAddBookmark", +"(" +"I" +"Ljava/lang/String;" +"Ljava/lang/String;" +"Z" +"J" +")" +"J", reinterpret_cast<void*>(AddBookmark) }, + { "nativeGetDomainAndRegistry", +"(" +"Ljava/lang/String;" +")" +"Ljava/lang/String;", reinterpret_cast<void*>(GetDomainAndRegistry) }, + { "nativeCreateHistoricalTabFromState", +"(" +"[B" +"I" +")" +"V", reinterpret_cast<void*>(CreateHistoricalTabFromState) }, + { "nativeGetStateAsByteArray", +"(" +"Lorg/chromium/chromeview/ChromeView;" +")" +"[B", reinterpret_cast<void*>(GetStateAsByteArray) }, + { "nativeGetAutofillProfileGUIDs", +"(" +")" +"[Ljava/lang/String;", reinterpret_cast<void*>(GetAutofillProfileGUIDs) }, + { "nativeSetRecognitionResults", +"(" +"I" +"[Ljava/lang/String;" +")" +"V", reinterpret_cast<void*>(SetRecognitionResults) }, + { "nativeAddBookmarkFromAPI", +"(" +"I" +"Ljava/lang/String;" +"Ljava/lang/Long;" +"Ljava/lang/Boolean;" +"Ljava/lang/Long;" +"[B" +"Ljava/lang/String;" +"Ljava/lang/Integer;" +")" +"J", reinterpret_cast<void*>(AddBookmarkFromAPI) }, + { "nativeFindAll", +"(" +"Ljava/lang/String;" +")" +"I", reinterpret_cast<void*>(FindAll) }, + { "nativeGetDefaultBookmarkFolder", +"(" +")" +"Lcom/android/chrome/ChromeBrowserProvider$BookmarkNode;", + reinterpret_cast<void*>(GetDefaultBookmarkFolder) }, + { "nativeQueryBookmarkFromAPI", +"(" +"I" +"[Ljava/lang/String;" +"Ljava/lang/String;" +"[Ljava/lang/String;" +"Ljava/lang/String;" +")" +"Lcom/android/chrome/database/SQLiteCursor;", + reinterpret_cast<void*>(QueryBookmarkFromAPI) }, + { "nativeGotOrientation", +"(" +"I" +"D" +"D" +"D" +")" +"V", reinterpret_cast<void*>(GotOrientation) }, + }; + const int kMethodsTestJniSize = arraysize(kMethodsTestJni); + + if (env->RegisterNatives(g_TestJni_clazz.obj(), + kMethodsTestJni, + kMethodsTestJniSize) < 0) { + LOG(ERROR) << "RegisterNatives failed in " << __FILE__; + return false; + } + + return true; +} + +#endif // org_chromium_TestJni_JNI +""" + self.assertTextEquals(golden_content, h.GetContent()) + + def testInnerClassNatives(self): + test_data = """ + class MyInnerClass { + @NativeCall("MyInnerClass") + private native int nativeInit(); + } + """ + natives = jni_generator.ExtractNatives(test_data) + golden_natives = [ + NativeMethod(return_type='int', static=False, + name='Init', params=[], + java_class_name='MyInnerClass', + type='function') + ] + self.assertListEquals(golden_natives, natives) + h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', + natives, []) + golden_content = """\ +// 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 is autogenerated by +// base/android/jni_generator/jni_generator_tests.py +// For +// org/chromium/TestJni + +#ifndef org_chromium_TestJni_JNI +#define org_chromium_TestJni_JNI + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" +#include "base/logging.h" + +using base::android::ScopedJavaLocalRef; + +// Step 1: forward declarations. +namespace { +const char* const kTestJniClassPath = "org/chromium/TestJni"; +const char* const kMyInnerClassClassPath = "org/chromium/TestJni$MyInnerClass"; +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_TestJni_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>()); +} // namespace + +static jint Init(JNIEnv* env, jobject obj); + + +// Step 2: method stubs. + + +// Step 3: GetMethodIDs and RegisterNatives. + + +static void GetMethodIDsImpl(JNIEnv* env) { + g_TestJni_clazz.Reset( + base::android::GetClass(env, kTestJniClassPath)); +} + +static bool RegisterNativesImpl(JNIEnv* env) { + GetMethodIDsImpl(env); + + static const JNINativeMethod kMethodsMyInnerClass[] = { + { "nativeInit", +"(" +")" +"I", reinterpret_cast<void*>(Init) }, + }; + const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass); + + if (env->RegisterNatives(g_MyInnerClass_clazz.obj(), + kMethodsMyInnerClass, + kMethodsMyInnerClassSize) < 0) { + LOG(ERROR) << "RegisterNatives failed in " << __FILE__; + return false; + } + + return true; +} + +#endif // org_chromium_TestJni_JNI +""" + self.assertTextEquals(golden_content, h.GetContent()) + + def testInnerClassNativesMultiple(self): + test_data = """ + class MyInnerClass { + @NativeCall("MyInnerClass") + private native int nativeInit(); + } + class MyOtherInnerClass { + @NativeCall("MyOtherInnerClass") + private native int nativeInit(); + } + """ + natives = jni_generator.ExtractNatives(test_data) + golden_natives = [ + NativeMethod(return_type='int', static=False, + name='Init', params=[], + java_class_name='MyInnerClass', + type='function'), + NativeMethod(return_type='int', static=False, + name='Init', params=[], + java_class_name='MyOtherInnerClass', + type='function') + ] + self.assertListEquals(golden_natives, natives) + h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', + natives, []) + golden_content = """\ +// 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 is autogenerated by +// base/android/jni_generator/jni_generator_tests.py +// For +// org/chromium/TestJni + +#ifndef org_chromium_TestJni_JNI +#define org_chromium_TestJni_JNI + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" +#include "base/logging.h" + +using base::android::ScopedJavaLocalRef; + +// Step 1: forward declarations. +namespace { +const char* const kMyOtherInnerClassClassPath = + "org/chromium/TestJni$MyOtherInnerClass"; +const char* const kTestJniClassPath = "org/chromium/TestJni"; +const char* const kMyInnerClassClassPath = "org/chromium/TestJni$MyInnerClass"; +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_TestJni_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>()); +} // namespace + +static jint Init(JNIEnv* env, jobject obj); + + +static jint Init(JNIEnv* env, jobject obj); + + +// Step 2: method stubs. + + +// Step 3: GetMethodIDs and RegisterNatives. + + +static void GetMethodIDsImpl(JNIEnv* env) { + g_TestJni_clazz.Reset( + base::android::GetClass(env, kTestJniClassPath)); +} + +static bool RegisterNativesImpl(JNIEnv* env) { + GetMethodIDsImpl(env); + + static const JNINativeMethod kMethodsMyOtherInnerClass[] = { + { "nativeInit", +"(" +")" +"I", reinterpret_cast<void*>(Init) }, + }; + const int kMethodsMyOtherInnerClassSize = + arraysize(kMethodsMyOtherInnerClass); + + if (env->RegisterNatives(g_MyOtherInnerClass_clazz.obj(), + kMethodsMyOtherInnerClass, + kMethodsMyOtherInnerClassSize) < 0) { + LOG(ERROR) << "RegisterNatives failed in " << __FILE__; + return false; + } + + static const JNINativeMethod kMethodsMyInnerClass[] = { + { "nativeInit", +"(" +")" +"I", reinterpret_cast<void*>(Init) }, + }; + const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass); + + if (env->RegisterNatives(g_MyInnerClass_clazz.obj(), + kMethodsMyInnerClass, + kMethodsMyInnerClassSize) < 0) { + LOG(ERROR) << "RegisterNatives failed in " << __FILE__; + return false; + } + + return true; +} + +#endif // org_chromium_TestJni_JNI +""" + self.assertTextEquals(golden_content, h.GetContent()) + + def testInnerClassNativesBothInnerAndOuter(self): + test_data = """ + class MyOuterClass { + private native int nativeInit(); + class MyOtherInnerClass { + @NativeCall("MyOtherInnerClass") + private native int nativeInit(); + } + } + """ + natives = jni_generator.ExtractNatives(test_data) + golden_natives = [ + NativeMethod(return_type='int', static=False, + name='Init', params=[], + java_class_name='', + type='function'), + NativeMethod(return_type='int', static=False, + name='Init', params=[], + java_class_name='MyOtherInnerClass', + type='function') + ] + self.assertListEquals(golden_natives, natives) + h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', + natives, []) + golden_content = """\ +// 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 is autogenerated by +// base/android/jni_generator/jni_generator_tests.py +// For +// org/chromium/TestJni + +#ifndef org_chromium_TestJni_JNI +#define org_chromium_TestJni_JNI + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" +#include "base/logging.h" + +using base::android::ScopedJavaLocalRef; + +// Step 1: forward declarations. +namespace { +const char* const kMyOtherInnerClassClassPath = + "org/chromium/TestJni$MyOtherInnerClass"; +const char* const kTestJniClassPath = "org/chromium/TestJni"; +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_TestJni_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>()); +} // namespace + +static jint Init(JNIEnv* env, jobject obj); + + +static jint Init(JNIEnv* env, jobject obj); + + +// Step 2: method stubs. + + +// Step 3: GetMethodIDs and RegisterNatives. + + +static void GetMethodIDsImpl(JNIEnv* env) { + g_TestJni_clazz.Reset( + base::android::GetClass(env, kTestJniClassPath)); +} + +static bool RegisterNativesImpl(JNIEnv* env) { + GetMethodIDsImpl(env); + + static const JNINativeMethod kMethodsMyOtherInnerClass[] = { + { "nativeInit", +"(" +")" +"I", reinterpret_cast<void*>(Init) }, + }; + const int kMethodsMyOtherInnerClassSize = + arraysize(kMethodsMyOtherInnerClass); + + if (env->RegisterNatives(g_MyOtherInnerClass_clazz.obj(), + kMethodsMyOtherInnerClass, + kMethodsMyOtherInnerClassSize) < 0) { + LOG(ERROR) << "RegisterNatives failed in " << __FILE__; + return false; + } + + static const JNINativeMethod kMethodsTestJni[] = { + { "nativeInit", +"(" +")" +"I", reinterpret_cast<void*>(Init) }, + }; + const int kMethodsTestJniSize = arraysize(kMethodsTestJni); + + if (env->RegisterNatives(g_TestJni_clazz.obj(), + kMethodsTestJni, + kMethodsTestJniSize) < 0) { + LOG(ERROR) << "RegisterNatives failed in " << __FILE__; + return false; + } + + return true; +} + +#endif // org_chromium_TestJni_JNI +""" + self.assertTextEquals(golden_content, h.GetContent()) + + def testCalledByNatives(self): + test_data = """" + @CalledByNative + NativeInfoBar showConfirmInfoBar(int nativeInfoBar, String buttonOk, + String buttonCancel, String title, + Bitmap icon) { + InfoBar infobar = new ConfirmInfoBar(nativeInfoBar, mContext, + buttonOk, buttonCancel, + title, icon); + return infobar; + } + @CalledByNative + NativeInfoBar showAutoLoginInfoBar(int nativeInfoBar, + String realm, String account, + String args) { + AutoLoginInfoBar infobar = new AutoLoginInfoBar(nativeInfoBar, mContext, + realm, account, args); + if (infobar.displayedAccountCount() == 0) + infobar = null; + return infobar; + } + @CalledByNative("InfoBar") + void dismiss(); + @SuppressWarnings("unused") + @CalledByNative + private static boolean shouldShowAutoLogin(ChromeView chromeView, + String realm, String account, String args) { + AccountManagerContainer accountManagerContainer = + new AccountManagerContainer((Activity)chromeView.getContext(), + realm, account, args); + String[] logins = accountManagerContainer.getAccountLogins(null); + return logins.length != 0; + } + @CalledByNative + static InputStream openUrl(String url) { + return null; + } + @CalledByNative + private void activateHardwareAcceleration(final boolean activated, + final int iPid, final int iType, + final int iPrimaryID, final int iSecondaryID) { + if (!activated) { + return + } + } + @CalledByNativeUnchecked + private void uncheckedCall(int iParam); + """ + called_by_natives = jni_generator.ExtractCalledByNatives(test_data) + golden_called_by_natives = [ + CalledByNative( + return_type='NativeInfoBar', + system_class=False, + static=False, + name='showConfirmInfoBar', + method_id_var_name='showConfirmInfoBar', + java_class_name='', + params=[Param(datatype='int', name='nativeInfoBar'), + Param(datatype='String', name='buttonOk'), + Param(datatype='String', name='buttonCancel'), + Param(datatype='String', name='title'), + Param(datatype='Bitmap', name='icon')], + env_call=('Object', ''), + unchecked=False, + ), + CalledByNative( + return_type='NativeInfoBar', + system_class=False, + static=False, + name='showAutoLoginInfoBar', + method_id_var_name='showAutoLoginInfoBar', + java_class_name='', + params=[Param(datatype='int', name='nativeInfoBar'), + Param(datatype='String', name='realm'), + Param(datatype='String', name='account'), + Param(datatype='String', name='args')], + env_call=('Object', ''), + unchecked=False, + ), + CalledByNative( + return_type='void', + system_class=False, + static=False, + name='dismiss', + method_id_var_name='dismiss', + java_class_name='InfoBar', + params=[], + env_call=('Void', ''), + unchecked=False, + ), + CalledByNative( + return_type='boolean', + system_class=False, + static=True, + name='shouldShowAutoLogin', + method_id_var_name='shouldShowAutoLogin', + java_class_name='', + params=[Param(datatype='ChromeView', name='chromeView'), + Param(datatype='String', name='realm'), + Param(datatype='String', name='account'), + Param(datatype='String', name='args')], + env_call=('Boolean', ''), + unchecked=False, + ), + CalledByNative( + return_type='InputStream', + system_class=False, + static=True, + name='openUrl', + method_id_var_name='openUrl', + java_class_name='', + params=[Param(datatype='String', name='url')], + env_call=('Object', ''), + unchecked=False, + ), + CalledByNative( + return_type='void', + system_class=False, + static=False, + name='activateHardwareAcceleration', + method_id_var_name='activateHardwareAcceleration', + java_class_name='', + params=[Param(datatype='boolean', name='activated'), + Param(datatype='int', name='iPid'), + Param(datatype='int', name='iType'), + Param(datatype='int', name='iPrimaryID'), + Param(datatype='int', name='iSecondaryID'), + ], + env_call=('Void', ''), + unchecked=False, + ), + CalledByNative( + return_type='void', + system_class=False, + static=False, + name='uncheckedCall', + method_id_var_name='uncheckedCall', + java_class_name='', + params=[Param(datatype='int', name='iParam')], + env_call=('Void', ''), + unchecked=True, + ), + ] + self.assertListEquals(golden_called_by_natives, called_by_natives) + h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', + [], called_by_natives) + golden_content = """\ +// 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 is autogenerated by +// base/android/jni_generator/jni_generator_tests.py +// For +// org/chromium/TestJni + +#ifndef org_chromium_TestJni_JNI +#define org_chromium_TestJni_JNI + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" +#include "base/logging.h" + +using base::android::ScopedJavaLocalRef; + +// Step 1: forward declarations. +namespace { +const char* const kTestJniClassPath = "org/chromium/TestJni"; +const char* const kInfoBarClassPath = "org/chromium/TestJni$InfoBar"; +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_TestJni_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>()); +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_InfoBar_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>()); +} // namespace + + +// Step 2: method stubs. + +static jmethodID g_TestJni_showConfirmInfoBar = 0; +static ScopedJavaLocalRef<jobject> Java_TestJni_showConfirmInfoBar(JNIEnv* env, + jobject obj, jint nativeInfoBar, + jstring buttonOk, + jstring buttonCancel, + jstring title, + jobject icon) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_TestJni_clazz.is_null()); + DCHECK(g_TestJni_showConfirmInfoBar); + jobject ret = + env->CallObjectMethod(obj, + g_TestJni_showConfirmInfoBar, nativeInfoBar, buttonOk, buttonCancel, + title, icon); + base::android::CheckException(env); + return ScopedJavaLocalRef<jobject>(env, ret); +} + +static jmethodID g_TestJni_showAutoLoginInfoBar = 0; +static ScopedJavaLocalRef<jobject> Java_TestJni_showAutoLoginInfoBar(JNIEnv* + env, jobject obj, jint nativeInfoBar, + jstring realm, + jstring account, + jstring args) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_TestJni_clazz.is_null()); + DCHECK(g_TestJni_showAutoLoginInfoBar); + jobject ret = + env->CallObjectMethod(obj, + g_TestJni_showAutoLoginInfoBar, nativeInfoBar, realm, account, args); + base::android::CheckException(env); + return ScopedJavaLocalRef<jobject>(env, ret); +} + +static jmethodID g_InfoBar_dismiss = 0; +static void Java_InfoBar_dismiss(JNIEnv* env, jobject obj) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InfoBar_clazz.is_null()); + DCHECK(g_InfoBar_dismiss); + + env->CallVoidMethod(obj, + g_InfoBar_dismiss); + base::android::CheckException(env); + +} + +static jmethodID g_TestJni_shouldShowAutoLogin = 0; +static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, jobject + chromeView, + jstring realm, + jstring account, + jstring args) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_TestJni_clazz.is_null()); + DCHECK(g_TestJni_shouldShowAutoLogin); + jboolean ret = + env->CallStaticBooleanMethod(g_TestJni_clazz.obj(), + g_TestJni_shouldShowAutoLogin, chromeView, realm, account, args); + base::android::CheckException(env); + return ret; +} + +static jmethodID g_TestJni_openUrl = 0; +static ScopedJavaLocalRef<jobject> Java_TestJni_openUrl(JNIEnv* env, jstring + url) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_TestJni_clazz.is_null()); + DCHECK(g_TestJni_openUrl); + jobject ret = + env->CallStaticObjectMethod(g_TestJni_clazz.obj(), + g_TestJni_openUrl, url); + base::android::CheckException(env); + return ScopedJavaLocalRef<jobject>(env, ret); +} + +static jmethodID g_TestJni_activateHardwareAcceleration = 0; +static void Java_TestJni_activateHardwareAcceleration(JNIEnv* env, jobject obj, + jboolean activated, + jint iPid, + jint iType, + jint iPrimaryID, + jint iSecondaryID) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_TestJni_clazz.is_null()); + DCHECK(g_TestJni_activateHardwareAcceleration); + + env->CallVoidMethod(obj, + g_TestJni_activateHardwareAcceleration, activated, iPid, iType, + iPrimaryID, iSecondaryID); + base::android::CheckException(env); + +} + +static jmethodID g_TestJni_uncheckedCall = 0; +static void Java_TestJni_uncheckedCall(JNIEnv* env, jobject obj, jint iParam) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_TestJni_clazz.is_null()); + DCHECK(g_TestJni_uncheckedCall); + + env->CallVoidMethod(obj, + g_TestJni_uncheckedCall, iParam); + + +} + +// Step 3: GetMethodIDs and RegisterNatives. + + +static void GetMethodIDsImpl(JNIEnv* env) { + g_TestJni_clazz.Reset( + base::android::GetClass(env, kTestJniClassPath)); + g_InfoBar_clazz.Reset( + base::android::GetClass(env, kInfoBarClassPath)); + g_TestJni_showConfirmInfoBar = base::android::GetMethodID( + env, g_TestJni_clazz, + "showConfirmInfoBar", + +"(" +"I" +"Ljava/lang/String;" +"Ljava/lang/String;" +"Ljava/lang/String;" +"Landroid/graphics/Bitmap;" +")" +"Lcom/android/chrome/infobar/InfoBarContainer$NativeInfoBar;"); + + g_TestJni_showAutoLoginInfoBar = base::android::GetMethodID( + env, g_TestJni_clazz, + "showAutoLoginInfoBar", + +"(" +"I" +"Ljava/lang/String;" +"Ljava/lang/String;" +"Ljava/lang/String;" +")" +"Lcom/android/chrome/infobar/InfoBarContainer$NativeInfoBar;"); + + g_InfoBar_dismiss = base::android::GetMethodID( + env, g_InfoBar_clazz, + "dismiss", + +"(" +")" +"V"); + + g_TestJni_shouldShowAutoLogin = base::android::GetStaticMethodID( + env, g_TestJni_clazz, + "shouldShowAutoLogin", + +"(" +"Lorg/chromium/chromeview/ChromeView;" +"Ljava/lang/String;" +"Ljava/lang/String;" +"Ljava/lang/String;" +")" +"Z"); + + g_TestJni_openUrl = base::android::GetStaticMethodID( + env, g_TestJni_clazz, + "openUrl", + +"(" +"Ljava/lang/String;" +")" +"Ljava/io/InputStream;"); + + g_TestJni_activateHardwareAcceleration = base::android::GetMethodID( + env, g_TestJni_clazz, + "activateHardwareAcceleration", + +"(" +"Z" +"I" +"I" +"I" +"I" +")" +"V"); + + g_TestJni_uncheckedCall = base::android::GetMethodID( + env, g_TestJni_clazz, + "uncheckedCall", + +"(" +"I" +")" +"V"); + +} + +static bool RegisterNativesImpl(JNIEnv* env) { + GetMethodIDsImpl(env); + + return true; +} + +#endif // org_chromium_TestJni_JNI +""" + self.assertTextEquals(golden_content, h.GetContent()) + + def testCalledByNativeParseError(self): + try: + jni_generator.ExtractCalledByNatives(""" +@CalledByNative +public static int foo(); // This one is fine + +@CalledByNative +scooby doo +""") + self.fail('Expected a ParseError') + except jni_generator.ParseError, e: + self.assertEquals(('@CalledByNative', 'scooby doo'), e.context_lines) + + 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.chromeview; + +import org.chromium.chromeview.legacy.DownloadListener; +""" + self.assertEquals('org/chromium/chromeview/Foo', + jni_generator.ExtractFullyQualifiedJavaClassName( + 'org/chromium/chromeview/Foo.java', contents)) + self.assertEquals('org/chromium/chromeview/Foo', + jni_generator.ExtractFullyQualifiedJavaClassName( + 'frameworks/Foo.java', contents)) + self.assertRaises(SyntaxError, + jni_generator.ExtractFullyQualifiedJavaClassName, + 'com/foo/Bar', 'no PACKAGE line') + + def testMethodNameMangling(self): + self.assertEquals('close_pqV', + jni_generator.GetMangledMethodName('close', '()V')) + self.assertEquals('read_paBIIqI', + jni_generator.GetMangledMethodName('read', '([BII)I')) + self.assertEquals('open_pLjava_lang_StringxqLjava_io_InputStreamx', + jni_generator.GetMangledMethodName( + 'open', + '(Ljava/lang/String;)Ljava/io/InputStream;')) + + def testFromJavaP(self): + contents = """ +public abstract class java.io.InputStream extends java.lang.Object + implements java.io.Closeable{ + public java.io.InputStream(); + public int available() throws java.io.IOException; + public void close() throws java.io.IOException; + public void mark(int); + public boolean markSupported(); + public abstract int read() throws java.io.IOException; + public int read(byte[]) throws java.io.IOException; + public int read(byte[], int, int) throws java.io.IOException; + public synchronized void reset() throws java.io.IOException; + public long skip(long) throws java.io.IOException; +} +""" + jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'), None) + self.assertEquals(9, len(jni_from_javap.called_by_natives)) + golden_content = """\ +// 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 is autogenerated by +// base/android/jni_generator/jni_generator_tests.py +// For +// java/io/InputStream + +#ifndef java_io_InputStream_JNI +#define java_io_InputStream_JNI + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" +#include "base/logging.h" + +using base::android::ScopedJavaLocalRef; + +// Step 1: forward declarations. +namespace { +const char* const kInputStreamClassPath = "java/io/InputStream"; +// Leaking this JavaRef as we cannot use LazyInstance from some threads. +base::android::ScopedJavaGlobalRef<jclass>& + g_InputStream_clazz = + *(new base::android::ScopedJavaGlobalRef<jclass>()); +} // namespace + + +// Step 2: method stubs. + +static jmethodID g_InputStream_available = 0; +static jint Java_InputStream_available(JNIEnv* env, jobject obj) __attribute__ + ((unused)); +static jint Java_InputStream_available(JNIEnv* env, jobject obj) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InputStream_clazz.is_null()); + DCHECK(g_InputStream_available); + jint ret = + env->CallIntMethod(obj, + g_InputStream_available); + base::android::CheckException(env); + return ret; +} + +static jmethodID g_InputStream_close = 0; +static void Java_InputStream_close(JNIEnv* env, jobject obj) __attribute__ + ((unused)); +static void Java_InputStream_close(JNIEnv* env, jobject obj) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InputStream_clazz.is_null()); + DCHECK(g_InputStream_close); + + env->CallVoidMethod(obj, + g_InputStream_close); + base::android::CheckException(env); + +} + +static jmethodID g_InputStream_mark = 0; +static void Java_InputStream_mark(JNIEnv* env, jobject obj, jint p0) + __attribute__ ((unused)); +static void Java_InputStream_mark(JNIEnv* env, jobject obj, jint p0) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InputStream_clazz.is_null()); + DCHECK(g_InputStream_mark); + + env->CallVoidMethod(obj, + g_InputStream_mark, p0); + base::android::CheckException(env); + +} + +static jmethodID g_InputStream_markSupported = 0; +static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) + __attribute__ ((unused)); +static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InputStream_clazz.is_null()); + DCHECK(g_InputStream_markSupported); + jboolean ret = + env->CallBooleanMethod(obj, + g_InputStream_markSupported); + base::android::CheckException(env); + return ret; +} + +static jmethodID g_InputStream_read_pqI = 0; +static jint Java_InputStream_read(JNIEnv* env, jobject obj) __attribute__ + ((unused)); +static jint Java_InputStream_read(JNIEnv* env, jobject obj) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InputStream_clazz.is_null()); + DCHECK(g_InputStream_read_pqI); + jint ret = + env->CallIntMethod(obj, + g_InputStream_read_pqI); + base::android::CheckException(env); + return ret; +} + +static jmethodID g_InputStream_read_paBqI = 0; +static jint Java_InputStream_read(JNIEnv* env, jobject obj, jbyteArray p0) + __attribute__ ((unused)); +static jint Java_InputStream_read(JNIEnv* env, jobject obj, jbyteArray p0) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InputStream_clazz.is_null()); + DCHECK(g_InputStream_read_paBqI); + jint ret = + env->CallIntMethod(obj, + g_InputStream_read_paBqI, p0); + base::android::CheckException(env); + return ret; +} + +static jmethodID g_InputStream_read_paBIIqI = 0; +static jint Java_InputStream_read(JNIEnv* env, jobject obj, jbyteArray p0, + jint p1, + jint p2) __attribute__ ((unused)); +static jint Java_InputStream_read(JNIEnv* env, jobject obj, jbyteArray p0, + jint p1, + jint p2) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InputStream_clazz.is_null()); + DCHECK(g_InputStream_read_paBIIqI); + jint ret = + env->CallIntMethod(obj, + g_InputStream_read_paBIIqI, p0, p1, p2); + base::android::CheckException(env); + return ret; +} + +static jmethodID g_InputStream_reset = 0; +static void Java_InputStream_reset(JNIEnv* env, jobject obj) __attribute__ + ((unused)); +static void Java_InputStream_reset(JNIEnv* env, jobject obj) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InputStream_clazz.is_null()); + DCHECK(g_InputStream_reset); + + env->CallVoidMethod(obj, + g_InputStream_reset); + base::android::CheckException(env); + +} + +static jmethodID g_InputStream_skip = 0; +static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) + __attribute__ ((unused)); +static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) { + /* Must call RegisterNativesImpl() */ + DCHECK(!g_InputStream_clazz.is_null()); + DCHECK(g_InputStream_skip); + jlong ret = + env->CallLongMethod(obj, + g_InputStream_skip, p0); + base::android::CheckException(env); + return ret; +} + +// Step 3: GetMethodIDs and RegisterNatives. +namespace JNI_InputStream { + +static void GetMethodIDsImpl(JNIEnv* env) { + g_InputStream_clazz.Reset( + base::android::GetClass(env, kInputStreamClassPath)); + g_InputStream_available = base::android::GetMethodID( + env, g_InputStream_clazz, + "available", + +"(" +")" +"I"); + + g_InputStream_close = base::android::GetMethodID( + env, g_InputStream_clazz, + "close", + +"(" +")" +"V"); + + g_InputStream_mark = base::android::GetMethodID( + env, g_InputStream_clazz, + "mark", + +"(" +"I" +")" +"V"); + + g_InputStream_markSupported = base::android::GetMethodID( + env, g_InputStream_clazz, + "markSupported", + +"(" +")" +"Z"); + + g_InputStream_read_pqI = base::android::GetMethodID( + env, g_InputStream_clazz, + "read", + +"(" +")" +"I"); + + g_InputStream_read_paBqI = base::android::GetMethodID( + env, g_InputStream_clazz, + "read", + +"(" +"[B" +")" +"I"); + + g_InputStream_read_paBIIqI = base::android::GetMethodID( + env, g_InputStream_clazz, + "read", + +"(" +"[B" +"I" +"I" +")" +"I"); + + g_InputStream_reset = base::android::GetMethodID( + env, g_InputStream_clazz, + "reset", + +"(" +")" +"V"); + + g_InputStream_skip = base::android::GetMethodID( + env, g_InputStream_clazz, + "skip", + +"(" +"J" +")" +"J"); + +} + +static bool RegisterNativesImpl(JNIEnv* env) { + JNI_InputStream::GetMethodIDsImpl(env); + + return true; +} +} // namespace JNI_InputStream + +#endif // java_io_InputStream_JNI +""" + self.assertTextEquals(golden_content, jni_from_javap.GetContent()) + + def testREForNatives(self): + # We should not match "native SyncSetupFlow" inside the comment. + test_data = """ + /** + * Invoked when the setup process is complete so we can disconnect from the + * native-side SyncSetupFlowHandler. + */ + public void destroy() { + Log.v(TAG, "Destroying native SyncSetupFlow"); + if (mNativeSyncSetupFlow != 0) { + nativeSyncSetupEnded(mNativeSyncSetupFlow); + mNativeSyncSetupFlow = 0; + } + } + private native void nativeSyncSetupEnded( + int nativeAndroidSyncSetupFlowHandler); + """ + 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 testJniSelfDocumentingExample(self): + script_dir = os.path.dirname(sys.argv[0]) + content = file(os.path.join(script_dir, 'SampleForTests.java')).read() + golden_content = file(os.path.join(script_dir, + 'golden_sample_for_tests_jni.h')).read() + jni_from_java = jni_generator.JNIFromJavaSource( + content, 'org/chromium/example/jni_generator/SampleForTests') + self.assertTextEquals(golden_content, jni_from_java.GetContent()) + + def testCheckFilenames(self): + self.assertRaises(SystemExit, jni_generator.CheckFilenames, + ['more', 'input', 'than'], ['output']) + self.assertRaises(SystemExit, jni_generator.CheckFilenames, + ['more'], ['output', 'than', 'input']) + self.assertRaises(SystemExit, jni_generator.CheckFilenames, + ['NotTheSame.java'], ['not_good.h']) + self.assertRaises(SystemExit, jni_generator.CheckFilenames, + ['MissingJniSuffix.java'], ['missing_jni_suffix.h']) + jni_generator.CheckFilenames(['ThisIsFine.java'], ['this_is_fine_jni.h']) + jni_generator.CheckFilenames([], []) + + +if __name__ == '__main__': + unittest.main() diff --git a/base/android/jni_generator/sample_for_tests.cc b/base/android/jni_generator/sample_for_tests.cc new file mode 100644 index 0000000..04222b9 --- /dev/null +++ b/base/android/jni_generator/sample_for_tests.cc @@ -0,0 +1,56 @@ +// 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. + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" + +// The main purpose of this is to ensure sample_for_tests_jni.h +// compiles and the functions declared in it as expected. + +using base::android::AttachCurrentThread; +using base::android::ScopedJavaLocalRef; + +class CPPClass { + public: + void Destroy(JNIEnv* env, jobject obj) { + delete this; + } + + jint Method(JNIEnv* env, jobject obj) { + return 0; + } + + ScopedJavaLocalRef<jstring> InnerMethod(JNIEnv* env, jobject obj) { + return ScopedJavaLocalRef<jstring>(); + } +}; + +namespace cpp_namespace { + +class CPPClass { + public: + jdouble MethodOtherP0(JNIEnv* env, jobject obj) { + return 0.0; + } +}; + +} // namespace cpp_namespace + +#include "jni/sample_for_tests_jni.h" + +int main() { + // On a regular application, you'd call AttachCurrentThread(). This sample is + // not yet linking with all the libraries. + JNIEnv* env = /* AttachCurrentThread() */ NULL; + + // This is how you call a java static method from C++. + bool foo = Java_SampleForTests_staticJavaMethod(env); + + // This is how you call a java method from C++. Note that you must have + // obtained the jobject somehow. + jobject my_java_object = NULL; + int bar = Java_SampleForTests_javaMethod(env, my_java_object, 1, 2); + + return 0; +} |