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