summaryrefslogtreecommitdiff
path: root/modules/core/_common.js
blob: 780483b9ca41d48304c1ac9f39b18e1ad6f82af5 (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
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-

// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2020 Philip Chimento <philip.chimento@gmail.com>

/* exported _checkAccessors, _registerType, definePublicProperties, definePrivateProperties */

// This is a helper module in which to put code that is common between the
// legacy GObject.Class system and the new GObject.registerClass system.

var _registerType = Symbol('GObject register type hook');

function _generateAccessors(pspec, propdesc, GObject) {
    const {name, flags} = pspec;
    const readable = flags & GObject.ParamFlags.READABLE;
    const writable = flags & GObject.ParamFlags.WRITABLE;

    if (!propdesc) {
        propdesc = {
            configurable: true,
            enumerable: true,
        };
    }

    if (readable && writable) {
        if (!propdesc.get && !propdesc.set) {
            const privateName = Symbol(`__autogeneratedAccessor__${name}`);
            const defaultValue = pspec.get_default_value();
            propdesc.get = function () {
                if (!(privateName in this))
                    this[privateName] = defaultValue;
                return this[privateName];
            };
            propdesc.set = function (value) {
                if (value !== this[privateName]) {
                    this[privateName] = value;
                    this.notify(name);
                }
            };
        } else if (!propdesc.get) {
            propdesc.get = function () {
                throw new Error(`setter defined without getter for property ${name}`);
            };
        } else if (!propdesc.set) {
            propdesc.set = function () {
                throw new Error(`getter defined without setter for property ${name}`);
            };
        }
    } else if (readable && !propdesc.get) {
        propdesc.get = function () {
            throw new Error(`missing getter for read-only property ${name}`);
        };
    } else if (writable && !propdesc.set) {
        propdesc.set = function () {
            throw new Error(`missing setter for write-only property ${name}`);
        };
    }

    return propdesc;
}

function _checkAccessors(proto, pspec, GObject, {generateAccessors = true, accessorMappingSymbol = null} = {}) {
    const {name, flags} = pspec;
    if (flags & GObject.ParamFlags.CONSTRUCT_ONLY)
        return;

    const underscoreName = name.replace(/-/g, '_');
    const camelName = name.replace(/-([a-z])/g, match => match[1].toUpperCase());
    let propdesc = Object.getOwnPropertyDescriptor(proto, name);
    let dashPropdesc = propdesc, underscorePropdesc, camelPropdesc;
    const nameIsCompound = name.includes('-');
    if (nameIsCompound) {
        underscorePropdesc = Object.getOwnPropertyDescriptor(proto, underscoreName);
        camelPropdesc = Object.getOwnPropertyDescriptor(proto, camelName);
        if (!propdesc)
            propdesc = underscorePropdesc;
        if (!propdesc)
            propdesc = camelPropdesc;
    }

    const readable = flags & GObject.ParamFlags.READABLE;
    const writable = flags & GObject.ParamFlags.WRITABLE;
    if (!propdesc || (readable && !propdesc.get) || (writable && !propdesc.set))
        propdesc = _generateAccessors(pspec, propdesc, GObject);

    if (generateAccessors) {
        if (!dashPropdesc)
            Object.defineProperty(proto, name, propdesc);
        if (nameIsCompound) {
            if (!underscorePropdesc)
                Object.defineProperty(proto, underscoreName, propdesc);
            if (!camelPropdesc)
                Object.defineProperty(proto, camelName, propdesc);
        }
    }

    if (accessorMappingSymbol) {
        proto[accessorMappingSymbol] = proto[accessorMappingSymbol] ?? {};
        if (nameIsCompound) {
            proto[accessorMappingSymbol][underscoreName] = propdesc;
            proto[accessorMappingSymbol][camelName] = propdesc;
        } else {
            proto[accessorMappingSymbol][name] = propdesc;
        }
    }
}

function definePublicProperties(object, values) {
    Object.defineProperties(object, Object.getOwnPropertyDescriptors(values));
}

function definePrivateProperties(object, values) {
    const propertyKeys = [...Object.getOwnPropertyNames(values), ...Object.getOwnPropertySymbols(values)];

    const privateDescriptors = Object.fromEntries(propertyKeys.map(key => {
        const descriptor = Object.getOwnPropertyDescriptor(values, key);

        return [key, {
            ...descriptor,
            ...'value' in descriptor ? {writable: false} : {},
            configurable: false,
            enumerable: false,
        }];
    }));

    Object.defineProperties(object, privateDescriptors);
}