Commit 46aa0c1e authored by Giovanni Campagna's avatar Giovanni Campagna Committed by Philip Chimento

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.)
parent 9b26559e
......@@ -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,
......@@ -921,6 +941,455 @@ 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 flags 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 = 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());
GClosure *closure = gjs_closure_new_marshaled(cx, object, "boxed");
arg->v_pointer = closure;
g_closure_ref(closure);
g_closure_sink(closure);
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)
......@@ -996,6 +1465,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:
......
......@@ -78,6 +78,22 @@ typedef struct _GjsArgumentCache {
bool is_unsigned : 1;
} number;
/* 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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment