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