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 : : // SPDX-FileCopyrightText: 2012 Red Hat, Inc.
5 : :
6 : : #include <config.h>
7 : :
8 : : #include <girepository.h>
9 : :
10 : : #include <js/Class.h>
11 : : #include <js/ErrorReport.h> // for JS_ReportOutOfMemory
12 : : #include <js/Id.h> // for PropertyKey, jsid
13 : : #include <js/TypeDecls.h>
14 : : #include <js/Utility.h> // for UniqueChars
15 : :
16 : : #include "gi/function.h"
17 : : #include "gi/interface.h"
18 : : #include "gi/object.h"
19 : : #include "gi/repo.h"
20 : : #include "gjs/atoms.h"
21 : : #include "gjs/context-private.h"
22 : : #include "gjs/jsapi-util.h"
23 : : #include "gjs/mem-private.h"
24 : :
25 : 143 : InterfacePrototype::InterfacePrototype(GIInterfaceInfo* info, GType gtype)
26 : : : GIWrapperPrototype(info, gtype),
27 : 143 : m_vtable(
28 : 143 : static_cast<GTypeInterface*>(g_type_default_interface_ref(gtype))) {
29 : 143 : GJS_INC_COUNTER(interface);
30 : 143 : }
31 : :
32 : 143 : InterfacePrototype::~InterfacePrototype(void) {
33 [ + - ]: 143 : g_clear_pointer(&m_vtable, g_type_default_interface_unref);
34 : 143 : GJS_DEC_COUNTER(interface);
35 : 143 : }
36 : :
37 : 22 : static bool append_inferface_properties(JSContext* cx,
38 : : JS::MutableHandleIdVector properties,
39 : : GIInterfaceInfo* iface_info) {
40 : 22 : int n_methods = g_interface_info_get_n_methods(iface_info);
41 [ - + ]: 22 : if (!properties.reserve(properties.length() + n_methods)) {
42 : 0 : JS_ReportOutOfMemory(cx);
43 : 0 : return false;
44 : : }
45 : :
46 [ + + ]: 475 : for (int i = 0; i < n_methods; i++) {
47 : : GjsAutoFunctionInfo meth_info =
48 : 453 : g_interface_info_get_method(iface_info, i);
49 : 453 : GIFunctionInfoFlags flags = g_function_info_get_flags(meth_info);
50 : :
51 [ + + ]: 453 : if (flags & GI_FUNCTION_IS_METHOD) {
52 : 408 : const char* name = meth_info.name();
53 : 408 : jsid id = gjs_intern_string_to_id(cx, name);
54 [ - + ]: 408 : if (id.isVoid())
55 : 0 : return false;
56 : 408 : properties.infallibleAppend(id);
57 : : }
58 [ + - ]: 453 : }
59 : :
60 : 22 : return true;
61 : : }
62 : :
63 : 48 : bool InterfacePrototype::new_enumerate_impl(
64 : : JSContext* cx, JS::HandleObject obj [[maybe_unused]],
65 : : JS::MutableHandleIdVector properties,
66 : : bool only_enumerable [[maybe_unused]]) {
67 : : unsigned n_interfaces;
68 : 0 : GjsAutoPointer<GType, void, &g_free> interfaces =
69 : 48 : g_type_interfaces(gtype(), &n_interfaces);
70 : :
71 [ - + ]: 48 : for (unsigned k = 0; k < n_interfaces; k++) {
72 : : GjsAutoInterfaceInfo iface_info =
73 : 0 : g_irepository_find_by_gtype(nullptr, interfaces[k]);
74 : :
75 [ # # ]: 0 : if (!iface_info)
76 : 0 : continue;
77 : :
78 [ # # ]: 0 : if (!append_inferface_properties(cx, properties, iface_info))
79 : 0 : return false;
80 [ # # # ]: 0 : }
81 : :
82 [ + + ]: 48 : if (!info())
83 : 26 : return true;
84 : :
85 : 22 : return append_inferface_properties(cx, properties, info());
86 : 48 : }
87 : :
88 : : // See GIWrapperBase::resolve().
89 : 979 : bool InterfacePrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
90 : : JS::HandleId id, bool* resolved) {
91 : : /* If we have no GIRepository information then this interface was defined
92 : : * from within GJS. In that case, it has no properties that need to be
93 : : * resolved from within C code, as interfaces cannot inherit. */
94 [ + + ]: 979 : if (!info()) {
95 : 21 : *resolved = false;
96 : 21 : return true;
97 : : }
98 : :
99 : 958 : JS::UniqueChars prop_name;
100 [ - + ]: 958 : if (!gjs_get_string_id(context, id, &prop_name))
101 : 0 : return false;
102 [ - + ]: 958 : if (!prop_name) {
103 : 0 : *resolved = false;
104 : 0 : return true; // not resolved, but no error
105 : : }
106 : :
107 : : GjsAutoFunctionInfo method_info =
108 : 958 : g_interface_info_find_method(m_info, prop_name.get());
109 : :
110 [ + + ]: 958 : if (method_info) {
111 [ + - ]: 308 : if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) {
112 [ - + ]: 308 : if (!gjs_define_function(context, obj, m_gtype, method_info))
113 : 0 : return false;
114 : :
115 : 308 : *resolved = true;
116 : : } else {
117 : 0 : *resolved = false;
118 : : }
119 : : } else {
120 : 650 : *resolved = false;
121 : : }
122 : :
123 : 958 : return true;
124 : 958 : }
125 : :
126 : : /*
127 : : * InterfaceBase::has_instance:
128 : : *
129 : : * JSNative implementation of `[Symbol.hasInstance]()`. This method is never
130 : : * called directly, but instead is called indirectly by the JS engine as part of
131 : : * an `instanceof` expression.
132 : : */
133 : 19 : bool InterfaceBase::has_instance(JSContext* cx, unsigned argc, JS::Value* vp) {
134 [ - + ]: 19 : GJS_GET_THIS(cx, argc, vp, args, interface_constructor);
135 : :
136 : 19 : JS::RootedObject interface_proto(cx);
137 : 19 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
138 [ - + ]: 19 : if (!gjs_object_require_property(cx, interface_constructor,
139 : : "interface constructor", atoms.prototype(),
140 : : &interface_proto))
141 : 0 : return false;
142 : :
143 : : InterfaceBase* priv;
144 [ - + ]: 19 : if (!for_js_typecheck(cx, interface_proto, &priv))
145 : 0 : return false;
146 : :
147 : 19 : return priv->to_prototype()->has_instance_impl(cx, args);
148 : 19 : }
149 : :
150 : : // See InterfaceBase::has_instance().
151 : 19 : bool InterfacePrototype::has_instance_impl(JSContext* cx,
152 : : const JS::CallArgs& args) {
153 : : // This method is never called directly, so no need for error messages.
154 : 19 : g_assert(args.length() == 1);
155 : :
156 [ + + ]: 19 : if (!args[0].isObject()) {
157 : 6 : args.rval().setBoolean(false);
158 : 6 : return true;
159 : : }
160 : :
161 : 13 : JS::RootedObject instance(cx, &args[0].toObject());
162 : 13 : bool isinstance = ObjectBase::typecheck(cx, instance, nullptr, m_gtype,
163 : : GjsTypecheckNoThrow());
164 : 13 : args.rval().setBoolean(isinstance);
165 : 13 : return true;
166 : 13 : }
167 : :
168 : : // clang-format off
169 : : const struct JSClassOps InterfaceBase::class_ops = {
170 : : nullptr, // addProperty
171 : : nullptr, // deleteProperty
172 : : nullptr, // enumerate
173 : : &InterfaceBase::new_enumerate,
174 : : &InterfaceBase::resolve,
175 : : nullptr, // mayResolve
176 : : &InterfaceBase::finalize,
177 : : };
178 : :
179 : : const struct JSClass InterfaceBase::klass = {
180 : : "GObject_Interface",
181 : : JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
182 : : &InterfaceBase::class_ops
183 : : };
184 : :
185 : : JSFunctionSpec InterfaceBase::static_methods[] = {
186 : : JS_SYM_FN(hasInstance, &InterfaceBase::has_instance, 1, 0),
187 : : JS_FS_END
188 : : };
189 : : // clang-format on
190 : :
191 : : bool
192 : 0 : gjs_lookup_interface_constructor(JSContext *context,
193 : : GType gtype,
194 : : JS::MutableHandleValue value_p)
195 : : {
196 : : JSObject *constructor;
197 : : GIBaseInfo *interface_info;
198 : :
199 : 0 : interface_info = g_irepository_find_by_gtype(nullptr, gtype);
200 : :
201 [ # # ]: 0 : if (!interface_info) {
202 : 0 : gjs_throw(context, "Cannot expose non introspectable interface %s",
203 : : g_type_name(gtype));
204 : 0 : return false;
205 : : }
206 : :
207 : 0 : g_assert(g_base_info_get_type(interface_info) ==
208 : : GI_INFO_TYPE_INTERFACE);
209 : :
210 : 0 : constructor = gjs_lookup_generic_constructor(context, interface_info);
211 [ # # ]: 0 : if (G_UNLIKELY(!constructor))
212 : 0 : return false;
213 : :
214 : 0 : g_base_info_unref(interface_info);
215 : :
216 : 0 : value_p.setObject(*constructor);
217 : 0 : return true;
218 : : }
|