Bug 992357 - c. Add primitive array support to NativeJSObject; r=blassey

This commit is contained in:
Jim Chen 2014-05-16 18:25:29 -04:00
parent c5ae95c1bf
commit b5e09b233d

View File

@ -72,6 +72,18 @@ public:
env, jBundle, "putString", env, jBundle, "putString",
"(Ljava/lang/String;Ljava/lang/String;)V"); "(Ljava/lang/String;Ljava/lang/String;)V");
MOZ_ASSERT(jBundlePutString); MOZ_ASSERT(jBundlePutString);
jBundlePutBooleanArray = AndroidBridge::GetMethodID(
env, jBundle, "putBooleanArray",
"(Ljava/lang/String;[Z)V");
MOZ_ASSERT(jBundlePutBooleanArray);
jBundlePutDoubleArray = AndroidBridge::GetMethodID(
env, jBundle, "putDoubleArray",
"(Ljava/lang/String;[D)V");
MOZ_ASSERT(jBundlePutDoubleArray);
jBundlePutIntArray = AndroidBridge::GetMethodID(
env, jBundle, "putIntArray",
"(Ljava/lang/String;[I)V");
MOZ_ASSERT(jBundlePutIntArray);
} }
static jobject CreateInstance(JNIEnv* env, JSContext* cx, static jobject CreateInstance(JNIEnv* env, JSContext* cx,
@ -227,6 +239,9 @@ public:
static jmethodID jBundlePutDouble; static jmethodID jBundlePutDouble;
static jmethodID jBundlePutInt; static jmethodID jBundlePutInt;
static jmethodID jBundlePutString; static jmethodID jBundlePutString;
static jmethodID jBundlePutBooleanArray;
static jmethodID jBundlePutDoubleArray;
static jmethodID jBundlePutIntArray;
private: private:
static jclass jNativeJSContainer; static jclass jNativeJSContainer;
@ -299,6 +314,9 @@ jmethodID NativeJSContainer::jBundlePutBundle = 0;
jmethodID NativeJSContainer::jBundlePutDouble = 0; jmethodID NativeJSContainer::jBundlePutDouble = 0;
jmethodID NativeJSContainer::jBundlePutInt = 0; jmethodID NativeJSContainer::jBundlePutInt = 0;
jmethodID NativeJSContainer::jBundlePutString = 0; jmethodID NativeJSContainer::jBundlePutString = 0;
jmethodID NativeJSContainer::jBundlePutBooleanArray = 0;
jmethodID NativeJSContainer::jBundlePutDoubleArray = 0;
jmethodID NativeJSContainer::jBundlePutIntArray = 0;
jobject jobject
CreateNativeJSContainer(JNIEnv* env, JSContext* cx, JS::HandleObject object) CreateNativeJSContainer(JNIEnv* env, JSContext* cx, JS::HandleObject object)
@ -360,6 +378,17 @@ CheckJNIArgument(JNIEnv* env, jobject arg)
return true; return true;
} }
template <bool (*InValue)(JSContext*, JS::HandleValue)> bool
CheckProperty(JNIEnv* env, JSContext* cx, JS::HandleValue val) {
if (!(*InValue)(cx, val)) {
AndroidBridge::ThrowException(env,
"java/lang/IllegalArgumentException",
"Property type mismatch");
return false;
}
return true;
}
bool bool
AppendJSON(const jschar* buf, uint32_t len, void* data) AppendJSON(const jschar* buf, uint32_t len, void* data)
{ {
@ -369,13 +398,17 @@ AppendJSON(const jschar* buf, uint32_t len, void* data)
template <typename U, typename V, template <typename U, typename V,
bool (JS::Value::*IsMethod)() const, bool (JS::Value::*IsMethod)() const,
V (JS::Value::*ToMethod)() const> V (JS::Value::*ToMethod)() const,
typename UA,
UA (JNIEnv::*NewArrayMethod)(jsize),
void (JNIEnv::*SetArrayRegionMethod)(UA, jsize, jsize, const U*)>
struct PrimitiveProperty struct PrimitiveProperty
{ {
typedef U Type; // JNI type typedef U Type; // JNI type
typedef UA ArrayType; // JNI array type
typedef V NativeType; // JSAPI type typedef V NativeType; // JSAPI type
static bool InValue(JS::HandleValue val) { static bool InValue(JSContext* cx, JS::HandleValue val) {
return (static_cast<const JS::Value&>(val).*IsMethod)(); return (static_cast<const JS::Value&>(val).*IsMethod)();
} }
@ -384,25 +417,54 @@ struct PrimitiveProperty
return static_cast<Type>( return static_cast<Type>(
(static_cast<const JS::Value&>(val).*ToMethod)()); (static_cast<const JS::Value&>(val).*ToMethod)());
} }
static ArrayType NewArray(JNIEnv* env, jobject instance, JSContext* cx,
JS::HandleObject array, size_t length) {
ScopedDeleteArray<Type> buffer(new Type[length]);
for (size_t i = 0; i < length; i++) {
JS::RootedValue elem(cx);
if (!CheckJSCall(env, JS_GetElement(cx, array, i, &elem)) ||
!CheckProperty<InValue>(env, cx, elem)) {
return nullptr;
}
buffer[i] = FromValue(env, instance, cx, elem);
}
AutoLocalJNIFrame frame(env, 1);
ArrayType jarray = (env->*NewArrayMethod)(length);
if (!jarray) {
return nullptr;
}
(env->*SetArrayRegionMethod)(jarray, 0, length, buffer);
if (env->ExceptionCheck()) {
return nullptr;
}
return frame.Pop(jarray);
}
}; };
// Statically cast from bool to jboolean (unsigned char); it works // Statically cast from bool to jboolean (unsigned char); it works
// since false and JNI_FALSE have the same value (0), and true and // since false and JNI_FALSE have the same value (0), and true and
// JNI_TRUE have the same value (1). // JNI_TRUE have the same value (1).
typedef PrimitiveProperty<jboolean, bool, typedef PrimitiveProperty<jboolean, bool,
&JS::Value::isBoolean, &JS::Value::toBoolean> BooleanProperty; &JS::Value::isBoolean, &JS::Value::toBoolean,
jbooleanArray, &JNIEnv::NewBooleanArray,
&JNIEnv::SetBooleanArrayRegion> BooleanProperty;
typedef PrimitiveProperty<jdouble, double, typedef PrimitiveProperty<jdouble, double,
&JS::Value::isNumber, &JS::Value::toNumber> DoubleProperty; &JS::Value::isNumber, &JS::Value::toNumber,
jdoubleArray, &JNIEnv::NewDoubleArray,
&JNIEnv::SetDoubleArrayRegion> DoubleProperty;
typedef PrimitiveProperty<jint, int32_t, typedef PrimitiveProperty<jint, int32_t,
&JS::Value::isInt32, &JS::Value::toInt32> IntProperty; &JS::Value::isInt32, &JS::Value::toInt32,
jintArray, &JNIEnv::NewIntArray,
&JNIEnv::SetIntArrayRegion> IntProperty;
struct StringProperty struct StringProperty
{ {
typedef jstring Type; typedef jstring Type;
static bool InValue(JS::HandleValue val) { static bool InValue(JSContext* cx, JS::HandleValue val) {
return val.isString(); return val.isString();
} }
@ -434,7 +496,7 @@ struct BaseObjectProperty
{ {
typedef jobject Type; typedef jobject Type;
static bool InValue(JS::HandleValue val) { static bool InValue(JSContext* cx, JS::HandleValue val) {
return val.isObjectOrNull(); return val.isObjectOrNull();
} }
@ -457,11 +519,53 @@ typedef BaseObjectProperty<
// Returns a Bundle from a JSObject // Returns a Bundle from a JSObject
typedef BaseObjectProperty<GetBundle> BundleProperty; typedef BaseObjectProperty<GetBundle> BundleProperty;
template <class T>
struct ArrayProperty
{
typedef T Base;
typedef typename T::ArrayType Type;
static bool InValue(JSContext* cx, JS::HandleValue val) {
if (!val.isObject()) {
return false;
}
JS::RootedObject obj(cx, &val.toObject());
uint32_t length = 0;
if (!JS_IsArrayObject(cx, obj) ||
!JS_GetArrayLength(cx, obj, &length)) {
return false;
}
if (!length) {
// Empty arrays are always okay.
return true;
}
// We only check to see the first element is the target type. If the
// array has mixed types, we'll throw an error during actual conversion.
JS::RootedValue element(cx);
return JS_GetElement(cx, obj, 0, &element) &&
Base::InValue(cx, element);
}
static Type FromValue(JNIEnv* env, jobject instance,
JSContext* cx, JS::HandleValue val) {
JS::RootedObject obj(cx, &val.toObject());
uint32_t length = 0;
if (!CheckJSCall(env, JS_GetArrayLength(cx, obj, &length))) {
return nullptr;
}
return Base::NewArray(env, instance, cx, obj, length);
}
};
typedef ArrayProperty<BooleanProperty> BooleanArrayProperty;
typedef ArrayProperty<DoubleProperty> DoubleArrayProperty;
typedef ArrayProperty<IntProperty> IntArrayProperty;
struct HasProperty struct HasProperty
{ {
typedef jboolean Type; typedef jboolean Type;
static bool InValue(JS::HandleValue val) { static bool InValue(JSContext* cx, JS::HandleValue val) {
return true; return true;
} }
@ -510,10 +614,7 @@ GetProperty(JNIEnv* env, jobject instance, jstring name,
} }
return fallback; return fallback;
} }
if (!Property::InValue(val)) { if (!CheckProperty<Property::InValue>(env, cx, val)) {
AndroidBridge::ThrowException(env,
"java/lang/IllegalArgumentException",
"Property type mismatch");
return fallback; return fallback;
} }
return Property::FromValue(env, instance, cx, val); return Property::FromValue(env, instance, cx, val);
@ -562,7 +663,7 @@ GetBundle(JNIEnv* env, jobject instance, JSContext* cx, JS::HandleObject obj)
} }
#define PUT_IN_BUNDLE_IF_TYPE_IS(Type) \ #define PUT_IN_BUNDLE_IF_TYPE_IS(Type) \
if (Type##Property::InValue(val)) { \ if (Type##Property::InValue(cx, val)) { \
env->CallVoidMethod( \ env->CallVoidMethod( \
newBundle, \ newBundle, \
NativeJSContainer::jBundlePut##Type, \ NativeJSContainer::jBundlePut##Type, \
@ -573,11 +674,21 @@ GetBundle(JNIEnv* env, jobject instance, JSContext* cx, JS::HandleObject obj)
} \ } \
((void) 0) // Accommodate trailing semicolon. ((void) 0) // Accommodate trailing semicolon.
// Scalar values are faster to check, so check them first.
PUT_IN_BUNDLE_IF_TYPE_IS(Boolean); PUT_IN_BUNDLE_IF_TYPE_IS(Boolean);
// Int can be casted to double, so check int first. // Int can be casted to double, so check int first.
PUT_IN_BUNDLE_IF_TYPE_IS(Int); PUT_IN_BUNDLE_IF_TYPE_IS(Int);
PUT_IN_BUNDLE_IF_TYPE_IS(Double); PUT_IN_BUNDLE_IF_TYPE_IS(Double);
PUT_IN_BUNDLE_IF_TYPE_IS(String); PUT_IN_BUNDLE_IF_TYPE_IS(String);
// There's no "putObject", so don't check ObjectProperty
// Check for array types if scalar checks all failed.
PUT_IN_BUNDLE_IF_TYPE_IS(BooleanArray);
// XXX because we only check the first element of an array,
// a double array can potentially be seen as an int array.
PUT_IN_BUNDLE_IF_TYPE_IS(IntArray);
PUT_IN_BUNDLE_IF_TYPE_IS(DoubleArray);
// Use Bundle as the default catch-all for objects // Use Bundle as the default catch-all for objects
PUT_IN_BUNDLE_IF_TYPE_IS(Bundle); PUT_IN_BUNDLE_IF_TYPE_IS(Bundle);
@ -627,14 +738,14 @@ NS_EXPORT jbooleanArray JNICALL
Java_org_mozilla_gecko_util_NativeJSObject_getBooleanArray( Java_org_mozilla_gecko_util_NativeJSObject_getBooleanArray(
JNIEnv* env, jobject instance, jstring name) JNIEnv* env, jobject instance, jstring name)
{ {
return nullptr; // TODO add implementation return GetProperty<BooleanArrayProperty>(env, instance, name);
} }
NS_EXPORT jbooleanArray JNICALL NS_EXPORT jbooleanArray JNICALL
Java_org_mozilla_gecko_util_NativeJSObject_optBooleanArray( Java_org_mozilla_gecko_util_NativeJSObject_optBooleanArray(
JNIEnv* env, jobject instance, jstring name, jbooleanArray fallback) JNIEnv* env, jobject instance, jstring name, jbooleanArray fallback)
{ {
return nullptr; // TODO add implementation return GetProperty<BooleanArrayProperty>(env, instance, name, FallbackOption::RETURN, fallback);
} }
NS_EXPORT jobject JNICALL NS_EXPORT jobject JNICALL
@ -681,14 +792,14 @@ NS_EXPORT jdoubleArray JNICALL
Java_org_mozilla_gecko_util_NativeJSObject_getDoubleArray( Java_org_mozilla_gecko_util_NativeJSObject_getDoubleArray(
JNIEnv* env, jobject instance, jstring name) JNIEnv* env, jobject instance, jstring name)
{ {
return nullptr; // TODO add implementation return GetProperty<DoubleArrayProperty>(env, instance, name);
} }
NS_EXPORT jdoubleArray JNICALL NS_EXPORT jdoubleArray JNICALL
Java_org_mozilla_gecko_util_NativeJSObject_optDoubleArray( Java_org_mozilla_gecko_util_NativeJSObject_optDoubleArray(
JNIEnv* env, jobject instance, jstring name, jdoubleArray fallback) JNIEnv* env, jobject instance, jstring name, jdoubleArray fallback)
{ {
return nullptr; // TODO add implementation return GetProperty<DoubleArrayProperty>(env, instance, name, FallbackOption::RETURN, fallback);
} }
NS_EXPORT jint JNICALL NS_EXPORT jint JNICALL
@ -708,14 +819,14 @@ NS_EXPORT jintArray JNICALL
Java_org_mozilla_gecko_util_NativeJSObject_getIntArray( Java_org_mozilla_gecko_util_NativeJSObject_getIntArray(
JNIEnv* env, jobject instance, jstring name) JNIEnv* env, jobject instance, jstring name)
{ {
return nullptr; // TODO add implementation return GetProperty<IntArrayProperty>(env, instance, name);
} }
NS_EXPORT jintArray JNICALL NS_EXPORT jintArray JNICALL
Java_org_mozilla_gecko_util_NativeJSObject_optIntArray( Java_org_mozilla_gecko_util_NativeJSObject_optIntArray(
JNIEnv* env, jobject instance, jstring name, jintArray fallback) JNIEnv* env, jobject instance, jstring name, jintArray fallback)
{ {
return nullptr; // TODO add implementation return GetProperty<IntArrayProperty>(env, instance, name, FallbackOption::RETURN, fallback);
} }
NS_EXPORT jobject JNICALL NS_EXPORT jobject JNICALL