[gjs/wip/binary-data: 2/2] wip: start to take a crack at 680730
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/binary-data: 2/2] wip: start to take a crack at 680730
- Date: Tue, 9 Oct 2012 05:20:24 +0000 (UTC)
commit cab3509598c8d024eca25ba01b6d4808e3ab0481
Author: Ray Strode <rstrode redhat com>
Date: Tue Oct 9 01:08:15 2012 -0400
wip: start to take a crack at 680730
This doesn't compile yet, puts function prototypes in the wrong
place, probably has off-by-one errors etc.
The point is to sketch out tagging binary data so it can be
converted as necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=680730
gjs/context.c | 31 ++++++
gjs/jsapi-private.h | 6 +
gjs/jsapi-util-string.c | 244 ++++++++++++++++++++++++++++++++++++++++-------
gjs/jsapi-util.c | 2 +
gjs/jsapi-util.h | 2 +
5 files changed, 249 insertions(+), 36 deletions(-)
---
diff --git a/gjs/context.c b/gjs/context.c
index 06b8af2..eaf4b6a 100644
--- a/gjs/context.c
+++ b/gjs/context.c
@@ -40,6 +40,7 @@
#include <string.h>
#include <jsapi.h>
+#include "jsapi-private.h"
#define _GJS_JS_VERSION_DEFAULT "1.8"
@@ -73,6 +74,7 @@ struct _GjsContext {
char **search_path;
guint idle_emit_gc_id;
+ int string_finalizer_id;
guint we_own_runtime : 1;
guint gc_notifications_enabled : 1;
@@ -361,6 +363,29 @@ gjs_context_class_init(GjsContextClass *klass)
}
static void
+gjs_context_register_string_finalizer(GjsContext *js_context)
+{
+ js_context->string_finalizer_id = JS_AddExternalStringFinalizer(gjs_string_free);
+}
+
+static void
+gjs_context_unregister_string_finalizer (GjsContext *js_context)
+{
+ if (js_context->string_finalizer_id < 0) {
+ js_context->string_finalizer_id = JS_RemoveExternalStringFinalizer(gjs_string_free);
+ js_context->string_finalizer_id = -1;
+ }
+}
+
+int
+gjs_context_get_string_finalizer_id (JSContext *context)
+{
+ GjsContext *js_context = JS_GetContextPrivate(context);
+
+ return js_context->string_finalizer_id;
+}
+
+static void
gjs_context_dispose(GObject *object)
{
GjsContext *js_context;
@@ -372,6 +397,8 @@ gjs_context_dispose(GObject *object)
js_context->profiler = NULL;
}
+ gjs_context_unregister_string_finalizer (js_context);
+
if (js_context->global != NULL) {
js_context->global = NULL;
}
@@ -702,6 +729,10 @@ gjs_context_constructor (GType type,
if (js_context->gc_notifications_enabled)
JS_SetGCCallback(js_context->context, gjs_on_context_gc);
+ /* Register a string finalizer for binary data
+ */
+ gjs_context_register_string_finalizer (js_context);
+
JS_EndRequest(js_context->context);
g_static_mutex_lock (&contexts_lock);
diff --git a/gjs/jsapi-private.h b/gjs/jsapi-private.h
index f48affe..38955e7 100644
--- a/gjs/jsapi-private.h
+++ b/gjs/jsapi-private.h
@@ -35,6 +35,12 @@
G_BEGIN_DECLS
+/* called from jsapi-util-string.c (defined in context.c) to get the custom
+ * finalizer id for allocating strings containing binary data
+ */
+int gjs_context_get_string_finalizer_id (JSContext *context);
+
+
G_END_DECLS
#endif /* __GJS_JSAPI_PRIVATE_H__ */
diff --git a/gjs/jsapi-util-string.c b/gjs/jsapi-util-string.c
index a660250..e4a1302 100644
--- a/gjs/jsapi-util-string.c
+++ b/gjs/jsapi-util-string.c
@@ -28,6 +28,42 @@
#include "jsapi-util.h"
#include "compat.h"
+static const char *
+gjs_string_get_bytes(JSContext *context,
+ jsval value,
+ gsize *len_p)
+{
+ JSString *str;
+ GByteArray *array;
+
+ if (!JSVAL_IS_STRING(value)) {
+ gjs_throw(context,
+ "Value is not a string, can't return binary data from it");
+ return NULL;
+ }
+
+ str = JSVAL_TO_STRING(value);
+
+ if (!JS_IsExternalString(context, str)) {
+ gjs_throw(context,
+ "Value is not a GJS string, can't return binary data from it");
+ return NULL;
+ }
+
+ array = JS_GetExternalStringClosure(context, str);
+
+ if (array == NULL) {
+ gjs_throw(context,
+ "Value is not a valid GJS string, can't return binary data from it");
+ return NULL;
+ }
+
+ if (len_p)
+ *len_p = array->len;
+
+ return (const char *) array->data;
+}
+
gboolean
gjs_try_string_to_utf8 (JSContext *context,
const jsval string_val,
@@ -39,15 +75,25 @@ gjs_try_string_to_utf8 (JSContext *context,
char *utf8_string;
long read_items;
long utf8_length;
+ const char *bytes;
+ gsize len;
GError *convert_error = NULL;
JS_BeginRequest(context);
- if (!JSVAL_IS_STRING(string_val)) {
- g_set_error_literal(error, GJS_UTIL_ERROR, GJS_UTIL_ERROR_ARGUMENT_TYPE_MISMATCH,
- "Object is not a string, cannot convert to UTF-8");
+ bytes = gjs_string_get_bytes(context, string_val, &len);
+
+ if (bytes) {
JS_EndRequest(context);
- return FALSE;
+
+ if (!g_utf8_validate (bytes, len, NULL)) {
+ g_set_error_literal(error, GJS_UTIL_ERROR, GJS_UTIL_ERROR_ARGUMENT_INVALID,
+ "JS string contains invalid Unicode characters");
+ return JS_FALSE;
+ }
+
+ *utf8_string_p = g_memdup (bytes, len);
+ return JS_TRUE;
}
s = JS_GetStringCharsAndLength(context, JSVAL_TO_STRING(string_val), &s_length);
@@ -262,18 +308,22 @@ gjs_string_get_ascii(JSContext *context,
return ascii;
}
-static JSBool
-throw_if_binary_strings_broken(JSContext *context)
+void
+gjs_string_free (JSContext *context,
+ JSString *string)
{
- /* JS_GetStringBytes() returns low byte of each jschar,
- * unless JS_CStringsAreUTF8() in which case we're screwed.
- */
- if (JS_CStringsAreUTF8()) {
- gjs_throw(context, "If JS_CStringsAreUTF8(), gjs doesn't know how to do binary strings");
- return JS_TRUE;
- }
+ GByteArray *array;
+ const jschar *description;
+ size_t len;
- return JS_FALSE;
+ description = JS_GetStringCharsAndLength(context, string, &len);
+ g_free((gpointer) description);
+
+ array = JS_GetExternalStringClosure(context, string);
+
+ if (array != NULL) {
+ g_byte_array_unref (array);
+ }
}
/**
@@ -296,18 +346,27 @@ gjs_string_get_binary_data(JSContext *context,
{
JSString *str;
gsize len;
+ const char *raw_bytes;
char *bytes;
JS_BeginRequest(context);
- if (!JSVAL_IS_STRING(value)) {
- gjs_throw(context,
- "Value is not a string, can't return binary data from it");
+ raw_bytes = gjs_string_get_bytes(context, value, &len);
+
+ if (raw_bytes) {
+ if (data_p)
+ *data_p = g_memdup(raw_bytes, len);
+
+ if (len_p)
+ *len_p = len;
JS_EndRequest(context);
- return JS_FALSE;
+
+ return JS_TRUE;
}
- if (throw_if_binary_strings_broken(context)) {
+ if (!JSVAL_IS_STRING(value)) {
+ gjs_throw(context,
+ "Value is not a string, can't return binary data from it");
JS_EndRequest(context);
return JS_FALSE;
}
@@ -315,8 +374,10 @@ gjs_string_get_binary_data(JSContext *context,
str = JSVAL_TO_STRING(value);
len = JS_GetStringEncodingLength(context, str);
- if (len == (gsize)(-1))
+ if (len == (gsize)(-1)) {
+ JS_EndRequest(context);
return JS_FALSE;
+ }
if (data_p) {
bytes = g_malloc((len + 1) * sizeof(char));
@@ -333,6 +394,85 @@ gjs_string_get_binary_data(JSContext *context,
return JS_TRUE;
}
+static void
+gjs_string_escape (JSContext *context,
+ const char *data,
+ gsize len,
+ const char *prefix,
+ const char *suffix,
+ char **escaped_data_p,
+ gsize *escaped_len_p)
+{
+ gsize i, escaped_len, prefix_len, suffix_len;
+ char *escaped_data;
+
+ prefix_len = strlen(prefix);
+ suffix_len = strlen(suffix);
+
+ escaped_len = prefix_len + 3 * len + suffix_len + 1;
+ escaped_data = g_malloc(escaped_len);
+
+ strcpy(escaped_data, prefix);
+ for (i = 0; i < len; i++) {
+ g_snprintf(escaped_data + prefix_len + 3 * i, 4, "\\%02hhx", data[i]);
+ }
+ escaped_data[3 * len] = '\0';
+ strcpy(escaped_data + escaped_len - suffix_len, suffix);
+
+ *escaped_data_p = escaped_data;
+ *escaped_len_p = escaped_len;
+}
+
+static jschar *
+gjs_string_get_chars_description (JSContext *context,
+ const char *data,
+ gsize len,
+ gsize *description_len)
+{
+ char *escaped_data;
+ gsize escaped_len;
+ jschar *u16_string;
+ glong u16_string_length;
+ GError *error;
+
+ if (len > 20) {
+ gjs_string_escape(context,
+ data,
+ 20,
+ "[binary data: ",
+ "...]",
+ &escaped_data,
+ &escaped_len);
+ } else {
+ gjs_string_escape(context,
+ data,
+ len,
+ "",
+ "",
+ &escaped_data,
+ &escaped_len);
+ }
+
+ error = NULL;
+ u16_string = g_utf8_to_utf16(escaped_data,
+ escaped_len,
+ NULL,
+ &u16_string_length,
+ &error);
+ g_free (escaped_data);
+
+ if (!u16_string) {
+ gjs_throw(context,
+ "Failed to convert UTF-8 string to "
+ "JS string: %s",
+ error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ return (jschar *) u16_string;
+}
+
/**
* gjs_string_from_binary_data:
* @context: js context
@@ -351,19 +491,26 @@ gjs_string_from_binary_data(JSContext *context,
jsval *value_p)
{
JSString *s;
+ GByteArray *array;
+ jschar *description;
+ gsize description_len;
JS_BeginRequest(context);
- if (throw_if_binary_strings_broken(context)) {
+ description = gjs_string_get_chars_description (context, data, len, &description_len);
+
+ if (!description) {
JS_EndRequest(context);
return JS_FALSE;
}
- /* store each byte in a 16-bit jschar so all high bytes are 0;
- * we can't put two bytes per jschar because then we'd lose
- * track of the string length if it was an odd number.
- */
- s = JS_NewStringCopyN(context, data, len);
+ array = g_byte_array_new_take(g_memdup(data, len), len);
+
+ s = JS_NewExternalStringWithClosure(context,
+ description,
+ description_len,
+ gjs_context_get_string_finalizer_id(context),
+ array);
if (s == NULL) {
/* gjs_throw() does nothing if exception already set */
gjs_throw(context, "Failed to allocate binary string");
@@ -395,27 +542,52 @@ gjs_string_get_uint16_data(JSContext *context,
guint16 **data_p,
gsize *len_p)
{
- const jschar *js_data;
- JSBool retval = JS_FALSE;
+ JSString *str;
+ GByteArray *array;
JS_BeginRequest(context);
if (!JSVAL_IS_STRING(value)) {
gjs_throw(context,
"Value is not a string, can't return binary data from it");
- goto out;
+ JS_EndRequest(context);
+ return JS_FALSE;
+ }
+
+ str = JSVAL_TO_STRING(value);
+
+ if (!JS_IsExternalString(context, str)) {
+ gjs_throw(context,
+ "Value is not a GJS string, can't return binary data from it");
+ JS_EndRequest(context);
+ return JS_FALSE;
}
- js_data = JS_GetStringCharsAndLength(context, JSVAL_TO_STRING(value), len_p);
- if (js_data == NULL)
- goto out;
+ array = JS_GetExternalStringClosure(context, str);
- *data_p = g_memdup(js_data, sizeof(*js_data)*(*len_p));
+ if (array == NULL) {
+ gjs_throw(context,
+ "Value is not a valid GJS string, can't return binary data from it");
+ JS_EndRequest(context);
+ return JS_FALSE;
+ }
+
+ if (data_p) {
+ gsize i;
+
+ *data_p = g_malloc0(array->len * sizeof (guint16));
+
+ for (i = 0; i < array->len; i++) {
+ data_p[i] = (guint16) array->data[i];
+ }
+ }
+
+ if (len_p)
+ *len_p = array->len;
- retval = JS_TRUE;
-out:
JS_EndRequest(context);
- return retval;
+
+ return JS_TRUE;
}
/**
diff --git a/gjs/jsapi-util.c b/gjs/jsapi-util.c
index 59df435..62617db 100644
--- a/gjs/jsapi-util.c
+++ b/gjs/jsapi-util.c
@@ -53,6 +53,8 @@ typedef struct {
JSContext *default_context;
+ int string_finalizer_id;
+
/* In a thread-safe future we'd keep this in per-thread data */
ContextFrame current_frame;
GSList *context_stack;
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 191dfd8..02fc974 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -402,6 +402,8 @@ gboolean gjs_try_string_to_utf8 (JSContext *context,
const jsval string_val,
char **utf8_string_p,
GError **error);
+void gjs_string_free (JSContext *context,
+ JSString *string);
void gjs_maybe_gc (JSContext *context);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]