summaryrefslogtreecommitdiff
path: root/gi/function.h
blob: 799c149038b6ecc67cd9e5a9d3d91fa4ecb8a42f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2008 litl, LLC

#ifndef GI_FUNCTION_H_
#define GI_FUNCTION_H_

#include <config.h>

#include <stdint.h>

#include <unordered_set>
#include <vector>

#include <ffi.h>
#include <girepository.h>
#include <glib-object.h>
#include <glib.h>

#include <js/GCVector.h>
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include <js/Value.h>  // IWYU pragma: keep

#include "gi/closure.h"
#include "gjs/jsapi-util.h"
#include "gjs/macros.h"

namespace JS {
class CallArgs;
}

typedef enum {
    PARAM_NORMAL,
    PARAM_SKIPPED,
    PARAM_ARRAY,
    PARAM_CALLBACK,
    PARAM_UNKNOWN,
} GjsParamType;

using GjsAutoGClosure =
    GjsAutoPointer<GClosure, GClosure, g_closure_unref, g_closure_ref>;

struct GjsCallbackTrampoline : public Gjs::Closure {
    GJS_JSAPI_RETURN_CONVENTION static GjsCallbackTrampoline* create(
        JSContext* cx, JS::HandleFunction function,
        GICallableInfo* callable_info, GIScopeType scope, bool has_scope_object,
        bool is_vfunc);

    ~GjsCallbackTrampoline();

    void* closure() const {
#if GI_CHECK_VERSION(1, 71, 0)
        return g_callable_info_get_closure_native_address(m_info, m_closure);
#else
        return m_closure;
#endif
    }

    void mark_forever();

    static void prepare_shutdown();

 private:
    ffi_closure* create_closure();
    GJS_JSAPI_RETURN_CONVENTION bool initialize();
    GjsCallbackTrampoline(JSContext* cx, JS::HandleFunction function,
                          GICallableInfo* callable_info, GIScopeType scope,
                          bool has_scope_object, bool is_vfunc);

    void callback_closure(GIArgument** args, void* result);
    GJS_JSAPI_RETURN_CONVENTION
    bool callback_closure_inner(JSContext* cx, JS::HandleObject this_object,
                                JS::MutableHandleValue rval, GIArgument** args,
                                GITypeInfo* ret_type, int n_args,
                                int c_args_offset, void* result);
    void warn_about_illegal_js_callback(const char* when, const char* reason);

    static std::vector<GjsAutoGClosure> s_forever_closure_list;

    GjsAutoCallableInfo m_info;
    ffi_closure* m_closure = nullptr;
    std::vector<GjsParamType> m_param_types;
    ffi_cif m_cif;

    GIScopeType m_scope : 3;
    bool m_is_vfunc : 1;
};

// Stack allocation only!
class GjsFunctionCallState {
    GIArgument* m_in_cvalues;
    GIArgument* m_out_cvalues;
    GIArgument* m_inout_original_cvalues;

 public:
    std::unordered_set<GIArgument*> ignore_release;
    JS::RootedObject instance_object;
    JS::RootedValueVector return_values;
    GjsAutoError local_error;
    GICallableInfo* info;
    uint8_t gi_argc = 0;
    unsigned processed_c_args = 0;
    bool failed : 1;
    bool can_throw_gerror : 1;
    bool is_method : 1;

    GjsFunctionCallState(JSContext* cx, GICallableInfo* callable)
        : instance_object(cx),
          return_values(cx),
          info(callable),
          gi_argc(g_callable_info_get_n_args(callable)),
          failed(false),
          can_throw_gerror(g_callable_info_can_throw_gerror(callable)),
          is_method(g_callable_info_is_method(callable)) {
        int size = gi_argc + first_arg_offset();
        m_in_cvalues = new GIArgument[size];
        m_out_cvalues = new GIArgument[size];
        m_inout_original_cvalues = new GIArgument[size];
    }

    ~GjsFunctionCallState() {
        delete[] m_in_cvalues;
        delete[] m_out_cvalues;
        delete[] m_inout_original_cvalues;
    }

    GjsFunctionCallState(const GjsFunctionCallState&) = delete;
    GjsFunctionCallState& operator=(const GjsFunctionCallState&) = delete;

    constexpr int first_arg_offset() const { return is_method ? 2 : 1; }

    // The list always contains the return value, and the arguments
    constexpr GIArgument* instance() {
        return is_method ? &m_in_cvalues[1] : nullptr;
    }

    constexpr GIArgument* return_value() { return &m_out_cvalues[0]; }

    constexpr GIArgument& in_cvalue(int index) const {
        return m_in_cvalues[index + first_arg_offset()];
    }

    constexpr GIArgument& out_cvalue(int index) const {
        return m_out_cvalues[index + first_arg_offset()];
    }

    constexpr GIArgument& inout_original_cvalue(int index) const {
        return m_inout_original_cvalues[index + first_arg_offset()];
    }

    constexpr bool did_throw_gerror() const {
        return can_throw_gerror && local_error;
    }

    constexpr bool call_completed() { return !failed && !did_throw_gerror(); }

    constexpr uint8_t last_processed_index() {
        return first_arg_offset() + processed_c_args;
    }

    [[nodiscard]] GjsAutoChar display_name() {
        GIBaseInfo* container = g_base_info_get_container(info);  // !owned
        if (container) {
            return g_strdup_printf(
                "%s.%s.%s", g_base_info_get_namespace(container),
                g_base_info_get_name(container), g_base_info_get_name(info));
        }
        return g_strdup_printf("%s.%s", g_base_info_get_namespace(info),
                               g_base_info_get_name(info));
    }
};

GJS_JSAPI_RETURN_CONVENTION
JSObject *gjs_define_function(JSContext       *context,
                              JS::HandleObject in_object,
                              GType            gtype,
                              GICallableInfo  *info);

GJS_JSAPI_RETURN_CONVENTION
bool gjs_invoke_constructor_from_c(JSContext* cx, GIFunctionInfo* info,
                                   JS::HandleObject this_obj,
                                   const JS::CallArgs& args,
                                   GIArgument* rvalue);

#endif  // GI_FUNCTION_H_