Commit 047f375e authored by Magnus Jedvert's avatar Magnus Jedvert Committed by Commit Bot

Android: Add support for generating JNI code for calling constructors

The current workaround is to make static create functions that call the
constructor and then generate the JNI code for the static create
function. We have a quite of lot of these cases in WebRTC and it would
be more convenient and clean for us to be able to call the constructor
directly.

Bug: webrtc:8278,webrtc:8551
Change-Id: I0726fb620526f810b2b06044a8b12deb1ae50f19
Reviewed-on: https://chromium-review.googlesource.com/772410Reviewed-by: default avataragrieve <agrieve@chromium.org>
Commit-Queue: Magnus Jedvert <magjed@chromium.org>
Cr-Commit-Position: refs/heads/master@{#518400}
parent 5bea8686
...@@ -13,7 +13,7 @@ import java.lang.annotation.Target; ...@@ -13,7 +13,7 @@ import java.lang.annotation.Target;
* @CalledByNative is used by the JNI generator to create the necessary JNI * @CalledByNative is used by the JNI generator to create the necessary JNI
* bindings and expose this method to native code. * bindings and expose this method to native code.
*/ */
@Target(ElementType.METHOD) @Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
@Retention(RetentionPolicy.CLASS) @Retention(RetentionPolicy.CLASS)
public @interface CalledByNative { public @interface CalledByNative {
/* /*
......
...@@ -300,6 +300,33 @@ static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, const ...@@ -300,6 +300,33 @@ static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, const
jni_generator::CheckException(env); jni_generator::CheckException(env);
} }
static base::subtle::AtomicWord
g_org_chromium_example_jni_1generator_SampleForTests_Constructor = 0;
static base::android::ScopedJavaLocalRef<jobject>
Java_SampleForTests_Constructor(JNIEnv* env, JniIntWrapper foo,
JniIntWrapper bar) {
CHECK_CLAZZ(env,
org_chromium_example_jni_1generator_SampleForTests_clazz(env),
org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
"<init>",
"("
"I"
"I"
")"
"V",
&g_org_chromium_example_jni_1generator_SampleForTests_Constructor);
jobject ret =
env->NewObject(org_chromium_example_jni_1generator_SampleForTests_clazz(env),
method_id, as_jint(foo), as_jint(bar));
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jobject>(env, ret);
}
static base::subtle::AtomicWord static base::subtle::AtomicWord
g_org_chromium_example_jni_1generator_SampleForTests_methodThatThrowsException g_org_chromium_example_jni_1generator_SampleForTests_methodThatThrowsException
= 0; = 0;
......
...@@ -67,9 +67,6 @@ import java.util.List; ...@@ -67,9 +67,6 @@ import java.util.List;
// expose a lot of internal details. This is specially significant for system classes where it's // expose a lot of internal details. This is specially significant for system classes where it's
// simpler to wrap factory methods and a few getters / setters than expose the entire class. // simpler to wrap factory methods and a few getters / setters than expose the entire class.
// //
// - Use static factory functions annotated with @CalledByNative rather than calling the
// constructors directly.
//
// - Iterate over containers where they are originally owned, then create inner structs or // - Iterate over containers where they are originally owned, then create inner structs or
// directly call methods on the other side. It's much simpler than trying to amalgamate // directly call methods on the other side. It's much simpler than trying to amalgamate
// java and stl containers. // java and stl containers.
...@@ -147,6 +144,11 @@ class SampleForTests { ...@@ -147,6 +144,11 @@ class SampleForTests {
void packagePrivateJavaMethod() { void packagePrivateJavaMethod() {
} }
// Constructors will be exported to C++ as:
// Java_SampleForTests_Constructor(JNIEnv* env, jint foo, jint bar)
@CalledByNative
public SampleForTests(int foo, int bar) {}
// Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that
// call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to
// call ClearException() and act as appropriate. // call ClearException() and act as appropriate.
......
...@@ -580,10 +580,12 @@ RE_SCOPED_JNI_TYPES = re.compile('jobject|jclass|jstring|jthrowable|.*Array') ...@@ -580,10 +580,12 @@ RE_SCOPED_JNI_TYPES = re.compile('jobject|jclass|jstring|jthrowable|.*Array')
# Regex to match a string like "@CalledByNative public void foo(int bar)". # Regex to match a string like "@CalledByNative public void foo(int bar)".
RE_CALLED_BY_NATIVE = re.compile( RE_CALLED_BY_NATIVE = re.compile(
'@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?' '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
'\s+(?P<prefix>[\w ]*?)' '\s+(?P<prefix>('
'(private|protected|public|static|abstract|final|default|synchronized)'
'\s*)*)'
'(:?\s*@\w+)?' # Ignore annotations in return types. '(:?\s*@\w+)?' # Ignore annotations in return types.
'\s*(?P<return_type>\S+?)' '\s*(?P<return_type>\S*?)'
'\s+(?P<name>\w+)' '\s*(?P<name>\w+)'
'\s*\((?P<params>[^\)]*)\)') '\s*\((?P<params>[^\)]*)\)')
...@@ -608,13 +610,23 @@ def ExtractCalledByNatives(jni_params, contents): ...@@ -608,13 +610,23 @@ def ExtractCalledByNatives(jni_params, contents):
""" """
called_by_natives = [] called_by_natives = []
for match in re.finditer(RE_CALLED_BY_NATIVE, contents): for match in re.finditer(RE_CALLED_BY_NATIVE, contents):
return_type = match.group('return_type')
name = match.group('name')
if not return_type:
is_constructor = True
return_type = name
name = "Constructor"
else:
is_constructor = False
called_by_natives += [CalledByNative( called_by_natives += [CalledByNative(
system_class=False, system_class=False,
unchecked='Unchecked' in match.group('Unchecked'), unchecked='Unchecked' in match.group('Unchecked'),
static='static' in match.group('prefix'), static='static' in match.group('prefix'),
java_class_name=match.group('annotation') or '', java_class_name=match.group('annotation') or '',
return_type=match.group('return_type'), return_type=return_type,
name=match.group('name'), name=name,
is_constructor=is_constructor,
params=JniParams.Parse(match.group('params')))] params=JniParams.Parse(match.group('params')))]
# Check for any @CalledByNative occurrences that weren't matched. # Check for any @CalledByNative occurrences that weren't matched.
unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n') unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
......
...@@ -122,6 +122,10 @@ int main() { ...@@ -122,6 +122,10 @@ int main() {
int bar = base::android::Java_SampleForTests_javaMethod( int bar = base::android::Java_SampleForTests_javaMethod(
env, my_java_object, 1, 2); env, my_java_object, 1, 2);
// This is how you call a java constructor method from C++.
ScopedJavaLocalRef<jobject> my_created_object =
base::android::Java_SampleForTests_Constructor(env, 1, 2);
std::cout << foo << bar; std::cout << foo << bar;
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment