File: struct.cpp

package info (click to toggle)
gjs 1.87.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 7,128 kB
  • sloc: cpp: 38,390; javascript: 31,939; ansic: 15,994; sh: 1,743; python: 791; xml: 137; makefile: 40
file content (147 lines) | stat: -rw-r--r-- 5,010 bytes parent folder | download
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
/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Philip Chimento <philip.chimento@gmail.com>

#include <config.h>

#include <utility>  // for move

#include <js/CallArgs.h>
#include <js/Class.h>
#include <js/PropertyAndElement.h>  // for JS_DefineFunction
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>

#include "gi/boxed.h"
#include "gi/gerror.h"
#include "gi/struct.h"
#include "gjs/atoms.h"
#include "gjs/context-private.h"
#include "gjs/jsapi-util.h"
#include "gjs/mem-private.h"

// clang-format off
const struct JSClassOps StructBase::class_ops = {
    nullptr,  // addProperty
    nullptr,  // deleteProperty
    nullptr,  // enumerate
    &StructBase::BoxedBase::new_enumerate,
    &StructBase::BoxedBase::resolve,
    nullptr,  // mayResolve
    &StructBase::BoxedBase::finalize,
    nullptr,  // call
    nullptr,  // construct
    &StructBase::BoxedBase::trace
};

// We allocate 1 extra reserved slot; this is typically unused, but if the boxed
// is for a nested structure inside a parent structure, the reserved slot is
// used to hold onto the parent Javascript object and make sure it doesn't get
// freed.
const struct JSClass StructBase::klass = {
    "GObject_Struct",
    JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_FOREGROUND_FINALIZE,
    &StructBase::class_ops
};
// clang-format on

StructPrototype::StructPrototype(const GI::StructInfo info, GType gtype)
    : BoxedPrototype(info, gtype) {
    GJS_INC_COUNTER(boxed_prototype);
}

StructPrototype::~StructPrototype() { GJS_DEC_COUNTER(boxed_prototype); }

bool StructPrototype::define_class(JSContext* cx, JS::HandleObject in_object,
                                   const GI::StructInfo info) {
    JS::RootedObject prototype{cx};
    if (!BoxedPrototype::define_class_impl(cx, in_object, info, &prototype))
        return false;

    if (info.gtype() == G_TYPE_ERROR &&
        !JS_DefineFunction(cx, prototype, "toString", &ErrorBase::to_string, 0,
                           GJS_MODULE_PROP_FLAGS))
        return false;

    return true;
}

StructInstance::StructInstance(StructPrototype* prototype, JS::HandleObject obj)
    : BoxedInstance(prototype, obj) {
    GJS_INC_COUNTER(boxed_instance);
}

StructInstance::~StructInstance() {
    if (m_owning_ptr && m_allocated_directly && gtype() == G_TYPE_VALUE)
        g_value_unset(m_ptr.template as<GValue>());

    GJS_DEC_COUNTER(boxed_instance);
}

bool StructInstance::constructor_impl(JSContext* cx, JS::HandleObject obj,
                                      const JS::CallArgs& args) {
    if (gtype() == G_TYPE_VARIANT) {
        // Short-circuit construction for GVariants by calling into the JS
        // packing function
        const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
        if (!invoke_static_method(cx, obj, atoms.new_internal(), args))
            return false;

        // The return value of GLib.Variant.new_internal() gets its own
        // BoxedInstance, and the one we're setting up in this constructor is
        // discarded.
        debug_lifecycle(
            "Boxed construction delegated to GVariant constructor, boxed "
            "object discarded");

        return true;
    }

    if (!BoxedInstance::constructor_impl(cx, obj, args))
        return false;

    // Define the expected Error properties
    if (gtype() == G_TYPE_ERROR) {
        JS::RootedObject gerror(cx, &args.rval().toObject());
        if (!gjs_define_error_properties(cx, gerror))
            return false;
    }

    return true;
}

static bool define_extra_error_properties(JSContext* cx, JS::HandleObject obj) {
    StructBase* priv = StructBase::for_js(cx, obj);
    if (priv->gtype() != G_TYPE_ERROR)
        return true;
    return gjs_define_error_properties(cx, obj);
}

/**
 * StructInstance::new_for_c_struct:
 *
 * Creates a new StructInstance JS object from a C boxed struct pointer.
 *
 * There are two overloads of this method; the NoCopy overload will simply take
 * the passed-in pointer but not own it, while the normal method will take a
 * reference, or if the boxed type can be directly allocated, copy the memory.
 */
JSObject* StructInstance::new_for_c_struct(JSContext* cx,
                                           const GI::StructInfo info,
                                           void* gboxed) {
    JS::RootedObject obj{cx, new_for_c_struct_impl(cx, info, gboxed)};
    if (!obj || !define_extra_error_properties(cx, obj))
        return nullptr;
    return obj;
}

JSObject* StructInstance::new_for_c_struct(JSContext* cx,
                                           const GI::StructInfo info,
                                           void* gboxed,
                                           Boxed::NoCopy no_copy) {
    JS::RootedObject obj{
        cx, new_for_c_struct_impl(cx, info, gboxed, std::move(no_copy))};
    if (!obj || !define_extra_error_properties(cx, obj))
        return nullptr;
    return obj;
}