diff options
| author | Ray Strode <rstrode@redhat.com> | 2012-10-09 01:08:15 -0400 |
|---|---|---|
| committer | Ray Strode <rstrode@redhat.com> | 2012-10-09 01:19:36 -0400 |
| commit | cab3509598c8d024eca25ba01b6d4808e3ab0481 (patch) | |
| tree | 2cc5d6d49acb7dccf869ef78ca88ac7b65012179 | |
| parent | aaecab581b2d372b92cbb337cbc788de534a5ed2 (diff) | |
| download | gjs-wip/binary-data.tar.gz | |
wip: start to take a crack at 680730wip/binary-data
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
| -rw-r--r-- | gjs/context.c | 31 | ||||
| -rw-r--r-- | gjs/jsapi-private.h | 6 | ||||
| -rw-r--r-- | gjs/jsapi-util-string.c | 244 | ||||
| -rw-r--r-- | gjs/jsapi-util.c | 2 | ||||
| -rw-r--r-- | gjs/jsapi-util.h | 2 |
5 files changed, 249 insertions, 36 deletions
diff --git a/gjs/context.c b/gjs/context.c index 06b8af25..eaf4b6a1 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 f48affe7..38955e78 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 a660250d..e4a13029 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 59df4356..62617db5 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 191dfd85..02fc9745 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); |
