summaryrefslogtreecommitdiff
path: root/gi/wrapperutils.cpp
blob: a84c55809ce823ecb768fc8c01c92b29bafd231c (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
/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2012 Red Hat, Inc.

#include <config.h>

#include <sstream>

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

#include <js/TypeDecls.h>

#include "gi/function.h"
#include "gi/wrapperutils.h"
#include "gjs/jsapi-util.h"

/* Default spidermonkey toString is worthless.  Replace it
 * with something that gives us both the introspection name
 * and a memory address.
 */
bool gjs_wrapper_to_string_func(JSContext* context, JSObject* this_obj,
                                const char* objtype, GIBaseInfo* info,
                                GType gtype, const void* native_address,
                                JS::MutableHandleValue rval) {
    std::ostringstream out;
    out << '[' << objtype;
    if (!native_address)
        out << " prototype of";
    else
        out << " instance wrapper";

    if (info) {
        out << " GIName:" << g_base_info_get_namespace(info) << "."
            << g_base_info_get_name(info);
    } else {
        out << " GType:" << g_type_name(gtype);
    }

    out << " jsobj@" << this_obj;
    if (native_address)
        out << " native@" << native_address;

    out << ']';

    return gjs_string_from_utf8(context, out.str().c_str(), rval);
}

bool gjs_wrapper_throw_nonexistent_field(JSContext* cx, GType gtype,
                                         const char* field_name) {
    gjs_throw(cx, "No property %s on %s", field_name, g_type_name(gtype));
    return false;
}

bool gjs_wrapper_throw_readonly_field(JSContext* cx, GType gtype,
                                      const char* field_name) {
    gjs_throw(cx, "Property %s.%s is not writable", g_type_name(gtype),
              field_name);
    return false;
}

// These policies work around having separate g_foo_info_get_n_methods() and
// g_foo_info_get_method() functions for different GIInfoTypes. It's not
// possible to use GIFooInfo* as the template parameter, because the GIFooInfo
// structs are all typedefs of GIBaseInfo. It's also not possible to use the
// GIInfoType enum value as the template parameter, because GI_INFO_TYPE_BOXED
// could be either a GIStructInfo or GIUnionInfo.
template <typename InfoT>
static inline GIStructInfo* no_type_struct(InfoT*) {
    return nullptr;
}

template <InfoType::Tag TAG, typename InfoT = void,
          int (*NMethods)(InfoT*) = nullptr,
          GIFunctionInfo* (*Method)(InfoT*, int) = nullptr,
          GIStructInfo* (*TypeStruct)(InfoT*) = &no_type_struct<InfoT>>
struct InfoMethodsPolicy {
    static constexpr decltype(NMethods) n_methods = NMethods;
    static constexpr decltype(Method) method = Method;
    static constexpr decltype(TypeStruct) type_struct = TypeStruct;
};

template <>
struct InfoMethodsPolicy<InfoType::Enum>
    : InfoMethodsPolicy<InfoType::Enum, GIEnumInfo, &g_enum_info_get_n_methods,
                        &g_enum_info_get_method> {};
template <>
struct InfoMethodsPolicy<InfoType::Interface>
    : InfoMethodsPolicy<
          InfoType::Interface, GIInterfaceInfo, &g_interface_info_get_n_methods,
          &g_interface_info_get_method, &g_interface_info_get_iface_struct> {};
template <>
struct InfoMethodsPolicy<InfoType::Object>
    : InfoMethodsPolicy<InfoType::Object, GIObjectInfo,
                        &g_object_info_get_n_methods, &g_object_info_get_method,
                        &g_object_info_get_class_struct> {};
template <>
struct InfoMethodsPolicy<InfoType::Struct>
    : InfoMethodsPolicy<InfoType::Struct, GIStructInfo,
                        &g_struct_info_get_n_methods,
                        &g_struct_info_get_method> {};
template <>
struct InfoMethodsPolicy<InfoType::Union>
    : InfoMethodsPolicy<InfoType::Union, GIUnionInfo,
                        &g_union_info_get_n_methods, &g_union_info_get_method> {
};

template <InfoType::Tag TAG>
bool gjs_define_static_methods(JSContext* cx, JS::HandleObject constructor,
                               GType gtype, GIBaseInfo* info) {
    int n_methods = InfoMethodsPolicy<TAG>::n_methods(info);

    for (int ix = 0; ix < n_methods; ix++) {
        GjsAutoFunctionInfo meth_info =
            InfoMethodsPolicy<TAG>::method(info, ix);
        GIFunctionInfoFlags flags = g_function_info_get_flags(meth_info);

        // Anything that isn't a method we put on the constructor. This
        // includes <constructor> introspection methods, as well as static
        // methods. We may want to change this to use
        // GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the like
        // in the future.
        if (!(flags & GI_FUNCTION_IS_METHOD)) {
            if (!gjs_define_function(cx, constructor, gtype, meth_info))
                return false;
        }
    }

    // Also define class/interface methods if there is a gtype struct

    GjsAutoStructInfo type_struct = InfoMethodsPolicy<TAG>::type_struct(info);
    // Not an error for it to be null even in the case of Object and Interface;
    // documentation says g_object_info_get_class_struct() and
    // g_interface_info_get_iface_struct() can validly return a null pointer.
    if (!type_struct)
        return true;

    n_methods = g_struct_info_get_n_methods(type_struct);

    for (int ix = 0; ix < n_methods; ix++) {
        GjsAutoFunctionInfo meth_info =
            g_struct_info_get_method(type_struct, ix);

        if (!gjs_define_function(cx, constructor, gtype, meth_info))
            return false;
    }

    return true;
}

// All possible instantiations are needed
template bool gjs_define_static_methods<InfoType::Enum>(
    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
template bool gjs_define_static_methods<InfoType::Interface>(
    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
template bool gjs_define_static_methods<InfoType::Object>(
    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
template bool gjs_define_static_methods<InfoType::Struct>(
    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
template bool gjs_define_static_methods<InfoType::Union>(
    JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);