From c4231d5917b1a06d1e3b788322c71cfdb41a0249 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Sun, 21 Mar 2021 11:37:58 -0700 Subject: [PATCH 3/6] GObject: Define camel and kebab variants of CONSTRUCT_ONLY properties Since we redefine CONSTRUCT_ONLY properties to be readonly data properties when they are set, we must also define camelCase and kebab-case variations in order to be consistent with the other property accessors. Closes: #391 --- gi/gobject.cpp | 11 +++++++---- gjs/jsapi-util-string.cpp | 21 +++++++++++++++++++++ gjs/jsapi-util.h | 1 + installed-tests/js/testGObjectClass.js | 19 +++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/gi/gobject.cpp b/gi/gobject.cpp index 27c7d13c..65ed6638 100644 --- a/gi/gobject.cpp +++ b/gi/gobject.cpp @@ -55,10 +55,13 @@ static bool jsobj_set_gproperty(JSContext* cx, JS::HandleObject object, GjsAutoChar underscore_name = gjs_hyphen_to_underscore(pspec->name); - if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) - return JS_DefineProperty( - cx, object, underscore_name, jsvalue, - GJS_MODULE_PROP_FLAGS | JSPROP_READONLY); + if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) { + unsigned flags = GJS_MODULE_PROP_FLAGS | JSPROP_READONLY; + GjsAutoChar camel_name = gjs_hyphen_to_camel(pspec->name); + return JS_DefineProperty(cx, object, underscore_name, jsvalue, flags) && + JS_DefineProperty(cx, object, camel_name, jsvalue, flags) && + JS_DefineProperty(cx, object, pspec->name, jsvalue, flags); + } return JS_SetProperty(cx, object, underscore_name, jsvalue); } diff --git a/gjs/jsapi-util-string.cpp b/gjs/jsapi-util-string.cpp index e318b514..45f297a7 100644 --- a/gjs/jsapi-util-string.cpp +++ b/gjs/jsapi-util-string.cpp @@ -4,6 +4,7 @@ #include +#include // for toupper #include #include // for size_t, strlen #include // for ssize_t @@ -53,6 +54,26 @@ char* gjs_hyphen_to_underscore(const char* str) { return retval; } +GjsAutoChar gjs_hyphen_to_camel(const char* str) { + GjsAutoChar retval = static_cast(g_malloc(strlen(str) + 1)); + const char* input_iter = str; + char* output_iter = retval.get(); + bool uppercase_next = false; + while (*input_iter != '\0') { + if (*input_iter == '-') { + uppercase_next = true; + } else if (uppercase_next) { + *output_iter++ = toupper(*input_iter); + uppercase_next = false; + } else { + *output_iter++ = *input_iter; + } + input_iter++; + } + *output_iter = '\0'; + return retval; +} + /** * gjs_string_to_utf8: * @cx: JSContext diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h index 11c23776..a6b66261 100644 --- a/gjs/jsapi-util.h +++ b/gjs/jsapi-util.h @@ -542,6 +542,7 @@ bool gjs_object_require_converted_property(JSContext *context, [[nodiscard]] std::string gjs_debug_id(jsid id); [[nodiscard]] char* gjs_hyphen_to_underscore(const char* str); +[[nodiscard]] GjsAutoChar gjs_hyphen_to_camel(const char* str); #if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900)) [[nodiscard]] std::wstring gjs_win32_vc140_utf8_to_utf16(const char* str); diff --git a/installed-tests/js/testGObjectClass.js b/installed-tests/js/testGObjectClass.js index f0a57a84..7073ccba 100644 --- a/installed-tests/js/testGObjectClass.js +++ b/installed-tests/js/testGObjectClass.js @@ -853,6 +853,25 @@ describe('Auto accessor generation', function () { expect(a['long-long-name']).toEqual(48); expect(a.construct).toEqual(96); expect(a.construct_only).toEqual(80); + expect(a.constructOnly).toEqual(80); + expect(a['construct-only']).toEqual(80); + }); + + it('set properties at construct time', function () { + a = new AutoAccessors({ + simple: 1, + longLongName: 1, + construct: 1, + 'construct-only': 1, + }); + expect(a.simple).toEqual(1); + expect(a.long_long_name).toEqual(1); + expect(a.longLongName).toEqual(1); + expect(a['long-long-name']).toEqual(1); + expect(a.construct).toEqual(1); + expect(a.construct_only).toEqual(1); + expect(a.constructOnly).toEqual(1); + expect(a['construct-only']).toEqual(1); }); it('notify when the property changes', function () { -- 2.30.2