gjs r116 - in trunk: gi test/js
- From: otaylor svn gnome org
- To: svn-commits-list gnome org
- Subject: gjs r116 - in trunk: gi test/js
- Date: Wed, 19 Nov 2008 22:31:00 +0000 (UTC)
Author: otaylor
Date: Wed Nov 19 22:30:59 2008
New Revision: 116
URL: http://svn.gnome.org/viewvc/gjs?rev=116&view=rev
Log:
Allow "copy construction" of boxed types
gi/boxed.c: Allow a single argument to be passed to the constructor,
which can be:
- Another object of the same type (copied with g_boxed_copy(),
or, as proofing against future support for non-boxed structures,
memcpy)
- A hash table of properties to set as fields of the object.
test/js/testEverythingEncapsulated.js: Test copy construction
https://bugzilla.gnome.org/show_bug.cgi?id=560808
Modified:
trunk/gi/boxed.c
trunk/test/js/testEverythingEncapsulated.js
Modified: trunk/gi/boxed.c
==============================================================================
--- trunk/gi/boxed.c (original)
+++ trunk/gi/boxed.c Wed Nov 19 22:30:59 2008
@@ -54,6 +54,11 @@
static gboolean struct_is_simple(GIStructInfo *info);
+static JSBool boxed_set_field_from_value(JSContext *context,
+ Boxed *priv,
+ GIFieldInfo *field_info,
+ jsval value);
+
static BoxedConstructInfo unthreadsafe_template_for_constructor = { NULL, NULL, JSVAL_NULL };
static struct JSClass gjs_boxed_class;
@@ -152,6 +157,32 @@
return JS_TRUE;
}
+/* Check to see if jsval passed in is another Boxed object of the same,
+ * and if so, retrieves the Boxed private structure for it.
+ */
+static JSBool
+boxed_get_copy_source(JSContext *context,
+ Boxed *priv,
+ jsval value,
+ Boxed **source_priv_out)
+{
+ Boxed *source_priv;
+
+ if (!JSVAL_IS_OBJECT(value))
+ return JS_FALSE;
+
+ if (!priv_from_js_with_typecheck(context, JSVAL_TO_OBJECT(value), &source_priv))
+ return JS_FALSE;
+
+ if (strcmp(g_base_info_get_name((GIBaseInfo*) priv->info),
+ g_base_info_get_name((GIBaseInfo*) source_priv->info)) != 0)
+ return JS_FALSE;
+
+ *source_priv_out = source_priv;
+
+ return JS_TRUE;
+}
+
static JSBool
boxed_new(JSContext *context,
JSObject *obj, /* "this" for constructor */
@@ -222,6 +253,140 @@
return JS_FALSE;
}
+/* When initializing a boxed object from a hash of properties, we don't want
+ * to do n O(n) lookups, so put temporarily put the fields into a hash table
+ * for fast lookup. We could also do this ahead of time and store it on proto->priv.
+ */
+static GHashTable *
+get_field_map(GIStructInfo *struct_info)
+{
+ GHashTable *result;
+ int n_fields;
+ int i;
+
+ result = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)g_base_info_unref);
+ n_fields = g_struct_info_get_n_fields(struct_info);
+
+ for (i = 0; i < n_fields; i++) {
+ GIFieldInfo *field_info = g_struct_info_get_field(struct_info, i);
+ g_hash_table_insert(result, (char *)g_base_info_get_name((GIBaseInfo *)field_info), field_info);
+ }
+
+ return result;
+}
+
+/* Initialize a newly created Boxed from an object that is a "hash" of
+ * properties to set as fieds of the object. We don't require that every field
+ * of the object be set.
+ */
+static JSBool
+boxed_init_from_props(JSContext *context,
+ JSObject *obj,
+ Boxed *priv,
+ jsval props_value)
+{
+ JSObject *props;
+ JSObject *iter;
+ jsid prop_id;
+ GHashTable *field_map;
+ gboolean success;
+
+ success = FALSE;
+
+ if (!JSVAL_IS_OBJECT(props_value)) {
+ gjs_throw(context, "argument should be a hash with fields to set");
+ return JS_FALSE;
+ }
+
+ props = JSVAL_TO_OBJECT(props_value);
+
+ iter = JS_NewPropertyIterator(context, props);
+ if (iter == NULL) {
+ gjs_throw(context, "Failed to create property iterator for fields hash");
+ return JS_FALSE;
+ }
+
+ field_map = get_field_map(priv->info);
+
+ prop_id = JSVAL_VOID;
+ if (!JS_NextProperty(context, iter, &prop_id))
+ goto out;
+
+ while (prop_id != JSVAL_VOID) {
+ GIFieldInfo *field_info;
+ jsval nameval;
+ const char *name;
+ jsval value;
+
+ if (!JS_IdToValue(context, prop_id, &nameval))
+ goto out;
+
+ if (!gjs_get_string_id(nameval, &name))
+ goto out;
+
+ field_info = g_hash_table_lookup(field_map, name);
+ if (field_info == NULL) {
+ gjs_throw(context, "No field %s on boxed type %s",
+ name, g_base_info_get_name((GIBaseInfo *)priv->info));
+ goto out;
+ }
+
+ if (!gjs_object_require_property(context, props, name, &value))
+ goto out;
+
+ if (!boxed_set_field_from_value(context, priv, field_info, value)) {
+ goto out;
+ }
+
+ prop_id = JSVAL_VOID;
+ if (!JS_NextProperty(context, iter, &prop_id))
+ goto out;
+ }
+
+ success = TRUE;
+
+out:
+ g_hash_table_destroy(field_map);
+
+ return success;
+}
+
+/* Do any initialization of a newly constructed object from the arguments passed
+ * in from Javascript.
+ */
+static JSBool
+boxed_init(JSContext *context,
+ JSObject *obj, /* "this" for constructor */
+ Boxed *priv,
+ uintN argc,
+ jsval *argv)
+{
+ if (argc == 0)
+ return JS_TRUE;
+
+ if (argc == 1) {
+ Boxed *source_priv;
+
+ /* Short-cut to memcpy when possible */
+ if (priv->can_allocate_directly &&
+ boxed_get_copy_source (context, priv, argv[0], &source_priv)) {
+
+ memcpy(priv->gboxed, source_priv->gboxed,
+ g_struct_info_get_size (priv->info));
+
+ return JS_TRUE;
+ }
+
+ return boxed_init_from_props(context, obj, priv, argv[0]);
+ }
+
+ gjs_throw(context, "Constructor with multiple arguments not supported for %s",
+ g_base_info_get_name((GIBaseInfo *)priv->info));
+
+ return JS_FALSE;
+}
+
/* If we set JSCLASS_CONSTRUCT_PROTOTYPE flag, then this is called on
* the prototype in addition to on each instance. When called on the
* prototype, "obj" is the prototype, and "retval" is the prototype
@@ -302,8 +467,25 @@
unthreadsafe_template_for_constructor.info = NULL;
if (unthreadsafe_template_for_constructor.gboxed == NULL) {
+ Boxed *source_priv;
+
+ /* Short-circuit copy-construction in the case where we can use g_boxed_copy */
+ if (argc == 1 &&
+ boxed_get_copy_source(context, priv, argv[0], &source_priv)) {
+
+ GType gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) priv->info);
+ if (gtype != G_TYPE_NONE) {
+ priv->gboxed = g_boxed_copy(gtype, source_priv->gboxed);
+ return JS_TRUE;
+ }
+ }
+
if (!boxed_new(context, obj, priv))
return JS_FALSE;
+
+ if (!boxed_init(context, obj, priv, argc, argv))
+ return JS_FALSE;
+
} else if (!JSVAL_IS_NULL(unthreadsafe_template_for_constructor.parent_jsval)) {
/* A structure nested inside a parent object; doens't have an independent allocation */
@@ -496,36 +678,19 @@
}
static JSBool
-boxed_field_setter (JSContext *context,
- JSObject *obj,
- jsval id,
- jsval *value)
+boxed_set_field_from_value(JSContext *context,
+ Boxed *priv,
+ GIFieldInfo *field_info,
+ jsval value)
{
- Boxed *priv;
- GIFieldInfo *field_info;
GITypeInfo *type_info;
GArgument arg;
gboolean success = FALSE;
gboolean need_release = FALSE;
- priv = priv_from_js(context, obj);
- if (!priv)
- return JS_FALSE;
-
- field_info = get_field_info(context, priv, id);
- if (!field_info)
- return JS_FALSE;
-
type_info = g_field_info_get_type (field_info);
- if (priv->gboxed == NULL) { /* direct access to proto field */
- gjs_throw(context, "Can't set field %s.%s on prototype",
- g_base_info_get_name ((GIBaseInfo *)priv->info),
- g_base_info_get_name ((GIBaseInfo *)field_info));
- goto out;
- }
-
- if (!gjs_value_to_g_argument(context, *value,
+ if (!gjs_value_to_g_argument(context, value,
type_info,
g_base_info_get_name ((GIBaseInfo *)field_info),
GJS_ARGUMENT_FIELD,
@@ -549,13 +714,45 @@
type_info,
&arg);
- g_base_info_unref ((GIBaseInfo *)field_info);
g_base_info_unref ((GIBaseInfo *)type_info);
return success;
}
static JSBool
+boxed_field_setter (JSContext *context,
+ JSObject *obj,
+ jsval id,
+ jsval *value)
+{
+ Boxed *priv;
+ GIFieldInfo *field_info;
+ gboolean success = FALSE;
+
+ priv = priv_from_js(context, obj);
+ if (!priv)
+ return JS_FALSE;
+
+ field_info = get_field_info(context, priv, id);
+ if (!field_info)
+ return JS_FALSE;
+
+ if (priv->gboxed == NULL) { /* direct access to proto field */
+ gjs_throw(context, "Can't set field %s.%s on prototype",
+ g_base_info_get_name ((GIBaseInfo *)priv->info),
+ g_base_info_get_name ((GIBaseInfo *)field_info));
+ goto out;
+ }
+
+ success = boxed_set_field_from_value (context, priv, field_info, *value);
+
+out:
+ g_base_info_unref ((GIBaseInfo *)field_info);
+
+ return success;
+}
+
+static JSBool
define_boxed_class_fields (JSContext *context,
Boxed *priv,
JSObject *proto)
@@ -861,8 +1058,8 @@
* Math - rarely correct)
*/
boxed_constructor,
- /* number of constructor args */
- 0,
+ /* number of constructor args (less can be passed) */
+ 1,
/* props of prototype */
&gjs_boxed_proto_props[0],
/* funcs of prototype */
Modified: trunk/test/js/testEverythingEncapsulated.js
==============================================================================
--- trunk/test/js/testEverythingEncapsulated.js (original)
+++ trunk/test/js/testEverythingEncapsulated.js Wed Nov 19 22:30:59 2008
@@ -10,9 +10,33 @@
assertEquals(42.5, simple_boxed.some_double);
}
+function testBoxedCopyConstructor()
+{
+ // "Copy" an object from a hash of field values
+ let simple_boxed = new Everything.TestSimpleBoxedA({ some_int: 42,
+ some_int8: 43,
+ some_double: 42.5 });
+
+ assertEquals(42, simple_boxed.some_int);
+ assertEquals(43, simple_boxed.some_int8);
+ assertEquals(42.5, simple_boxed.some_double);
+
+ // Make sure we catch bad field names
+ assertRaises(function() {
+ let t = new Everything.TestSimpleBoxedA({ junk: 42 });
+ });
+
+ // Copy an object from another object of the same type, shortcuts to the boxed copy
+ let copy = new Everything.TestSimpleBoxedA(simple_boxed);
+
+ assertEquals(42, copy.some_int);
+ assertEquals(43, copy.some_int8);
+ assertEquals(42.5, copy.some_double);
+ }
+
function testNestedSimpleBoxed() {
let simple_boxed = new Everything.TestSimpleBoxedB();
- simple_boxed.some_int8 = 42
+ simple_boxed.some_int8 = 42;
simple_boxed.nested_a.some_int = 43;
assertEquals(42, simple_boxed.some_int8);
assertEquals(43, simple_boxed.nested_a.some_int);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]