diff options
-rw-r--r-- | gi/boxed.cpp | 63 | ||||
-rw-r--r-- | gi/boxed.h | 18 | ||||
-rw-r--r-- | installed-tests/js/testGObject.js | 11 |
3 files changed, 79 insertions, 13 deletions
diff --git a/gi/boxed.cpp b/gi/boxed.cpp index 7327c317..16eb6e61 100644 --- a/gi/boxed.cpp +++ b/gi/boxed.cpp @@ -25,6 +25,7 @@ #include <jsapi.h> // for IdVector, JS_AtomizeAndPinJSString #include <mozilla/HashTable.h> +#include "gi/arg-cache.h" #include "gi/arg-inl.h" #include "gi/arg.h" #include "gi/boxed.h" @@ -45,7 +46,8 @@ BoxedInstance::BoxedInstance(JSContext* cx, JS::HandleObject obj) GJS_INC_COUNTER(boxed_instance); } -[[nodiscard]] static bool struct_is_simple(GIStructInfo* info); +[[nodiscard]] static bool struct_is_simple( + GIStructInfo* info, Gjs::DirectAllocationPolicy allocation_policy); // See GIWrapperBase::resolve(). bool BoxedPrototype::resolve_impl(JSContext* cx, JS::HandleObject obj, @@ -366,7 +368,8 @@ bool BoxedInstance::constructor_impl(JSContext* context, JS::HandleObject obj, debug_lifecycle("Boxed pointer created from zero-args constructor"); - } else if (proto->can_allocate_directly()) { + } else if (proto->can_allocate_directly( + Gjs::DirectAllocationPolicy::NO_POINTERS)) { allocate_directly(); } else if (proto->has_default_constructor()) { /* for simplicity, we simply delegate all the work to the actual JS @@ -391,6 +394,8 @@ bool BoxedInstance::constructor_impl(JSContext* context, JS::HandleObject obj, "boxed object discarded"); return true; + } else if (get_prototype()->can_allocate_directly()) { + allocate_directly(); } else { gjs_throw(context, "Unable to construct struct type %s since it has no default " @@ -476,7 +481,8 @@ bool BoxedInstance::get_nested_interface_object( GIBaseInfo* interface_info, JS::MutableHandleValue value) const { int offset; - if (!struct_is_simple ((GIStructInfo *)interface_info)) { + if (!struct_is_simple(reinterpret_cast<GIStructInfo*>(interface_info), + Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS)) { gjs_throw(context, "Reading field %s.%s is not supported", name(), g_base_info_get_name(field_info)); @@ -552,6 +558,31 @@ bool BoxedInstance::field_getter_impl(JSContext* cx, JSObject* obj, return false; } + if (g_type_info_get_tag(type_info) == GI_TYPE_TAG_ARRAY && + g_type_info_get_array_length(type_info) != -1) { + auto length_field_ix = g_type_info_get_array_length(type_info); + GjsAutoFieldInfo length_field_info = + get_field_info(cx, length_field_ix); + if (!length_field_info) { + gjs_throw(cx, "Reading field %s.%s is not supported", name(), + g_base_info_get_name(length_field_info)); + return false; + } + + GIArgument length_arg; + if (!g_field_info_get_field(length_field_info, m_ptr, &length_arg)) { + gjs_throw(cx, "Reading field %s.%s is not supported", name(), + g_base_info_get_name(length_field_info)); + return false; + } + + GjsAutoTypeInfo length_type_info = + g_field_info_get_type(length_field_info); + size_t length = gjs_g_argument_get_array_length( + g_type_info_get_tag(length_type_info), &length_arg); + return gjs_value_from_explicit_array(cx, rval, type_info, &arg, length); + } + return gjs_value_from_g_argument(cx, rval, type_info, &arg, true); } @@ -573,7 +604,8 @@ bool BoxedInstance::set_nested_interface_object(JSContext* context, JS::HandleValue value) { int offset; - if (!struct_is_simple ((GIStructInfo *)interface_info)) { + if (!struct_is_simple(reinterpret_cast<GIStructInfo*>(interface_info), + Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS)) { gjs_throw(context, "Writing field %s.%s is not supported", name(), g_base_info_get_name(field_info)); @@ -749,7 +781,7 @@ const struct JSClass BoxedBase::klass = { // clang-format on [[nodiscard]] static bool type_can_be_allocated_directly( - GITypeInfo* type_info) { + GITypeInfo* type_info, Gjs::DirectAllocationPolicy allocation_policy) { bool is_simple = true; if (g_type_info_is_pointer(type_info)) { @@ -757,9 +789,13 @@ const struct JSClass BoxedBase::klass = { g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) { GjsAutoBaseInfo param_info = g_type_info_get_param_type(type_info, 0); - is_simple = type_can_be_allocated_directly(param_info); + is_simple = + type_can_be_allocated_directly(param_info, allocation_policy); } else if (g_type_info_get_tag(type_info) == GI_TYPE_TAG_VOID) { return true; + } else if (allocation_policy == + Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS) { + return true; } else { is_simple = false; } @@ -771,7 +807,9 @@ const struct JSClass BoxedBase::klass = { switch (g_base_info_get_type(interface)) { case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: - if (!struct_is_simple((GIStructInfo *)interface)) + if (!struct_is_simple( + interface.as<GIStructInfo>(), + Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS)) is_simple = false; break; case GI_INFO_TYPE_UNION: @@ -836,7 +874,8 @@ const struct JSClass BoxedBase::klass = { * type that we know how to assign to. If so, then we can allocate and free * instances without needing a constructor. */ -[[nodiscard]] static bool struct_is_simple(GIStructInfo* info) { +[[nodiscard]] static bool struct_is_simple( + GIStructInfo* info, Gjs::DirectAllocationPolicy allocation_policy) { int n_fields = g_struct_info_get_n_fields(info); bool is_simple = true; int i; @@ -849,7 +888,8 @@ const struct JSClass BoxedBase::klass = { GjsAutoBaseInfo field_info = g_struct_info_get_field(info, i); GjsAutoBaseInfo type_info = g_field_info_get_type(field_info); - is_simple = type_can_be_allocated_directly(type_info); + is_simple = + type_can_be_allocated_directly(type_info, allocation_policy); } return is_simple; @@ -860,7 +900,10 @@ BoxedPrototype::BoxedPrototype(GIStructInfo* info, GType gtype) m_zero_args_constructor(-1), m_default_constructor(-1), m_default_constructor_name(JSID_VOID), - m_can_allocate_directly(struct_is_simple(info)) { + m_can_allocate_directly( + struct_is_simple(info, Gjs::DirectAllocationPolicy::NO_POINTERS)), + m_can_allocate_directly_with_pointers(struct_is_simple( + info, Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS)) { GJS_INC_COUNTER(boxed_prototype); } @@ -37,6 +37,13 @@ namespace js { class SystemAllocPolicy; } +namespace Gjs { +enum class DirectAllocationPolicy { + NO_POINTERS, + ALLOCATE_POINTERS, +}; +}; + /* To conserve memory, we have two different kinds of private data for GBoxed * JS wrappers: BoxedInstance, and BoxedPrototype. Both inherit from BoxedBase * for their common functionality. For more information, see the notes in @@ -90,6 +97,7 @@ class BoxedPrototype : public GIWrapperPrototype<BoxedBase, BoxedPrototype, JS::Heap<jsid> m_default_constructor_name; std::unique_ptr<FieldMap> m_field_map; bool m_can_allocate_directly : 1; + bool m_can_allocate_directly_with_pointers : 1; explicit BoxedPrototype(GIStructInfo* info, GType gtype); ~BoxedPrototype(void); @@ -101,8 +109,14 @@ class BoxedPrototype : public GIWrapperPrototype<BoxedBase, BoxedPrototype, // Accessors public: - [[nodiscard]] bool can_allocate_directly() const { - return m_can_allocate_directly; + [[nodiscard]] bool can_allocate_directly( + Gjs::DirectAllocationPolicy allocation_policy = + Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS) const { + if (allocation_policy == Gjs::DirectAllocationPolicy::NO_POINTERS) { + return m_can_allocate_directly; + } + + return m_can_allocate_directly_with_pointers; } [[nodiscard]] bool has_zero_args_constructor() const { return m_zero_args_constructor >= 0; diff --git a/installed-tests/js/testGObject.js b/installed-tests/js/testGObject.js index 885a93fb..bdc1f6f4 100644 --- a/installed-tests/js/testGObject.js +++ b/installed-tests/js/testGObject.js @@ -20,7 +20,7 @@ describe('GObject overrides', function () { Signals: { test: {}, }, - }, class TestObj extends GObject.Object {}); + }, class TestObj extends GObject.Object { }); it('GObject.set()', function () { const o = new TestObj(); @@ -70,4 +70,13 @@ describe('GObject should', function () { expect(gtype.name).toEqual(type); }); }); + + it('be able to query signals', function () { + const query = GObject.signal_query(1); + + expect(query instanceof GObject.SignalQuery).toBeTruthy(); + expect(query.param_types).not.toBeNull(); + expect(Array.isArray(query.param_types)).toBeTruthy(); + expect(query.signal_id).toBe(1); + }); }); |