summaryrefslogtreecommitdiff
path: root/gi/enumeration.cpp
blob: 09c29701fac74bd5d31802f83de77dbd53f978c3 (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
/* -*- 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

#include <config.h>

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

#include <js/PropertyAndElement.h>
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include <jsapi.h>  // for JS_NewPlainObject

#include "gi/cwrapper.h"
#include "gi/enumeration.h"
#include "gi/wrapperutils.h"
#include "gjs/jsapi-util.h"
#include "gjs/macros.h"
#include "util/log.h"

GJS_JSAPI_RETURN_CONVENTION
static bool
gjs_define_enum_value(JSContext       *context,
                      JS::HandleObject in_object,
                      GIValueInfo     *info)
{
    const char* value_name;
    gsize i;
    gint64 value_val;

    value_name = g_base_info_get_name( (GIBaseInfo*) info);
    value_val = g_value_info_get_value(info);

    /* g-i converts enum members such as GDK_GRAVITY_SOUTH_WEST to
     * Gdk.GravityType.south-west (where 'south-west' is value_name)
     * Convert back to all SOUTH_WEST.
     */
    GjsAutoChar fixed_name = g_ascii_strup(value_name, -1);
    for (i = 0; fixed_name[i]; ++i) {
        char c = fixed_name[i];
        if (!(('A' <= c && c <= 'Z') ||
              ('0' <= c && c <= '9')))
            fixed_name[i] = '_';
    }

    gjs_debug(GJS_DEBUG_GENUM,
              "Defining enum value %s (fixed from %s) %" G_GINT64_MODIFIER "d",
              fixed_name.get(), value_name, value_val);

    if (!JS_DefineProperty(context, in_object,
                           fixed_name, (double) value_val,
                           GJS_MODULE_PROP_FLAGS)) {
        gjs_throw(context,
                  "Unable to define enumeration value %s %" G_GINT64_FORMAT
                  " (no memory most likely)",
                  fixed_name.get(), value_val);
        return false;
    }

    return true;
}

bool
gjs_define_enum_values(JSContext       *context,
                       JS::HandleObject in_object,
                       GIEnumInfo      *info)
{
    int i, n_values;

    /* Fill in enum values first, so we don't define the enum itself until we're
     * sure we can finish successfully.
     */
    n_values = g_enum_info_get_n_values(info);
    for (i = 0; i < n_values; ++i) {
        GjsAutoBaseInfo value_info = g_enum_info_get_value(info, i);

        if (!gjs_define_enum_value(context, in_object, value_info))
            return false;
    }
    return true;
}

bool
gjs_define_enumeration(JSContext       *context,
                       JS::HandleObject in_object,
                       GIEnumInfo      *info)
{
    const char *enum_name;

    /* An enumeration is simply an object containing integer attributes for
     * each enum value. It does not have a special JSClass.
     *
     * We could make this more typesafe and also print enum values as strings
     * if we created a class for each enum and made the enum values instances
     * of that class. However, it would have a lot more overhead and just
     * be more complicated in general. I think this is fine.
     */

    enum_name = g_base_info_get_name( (GIBaseInfo*) info);

    JS::RootedObject enum_obj(context, JS_NewPlainObject(context));
    if (!enum_obj) {
        gjs_throw(context, "Could not create enumeration %s.%s",
                  g_base_info_get_namespace(info), enum_name);
        return false;
    }

    GType gtype = g_registered_type_info_get_g_type(info);

    if (!gjs_define_enum_values(context, enum_obj, info) ||
        !gjs_define_static_methods<InfoType::Enum>(context, enum_obj, gtype,
                                                   info) ||
        !gjs_wrapper_define_gtype_prop(context, enum_obj, gtype))
        return false;

    gjs_debug(GJS_DEBUG_GENUM,
              "Defining %s.%s as %p",
              g_base_info_get_namespace( (GIBaseInfo*) info),
              enum_name, enum_obj.get());

    if (!JS_DefineProperty(context, in_object, enum_name, enum_obj,
                           GJS_MODULE_PROP_FLAGS)) {
        gjs_throw(context, "Unable to define enumeration property (no memory most likely)");
        return false;
    }

    return true;
}