[gjs] arg: Support gunichar-valued arrays
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs] arg: Support gunichar-valued arrays
- Date: Thu, 20 Oct 2016 04:56:39 +0000 (UTC)
commit 96f8a01d89b11171e04fcb53df2266425b343857
Author: Philip Chimento <philip chimento gmail com>
Date: Tue Oct 11 22:03:00 2016 -0700
arg: Support gunichar-valued arrays
Compiling with -Wswitch revealed that we do not support gunichar arrays.
This adds support for marshalling JS strings into arrays of gunichar,
containing UCS-4 encoded strings, and vice versa. For JS-to-C, one can
additionally supply a JS array of integers which will be marshalled into
a gunichar array.
https://bugzilla.gnome.org/show_bug.cgi?id=772790
gi/arg.cpp | 51 ++++++++++++++++--
gjs/jsapi-util-string.cpp | 91 +++++++++++++++++++++++++++++++
gjs/jsapi-util.h | 10 ++++
installed-tests/js/testGIMarshalling.js | 10 ++++
4 files changed, 157 insertions(+), 5 deletions(-)
---
diff --git a/gi/arg.cpp b/gi/arg.cpp
index 96693f3..3d252e7 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -540,6 +540,16 @@ gjs_string_to_intarray(JSContext *context,
*arr_p = result16;
return true;
+ case GI_TYPE_TAG_UNICHAR:
+ {
+ gunichar *result_ucs4;
+ JS::RootedValue root(context, string_val);
+ if (!gjs_string_to_ucs4(context, root, &result_ucs4, length))
+ return false;
+ *arr_p = result_ucs4;
+ return true;
+ }
+
default:
/* can't convert a string to this type */
gjs_throw(context, "Cannot convert string to array of '%s'",
@@ -924,6 +934,9 @@ gjs_array_to_array(JSContext *context,
return gjs_array_to_strv (context, array_value, length, arr_p);
case GI_TYPE_TAG_BOOLEAN:
return gjs_array_to_gboolean_array(context, array_value, length, arr_p);
+ case GI_TYPE_TAG_UNICHAR:
+ return gjs_array_to_intarray(context, array_value, length, arr_p,
+ sizeof(gunichar), UNSIGNED);
case GI_TYPE_TAG_UINT8:
return gjs_array_to_intarray
(context, array_value, length, arr_p, 1, UNSIGNED);
@@ -1004,6 +1017,9 @@ gjs_g_array_new_for_type(JSContext *context,
case GI_TYPE_TAG_BOOLEAN:
element_size = sizeof(gboolean);
break;
+ case GI_TYPE_TAG_UNICHAR:
+ element_size = sizeof(gunichar);
+ break;
case GI_TYPE_TAG_UINT8:
case GI_TYPE_TAG_INT8:
element_size = sizeof(guint8);
@@ -2127,7 +2143,16 @@ gjs_array_from_carray_internal (JSContext *context,
return false;
*value_p = JS::ObjectValue(*obj);
return true;
- }
+ }
+
+ /* Special case array(unichar) to be a string in JS */
+ if (element_type == GI_TYPE_TAG_UNICHAR) {
+ JS::RootedValue value_out(context);
+ if (!gjs_string_from_ucs4(context, (gunichar *) array, length, &value_out))
+ return false;
+ *value_p = value_out.get();
+ return true;
+ }
obj = JS_NewArrayObject(context, 0, NULL);
if (obj == NULL)
@@ -2149,9 +2174,10 @@ gjs_array_from_carray_internal (JSContext *context,
}
switch (element_type) {
+ /* Special cases handled above */
case GI_TYPE_TAG_UINT8:
- ITERATE(uint8);
- break;
+ case GI_TYPE_TAG_UNICHAR:
+ g_assert_not_reached();
case GI_TYPE_TAG_BOOLEAN:
ITERATE(boolean);
break;
@@ -2347,7 +2373,17 @@ gjs_array_from_zero_terminated_c_array (JSContext *context,
return false;
*value_p = JS::ObjectValue(*obj);
return true;
- }
+ }
+
+ /* Special case array(gunichar) to JS string */
+ if (element_type == GI_TYPE_TAG_UNICHAR) {
+ JS::RootedValue value_out(context);
+
+ if (!gjs_string_from_ucs4(context, (gunichar *) c_array, -1, &value_out))
+ return false;
+ *value_p = value_out.get();
+ return true;
+ }
obj = JS_NewArrayObject(context, 0, NULL);
if (obj == NULL)
@@ -2372,7 +2408,10 @@ gjs_array_from_zero_terminated_c_array (JSContext *context,
} while(0);
switch (element_type) {
- /* We handle GI_TYPE_TAG_UINT8 above. */
+ /* Special cases handled above. */
+ case GI_TYPE_TAG_UINT8:
+ case GI_TYPE_TAG_UNICHAR:
+ g_assert_not_reached();
case GI_TYPE_TAG_INT8:
ITERATE(int8);
break;
@@ -3159,6 +3198,7 @@ gjs_g_arg_release_internal(JSContext *context,
case GI_TYPE_TAG_INT64:
case GI_TYPE_TAG_FLOAT:
case GI_TYPE_TAG_DOUBLE:
+ case GI_TYPE_TAG_UNICHAR:
case GI_TYPE_TAG_GTYPE:
g_free (arg->v_pointer);
break;
@@ -3224,6 +3264,7 @@ gjs_g_arg_release_internal(JSContext *context,
switch (element_type) {
case GI_TYPE_TAG_BOOLEAN:
+ case GI_TYPE_TAG_UNICHAR:
case GI_TYPE_TAG_UINT8:
case GI_TYPE_TAG_UINT16:
case GI_TYPE_TAG_UINT32:
diff --git a/gjs/jsapi-util-string.cpp b/gjs/jsapi-util-string.cpp
index f845d15..dbab5eb 100644
--- a/gjs/jsapi-util-string.cpp
+++ b/gjs/jsapi-util-string.cpp
@@ -208,6 +208,97 @@ out:
}
/**
+ * gjs_string_to_ucs4:
+ * @cx: a #JSContext
+ * @value: JS::Value containing a string
+ * @ucs4_string_p: return location for a #gunichar array
+ * @len_p: return location for @ucs4_string_p length
+ *
+ * Returns: true on success, false otherwise in which case a JS error is thrown
+ */
+bool
+gjs_string_to_ucs4(JSContext *cx,
+ JS::HandleValue value,
+ gunichar **ucs4_string_p,
+ size_t *len_p)
+{
+ if (ucs4_string_p == NULL)
+ return true;
+
+ if (!value.isString()) {
+ gjs_throw(cx, "Value is not a string, cannot convert to UCS-4");
+ return false;
+ }
+
+ JSAutoRequest ar(cx);
+ JS::RootedString str(cx, value.toString());
+ size_t utf16_len;
+ GError *error = NULL;
+
+ const jschar *utf16 = JS_GetStringCharsAndLength(cx, str, &utf16_len);
+ if (utf16 == NULL) {
+ gjs_throw(cx, "Failed to get UTF-16 string data");
+ return false;
+ }
+
+ if (ucs4_string_p != NULL) {
+ long length;
+ *ucs4_string_p = g_utf16_to_ucs4(utf16, utf16_len, NULL, &length, &error);
+ if (*ucs4_string_p == NULL) {
+ gjs_throw(cx, "Failed to convert UTF-16 string to UCS-4: %s",
+ error->message);
+ g_clear_error(&error);
+ return false;
+ }
+ if (len_p != NULL)
+ *len_p = (size_t) length;
+ }
+
+ return true;
+}
+
+/**
+ * gjs_string_from_ucs4:
+ * @cx: a #JSContext
+ * @ucs4_string: string of #gunichar
+ * @n_chars: number of characters in @ucs4_string or -1 for zero-terminated
+ * @value_p: JS::Value that will be filled with a string
+ *
+ * Returns: true on success, false otherwise in which case a JS error is thrown
+ */
+bool
+gjs_string_from_ucs4(JSContext *cx,
+ const gunichar *ucs4_string,
+ ssize_t n_chars,
+ JS::MutableHandleValue value_p)
+{
+ uint16_t *u16_string;
+ long u16_string_length;
+ GError *error = NULL;
+
+ u16_string = g_ucs4_to_utf16(ucs4_string, n_chars, NULL,
+ &u16_string_length, &error);
+ if (!u16_string) {
+ gjs_throw(cx, "Failed to convert UCS-4 string to UTF-16: %s",
+ error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ JSAutoRequest ar(cx);
+ /* Avoid a copy - assumes that g_malloc == js_malloc == malloc */
+ JS::RootedString str(cx, JS_NewUCString(cx, u16_string, u16_string_length));
+
+ if (str == NULL) {
+ gjs_throw(cx, "Failed to convert UCS-4 string to UTF-16");
+ return false;
+ }
+
+ value_p.setString(str);
+ return true;
+}
+
+/**
* gjs_get_string_id:
* @context: a #JSContext
* @id: a jsid that is an object hash key (could be an int or string)
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 6cc164a..2016a86 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -418,6 +418,16 @@ bool gjs_string_get_uint16_data (JSContext *context,
JS::Value value,
guint16 **data_p,
gsize *len_p);
+
+bool gjs_string_to_ucs4(JSContext *cx,
+ JS::HandleValue value,
+ gunichar **ucs4_string_p,
+ size_t *len_p);
+bool gjs_string_from_ucs4(JSContext *cx,
+ const gunichar *ucs4_string,
+ ssize_t n_chars,
+ JS::MutableHandleValue value_p);
+
bool gjs_get_string_id (JSContext *context,
jsid id,
char **name_p);
diff --git a/installed-tests/js/testGIMarshalling.js b/installed-tests/js/testGIMarshalling.js
index c8db124..3cfd5c7 100644
--- a/installed-tests/js/testGIMarshalling.js
+++ b/installed-tests/js/testGIMarshalling.js
@@ -76,6 +76,10 @@ function testCArray() {
assertEquals(true, array[2]);
assertEquals(true, array[3]);
+ assertEquals('const \u2665 utf8', GIMarshallingTests.array_unichar_out());
+ assertEquals('const \u2665 utf8',
+ GIMarshallingTests.array_zero_terminated_return_unichar());
+
array = GIMarshallingTests.array_inout([-1, 0, 1, 2]);
assertEquals(5, array.length);
assertEquals(-2, array[0]);
@@ -108,6 +112,9 @@ function testCArray() {
GIMarshallingTests.array_struct_take_in(array);
GIMarshallingTests.array_uint8_in ("abcd");
+ GIMarshallingTests.array_unichar_in('const \u2665 utf8');
+ GIMarshallingTests.array_unichar_in([0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20,
+ 0x2665, 0x20, 0x75, 0x74, 0x66, 0x38]);
GIMarshallingTests.array_enum_in([GIMarshallingTests.Enum.VALUE1,
GIMarshallingTests.Enum.VALUE2,
GIMarshallingTests.Enum.VALUE3]);
@@ -146,6 +153,9 @@ function testGArray() {
GIMarshallingTests.garray_int_none_in([-1, 0, 1, 2]);
GIMarshallingTests.garray_utf8_none_in(["0", "1", "2"]);
GIMarshallingTests.garray_bool_none_in([-1, 0, 1, 2]);
+ GIMarshallingTests.garray_unichar_none_in('const \u2665 utf8');
+ GIMarshallingTests.garray_unichar_none_in([0x63, 0x6f, 0x6e, 0x73, 0x74,
+ 0x20, 0x2665, 0x20, 0x75, 0x74, 0x66, 0x38]);
array = GIMarshallingTests.garray_utf8_none_out();
assertEquals("0", array[0]);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]