Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2008 litl, LLC
4 : :
5 : : #include <config.h>
6 : :
7 : : #include <girepository.h>
8 : :
9 : : #include <js/CallArgs.h>
10 : : #include <js/Class.h>
11 : : #include <js/RootingAPI.h>
12 : : #include <js/TypeDecls.h>
13 : : #include <js/Utility.h> // for UniqueChars
14 : : #include <js/Warnings.h>
15 : : #include <mozilla/Maybe.h>
16 : :
17 : : #include "gi/arg-inl.h"
18 : : #include "gi/function.h"
19 : : #include "gi/info.h"
20 : : #include "gi/repo.h"
21 : : #include "gi/union.h"
22 : : #include "gjs/jsapi-util.h"
23 : : #include "gjs/macros.h"
24 : : #include "gjs/mem-private.h"
25 : : #include "util/log.h"
26 : :
27 : : using mozilla::Maybe;
28 : :
29 : 5 : UnionPrototype::UnionPrototype(const GI::UnionInfo info, GType gtype)
30 : 5 : : GIWrapperPrototype(info, gtype) {
31 : 5 : GJS_INC_COUNTER(union_prototype);
32 : 5 : }
33 : :
34 : 5 : UnionPrototype::~UnionPrototype(void) { GJS_DEC_COUNTER(union_prototype); }
35 : :
36 : 10 : UnionInstance::UnionInstance(UnionPrototype* prototype, JS::HandleObject obj)
37 : 10 : : GIWrapperInstance(prototype, obj) {
38 : 10 : GJS_INC_COUNTER(union_instance);
39 : 10 : }
40 : :
41 : 10 : UnionInstance::~UnionInstance(void) {
42 [ + - ]: 10 : if (m_ptr) {
43 : 10 : g_boxed_free(gtype(), m_ptr);
44 : 10 : m_ptr = nullptr;
45 : : }
46 : 10 : GJS_DEC_COUNTER(union_instance);
47 : 10 : }
48 : :
49 : : // See GIWrapperBase::resolve().
50 : 9 : bool UnionPrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
51 : : JS::HandleId id, bool* resolved) {
52 : 9 : JS::UniqueChars prop_name;
53 [ - + ]: 9 : if (!gjs_get_string_id(context, id, &prop_name))
54 : 0 : return false;
55 [ - + ]: 9 : if (!prop_name) {
56 : 0 : *resolved = false;
57 : 0 : return true; // not resolved, but no error
58 : : }
59 : :
60 : : // Look for methods and other class properties
61 : 9 : Maybe<GI::AutoFunctionInfo> method_info{info().method(prop_name.get())};
62 : :
63 [ + + ]: 9 : if (method_info) {
64 : 3 : method_info->log_usage();
65 [ + - ]: 3 : if (method_info->is_method()) {
66 : 6 : gjs_debug(GJS_DEBUG_GBOXED,
67 : : "Defining method %s in prototype for %s",
68 : 9 : method_info->name(), format_name().c_str());
69 : :
70 : : /* obj is union proto */
71 [ - + ]: 3 : if (!gjs_define_function(context, obj, gtype(), method_info.ref()))
72 : 0 : return false;
73 : :
74 : 3 : *resolved = true; /* we defined the prop in object_proto */
75 : : } else {
76 : 0 : *resolved = false;
77 : : }
78 : : } else {
79 : 6 : *resolved = false;
80 : : }
81 : :
82 : 9 : return true;
83 : 9 : }
84 : :
85 : : GJS_JSAPI_RETURN_CONVENTION
86 : 1 : static void* union_new(JSContext* context, JS::HandleObject this_obj,
87 : : const JS::CallArgs& args, const GI::UnionInfo info) {
88 : : /* Find a zero-args constructor and call it */
89 [ + - ]: 1 : for (GI::AutoFunctionInfo func_info : info.methods()) {
90 [ + - + - : 1 : if (func_info.is_constructor() && func_info.n_args() == 0) {
+ - ]
91 : : GIArgument rval;
92 [ - + ]: 1 : if (!gjs_invoke_constructor_from_c(context, func_info, this_obj,
93 : : args, &rval))
94 : 0 : return nullptr;
95 : :
96 [ - + ]: 1 : if (!gjs_arg_get<void*>(&rval)) {
97 : 0 : gjs_throw(context,
98 : : "Unable to construct union type %s as its"
99 : : "constructor function returned null",
100 : : info.name());
101 : 0 : return nullptr;
102 : : }
103 : :
104 : 1 : return gjs_arg_get<void*>(&rval);
105 : : }
106 [ - + ]: 1 : }
107 : :
108 : 0 : gjs_throw(context,
109 : : "Unable to construct union type %s since it has no zero-args "
110 : : "<constructor>, can only wrap an existing one",
111 : : info.name());
112 : :
113 : 0 : return nullptr;
114 : : }
115 : :
116 : : // See GIWrapperBase::constructor().
117 : 1 : bool UnionInstance::constructor_impl(JSContext* context,
118 : : JS::HandleObject object,
119 : : const JS::CallArgs& args) {
120 [ - + - + ]: 1 : if (args.length() > 0 &&
121 [ # # ]: 0 : !JS::WarnUTF8(context, "Arguments to constructor of %s ignored",
122 : : name()))
123 : 0 : return false;
124 : :
125 : 1 : m_ptr = union_new(context, object, args, info());
126 : 1 : return !!m_ptr;
127 : : }
128 : :
129 : : // clang-format off
130 : : const struct JSClassOps UnionBase::class_ops = {
131 : : nullptr, // addProperty
132 : : nullptr, // deleteProperty
133 : : nullptr, // enumerate
134 : : nullptr, // newEnumerate
135 : : &UnionBase::resolve,
136 : : nullptr, // mayResolve
137 : : &UnionBase::finalize,
138 : : };
139 : :
140 : : const struct JSClass UnionBase::klass = {
141 : : "GObject_Union",
142 : : JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
143 : : &UnionBase::class_ops
144 : : };
145 : : // clang-format on
146 : :
147 : 5 : bool UnionPrototype::define_class(JSContext* context,
148 : : JS::HandleObject in_object,
149 : : const GI::UnionInfo info) {
150 : 5 : JS::RootedObject prototype(context), constructor(context);
151 : :
152 : : /* For certain unions, we may be able to relax this in the future by
153 : : * directly allocating union memory, as we do for structures in boxed.c
154 : : */
155 : 5 : GType gtype = info.gtype();
156 [ - + ]: 5 : if (gtype == G_TYPE_NONE) {
157 : 0 : gjs_throw(context, "Unions must currently be registered as boxed types");
158 : 0 : return false;
159 : : }
160 : :
161 : 5 : return !!UnionPrototype::create_class(context, in_object, info, gtype,
162 : 5 : &constructor, &prototype);
163 : 5 : }
164 : :
165 : 9 : JSObject* UnionInstance::new_for_c_union(JSContext* context,
166 : : const GI::UnionInfo info,
167 : : void* gboxed) {
168 [ - + ]: 9 : if (!gboxed)
169 : 0 : return nullptr;
170 : :
171 : : /* For certain unions, we may be able to relax this in the future by
172 : : * directly allocating union memory, as we do for structures in boxed.c
173 : : */
174 [ - + ]: 9 : if (info.gtype() == G_TYPE_NONE) {
175 : 0 : gjs_throw(context, "Unions must currently be registered as boxed types");
176 : 0 : return nullptr;
177 : : }
178 : :
179 : : gjs_debug_marshal(GJS_DEBUG_GBOXED, "Wrapping union %s %p with JSObject",
180 : : info.name(), gboxed);
181 : :
182 : : JS::RootedObject obj(context,
183 : 9 : gjs_new_object_with_generic_prototype(context, info));
184 [ - + ]: 9 : if (!obj)
185 : 0 : return nullptr;
186 : :
187 : 9 : UnionInstance* priv = UnionInstance::new_for_js_object(context, obj);
188 : 9 : priv->copy_union(gboxed);
189 : :
190 : 9 : return obj;
191 : 9 : }
192 : :
193 : 0 : void* UnionInstance::copy_ptr(JSContext* cx, GType gtype, void* ptr) {
194 [ # # # # : 0 : if (g_type_is_a(gtype, G_TYPE_BOXED))
# # ]
195 : 0 : return g_boxed_copy(gtype, ptr);
196 : :
197 : 0 : gjs_throw(cx,
198 : : "Can't transfer ownership of a union type not registered as "
199 : : "boxed");
200 : 0 : return nullptr;
201 : : }
|