[gjs/wip/gcampax/70-arg-cache: 5/6] arg-cache: extend to handle interface types too
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/gcampax/70-arg-cache: 5/6] arg-cache: extend to handle interface types too
- Date: Wed, 31 Jan 2018 07:40:02 +0000 (UTC)
commit 84c6666933fca33396fa99fba3c7638cc6975c5e
Author: Giovanni Campagna <gcampagna src gnome org>
Date: Mon Apr 22 18:28:52 2013 +0200
arg-cache: extend to handle interface types too
Handle enums, objects and boxed types through the argument cache
rather than using the generic marshaller.
(Philip Chimento: rebased and fixed coding style.)
gi/arg-cache.cpp | 472 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
gi/arg-cache.h | 26 ++-
2 files changed, 496 insertions(+), 2 deletions(-)
---
diff --git a/gi/arg-cache.cpp b/gi/arg-cache.cpp
index 576719b2..3610e279 100644
--- a/gi/arg-cache.cpp
+++ b/gi/arg-cache.cpp
@@ -23,6 +23,7 @@
#include <config.h>
+#include <inttypes.h>
#include <string.h>
#include <girepository.h>
@@ -30,8 +31,15 @@
#include "arg.h"
#include "arg-cache.h"
+#include "boxed.h"
+#include "foreign.h"
#include "function.h"
+#include "gerror.h"
+#include "gjs/byteArray.h"
#include "gjs/jsapi-wrapper.h"
+#include "object.h"
+#include "union.h"
+#include "value.h"
static bool gjs_arg_cache_build_normal_in_arg(GjsArgumentCache *self,
GITypeTag tag);
@@ -722,6 +730,18 @@ report_primitive_type_mismatch(JSContext *cx,
return false;
}
+static bool
+report_object_primitive_type_mismatch(JSContext *cx,
+ GjsArgumentCache *self,
+ JS::Value value,
+ GType expected)
+{
+ gjs_throw(cx, "Expected an object of type %s for argument '%s' but got type %s",
+ g_type_name(expected), self->arg_name,
+ gjs_get_type_name(value));
+ return false;
+}
+
static bool
report_out_of_range(JSContext *cx,
GjsArgumentCache *self,
@@ -925,6 +945,456 @@ gjs_marshal_string_in_release(JSContext *cx,
return true;
}
+static bool
+gjs_marshal_enum_in_in(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *arg,
+ JS::HandleValue value)
+{
+ int64_t number;
+ if (!JS::ToInt64(cx, value, &number))
+ return false;
+
+ if (number > self->contents.enum_type.enum_max ||
+ number < self->contents.enum_type.enum_min) {
+ gjs_throw(cx, "%" PRId64 " is not a valid value for enum argument %s",
+ number, self->arg_name);
+ return false;
+ }
+
+ if (self->contents.enum_type.enum_max <= G_MAXINT32)
+ arg->v_int = number;
+ else if (self->contents.enum_type.enum_max <= G_MAXUINT32)
+ arg->v_uint = number;
+ else
+ arg->v_int64 = number;
+
+ return true;
+}
+
+static bool
+gjs_marshal_flags_in_in(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *arg,
+ JS::HandleValue value)
+{
+ int64_t number;
+ if (!JS::ToInt64(cx, value, &number))
+ return false;
+
+ if ((uint64_t(number) & self->contents.flags_mask) != uint64_t(number)) {
+ gjs_throw(cx, "%" PRId64 " is not a valid value for enum argument %s",
+ number, self->arg_name);
+ return false;
+ }
+
+ if (self->contents.flags_mask <= G_MAXUINT32)
+ arg->v_uint = number;
+ else
+ arg->v_uint64 = number;
+
+ return true;
+}
+
+static bool
+gjs_marshal_foreign_in_in(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *arg,
+ JS::HandleValue value)
+{
+ GIStructInfo *foreign_info;
+
+ foreign_info = (GIStructInfo*) g_type_info_get_interface(&self->type_info);
+ self->contents.tmp_foreign_info = foreign_info;
+ return gjs_struct_foreign_convert_to_g_argument(cx, value, foreign_info,
+ self->arg_name,
+ GJS_ARGUMENT_ARGUMENT,
+ self->transfer,
+ self->nullable, arg);
+}
+
+static bool
+gjs_marshal_foreign_in_release(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *in_arg,
+ GIArgument *out_arg)
+{
+ bool ok = true;
+
+ if (self->transfer == GI_TRANSFER_NOTHING)
+ ok = gjs_struct_foreign_release_g_argument(cx, self->transfer,
+ self->contents.tmp_foreign_info,
+ in_arg);
+
+ g_base_info_unref(self->contents.tmp_foreign_info);
+ return ok;
+}
+
+static bool
+gjs_marshal_gvalue_in_in(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *arg,
+ JS::HandleValue value)
+{
+ GValue gvalue = G_VALUE_INIT;
+
+ if (!gjs_value_to_g_value(cx, value, &gvalue))
+ return false;
+
+ arg->v_pointer = g_boxed_copy(G_TYPE_VALUE, &gvalue);
+
+ g_value_unset(&gvalue);
+ return true;
+}
+
+static bool
+gjs_marshal_boxed_in_in(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *arg,
+ JS::HandleValue value)
+{
+ if (value.isNull()) {
+ if (!self->nullable)
+ return report_invalid_null(cx, self);
+
+ arg->v_pointer = nullptr;
+ return true;
+ }
+
+ GType gtype = self->contents.object.gtype;
+
+ if (!value.isObject())
+ return report_object_primitive_type_mismatch(cx, self,
+ value, gtype);
+
+ JS::RootedObject object(cx, &value.toObject());
+ if (gtype == G_TYPE_ERROR) {
+ if (!gjs_typecheck_gerror(cx, object, true))
+ return false;
+
+ arg->v_pointer = gjs_gerror_from_error(cx, object);
+ } else {
+ if (!gjs_typecheck_boxed(cx, object, self->contents.object.info,
+ gtype, true))
+ return false;
+
+ arg->v_pointer = gjs_c_struct_from_boxed(cx, object);
+ }
+
+ if (self->transfer != GI_TRANSFER_NOTHING) {
+ g_assert(gtype != G_TYPE_NONE);
+
+ if (gtype == G_TYPE_VARIANT)
+ g_variant_ref(static_cast<GVariant *>(arg->v_pointer));
+ else
+ arg->v_pointer = g_boxed_copy(gtype, arg->v_pointer);
+ }
+
+ return true;
+}
+
+/* Unions include ClutterEvent and GdkEvent, which occur fairly often in an
+ * interactive application, so they're worth a special case in a different
+ * virtual function. */
+static bool
+gjs_marshal_union_in_in(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *arg,
+ JS::HandleValue value)
+{
+ if (value.isNull()) {
+ if (!self->nullable)
+ return report_invalid_null(cx, self);
+
+ arg->v_pointer = nullptr;
+ return true;
+ }
+
+ GType gtype = self->contents.object.gtype;
+ g_assert(gtype != G_TYPE_NONE);
+
+ if (!value.isObject())
+ return report_object_primitive_type_mismatch(cx, self,
+ value, gtype);
+
+ JS::RootedObject object(cx, &value.toObject());
+ if (!gjs_typecheck_union(cx, object, self->contents.object.info,
+ gtype, true))
+ return false;
+
+ arg->v_pointer = gjs_c_union_from_union(cx, object);
+
+ if (self->transfer != GI_TRANSFER_NOTHING)
+ arg->v_pointer = g_boxed_copy(gtype, arg->v_pointer);
+
+ return true;
+}
+
+static bool
+gjs_marshal_gclosure_in_in(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *arg,
+ JS::HandleValue value)
+{
+ if (value.isNull()) {
+ if (!self->nullable)
+ return report_invalid_null(cx, self);
+
+ arg->v_pointer = nullptr;
+ return true;
+ }
+
+ if (!value.isObject())
+ return report_primitive_type_mismatch(cx, self, value, JSTYPE_FUNCTION);
+
+ JS::RootedObject object(cx, &value.toObject());
+ arg->v_pointer = gjs_closure_new_marshaled(cx, object, "boxed");
+ g_closure_ref(static_cast<GClosure *>(arg->v_pointer));
+ g_closure_sink(static_cast<GClosure *>(arg->v_pointer));
+
+ return true;
+}
+
+static bool
+gjs_marshal_gbytes_in_in(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *arg,
+ JS::HandleValue value)
+{
+ if (value.isNull()) {
+ if (!self->nullable)
+ return report_invalid_null(cx, self);
+
+ arg->v_pointer = nullptr;
+ return true;
+ }
+
+ if (!value.isObject())
+ return report_object_primitive_type_mismatch(cx, self,
+ value, G_TYPE_BYTES);
+
+ JS::RootedObject object(cx, &value.toObject());
+ if (gjs_typecheck_bytearray(cx, object, false)) {
+ arg->v_pointer = gjs_byte_array_get_bytes(cx, object);
+ } else {
+ if (!gjs_typecheck_boxed(cx, object, self->contents.object.info,
+ G_TYPE_BYTES, true))
+ return false;
+
+ arg->v_pointer = gjs_c_struct_from_boxed(cx, object);
+
+ /* The bytearray path is taking an extra ref irrespective of transfer
+ * ownership, so we need to do the same here. */
+ g_bytes_ref(static_cast<GBytes *>(arg->v_pointer));
+ }
+
+ return true;
+}
+
+static bool
+gjs_marshal_object_in_in(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *arg,
+ JS::HandleValue value)
+{
+ if (value.isNull()) {
+ if (!self->nullable)
+ return report_invalid_null(cx, self);
+
+ arg->v_pointer = nullptr;
+ return true;
+ }
+
+ GType gtype = self->contents.object.gtype;
+ g_assert(gtype != G_TYPE_NONE);
+
+ if (!value.isObject())
+ return report_object_primitive_type_mismatch(cx, self,
+ value, gtype);
+
+ JS::RootedObject object(cx, &value.toObject());
+ if (!gjs_typecheck_object(cx, object, gtype, true))
+ return false;
+
+ arg->v_pointer = gjs_g_object_from_object(cx, object);
+
+ if (self->transfer != GI_TRANSFER_NOTHING)
+ g_object_ref(arg->v_pointer);
+
+ return true;
+}
+
+static bool
+gjs_marshal_boxed_in_release(JSContext *cx,
+ GjsArgumentCache *self,
+ GjsFunctionCallState *state,
+ GIArgument *in_arg,
+ GIArgument *out_arg)
+{
+ GType gtype = self->contents.object.gtype;
+ g_assert(g_type_is_a(gtype, G_TYPE_BOXED));
+
+ g_boxed_free(gtype, in_arg->v_pointer);
+ return true;
+}
+
+static void
+gjs_arg_cache_build_enum_bounds(GjsArgumentCache *self,
+ GIEnumInfo *enum_info)
+{
+ int64_t min = G_MAXINT64;
+ int64_t max = G_MININT64;
+ int n = g_enum_info_get_n_values(enum_info);
+ for (int i = 0; i < n; i++) {
+ GIValueInfo *value_info = g_enum_info_get_value(enum_info, i);
+ int64_t value = g_value_info_get_value(value_info);
+
+ if (value > max)
+ max = value;
+ if (value < min)
+ min = value;
+
+ g_base_info_unref(value_info);
+ }
+
+ self->contents.enum_type.enum_min = min;
+ self->contents.enum_type.enum_max = max;
+}
+
+static void
+gjs_arg_cache_build_flags_mask(GjsArgumentCache *self,
+ GIEnumInfo *enum_info)
+{
+ uint64_t mask = 0;
+ int n = g_enum_info_get_n_values(enum_info);
+ for (int i = 0; i < n; i++) {
+ GIValueInfo *value_info = g_enum_info_get_value(enum_info, i);
+ uint64_t value = uint64_t(g_value_info_get_value(value_info));
+ mask |= value;
+
+ g_base_info_unref(value_info);
+ }
+
+ self->contents.flags_mask = mask;
+}
+
+static bool
+gjs_arg_cache_build_interface_in_arg(GjsArgumentCache *self)
+{
+ GIBaseInfo *interface_info = g_type_info_get_interface(&self->type_info);
+ GIInfoType interface_type = g_base_info_get_type(interface_info);
+ bool ok = true;
+
+ /* We do some transfer magic later, so let's ensure we don't mess up.
+ * Should not happen in practice. */
+ if (G_UNLIKELY (self->transfer == GI_TRANSFER_CONTAINER))
+ return false;
+
+ switch (interface_type) {
+ case GI_INFO_TYPE_ENUM:
+ gjs_arg_cache_build_enum_bounds(self, interface_info);
+ self->marshal_in = gjs_marshal_enum_in_in;
+ break;
+
+ case GI_INFO_TYPE_FLAGS:
+ gjs_arg_cache_build_flags_mask(self, interface_info);
+ self->marshal_in = gjs_marshal_flags_in_in;
+ break;
+
+ case GI_INFO_TYPE_STRUCT:
+ if (g_struct_info_is_foreign(interface_info)) {
+ self->marshal_in = gjs_marshal_foreign_in_in;
+ self->release = gjs_marshal_foreign_in_release;
+ break;
+ } else {
+ /* fall through */
+ }
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_OBJECT:
+ case GI_INFO_TYPE_INTERFACE:
+ case GI_INFO_TYPE_UNION:
+ {
+ GType gtype = g_registered_type_info_get_g_type(interface_info);
+ self->contents.object.gtype = gtype;
+ self->contents.object.info = interface_info;
+ g_base_info_ref(self->contents.object.info);
+
+ /* Transfer handling is a bit complex here, because
+ some of our _in marshallers know not to copy stuff if we don't
+ need to.
+ */
+
+ if (gtype == G_TYPE_VALUE) {
+ self->marshal_in = gjs_marshal_gvalue_in_in;
+ if (self->transfer == GI_TRANSFER_NOTHING)
+ self->release = gjs_marshal_boxed_in_release;
+ } else if (gtype == G_TYPE_CLOSURE) {
+ self->marshal_in = gjs_marshal_gclosure_in_in;
+ if (self->transfer == GI_TRANSFER_NOTHING)
+ self->release = gjs_marshal_boxed_in_release;
+ } else if (gtype == G_TYPE_BYTES) {
+ self->marshal_in = gjs_marshal_gbytes_in_in;
+ if (self->transfer == GI_TRANSFER_NOTHING)
+ self->release = gjs_marshal_boxed_in_release;
+ } else if (g_type_is_a(gtype, G_TYPE_OBJECT) ||
+ g_type_is_a(gtype, G_TYPE_INTERFACE)) {
+ self->marshal_in = gjs_marshal_object_in_in;
+ /* This is a smart marshaller, no release needed */
+ } else if (interface_type == GI_INFO_TYPE_UNION) {
+ if (gtype != G_TYPE_NONE) {
+ self->marshal_in = gjs_marshal_union_in_in;
+ /* This is a smart marshaller, no release needed */
+ } else {
+ /* Can't handle unions without a GType */
+ ok = false;
+ }
+ } else { /* generic boxed type */
+ if (gtype == G_TYPE_NONE &&
+ self->transfer != GI_TRANSFER_NOTHING) {
+ /* Can't transfer ownership of a structure type not registered
+ * as a boxed */
+ ok = false;
+ } else {
+ self->marshal_in = gjs_marshal_boxed_in_in;
+ /* This is a smart marshaller, no release needed */
+ }
+ }
+ }
+ break;
+
+ case GI_INFO_TYPE_INVALID:
+ case GI_INFO_TYPE_FUNCTION:
+ case GI_INFO_TYPE_CALLBACK:
+ case GI_INFO_TYPE_CONSTANT:
+ case GI_INFO_TYPE_INVALID_0:
+ case GI_INFO_TYPE_VALUE:
+ case GI_INFO_TYPE_SIGNAL:
+ case GI_INFO_TYPE_VFUNC:
+ case GI_INFO_TYPE_PROPERTY:
+ case GI_INFO_TYPE_FIELD:
+ case GI_INFO_TYPE_ARG:
+ case GI_INFO_TYPE_TYPE:
+ case GI_INFO_TYPE_UNRESOLVED:
+ default:
+ /* Don't know how to handle this interface type (should not happen in
+ * practice, for typelibs emitted by g-ir-compiler) */
+ ok = false;
+ }
+
+ g_base_info_unref(interface_info);
+ return ok;
+}
+
static bool
gjs_arg_cache_build_normal_in_arg(GjsArgumentCache *self,
GITypeTag tag)
@@ -1000,6 +1470,8 @@ gjs_arg_cache_build_normal_in_arg(GjsArgumentCache *self,
break;
case GI_TYPE_TAG_INTERFACE:
+ return gjs_arg_cache_build_interface_in_arg(self);
+
case GI_TYPE_TAG_ARRAY:
case GI_TYPE_TAG_GLIST:
case GI_TYPE_TAG_GSLIST:
diff --git a/gi/arg-cache.h b/gi/arg-cache.h
index f35a7676..5ce1e8a3 100644
--- a/gi/arg-cache.h
+++ b/gi/arg-cache.h
@@ -61,22 +61,44 @@ typedef struct _GjsArgumentCache {
bool is_return : 1;
union {
+ /* for explicit array only */
struct {
int length_pos;
GITypeTag length_tag;
} array;
+
struct {
GIScopeType scope;
int closure_pos;
int destroy_pos;
} callback;
+
struct {
GITypeTag number_tag;
bool is_unsigned : 1;
} number;
- size_t caller_allocates_size;
+
+ /* boxed / union / GObject */
+ struct {
+ GType gtype;
+ GIBaseInfo *info;
+ } object;
+
+ /* foreign structures */
+ GIStructInfo *tmp_foreign_info;
+
+ /* enum / flags */
+ struct {
+ int64_t enum_min;
+ int64_t enum_max;
+ } enum_type;
+ uint64_t flags_mask;
+
+ /* string / filename */
bool string_is_filename : 1;
- int dummy;
+
+ /* out caller allocates (FIXME: should be in object) */
+ size_t caller_allocates_size;
} contents;
} GjsArgumentCache;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]