diff --git a/CHANGELOG.md b/CHANGELOG.md index 20fc3c5f2..d8e756d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ - new module, fully overhauled, replaces dialog, keyboard, submenu, textbox modules - higher barrier to entry than older modules (requires usage of `event_loop` and `gui.viewDispatcher`), but much more flexible, powerful and easier to extend - includes all previously available js gui functionality (except `widget`), and also adds `gui/loading` and `gui/empty_screen` views - - existing scripts using gui in any way will need a huge amount of reworking + - currently `gui/file_picker` works different than other new view objects, it is a simple `.pickFile()` synchronous function, but this [may change later](https://github.com/flipperdevices/flipperzero-firmware/pull/3961#discussion_r1805579153) - effort required to update old scripts using gui: extensive - `keyboard`: - removed, now replaced by `gui/text_input` and `gui/byte_input` (see above) @@ -38,10 +38,10 @@ - only gui functionality not ported to new gui module, remains unchanged for now but likely to be ported later on - globals: - `__filepath` and `__dirpath` renamed to `__filename` and `__dirname` like in nodejs - - `to_string()` renamed to `toString()`, now supports optional base parameter + - `to_string()` renamed and moved to number class as `n.toString()`, now supports optional base parameter - `to_hex_string()` removed, now use `toString(num, 16)` - `parse_int()` renamed to `parseInt()`, now supports optional base parameter - - `to_upper_case()` and `to_lower_case()` renamed and moved to string class as `"".toUpperCase()` and `"".toLowerCase()` + - `to_upper_case()` and `to_lower_case()` renamed and moved to string class as `s.toUpperCase()` and `s.toLowerCase()` - effort required to update old scripts using these: minimal - Added type definitions (typescript files for type checking in IDE, Flipper does not run typescript, and you code in javascript) - Documentation is incomplete and deprecated, from now on you should refer to type definitions (`applications/system/js_app/types`), those will always be correct diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index a29251f0e..65fafed76 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -53,7 +53,7 @@ App( appid="js_gui", apptype=FlipperAppType.PLUGIN, entry_point="js_gui_ep", - requires=["js_app", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/js_gui.c", "modules/js_gui/js_gui_api_table.cpp"], ) @@ -61,7 +61,7 @@ App( appid="js_gui__loading", apptype=FlipperAppType.PLUGIN, entry_point="js_view_loading_ep", - requires=["js_app", "js_gui", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/loading.c"], ) @@ -69,7 +69,7 @@ App( appid="js_gui__empty_screen", apptype=FlipperAppType.PLUGIN, entry_point="js_view_empty_screen_ep", - requires=["js_app", "js_gui", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/empty_screen.c"], ) @@ -77,7 +77,7 @@ App( appid="js_gui__submenu", apptype=FlipperAppType.PLUGIN, entry_point="js_view_submenu_ep", - requires=["js_app", "js_gui"], + requires=["js_app"], sources=["modules/js_gui/submenu.c"], ) @@ -85,7 +85,7 @@ App( appid="js_gui__text_input", apptype=FlipperAppType.PLUGIN, entry_point="js_view_text_input_ep", - requires=["js_app", "js_gui", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/text_input.c"], ) @@ -93,7 +93,7 @@ App( appid="js_gui__byte_input", apptype=FlipperAppType.PLUGIN, entry_point="js_view_byte_input_ep", - requires=["js_app", "js_gui", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/byte_input.c"], ) @@ -149,7 +149,7 @@ App( appid="js_gpio", apptype=FlipperAppType.PLUGIN, entry_point="js_gpio_ep", - requires=["js_app", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gpio.c"], ) diff --git a/applications/system/js_app/examples/apps/Scripts/Examples/badusb_demo.js b/applications/system/js_app/examples/apps/Scripts/Examples/badusb_demo.js index 758cffd13..d1ace3845 100644 --- a/applications/system/js_app/examples/apps/Scripts/Examples/badusb_demo.js +++ b/applications/system/js_app/examples/apps/Scripts/Examples/badusb_demo.js @@ -45,7 +45,7 @@ eventLoop.subscribe(views.dialog.input, function (_sub, button, eventLoop, gui) badusb.println("Flipper Model: " + flipper.getModel()); badusb.println("Flipper Name: " + flipper.getName()); - badusb.println("Battery level: " + toString(flipper.getBatteryCharge()) + "%"); + badusb.println("Battery level: " + flipper.getBatteryCharge().toString() + "%"); // Alt+Numpad method works only on Windows!!! badusb.altPrintln("This was printed with Alt+Numpad method!"); diff --git a/applications/system/js_app/examples/apps/Scripts/Examples/blebeacon.js b/applications/system/js_app/examples/apps/Scripts/Examples/blebeacon.js index 9a30ad151..1187f1c20 100644 --- a/applications/system/js_app/examples/apps/Scripts/Examples/blebeacon.js +++ b/applications/system/js_app/examples/apps/Scripts/Examples/blebeacon.js @@ -45,7 +45,7 @@ function sendRandomModelAdvertisement() { blebeacon.start(); - print("Sent data for model ID " + toString(model)); + print("Sent data for model ID " + model.toString()); currentIndex = (currentIndex + 1) % watchValues.length; diff --git a/applications/system/js_app/examples/apps/Scripts/Examples/gui.js b/applications/system/js_app/examples/apps/Scripts/Examples/gui.js index 10e88b649..5faeb6d32 100644 --- a/applications/system/js_app/examples/apps/Scripts/Examples/gui.js +++ b/applications/system/js_app/examples/apps/Scripts/Examples/gui.js @@ -97,7 +97,7 @@ eventLoop.subscribe(views.bytekb.input, function (_sub, data, gui, views) { let data_view = Uint8Array(data); let text = "0x"; for (let i = 0; i < data_view.length; i++) { - text += toString(data_view[i], 16); + text += data_view[i].toString(16); } views.helloDialog.set("text", "You typed:\n" + text); views.helloDialog.set("center", "Cool!"); diff --git a/applications/system/js_app/examples/apps/Scripts/Examples/stringutils.js b/applications/system/js_app/examples/apps/Scripts/Examples/stringutils.js index 7c57f9bc5..b2facb237 100644 --- a/applications/system/js_app/examples/apps/Scripts/Examples/stringutils.js +++ b/applications/system/js_app/examples/apps/Scripts/Examples/stringutils.js @@ -1,6 +1,6 @@ let sampleText = "Hello, World!"; -let lengthOfText = "Length of text: " + toString(sampleText.length); +let lengthOfText = "Length of text: " + sampleText.length.toString(); print(lengthOfText); let start = 7; @@ -9,7 +9,7 @@ let substringResult = sampleText.slice(start, end); print(substringResult); let searchStr = "World"; -let result2 = toString(sampleText.indexOf(searchStr)); +let result2 = sampleText.indexOf(searchStr).toString(); print(result2); let upperCaseText = "Text in upper case: " + sampleText.toUpperCase(); diff --git a/applications/system/js_app/examples/apps/Scripts/Examples/uart_echo.js b/applications/system/js_app/examples/apps/Scripts/Examples/uart_echo.js index 72b4617a6..d43c0baa3 100644 --- a/applications/system/js_app/examples/apps/Scripts/Examples/uart_echo.js +++ b/applications/system/js_app/examples/apps/Scripts/Examples/uart_echo.js @@ -6,7 +6,7 @@ while (1) { if (rx_data !== undefined) { serial.write(rx_data); let data_view = Uint8Array(rx_data); - print("0x" + toString(data_view[0], 16)); + print("0x" + data_view[0].toString(16)); } } diff --git a/applications/system/js_app/examples/apps/Scripts/interactive.js b/applications/system/js_app/examples/apps/Scripts/interactive.js index 29f3bb253..34639cdac 100644 --- a/applications/system/js_app/examples/apps/Scripts/interactive.js +++ b/applications/system/js_app/examples/apps/Scripts/interactive.js @@ -42,7 +42,7 @@ eventLoop.subscribe(views.dialog.input, function (_sub, button, gui, views) { eventLoop.subscribe(views.textInput.input, function (_sub, text, gui, views, ctx) { gui.viewDispatcher.switchTo(views.loading); - let path = ctx.tmpTemplate + toString(ctx.tmpNumber++); + let path = ctx.tmpTemplate + (ctx.tmpNumber++).toString(); let file = storage.openFile(path, "w", "create_always"); file.write(text); file.close(); @@ -58,7 +58,7 @@ eventLoop.subscribe(views.textInput.input, function (_sub, text, gui, views, ctx } else if (typeof result === "string") { result = "'" + result + "'"; } else if (typeof result === "number") { - result = toString(result); + result = result.toString(); } else if (typeof result === "bigint") { // mJS doesn't support BigInt() but might aswell check result = "bigint"; } else if (typeof result === "boolean") { diff --git a/applications/system/js_app/js_thread.c b/applications/system/js_app/js_thread.c index 31e9f721e..7a774d324 100644 --- a/applications/system/js_app/js_thread.c +++ b/applications/system/js_app/js_thread.c @@ -196,16 +196,6 @@ static void js_require(struct mjs* mjs) { mjs_return(mjs, req_object); } -static void js_global_to_string(struct mjs* mjs) { - int base = 10; - if(mjs_nargs(mjs) > 1) base = mjs_get_int(mjs, mjs_arg(mjs, 1)); - double num = mjs_get_double(mjs, mjs_arg(mjs, 0)); - char tmp_str[] = "-2147483648"; - itoa(num, tmp_str, base); - mjs_val_t ret = mjs_mk_string(mjs, tmp_str, ~0, true); - mjs_return(mjs, ret); -} - static void js_parse_int(struct mjs* mjs) { const char* str; int32_t base = 10; @@ -268,7 +258,6 @@ static int32_t js_thread(void* arg) { } mjs_set(mjs, global, "print", ~0, MJS_MK_FN(js_print)); mjs_set(mjs, global, "delay", ~0, MJS_MK_FN(js_delay)); - mjs_set(mjs, global, "toString", ~0, MJS_MK_FN(js_global_to_string)); mjs_set(mjs, global, "ffi_address", ~0, MJS_MK_FN(js_ffi_address)); mjs_set(mjs, global, "require", ~0, MJS_MK_FN(js_require)); mjs_set(mjs, global, "parseInt", ~0, MJS_MK_FN(js_parse_int)); diff --git a/applications/system/js_app/modules/js_gui/js_gui.c b/applications/system/js_app/modules/js_gui/js_gui.c index cd87e5c4b..4bd4ccc31 100644 --- a/applications/system/js_app/modules/js_gui/js_gui.c +++ b/applications/system/js_app/modules/js_gui/js_gui.c @@ -260,26 +260,6 @@ static void js_gui_view_set(struct mjs* mjs) { mjs_return(mjs, MJS_UNDEFINED); } -/** - * @brief `View.hasProperty` - */ -static void js_gui_view_has_property(struct mjs* mjs) { - const char* name; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&name)); - JsGuiViewData* data = JS_GET_CONTEXT(mjs); - const JsViewDescriptor* descriptor = data->descriptor; - - for(size_t i = 0; i < descriptor->prop_cnt; i++) { - JsViewPropDescriptor prop = descriptor->props[i]; - if(strcmp(prop.name, name) != 0) continue; - - mjs_return(mjs, mjs_mk_boolean(mjs, true)); - return; - } - - mjs_return(mjs, mjs_mk_boolean(mjs, false)); -} - /** * @brief `View` destructor */ @@ -304,7 +284,6 @@ static mjs_val_t js_gui_make_view(struct mjs* mjs, const JsViewDescriptor* descr // generic view API mjs_val_t view_obj = mjs_mk_object(mjs); mjs_set(mjs, view_obj, "set", ~0, MJS_MK_FN(js_gui_view_set)); - mjs_set(mjs, view_obj, "hasProperty", ~0, MJS_MK_FN(js_gui_view_has_property)); // object data JsGuiViewData* data = malloc(sizeof(JsGuiViewData)); diff --git a/applications/system/js_app/types/global.d.ts b/applications/system/js_app/types/global.d.ts index 052a2bb6a..d132f89f5 100644 --- a/applications/system/js_app/types/global.d.ts +++ b/applications/system/js_app/types/global.d.ts @@ -11,13 +11,6 @@ declare function delay(ms: number): void; */ declare function print(...args: any[]): void; -/** - * @brief Converts a number to a string - * @param value The number to convert to a string - * @param base Integer base (`2`...`16`), default: 10 - */ -declare function toString(value: number, base?: number): string; - /** * @brief Converts a string to a number * @param text The string to convert to a number @@ -214,7 +207,13 @@ declare class Boolean { } declare class Function { } -declare class Number { } +declare class Number { + /** + * @brief Converts this number to a string + * @param base Integer base (`2`...`16`), default: 10 + */ + toString(base?: number): string; +} declare class Object { } diff --git a/applications/system/js_app/types/gui/index.d.ts b/applications/system/js_app/types/gui/index.d.ts index 5efcfdc03..d4cdc6bc9 100644 --- a/applications/system/js_app/types/gui/index.d.ts +++ b/applications/system/js_app/types/gui/index.d.ts @@ -9,11 +9,6 @@ export declare class View { * @param value Value to assign */ set

(property: P, value: Props[P]): void; - /** - * Check if property is available - * @param name Name of the property - */ - hasProperty(name: string): boolean; } export declare class ViewFactory> { diff --git a/lib/mjs/mjs_exec.c b/lib/mjs/mjs_exec.c index 0213a871e..8fdb2d7e5 100644 --- a/lib/mjs/mjs_exec.c +++ b/lib/mjs/mjs_exec.c @@ -475,6 +475,22 @@ static int getprop_builtin_string( return 0; } +static int getprop_builtin_number( + struct mjs* mjs, + mjs_val_t val, + const char* name, + size_t name_len, + mjs_val_t* res) { + if(strcmp(name, "toString") == 0) { + *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_number_to_string); + return 1; + } + + (void)val; + (void)name_len; + return 0; +} + static int getprop_builtin_array( struct mjs* mjs, mjs_val_t val, @@ -589,6 +605,8 @@ static int getprop_builtin(struct mjs* mjs, mjs_val_t val, mjs_val_t name, mjs_v } else if(s != NULL && n == 5 && strncmp(s, "apply", n) == 0) { *res = mjs_mk_foreign_func(mjs, (mjs_func_ptr_t)mjs_apply_); handled = 1; + } else if(mjs_is_number(val)) { + handled = getprop_builtin_number(mjs, val, s, n, res); } else if(mjs_is_array(val)) { handled = getprop_builtin_array(mjs, val, s, n, res); } else if(mjs_is_foreign(val)) { diff --git a/lib/mjs/mjs_primitive.c b/lib/mjs/mjs_primitive.c index b63a268e5..e73ae892d 100644 --- a/lib/mjs/mjs_primitive.c +++ b/lib/mjs/mjs_primitive.c @@ -6,6 +6,8 @@ #include "mjs_core.h" #include "mjs_internal.h" #include "mjs_primitive.h" +#include "mjs_string_public.h" +#include "mjs_util.h" mjs_val_t mjs_mk_null(void) { return MJS_NULL; @@ -158,3 +160,31 @@ MJS_PRIVATE void mjs_op_isnan(struct mjs* mjs) { mjs_return(mjs, ret); } + +MJS_PRIVATE void mjs_number_to_string(struct mjs* mjs) { + mjs_val_t ret = MJS_UNDEFINED; + mjs_val_t base_v = MJS_UNDEFINED; + int32_t base = 10; + int32_t num; + + /* get number from `this` */ + if(!mjs_check_arg(mjs, -1 /*this*/, "this", MJS_TYPE_NUMBER, NULL)) { + goto clean; + } + num = mjs_get_int32(mjs, mjs->vals.this_obj); + + if(mjs_nargs(mjs) >= 1) { + /* get base from arg 0 */ + if(!mjs_check_arg(mjs, 0, "base", MJS_TYPE_NUMBER, &base_v)) { + goto clean; + } + base = mjs_get_int(mjs, base_v); + } + + char tmp_str[] = "-2147483648"; + itoa(num, tmp_str, base); + ret = mjs_mk_string(mjs, tmp_str, ~0, true); + +clean: + mjs_return(mjs, ret); +} diff --git a/lib/mjs/mjs_primitive.h b/lib/mjs/mjs_primitive.h index 6d6f91785..f1c3c8220 100644 --- a/lib/mjs/mjs_primitive.h +++ b/lib/mjs/mjs_primitive.h @@ -34,6 +34,11 @@ MJS_PRIVATE void* get_ptr(mjs_val_t v); */ MJS_PRIVATE void mjs_op_isnan(struct mjs* mjs); +/* + * Implementation for JS Number.toString() + */ +MJS_PRIVATE void mjs_number_to_string(struct mjs* mjs); + #if defined(__cplusplus) } #endif /* __cplusplus */