Bug 1459297 - Use SDK JNI calls for boxing/unboxing GeckoBundle values; r?snorp
Instead of the helper methods in GeckoBundle, add SDK JNI calls for the
boxing/unboxing operations, and use those calls directly. Moreover, for
unboxing Boolean/Double/Integer, use their internal "value" field value
directly if possible, to avoid making a JNI method call.
MozReview-Commit-ID: Azvov1gCeje
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoBundle.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoBundle.java
@@ -35,31 +35,16 @@ public final class GeckoBundle implement
@WrapForJNI(calledFrom = "gecko")
private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
private static final int[] EMPTY_INT_ARRAY = new int[0];
private static final long[] EMPTY_LONG_ARRAY = new long[0];
private static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final GeckoBundle[] EMPTY_BUNDLE_ARRAY = new GeckoBundle[0];
- @WrapForJNI(calledFrom = "gecko")
- private static Object box(boolean b) { return b; }
- @WrapForJNI(calledFrom = "gecko")
- private static Object box(int i) { return i; }
- @WrapForJNI(calledFrom = "gecko")
- private static Object box(double d) { return d; }
- @WrapForJNI(calledFrom = "gecko")
- private static boolean unboxBoolean(Boolean b) { return b; }
- @WrapForJNI(calledFrom = "gecko")
- private static int unboxInteger(Number i) { return i.intValue(); }
- @WrapForJNI(calledFrom = "gecko")
- private static double unboxDouble(Number d) { return d.doubleValue(); }
- @WrapForJNI(calledFrom = "gecko")
- private static String unboxString(Object s) { return s.toString(); }
-
private SimpleArrayMap<String, Object> mMap;
/**
* Construct an empty GeckoBundle.
*/
public GeckoBundle() {
mMap = new SimpleArrayMap<>();
}
--- a/widget/android/EventDispatcher.cpp
+++ b/widget/android/EventDispatcher.cpp
@@ -1,16 +1,17 @@
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* vim: set sw=4 ts=4 expandtab:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "EventDispatcher.h"
+#include "JavaBuiltins.h"
#include "nsAppShell.h"
#include "nsIXPConnect.h"
#include "nsJSUtils.h"
#include "xpcpublic.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/ScriptSettings.h"
@@ -267,21 +268,22 @@ BoxObject(JSContext* aCx, JS::HandleValu
}
nsresult
BoxValue(JSContext* aCx, JS::HandleValue aData, jni::Object::LocalRef& aOut)
{
if (aData.isNullOrUndefined()) {
aOut = nullptr;
} else if (aData.isBoolean()) {
- aOut = java::GeckoBundle::Box(aData.toBoolean());
+ aOut = aData.toBoolean() ? java::sdk::Boolean::TRUE()
+ : java::sdk::Boolean::FALSE();
} else if (aData.isInt32()) {
- aOut = java::GeckoBundle::Box(aData.toInt32());
+ aOut = java::sdk::Integer::ValueOf(aData.toInt32());
} else if (aData.isNumber()) {
- aOut = java::GeckoBundle::Box(aData.toNumber());
+ aOut = java::sdk::Double::New(aData.toNumber());
} else if (aData.isString()) {
return BoxString(aCx, aData, aOut);
} else if (aData.isObject()) {
return BoxObject(aCx, aData, aOut);
} else {
NS_WARNING("Unknown type");
return NS_ERROR_INVALID_ARG;
}
@@ -475,36 +477,71 @@ UnboxArrayObject(JSContext* aCx, const j
NS_ENSURE_TRUE(CheckJS(aCx, JS_SetElement(aCx, obj, i, value)),
NS_ERROR_FAILURE);
}
aOut.setObject(*obj);
return NS_OK;
}
+template<class T> jfieldID
+GetValueFieldID(const char* aType)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ JNIEnv* const env = jni::GetGeckoThreadEnv();
+ const jfieldID id = env->GetFieldID(
+ typename T::Context(env, nullptr).ClassRef(), "value", aType);
+ env->ExceptionClear();
+ return id;
+}
+
nsresult
UnboxValue(JSContext* aCx, const jni::Object::LocalRef& aData,
JS::MutableHandleValue aOut)
{
+ static jfieldID booleanValueField =
+ GetValueFieldID<java::sdk::Boolean>("Z");
+ static jfieldID intValueField = GetValueFieldID<java::sdk::Integer>("I");
+ static jfieldID doubleValueField = GetValueFieldID<java::sdk::Double>("D");
+
if (!aData) {
aOut.setNull();
} else if (aData.IsInstanceOf<jni::Boolean>()) {
- aOut.setBoolean(java::GeckoBundle::UnboxBoolean(aData));
- } else if (aData.IsInstanceOf<jni::Integer>() ||
- aData.IsInstanceOf<jni::Byte>() ||
+ if (booleanValueField) {
+ aOut.setBoolean(aData.Env()->GetBooleanField(
+ aData.Get(), booleanValueField) != JNI_FALSE);
+ MOZ_CATCH_JNI_EXCEPTION(aData.Env());
+ } else {
+ aOut.setBoolean(
+ java::sdk::Boolean::Ref::From(aData)->BooleanValue());
+ }
+ } else if (aData.IsInstanceOf<jni::Integer>()) {
+ if (intValueField) {
+ aOut.setInt32(aData.Env()->GetIntField(aData.Get(), intValueField));
+ MOZ_CATCH_JNI_EXCEPTION(aData.Env());
+ } else {
+ aOut.setInt32(java::sdk::Number::Ref::From(aData)->IntValue());
+ }
+ } else if (aData.IsInstanceOf<jni::Byte>() ||
aData.IsInstanceOf<jni::Short>()) {
- aOut.setInt32(java::GeckoBundle::UnboxInteger(aData));
- } else if (aData.IsInstanceOf<jni::Double>() ||
- aData.IsInstanceOf<jni::Float>() ||
+ aOut.setInt32(java::sdk::Number::Ref::From(aData)->IntValue());
+ } else if (aData.IsInstanceOf<jni::Double>()) {
+ if (doubleValueField) {
+ aOut.setNumber(aData.Env()->GetDoubleField(
+ aData.Get(), doubleValueField));
+ } else {
+ aOut.setNumber(java::sdk::Number::Ref::From(aData)->DoubleValue());
+ }
+ } else if (aData.IsInstanceOf<jni::Float>() ||
aData.IsInstanceOf<jni::Long>()) {
- aOut.setNumber(java::GeckoBundle::UnboxDouble(aData));
+ aOut.setNumber(java::sdk::Number::Ref::From(aData)->DoubleValue());
} else if (aData.IsInstanceOf<jni::String>()) {
return UnboxString(aCx, aData, aOut);
} else if (aData.IsInstanceOf<jni::Character>()) {
- return UnboxString(aCx, java::GeckoBundle::UnboxString(aData), aOut);
+ return UnboxString(aCx, java::sdk::String::ValueOf(aData), aOut);
} else if (aData.IsInstanceOf<java::GeckoBundle>()) {
return UnboxBundle(aCx, aData, aOut);
} else if (aData.IsInstanceOf<jni::BooleanArray>()) {
return UnboxArrayPrimitive<bool, jboolean, jbooleanArray,
&JNIEnv::GetBooleanArrayElements,
&JNIEnv::ReleaseBooleanArrayElements,
&JS::BooleanValue>(aCx, aData, aOut);
new file mode 100644
--- /dev/null
+++ b/widget/android/bindings/JavaBuiltins-classes.txt
@@ -0,0 +1,22 @@
+[java.lang.Boolean = skip:true]
+# Use static fields for boxing boolean.
+TRUE =
+FALSE =
+booleanValue =
+
+[java.lang.Double = skip:true]
+<init>(D)V =
+
+[java.lang.Integer = skip:true]
+# Use valueOf() for boxing int; don't use constructor
+# because some Integer values are cached.
+valueOf(I)Ljava/lang/Integer; =
+
+[java.lang.Number = skip:true]
+# Use doubleValue() for unboxing Double/Float/Long.
+doubleValue =
+# Use intValue() for unboxing Byte/Int/Short.
+intValue =
+
+[java.lang.String = skip:true]
+valueOf(Ljava/lang/Object;)Ljava/lang/String; =
--- a/widget/android/bindings/moz.build
+++ b/widget/android/bindings/moz.build
@@ -7,16 +7,17 @@
with Files("**"):
BUG_COMPONENT = ("Firefox for Android", "Graphics, Panning and Zooming")
# List of stems to generate .cpp and .h files for. To add a stem, add it to
# this list and ensure that $(stem)-classes.txt exists in this directory.
generated = [
'AndroidBuild',
'AndroidRect',
+ 'JavaBuiltins',
'KeyEvent',
'MediaCodec',
'MotionEvent',
'SurfaceTexture',
'ViewConfiguration'
]
SOURCES += ['!%s.cpp' % stem for stem in generated]