1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 20:49:49 +04:00

[FL-3963] Move JS modules to new arg parser (#4139)

* js: value destructuring and tests

* js: temporary fix to see size impact

* js_val: reduce code size 1

* i may be stupid.

* test: js_value args

* Revert "js: temporary fix to see size impact"

This reverts commit f51d726dbafc4300d3552020de1c3b8f9ecd3ac1.

* pvs: silence warnings

* style: formatting

* pvs: silence warnings?

* pvs: silence warnings??

* js_value: redesign declaration types for less code

* js: temporary fix to see size impact

* style: formatting

* pvs: fix helpful warnings

* js_value: reduce .rodata size

* pvs: fix helpful warning

* js_value: reduce code size 1

* fix build error

* style: format

* Revert "js: temporary fix to see size impact"

This reverts commit d6a46f01794132e882e03fd273dec24386a4f8ba.

* style: format

* js: move to new arg parser

* style: format

---------

Co-authored-by: hedger <hedger@users.noreply.github.com>
This commit is contained in:
Anna Antonenko
2025-04-05 03:17:30 +04:00
committed by GitHub
parent 7192c9e68b
commit dac1457f0a
13 changed files with 525 additions and 620 deletions

View File

@@ -202,12 +202,15 @@ static JsSdkCompatStatus
return JsSdkCompatStatusCompatible;
}
#define JS_SDK_COMPAT_ARGS \
int32_t major, minor; \
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&major), JS_ARG_INT32(&minor));
static const JsValueDeclaration js_sdk_version_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeInt32),
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_sdk_version_args = JS_VALUE_ARGS(js_sdk_version_arg_list);
void js_sdk_compatibility_status(struct mjs* mjs) {
JS_SDK_COMPAT_ARGS;
int32_t major, minor;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor);
JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor);
switch(status) {
case JsSdkCompatStatusCompatible:
@@ -223,7 +226,8 @@ void js_sdk_compatibility_status(struct mjs* mjs) {
}
void js_is_sdk_compatible(struct mjs* mjs) {
JS_SDK_COMPAT_ARGS;
int32_t major, minor;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor);
JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor);
mjs_return(mjs, mjs_mk_boolean(mjs, status == JsSdkCompatStatusCompatible));
}
@@ -246,7 +250,8 @@ static bool js_internal_compat_ask_user(const char* message) {
}
void js_check_sdk_compatibility(struct mjs* mjs) {
JS_SDK_COMPAT_ARGS;
int32_t major, minor;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor);
JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor);
if(status != JsSdkCompatStatusCompatible) {
FURI_LOG_E(
@@ -300,15 +305,20 @@ static bool js_internal_supports_all_of(struct mjs* mjs, mjs_val_t feature_arr)
return true;
}
static const JsValueDeclaration js_sdk_features_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeAnyArray),
};
static const JsValueArguments js_sdk_features_args = JS_VALUE_ARGS(js_sdk_features_arg_list);
void js_does_sdk_support(struct mjs* mjs) {
mjs_val_t features;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ARR(&features));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_features_args, &features);
mjs_return(mjs, mjs_mk_boolean(mjs, js_internal_supports_all_of(mjs, features)));
}
void js_check_sdk_features(struct mjs* mjs) {
mjs_val_t features;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ARR(&features));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_features_args, &features);
if(!js_internal_supports_all_of(mjs, features)) {
FURI_LOG_E(TAG, "Script requests unsupported features");

View File

@@ -7,6 +7,10 @@
#include <flipper_application/plugins/plugin_manager.h>
#include <flipper_application/plugins/composite_resolver.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PLUGIN_APP_ID "js"
#define PLUGIN_API_VERSION 1
@@ -64,226 +68,6 @@ typedef enum {
JsForeignMagic_JsEventLoopContract,
} JsForeignMagic;
// Are you tired of your silly little JS+C glue code functions being 75%
// argument validation code and 25% actual logic? Introducing: ASS (Argument
// Schema for Scripts)! ASS is a set of macros that reduce the typical
// boilerplate code of "check argument count, get arguments, validate arguments,
// extract C values from arguments" down to just one line!
/**
* When passed as the second argument to `JS_FETCH_ARGS_OR_RETURN`, signifies
* that the function requires exactly as many arguments as were specified.
*/
#define JS_EXACTLY ==
/**
* When passed as the second argument to `JS_FETCH_ARGS_OR_RETURN`, signifies
* that the function requires at least as many arguments as were specified.
*/
#define JS_AT_LEAST >=
typedef struct {
const char* name;
size_t value;
} JsEnumMapping;
#define JS_ENUM_MAP(var_name, ...) \
static const JsEnumMapping var_name##_mapping[] = { \
{NULL, sizeof(var_name)}, \
__VA_ARGS__, \
{NULL, 0}, \
};
typedef struct {
const char* name;
size_t offset;
} JsObjectMapping;
#define JS_OBJ_MAP(var_name, ...) \
static const JsObjectMapping var_name##_mapping[] = { \
__VA_ARGS__, \
{NULL, 0}, \
};
typedef struct {
void* out;
int (*validator)(mjs_val_t);
void (*converter)(struct mjs*, mjs_val_t*, void* out, const void* extra);
const char* expected_type;
bool (*extended_validator)(struct mjs*, mjs_val_t, const void* extra);
const void* extra_data;
} _js_arg_decl;
static inline void _js_to_int32(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
UNUSED(extra);
*(int32_t*)out = mjs_get_int32(mjs, *in);
}
#define JS_ARG_INT32(out) ((_js_arg_decl){out, mjs_is_number, _js_to_int32, "number", NULL, NULL})
static inline void _js_to_ptr(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
UNUSED(extra);
*(void**)out = mjs_get_ptr(mjs, *in);
}
#define JS_ARG_PTR(out) \
((_js_arg_decl){out, mjs_is_foreign, _js_to_ptr, "opaque pointer", NULL, NULL})
static inline void _js_to_string(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
UNUSED(extra);
*(const char**)out = mjs_get_string(mjs, in, NULL);
}
#define JS_ARG_STR(out) ((_js_arg_decl){out, mjs_is_string, _js_to_string, "string", NULL, NULL})
static inline void _js_to_bool(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
UNUSED(extra);
*(bool*)out = !!mjs_get_bool(mjs, *in);
}
#define JS_ARG_BOOL(out) ((_js_arg_decl){out, mjs_is_boolean, _js_to_bool, "boolean", NULL, NULL})
static inline void _js_passthrough(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
UNUSED(extra);
UNUSED(mjs);
*(mjs_val_t*)out = *in;
}
#define JS_ARG_ANY(out) ((_js_arg_decl){out, NULL, _js_passthrough, "any", NULL, NULL})
#define JS_ARG_OBJ(out) ((_js_arg_decl){out, mjs_is_object, _js_passthrough, "any", NULL, NULL})
#define JS_ARG_FN(out) \
((_js_arg_decl){out, mjs_is_function, _js_passthrough, "function", NULL, NULL})
#define JS_ARG_ARR(out) ((_js_arg_decl){out, mjs_is_array, _js_passthrough, "array", NULL, NULL})
static inline bool _js_validate_struct(struct mjs* mjs, mjs_val_t val, const void* extra) {
JsForeignMagic expected_magic = (JsForeignMagic)(size_t)extra;
JsForeignMagic struct_magic = *(JsForeignMagic*)mjs_get_ptr(mjs, val);
return struct_magic == expected_magic;
}
#define JS_ARG_STRUCT(type, out) \
((_js_arg_decl){ \
out, \
mjs_is_foreign, \
_js_to_ptr, \
#type, \
_js_validate_struct, \
(void*)JsForeignMagic##_##type})
static inline bool _js_validate_obj_w_struct(struct mjs* mjs, mjs_val_t val, const void* extra) {
JsForeignMagic expected_magic = (JsForeignMagic)(size_t)extra;
JsForeignMagic struct_magic = *(JsForeignMagic*)JS_GET_INST(mjs, val);
return struct_magic == expected_magic;
}
#define JS_ARG_OBJ_WITH_STRUCT(type, out) \
((_js_arg_decl){ \
out, \
mjs_is_object, \
_js_passthrough, \
#type, \
_js_validate_obj_w_struct, \
(void*)JsForeignMagic##_##type})
static inline bool _js_validate_enum(struct mjs* mjs, mjs_val_t val, const void* extra) {
for(const JsEnumMapping* mapping = (JsEnumMapping*)extra + 1; mapping->name; mapping++)
if(strcmp(mapping->name, mjs_get_string(mjs, &val, NULL)) == 0) return true;
return false;
}
static inline void
_js_convert_enum(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) {
const JsEnumMapping* mapping = (JsEnumMapping*)extra;
size_t size = mapping->value; // get enum size from first entry
for(mapping++; mapping->name; mapping++) {
if(strcmp(mapping->name, mjs_get_string(mjs, val, NULL)) == 0) {
if(size == 1)
*(uint8_t*)out = mapping->value;
else if(size == 2)
*(uint16_t*)out = mapping->value;
else if(size == 4)
*(uint32_t*)out = mapping->value;
else if(size == 8)
*(uint64_t*)out = mapping->value;
return;
}
}
// unreachable, thanks to _js_validate_enum
}
#define JS_ARG_ENUM(var_name, name) \
((_js_arg_decl){ \
&var_name, \
mjs_is_string, \
_js_convert_enum, \
name " enum", \
_js_validate_enum, \
var_name##_mapping})
static inline bool _js_validate_object(struct mjs* mjs, mjs_val_t val, const void* extra) {
for(const JsObjectMapping* mapping = (JsObjectMapping*)extra; mapping->name; mapping++)
if(mjs_get(mjs, val, mapping->name, ~0) == MJS_UNDEFINED) return false;
return true;
}
static inline void
_js_convert_object(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) {
const JsObjectMapping* mapping = (JsObjectMapping*)extra;
for(; mapping->name; mapping++) {
mjs_val_t field_val = mjs_get(mjs, *val, mapping->name, ~0);
*(mjs_val_t*)((uint8_t*)out + mapping->offset) = field_val;
}
}
#define JS_ARG_OBJECT(var_name, name) \
((_js_arg_decl){ \
&var_name, \
mjs_is_object, \
_js_convert_object, \
name " object", \
_js_validate_object, \
var_name##_mapping})
/**
* @brief Validates and converts a JS value with a declarative interface
*
* Example: `int32_t my_value; JS_CONVERT_OR_RETURN(mjs, &mjs_val, JS_ARG_INT32(&my_value), "value source");`
*
* @warning This macro executes `return;` by design in case of a validation failure
*/
#define JS_CONVERT_OR_RETURN(mjs, value, decl, source, ...) \
if(decl.validator) \
if(!decl.validator(*value)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
source ": expected %s", \
##__VA_ARGS__, \
decl.expected_type); \
if(decl.extended_validator) \
if(!decl.extended_validator(mjs, *value, decl.extra_data)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
source ": expected %s", \
##__VA_ARGS__, \
decl.expected_type); \
decl.converter(mjs, value, decl.out, decl.extra_data);
//-V:JS_FETCH_ARGS_OR_RETURN:1008
/**
* @brief Fetches and validates the arguments passed to a JS function
*
* Example: `int32_t my_arg; JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&my_arg));`
*
* @warning This macro executes `return;` by design in case of an argument count
* mismatch or a validation failure
*/
#define JS_FETCH_ARGS_OR_RETURN(mjs, arg_operator, ...) \
_js_arg_decl _js_args[] = {__VA_ARGS__}; \
int _js_arg_cnt = COUNT_OF(_js_args); \
mjs_val_t _js_arg_vals[_js_arg_cnt]; \
if(!(mjs_nargs(mjs) arg_operator _js_arg_cnt)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
"expected %s%d arguments, got %d", \
#arg_operator, \
_js_arg_cnt, \
mjs_nargs(mjs)); \
for(int _i = 0; _i < _js_arg_cnt; _i++) { \
_js_arg_vals[_i] = mjs_arg(mjs, _i); \
JS_CONVERT_OR_RETURN(mjs, &_js_arg_vals[_i], _js_args[_i], "argument %d", _i); \
}
/**
* @brief Prepends an error, sets the JS return value to `undefined` and returns
* from the C function
@@ -358,3 +142,7 @@ void js_does_sdk_support(struct mjs* mjs);
* @brief `checkSdkFeatures` function
*/
void js_check_sdk_features(struct mjs* mjs);
#ifdef __cplusplus
}
#endif

View File

@@ -198,18 +198,15 @@ static void js_require(struct mjs* mjs) {
}
static void js_parse_int(struct mjs* mjs) {
const char* str;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_AT_LEAST, JS_ARG_STR(&str));
static const JsValueDeclaration js_parse_int_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeString),
JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, 10),
};
static const JsValueArguments js_parse_int_args = JS_VALUE_ARGS(js_parse_int_arg_list);
int32_t base = 10;
if(mjs_nargs(mjs) >= 2) {
mjs_val_t base_arg = mjs_arg(mjs, 1);
if(!mjs_is_number(base_arg)) {
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Base must be a number");
mjs_return(mjs, MJS_UNDEFINED);
}
base = mjs_get_int(mjs, base_arg);
}
const char* str;
int32_t base;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_parse_int_args, &str, &base);
int32_t num;
if(strint_to_int32(str, NULL, &num, base) != StrintParseNoError) {

View File

@@ -11,6 +11,10 @@
#include <mjs_primitive_public.h>
#include <mjs_array_buf_public.h>
#ifdef __cplusplus
extern "C" {
#endif
#define INST_PROP_NAME "_"
typedef enum {
@@ -23,3 +27,7 @@ bool js_delay_with_flags(struct mjs* mjs, uint32_t time);
void js_flags_set(struct mjs* mjs, uint32_t flags);
uint32_t js_flags_wait(struct mjs* mjs, uint32_t flags, uint32_t timeout);
#ifdef __cplusplus
}
#endif

View File

@@ -144,10 +144,16 @@ static void js_event_loop_subscribe(struct mjs* mjs) {
JsEventLoop* module = JS_GET_CONTEXT(mjs);
// get arguments
static const JsValueDeclaration js_loop_subscribe_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeRawPointer),
JS_VALUE_SIMPLE(JsValueTypeFunction),
};
static const JsValueArguments js_loop_subscribe_args =
JS_VALUE_ARGS(js_loop_subscribe_arg_list);
JsEventLoopContract* contract;
mjs_val_t callback;
JS_FETCH_ARGS_OR_RETURN(
mjs, JS_AT_LEAST, JS_ARG_STRUCT(JsEventLoopContract, &contract), JS_ARG_FN(&callback));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_subscribe_args, &contract, &callback);
// create subscription object
JsEventLoopSubscription* subscription = malloc(sizeof(JsEventLoopSubscription));
@@ -242,20 +248,22 @@ static void js_event_loop_stop(struct mjs* mjs) {
* event
*/
static void js_event_loop_timer(struct mjs* mjs) {
// get arguments
const char* mode_str;
int32_t interval;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&mode_str), JS_ARG_INT32(&interval));
JsEventLoop* module = JS_GET_CONTEXT(mjs);
static const JsValueEnumVariant js_loop_timer_mode_variants[] = {
{"periodic", FuriEventLoopTimerTypePeriodic},
{"oneshot", FuriEventLoopTimerTypeOnce},
};
static const JsValueDeclaration js_loop_timer_arg_list[] = {
JS_VALUE_ENUM(FuriEventLoopTimerType, js_loop_timer_mode_variants),
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_loop_timer_args = JS_VALUE_ARGS(js_loop_timer_arg_list);
FuriEventLoopTimerType mode;
if(strcasecmp(mode_str, "periodic") == 0) {
mode = FuriEventLoopTimerTypePeriodic;
} else if(strcasecmp(mode_str, "oneshot") == 0) {
mode = FuriEventLoopTimerTypeOnce;
} else {
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "argument 0: unknown mode");
}
int32_t interval;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_timer_args, &mode, &interval);
JsEventLoop* module = JS_GET_CONTEXT(mjs);
// make timer contract
JsEventLoopContract* contract = malloc(sizeof(JsEventLoopContract));
@@ -293,8 +301,14 @@ static mjs_val_t
*/
static void js_event_loop_queue_send(struct mjs* mjs) {
// get arguments
static const JsValueDeclaration js_loop_q_send_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeAny),
};
static const JsValueArguments js_loop_q_send_args = JS_VALUE_ARGS(js_loop_q_send_arg_list);
mjs_val_t message;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&message));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_q_send_args, &message);
JsEventLoopContract* contract = JS_GET_CONTEXT(mjs);
// send message
@@ -311,8 +325,14 @@ static void js_event_loop_queue_send(struct mjs* mjs) {
*/
static void js_event_loop_queue(struct mjs* mjs) {
// get arguments
static const JsValueDeclaration js_loop_q_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_loop_q_args = JS_VALUE_ARGS(js_loop_q_arg_list);
int32_t length;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&length));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_q_args, &length);
JsEventLoop* module = JS_GET_CONTEXT(mjs);
// make queue contract

View File

@@ -54,83 +54,114 @@ static void js_gpio_int_cb(void* arg) {
* ```
*/
static void js_gpio_init(struct mjs* mjs) {
// deconstruct mode object
mjs_val_t mode_arg;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_OBJ(&mode_arg));
mjs_val_t direction_arg = mjs_get(mjs, mode_arg, "direction", ~0);
mjs_val_t out_mode_arg = mjs_get(mjs, mode_arg, "outMode", ~0);
mjs_val_t in_mode_arg = mjs_get(mjs, mode_arg, "inMode", ~0);
mjs_val_t edge_arg = mjs_get(mjs, mode_arg, "edge", ~0);
mjs_val_t pull_arg = mjs_get(mjs, mode_arg, "pull", ~0);
// direction variants
typedef enum {
JsGpioDirectionIn,
JsGpioDirectionOut,
} JsGpioDirection;
static const JsValueEnumVariant js_gpio_direction_variants[] = {
{"in", JsGpioDirectionIn},
{"out", JsGpioDirectionOut},
};
static const JsValueDeclaration js_gpio_direction =
JS_VALUE_ENUM(JsGpioDirection, js_gpio_direction_variants);
// get strings
const char* direction = mjs_get_string(mjs, &direction_arg, NULL);
const char* out_mode = mjs_get_string(mjs, &out_mode_arg, NULL);
const char* in_mode = mjs_get_string(mjs, &in_mode_arg, NULL);
const char* edge = mjs_get_string(mjs, &edge_arg, NULL);
const char* pull = mjs_get_string(mjs, &pull_arg, NULL);
if(!direction)
JS_ERROR_AND_RETURN(
mjs, MJS_BAD_ARGS_ERROR, "Expected string in \"direction\" field of mode object");
if(!out_mode) out_mode = "open_drain";
if(!in_mode) in_mode = "plain_digital";
if(!edge) edge = "rising";
// inMode variants
typedef enum {
JsGpioInModeAnalog = (0 << 0),
JsGpioInModePlainDigital = (1 << 0),
JsGpioInModeInterrupt = (2 << 0),
JsGpioInModeEvent = (3 << 0),
} JsGpioInMode;
static const JsValueEnumVariant js_gpio_in_mode_variants[] = {
{"analog", JsGpioInModeAnalog},
{"plain_digital", JsGpioInModePlainDigital},
{"interrupt", JsGpioInModeInterrupt},
{"event", JsGpioInModeEvent},
};
static const JsValueDeclaration js_gpio_in_mode =
JS_VALUE_ENUM_W_DEFAULT(JsGpioInMode, js_gpio_in_mode_variants, JsGpioInModePlainDigital);
// outMode variants
typedef enum {
JsGpioOutModePushPull,
JsGpioOutModeOpenDrain,
} JsGpioOutMode;
static const JsValueEnumVariant js_gpio_out_mode_variants[] = {
{"push_pull", JsGpioOutModePushPull},
{"open_drain", JsGpioOutModeOpenDrain},
};
static const JsValueDeclaration js_gpio_out_mode =
JS_VALUE_ENUM_W_DEFAULT(JsGpioOutMode, js_gpio_out_mode_variants, JsGpioOutModeOpenDrain);
// edge variants
typedef enum {
JsGpioEdgeRising = (0 << 2),
JsGpioEdgeFalling = (1 << 2),
JsGpioEdgeBoth = (2 << 2),
} JsGpioEdge;
static const JsValueEnumVariant js_gpio_edge_variants[] = {
{"rising", JsGpioEdgeRising},
{"falling", JsGpioEdgeFalling},
{"both", JsGpioEdgeBoth},
};
static const JsValueDeclaration js_gpio_edge =
JS_VALUE_ENUM_W_DEFAULT(JsGpioEdge, js_gpio_edge_variants, JsGpioEdgeRising);
// pull variants
static const JsValueEnumVariant js_gpio_pull_variants[] = {
{"up", GpioPullUp},
{"down", GpioPullDown},
};
static const JsValueDeclaration js_gpio_pull =
JS_VALUE_ENUM_W_DEFAULT(GpioPull, js_gpio_pull_variants, GpioPullNo);
// complete mode object
static const JsValueObjectField js_gpio_mode_object_fields[] = {
{"direction", &js_gpio_direction},
{"inMode", &js_gpio_in_mode},
{"outMode", &js_gpio_out_mode},
{"edge", &js_gpio_edge},
{"pull", &js_gpio_pull},
};
// function args
static const JsValueDeclaration js_gpio_init_arg_list[] = {
JS_VALUE_OBJECT_W_DEFAULTS(js_gpio_mode_object_fields),
};
static const JsValueArguments js_gpio_init_args = JS_VALUE_ARGS(js_gpio_init_arg_list);
JsGpioDirection direction;
JsGpioInMode in_mode;
JsGpioOutMode out_mode;
JsGpioEdge edge;
GpioPull pull;
JS_VALUE_PARSE_ARGS_OR_RETURN(
mjs, &js_gpio_init_args, &direction, &in_mode, &out_mode, &edge, &pull);
// convert strings to mode
GpioMode mode;
if(strcmp(direction, "out") == 0) {
if(strcmp(out_mode, "push_pull") == 0)
mode = GpioModeOutputPushPull;
else if(strcmp(out_mode, "open_drain") == 0)
mode = GpioModeOutputOpenDrain;
else
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid outMode");
} else if(strcmp(direction, "in") == 0) {
if(strcmp(in_mode, "analog") == 0) {
mode = GpioModeAnalog;
} else if(strcmp(in_mode, "plain_digital") == 0) {
mode = GpioModeInput;
} else if(strcmp(in_mode, "interrupt") == 0) {
if(strcmp(edge, "rising") == 0)
mode = GpioModeInterruptRise;
else if(strcmp(edge, "falling") == 0)
mode = GpioModeInterruptFall;
else if(strcmp(edge, "both") == 0)
mode = GpioModeInterruptRiseFall;
else
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid edge");
} else if(strcmp(in_mode, "event") == 0) {
if(strcmp(edge, "rising") == 0)
mode = GpioModeEventRise;
else if(strcmp(edge, "falling") == 0)
mode = GpioModeEventFall;
else if(strcmp(edge, "both") == 0)
mode = GpioModeEventRiseFall;
else
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid edge");
if(direction == JsGpioDirectionOut) {
static const GpioMode js_gpio_out_mode_lut[] = {
[JsGpioOutModePushPull] = GpioModeOutputPushPull,
[JsGpioOutModeOpenDrain] = GpioModeOutputOpenDrain,
};
mode = js_gpio_out_mode_lut[out_mode];
} else {
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid inMode");
}
} else {
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid direction");
static const GpioMode js_gpio_in_mode_lut[] = {
[JsGpioInModeAnalog] = GpioModeAnalog,
[JsGpioInModePlainDigital] = GpioModeInput,
[JsGpioInModeInterrupt | JsGpioEdgeRising] = GpioModeInterruptRise,
[JsGpioInModeInterrupt | JsGpioEdgeFalling] = GpioModeInterruptFall,
[JsGpioInModeInterrupt | JsGpioEdgeBoth] = GpioModeInterruptRiseFall,
[JsGpioInModeEvent | JsGpioEdgeRising] = GpioModeEventRise,
[JsGpioInModeEvent | JsGpioEdgeFalling] = GpioModeEventFall,
[JsGpioInModeEvent | JsGpioEdgeBoth] = GpioModeEventRiseFall,
};
mode = js_gpio_in_mode_lut[in_mode | edge];
}
// convert pull
GpioPull pull_mode;
if(!pull) {
pull_mode = GpioPullNo;
} else if(strcmp(pull, "up") == 0) {
pull_mode = GpioPullUp;
} else if(strcmp(pull, "down") == 0) {
pull_mode = GpioPullDown;
} else {
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid pull");
}
// init GPIO
JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);
furi_hal_gpio_init(manager_data->pin, mode, pull_mode, GpioSpeedVeryHigh);
mjs_return(mjs, MJS_UNDEFINED);
furi_hal_gpio_init(manager_data->pin, mode, pull, GpioSpeedVeryHigh);
}
/**
@@ -146,8 +177,13 @@ static void js_gpio_init(struct mjs* mjs) {
* ```
*/
static void js_gpio_write(struct mjs* mjs) {
static const JsValueDeclaration js_gpio_write_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeBool),
};
static const JsValueArguments js_gpio_write_args = JS_VALUE_ARGS(js_gpio_write_arg_list);
bool level;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_BOOL(&level));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_write_args, &level);
JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);
furi_hal_gpio_write(manager_data->pin, level);
mjs_return(mjs, MJS_UNDEFINED);
@@ -261,9 +297,16 @@ static void js_gpio_is_pwm_supported(struct mjs* mjs) {
* ```
*/
static void js_gpio_pwm_write(struct mjs* mjs) {
JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);
static const JsValueDeclaration js_gpio_pwm_write_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeInt32),
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_gpio_pwm_write_args =
JS_VALUE_ARGS(js_gpio_pwm_write_arg_list);
int32_t frequency, duty;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&frequency), JS_ARG_INT32(&duty));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_pwm_write_args, &frequency, &duty);
JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);
if(manager_data->pwm_output == FuriHalPwmOutputIdNone) {
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "PWM is not supported on this pin");
}
@@ -326,8 +369,13 @@ static void js_gpio_pwm_stop(struct mjs* mjs) {
* ```
*/
static void js_gpio_get(struct mjs* mjs) {
static const JsValueDeclaration js_gpio_get_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeAny),
};
static const JsValueArguments js_gpio_get_args = JS_VALUE_ARGS(js_gpio_get_arg_list);
mjs_val_t name_arg;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&name_arg));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_get_args, &name_arg);
const char* name_string = mjs_get_string(mjs, &name_arg, NULL);
const GpioPinRecord* pin_record = NULL;

View File

@@ -3,8 +3,14 @@
#include <assets_icons.h>
static void js_gui_file_picker_pick_file(struct mjs* mjs) {
static const JsValueDeclaration js_picker_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeString),
JS_VALUE_SIMPLE(JsValueTypeString),
};
static const JsValueArguments js_picker_args = JS_VALUE_ARGS(js_picker_arg_list);
const char *base_path, *extension;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&base_path), JS_ARG_STR(&extension));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_picker_args, &base_path, &extension);
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
const DialogsFileBrowserOptions browser_options = {

View File

@@ -39,9 +39,14 @@ typedef struct {
FxbmIconWrapperList_t fxbm_list;
} JsGuiIconInst;
static const JsValueDeclaration js_icon_get_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeString),
};
static const JsValueArguments js_icon_get_args = JS_VALUE_ARGS(js_icon_get_arg_list);
static void js_gui_icon_get_builtin(struct mjs* mjs) {
const char* icon_name;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&icon_name));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_icon_get_args, &icon_name);
for(size_t i = 0; i < COUNT_OF(builtin_icons); i++) {
if(strcmp(icon_name, builtin_icons[i].name) == 0) {
@@ -55,7 +60,7 @@ static void js_gui_icon_get_builtin(struct mjs* mjs) {
static void js_gui_icon_load_fxbm(struct mjs* mjs) {
const char* fxbm_path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&fxbm_path));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_icon_get_args, &fxbm_path);
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);

View File

@@ -68,8 +68,14 @@ static bool js_gui_vd_nav_callback(void* context) {
* @brief `viewDispatcher.sendCustom`
*/
static void js_gui_vd_send_custom(struct mjs* mjs) {
static const JsValueDeclaration js_gui_vd_send_custom_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_gui_vd_send_custom_args =
JS_VALUE_ARGS(js_gui_vd_send_custom_arg_list);
int32_t event;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&event));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_send_custom_args, &event);
JsGui* module = JS_GET_CONTEXT(mjs);
view_dispatcher_send_custom_event(module->dispatcher, (uint32_t)event);
@@ -79,15 +85,25 @@ static void js_gui_vd_send_custom(struct mjs* mjs) {
* @brief `viewDispatcher.sendTo`
*/
static void js_gui_vd_send_to(struct mjs* mjs) {
enum {
SendDirToFront,
SendDirToBack,
} send_direction;
JS_ENUM_MAP(send_direction, {"front", SendDirToFront}, {"back", SendDirToBack});
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ENUM(send_direction, "SendDirection"));
typedef enum {
JsSendDirToFront,
JsSendDirToBack,
} JsSendDir;
static const JsValueEnumVariant js_send_dir_variants[] = {
{"front", JsSendDirToFront},
{"back", JsSendDirToBack},
};
static const JsValueDeclaration js_gui_vd_send_to_arg_list[] = {
JS_VALUE_ENUM(JsSendDir, js_send_dir_variants),
};
static const JsValueArguments js_gui_vd_send_to_args =
JS_VALUE_ARGS(js_gui_vd_send_to_arg_list);
JsSendDir send_direction;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_send_to_args, &send_direction);
JsGui* module = JS_GET_CONTEXT(mjs);
if(send_direction == SendDirToBack) {
if(send_direction == JsSendDirToBack) {
view_dispatcher_send_to_back(module->dispatcher);
} else {
view_dispatcher_send_to_front(module->dispatcher);
@@ -98,8 +114,15 @@ static void js_gui_vd_send_to(struct mjs* mjs) {
* @brief `viewDispatcher.switchTo`
*/
static void js_gui_vd_switch_to(struct mjs* mjs) {
static const JsValueDeclaration js_gui_vd_switch_to_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeAny),
};
static const JsValueArguments js_gui_vd_switch_to_args =
JS_VALUE_ARGS(js_gui_vd_switch_to_arg_list);
mjs_val_t view;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_OBJ(&view));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_switch_to_args, &view);
JsGuiViewData* view_data = JS_GET_INST(mjs, view);
mjs_val_t vd_obj = mjs_get_this(mjs);
JsGui* module = JS_GET_INST(mjs, vd_obj);
@@ -267,9 +290,16 @@ static bool
* @brief `View.set`
*/
static void js_gui_view_set(struct mjs* mjs) {
static const JsValueDeclaration js_gui_view_set_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeString),
JS_VALUE_SIMPLE(JsValueTypeAny),
};
static const JsValueArguments js_gui_view_set_args = JS_VALUE_ARGS(js_gui_view_set_arg_list);
const char* name;
mjs_val_t value;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&name), JS_ARG_ANY(&value));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_set_args, &name, &value);
JsGuiViewData* data = JS_GET_CONTEXT(mjs);
bool success = js_gui_view_assign(mjs, name, value, data);
UNUSED(success);
@@ -280,12 +310,19 @@ static void js_gui_view_set(struct mjs* mjs) {
* @brief `View.addChild`
*/
static void js_gui_view_add_child(struct mjs* mjs) {
static const JsValueDeclaration js_gui_view_add_child_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeAny),
};
static const JsValueArguments js_gui_view_add_child_args =
JS_VALUE_ARGS(js_gui_view_add_child_arg_list);
mjs_val_t child;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_add_child_args, &child);
JsGuiViewData* data = JS_GET_CONTEXT(mjs);
if(!data->descriptor->add_child || !data->descriptor->reset_children)
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "this View can't have children");
mjs_val_t child;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&child));
bool success = data->descriptor->add_child(mjs, data->specific_view, data->custom_data, child);
UNUSED(success);
mjs_return(mjs, MJS_UNDEFINED);
@@ -307,12 +344,19 @@ static void js_gui_view_reset_children(struct mjs* mjs) {
* @brief `View.setChildren`
*/
static void js_gui_view_set_children(struct mjs* mjs) {
static const JsValueDeclaration js_gui_view_set_children_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeAnyArray),
};
static const JsValueArguments js_gui_view_set_children_args =
JS_VALUE_ARGS(js_gui_view_set_children_arg_list);
mjs_val_t children;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_set_children_args, &children);
JsGuiViewData* data = JS_GET_CONTEXT(mjs);
if(!data->descriptor->add_child || !data->descriptor->reset_children)
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "this View can't have children");
mjs_val_t children;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ARR(&children));
js_gui_view_internal_set_children(mjs, children, data);
}
@@ -365,7 +409,6 @@ static mjs_val_t js_gui_make_view(struct mjs* mjs, const JsViewDescriptor* descr
* @brief `ViewFactory.make`
*/
static void js_gui_vf_make(struct mjs* mjs) {
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args
const JsViewDescriptor* descriptor = JS_GET_CONTEXT(mjs);
mjs_return(mjs, js_gui_make_view(mjs, descriptor));
}
@@ -374,8 +417,15 @@ static void js_gui_vf_make(struct mjs* mjs) {
* @brief `ViewFactory.makeWith`
*/
static void js_gui_vf_make_with(struct mjs* mjs) {
mjs_val_t props;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_AT_LEAST, JS_ARG_OBJ(&props));
static const JsValueDeclaration js_gui_vf_make_with_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeAnyObject),
JS_VALUE_SIMPLE(JsValueTypeAny),
};
static const JsValueArguments js_gui_vf_make_with_args =
JS_VALUE_ARGS(js_gui_vf_make_with_arg_list);
mjs_val_t props, children;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vf_make_with_args, &props, &children);
const JsViewDescriptor* descriptor = JS_GET_CONTEXT(mjs);
// make the object like normal
@@ -396,14 +446,10 @@ static void js_gui_vf_make_with(struct mjs* mjs) {
}
// assign children
if(mjs_nargs(mjs) >= 2) {
if(mjs_is_array(children)) {
if(!data->descriptor->add_child || !data->descriptor->reset_children)
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "this View can't have children");
mjs_val_t children = mjs_arg(mjs, 1);
if(!mjs_is_array(children))
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "argument 1: expected array");
if(!js_gui_view_internal_set_children(mjs, children, data)) return;
}

View File

@@ -35,60 +35,62 @@ static void
}
static void js_serial_setup(struct mjs* mjs) {
FuriHalSerialId serial_id;
int32_t baudrate;
JS_ENUM_MAP(serial_id, {"lpuart", FuriHalSerialIdLpuart}, {"usart", FuriHalSerialIdUsart});
JS_FETCH_ARGS_OR_RETURN(
mjs, JS_AT_LEAST, JS_ARG_ENUM(serial_id, "SerialId"), JS_ARG_INT32(&baudrate));
static const JsValueEnumVariant js_serial_id_variants[] = {
{"lpuart", FuriHalSerialIdLpuart},
{"usart", FuriHalSerialIdUsart},
};
FuriHalSerialDataBits data_bits = FuriHalSerialDataBits8;
FuriHalSerialParity parity = FuriHalSerialParityNone;
FuriHalSerialStopBits stop_bits = FuriHalSerialStopBits1;
if(mjs_nargs(mjs) > 2) {
struct framing {
mjs_val_t data_bits;
mjs_val_t parity;
mjs_val_t stop_bits;
} framing;
JS_OBJ_MAP(
framing,
{"dataBits", offsetof(struct framing, data_bits)},
{"parity", offsetof(struct framing, parity)},
{"stopBits", offsetof(struct framing, stop_bits)});
JS_ENUM_MAP(
data_bits,
static const JsValueEnumVariant js_serial_data_bit_variants[] = {
{"6", FuriHalSerialDataBits6},
{"7", FuriHalSerialDataBits7},
{"8", FuriHalSerialDataBits8},
{"9", FuriHalSerialDataBits9});
JS_ENUM_MAP(
parity,
{"9", FuriHalSerialDataBits9},
};
static const JsValueDeclaration js_serial_data_bits = JS_VALUE_ENUM_W_DEFAULT(
FuriHalSerialDataBits, js_serial_data_bit_variants, FuriHalSerialDataBits8);
static const JsValueEnumVariant js_serial_parity_variants[] = {
{"none", FuriHalSerialParityNone},
{"even", FuriHalSerialParityEven},
{"odd", FuriHalSerialParityOdd});
JS_ENUM_MAP(
stop_bits,
{"odd", FuriHalSerialParityOdd},
};
static const JsValueDeclaration js_serial_parity = JS_VALUE_ENUM_W_DEFAULT(
FuriHalSerialParity, js_serial_parity_variants, FuriHalSerialParityNone);
static const JsValueEnumVariant js_serial_stop_bit_variants[] = {
{"0.5", FuriHalSerialStopBits0_5},
{"1", FuriHalSerialStopBits1},
{"1.5", FuriHalSerialStopBits1_5},
{"2", FuriHalSerialStopBits2});
mjs_val_t framing_obj = mjs_arg(mjs, 2);
JS_CONVERT_OR_RETURN(mjs, &framing_obj, JS_ARG_OBJECT(framing, "Framing"), "argument 2");
JS_CONVERT_OR_RETURN(
mjs, &framing.data_bits, JS_ARG_ENUM(data_bits, "DataBits"), "argument 2: dataBits");
JS_CONVERT_OR_RETURN(
mjs, &framing.parity, JS_ARG_ENUM(parity, "Parity"), "argument 2: parity");
JS_CONVERT_OR_RETURN(
mjs, &framing.stop_bits, JS_ARG_ENUM(stop_bits, "StopBits"), "argument 2: stopBits");
}
{"2", FuriHalSerialStopBits2},
};
static const JsValueDeclaration js_serial_stop_bits = JS_VALUE_ENUM_W_DEFAULT(
FuriHalSerialStopBits, js_serial_stop_bit_variants, FuriHalSerialStopBits1);
static const JsValueObjectField js_serial_framing_fields[] = {
{"dataBits", &js_serial_data_bits},
{"parity", &js_serial_parity},
{"stopBits", &js_serial_stop_bits},
};
static const JsValueDeclaration js_serial_setup_arg_list[] = {
JS_VALUE_ENUM(FuriHalSerialId, js_serial_id_variants),
JS_VALUE_SIMPLE(JsValueTypeInt32),
JS_VALUE_OBJECT_W_DEFAULTS(js_serial_framing_fields),
};
static const JsValueArguments js_serial_setup_args = JS_VALUE_ARGS(js_serial_setup_arg_list);
FuriHalSerialId serial_id;
int32_t baudrate;
FuriHalSerialDataBits data_bits = FuriHalSerialDataBits8;
FuriHalSerialParity parity = FuriHalSerialParityNone;
FuriHalSerialStopBits stop_bits = FuriHalSerialStopBits1;
JS_VALUE_PARSE_ARGS_OR_RETURN(
mjs, &js_serial_setup_args, &serial_id, &baudrate, &data_bits, &parity, &stop_bits);
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
if(serial->setup_done) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is already configured");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
if(serial->setup_done)
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is already configured");
expansion_disable(furi_record_open(RECORD_EXPANSION));
furi_record_close(RECORD_EXPANSION);
@@ -123,28 +125,20 @@ static void js_serial_deinit(JsSerialInst* js_serial) {
}
static void js_serial_end(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
furi_assert(serial);
if(!serial->setup_done) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
if(!serial->setup_done)
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
js_serial_deinit(serial);
}
static void js_serial_write(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
furi_assert(serial);
if(!serial->setup_done) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
if(!serial->setup_done)
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
bool args_correct = true;
@@ -228,43 +222,20 @@ static size_t js_serial_receive(JsSerialInst* serial, char* buf, size_t len, uin
return bytes_read;
}
static const JsValueDeclaration js_serial_read_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeInt32),
JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, INT32_MAX),
};
static const JsValueArguments js_serial_read_args = JS_VALUE_ARGS(js_serial_read_arg_list);
static void js_serial_read(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
furi_assert(serial);
if(!serial->setup_done) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
if(!serial->setup_done)
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
size_t read_len = 0;
uint32_t timeout = FuriWaitForever;
do {
size_t num_args = mjs_nargs(mjs);
if(num_args == 1) {
mjs_val_t arg = mjs_arg(mjs, 0);
if(!mjs_is_number(arg)) {
break;
}
read_len = mjs_get_int32(mjs, arg);
} else if(num_args == 2) {
mjs_val_t len_arg = mjs_arg(mjs, 0);
mjs_val_t timeout_arg = mjs_arg(mjs, 1);
if((!mjs_is_number(len_arg)) || (!mjs_is_number(timeout_arg))) {
break;
}
read_len = mjs_get_int32(mjs, len_arg);
timeout = mjs_get_int32(mjs, timeout_arg);
}
} while(0);
if(read_len == 0) {
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
int32_t read_len, timeout;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_args, &read_len, &timeout);
char* read_buf = malloc(read_len);
size_t bytes_read = js_serial_receive(serial, read_buf, read_len, timeout);
@@ -278,37 +249,19 @@ static void js_serial_read(struct mjs* mjs) {
}
static void js_serial_readln(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
furi_assert(serial);
if(!serial->setup_done) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
if(!serial->setup_done)
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
bool args_correct = false;
uint32_t timeout = FuriWaitForever;
static const JsValueDeclaration js_serial_readln_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_serial_readln_args = JS_VALUE_ARGS(js_serial_readln_arg_list);
do {
size_t num_args = mjs_nargs(mjs);
if(num_args > 1) {
break;
} else if(num_args == 1) {
mjs_val_t arg = mjs_arg(mjs, 0);
if(!mjs_is_number(arg)) {
break;
}
timeout = mjs_get_int32(mjs, arg);
}
args_correct = true;
} while(0);
int32_t timeout;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_readln_args, &timeout);
if(!args_correct) {
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
FuriString* rx_buf = furi_string_alloc();
size_t bytes_read = 0;
char read_char = 0;
@@ -335,42 +288,13 @@ static void js_serial_readln(struct mjs* mjs) {
}
static void js_serial_read_bytes(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
furi_assert(serial);
if(!serial->setup_done) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
if(!serial->setup_done)
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
size_t read_len = 0;
uint32_t timeout = FuriWaitForever;
do {
size_t num_args = mjs_nargs(mjs);
if(num_args == 1) {
mjs_val_t arg = mjs_arg(mjs, 0);
if(!mjs_is_number(arg)) {
break;
}
read_len = mjs_get_int32(mjs, arg);
} else if(num_args == 2) {
mjs_val_t len_arg = mjs_arg(mjs, 0);
mjs_val_t timeout_arg = mjs_arg(mjs, 1);
if((!mjs_is_number(len_arg)) || (!mjs_is_number(timeout_arg))) {
break;
}
read_len = mjs_get_int32(mjs, len_arg);
timeout = mjs_get_int32(mjs, timeout_arg);
}
} while(0);
if(read_len == 0) {
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
int32_t read_len, timeout;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_args, &read_len, &timeout);
char* read_buf = malloc(read_len);
size_t bytes_read = js_serial_receive(serial, read_buf, read_len, timeout);
@@ -399,27 +323,19 @@ static char* js_serial_receive_any(JsSerialInst* serial, size_t* len, uint32_t t
}
static void js_serial_read_any(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
furi_assert(serial);
if(!serial->setup_done) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
if(!serial->setup_done)
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
uint32_t timeout = FuriWaitForever;
static const JsValueDeclaration js_serial_read_any_arg_list[] = {
JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, INT32_MAX),
};
static const JsValueArguments js_serial_read_any_args =
JS_VALUE_ARGS(js_serial_read_any_arg_list);
do {
size_t num_args = mjs_nargs(mjs);
if(num_args == 1) {
mjs_val_t timeout_arg = mjs_arg(mjs, 0);
if(!mjs_is_number(timeout_arg)) {
break;
}
timeout = mjs_get_int32(mjs, timeout_arg);
}
} while(0);
int32_t timeout;
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_any_args, &timeout);
size_t bytes_read = 0;
char* read_buf = js_serial_receive_any(serial, &bytes_read, timeout);
@@ -663,16 +579,19 @@ static void* js_serial_create(struct mjs* mjs, mjs_val_t* object, JsModules* mod
UNUSED(modules);
JsSerialInst* js_serial = malloc(sizeof(JsSerialInst));
js_serial->mjs = mjs;
mjs_val_t serial_obj = mjs_mk_object(mjs);
mjs_set(mjs, serial_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, js_serial));
mjs_set(mjs, serial_obj, "setup", ~0, MJS_MK_FN(js_serial_setup));
mjs_set(mjs, serial_obj, "end", ~0, MJS_MK_FN(js_serial_end));
mjs_set(mjs, serial_obj, "write", ~0, MJS_MK_FN(js_serial_write));
mjs_set(mjs, serial_obj, "read", ~0, MJS_MK_FN(js_serial_read));
mjs_set(mjs, serial_obj, "readln", ~0, MJS_MK_FN(js_serial_readln));
mjs_set(mjs, serial_obj, "readBytes", ~0, MJS_MK_FN(js_serial_read_bytes));
mjs_set(mjs, serial_obj, "readAny", ~0, MJS_MK_FN(js_serial_read_any));
mjs_set(mjs, serial_obj, "expect", ~0, MJS_MK_FN(js_serial_expect));
JS_ASSIGN_MULTI(mjs, serial_obj) {
JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, js_serial));
JS_FIELD("setup", MJS_MK_FN(js_serial_setup));
JS_FIELD("end", MJS_MK_FN(js_serial_end));
JS_FIELD("write", MJS_MK_FN(js_serial_write));
JS_FIELD("read", MJS_MK_FN(js_serial_read));
JS_FIELD("readln", MJS_MK_FN(js_serial_readln));
JS_FIELD("readBytes", MJS_MK_FN(js_serial_read_bytes));
JS_FIELD("readAny", MJS_MK_FN(js_serial_read_any));
JS_FIELD("expect", MJS_MK_FN(js_serial_expect));
}
*object = serial_obj;
return js_serial;

View File

@@ -1,42 +1,79 @@
#include "../js_modules.h" // IWYU pragma: keep
#include <path.h>
// ---=== file ops ===---
// ==========================
// Common argument signatures
// ==========================
static const JsValueDeclaration js_storage_1_int_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_storage_1_int_args = JS_VALUE_ARGS(js_storage_1_int_arg_list);
static const JsValueDeclaration js_storage_1_str_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeString),
};
static const JsValueArguments js_storage_1_str_args = JS_VALUE_ARGS(js_storage_1_str_arg_list);
static const JsValueDeclaration js_storage_2_str_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeString),
JS_VALUE_SIMPLE(JsValueTypeString),
};
static const JsValueArguments js_storage_2_str_args = JS_VALUE_ARGS(js_storage_2_str_arg_list);
// ======================
// File object operations
// ======================
static void js_storage_file_close(struct mjs* mjs) {
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args
File* file = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_close(file)));
}
static void js_storage_file_is_open(struct mjs* mjs) {
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args
File* file = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_is_open(file)));
}
static void js_storage_file_read(struct mjs* mjs) {
enum {
ReadModeAscii,
ReadModeBinary,
} read_mode;
JS_ENUM_MAP(read_mode, {"ascii", ReadModeAscii}, {"binary", ReadModeBinary});
typedef enum {
JsStorageReadModeAscii,
JsStorageReadModeBinary,
} JsStorageReadMode;
static const JsValueEnumVariant js_storage_read_mode_variants[] = {
{"ascii", JsStorageReadModeAscii},
{"binary", JsStorageReadModeBinary},
};
static const JsValueDeclaration js_storage_read_arg_list[] = {
JS_VALUE_ENUM(JsStorageReadMode, js_storage_read_mode_variants),
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_storage_read_args = JS_VALUE_ARGS(js_storage_read_arg_list);
JsStorageReadMode read_mode;
int32_t length;
JS_FETCH_ARGS_OR_RETURN(
mjs, JS_EXACTLY, JS_ARG_ENUM(read_mode, "ReadMode"), JS_ARG_INT32(&length));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_read_args, &read_mode, &length);
File* file = JS_GET_CONTEXT(mjs);
char buffer[length];
size_t actually_read = storage_file_read(file, buffer, length);
if(read_mode == ReadModeAscii) {
if(read_mode == JsStorageReadModeAscii) {
mjs_return(mjs, mjs_mk_string(mjs, buffer, actually_read, true));
} else if(read_mode == ReadModeBinary) {
} else if(read_mode == JsStorageReadModeBinary) {
mjs_return(mjs, mjs_mk_array_buf(mjs, buffer, actually_read));
}
}
static void js_storage_file_write(struct mjs* mjs) {
static const JsValueDeclaration js_storage_file_write_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeAny),
};
static const JsValueArguments js_storage_file_write_args =
JS_VALUE_ARGS(js_storage_file_write_arg_list);
mjs_val_t data;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&data));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_file_write_args, &data);
const void* buf;
size_t len;
if(mjs_is_string(data)) {
@@ -52,52 +89,58 @@ static void js_storage_file_write(struct mjs* mjs) {
static void js_storage_file_seek_relative(struct mjs* mjs) {
int32_t offset;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&offset));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_int_args, &offset);
File* file = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_seek(file, offset, false)));
}
static void js_storage_file_seek_absolute(struct mjs* mjs) {
int32_t offset;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&offset));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_int_args, &offset);
File* file = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_seek(file, offset, true)));
}
static void js_storage_file_tell(struct mjs* mjs) {
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args
File* file = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_number(mjs, storage_file_tell(file)));
}
static void js_storage_file_truncate(struct mjs* mjs) {
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args
File* file = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_truncate(file)));
}
static void js_storage_file_size(struct mjs* mjs) {
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args
File* file = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_number(mjs, storage_file_size(file)));
}
static void js_storage_file_eof(struct mjs* mjs) {
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args
File* file = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_eof(file)));
}
static void js_storage_file_copy_to(struct mjs* mjs) {
File* source = JS_GET_CONTEXT(mjs);
static const JsValueDeclaration js_storage_file_write_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeAny),
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_storage_file_write_args =
JS_VALUE_ARGS(js_storage_file_write_arg_list);
mjs_val_t dest_obj;
int32_t bytes;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_OBJ(&dest_obj), JS_ARG_INT32(&bytes));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_file_write_args, &dest_obj, &bytes);
File* source = JS_GET_CONTEXT(mjs);
File* destination = JS_GET_INST(mjs, dest_obj);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_copy_to_file(source, destination, bytes)));
}
// ---=== top-level file ops ===---
// =========================
// Top-level file operations
// =========================
// common destructor for file and dir objects
static void js_storage_file_destructor(struct mjs* mjs, mjs_val_t obj) {
@@ -106,23 +149,33 @@ static void js_storage_file_destructor(struct mjs* mjs, mjs_val_t obj) {
}
static void js_storage_open_file(struct mjs* mjs) {
const char* path;
FS_AccessMode access_mode;
FS_OpenMode open_mode;
JS_ENUM_MAP(access_mode, {"r", FSAM_READ}, {"w", FSAM_WRITE}, {"rw", FSAM_READ_WRITE});
JS_ENUM_MAP(
open_mode,
static const JsValueEnumVariant js_storage_fsam_variants[] = {
{"r", FSAM_READ},
{"w", FSAM_WRITE},
{"rw", FSAM_READ_WRITE},
};
static const JsValueEnumVariant js_storage_fsom_variants[] = {
{"open_existing", FSOM_OPEN_EXISTING},
{"open_always", FSOM_OPEN_ALWAYS},
{"open_append", FSOM_OPEN_APPEND},
{"create_new", FSOM_CREATE_NEW},
{"create_always", FSOM_CREATE_ALWAYS});
JS_FETCH_ARGS_OR_RETURN(
mjs,
JS_EXACTLY,
JS_ARG_STR(&path),
JS_ARG_ENUM(access_mode, "AccessMode"),
JS_ARG_ENUM(open_mode, "OpenMode"));
{"create_always", FSOM_CREATE_ALWAYS},
};
static const JsValueDeclaration js_storage_open_file_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeString),
JS_VALUE_ENUM(FS_AccessMode, js_storage_fsam_variants),
JS_VALUE_ENUM(FS_OpenMode, js_storage_fsom_variants),
};
static const JsValueArguments js_storage_open_file_args =
JS_VALUE_ARGS(js_storage_open_file_arg_list);
const char* path;
FS_AccessMode access_mode;
FS_OpenMode open_mode;
JS_VALUE_PARSE_ARGS_OR_RETURN(
mjs, &js_storage_open_file_args, &path, &access_mode, &open_mode);
Storage* storage = JS_GET_CONTEXT(mjs);
File* file = storage_file_alloc(storage);
@@ -152,16 +205,18 @@ static void js_storage_open_file(struct mjs* mjs) {
static void js_storage_file_exists(struct mjs* mjs) {
const char* path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);
Storage* storage = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_exists(storage, path)));
}
// ---=== dir ops ===---
// ====================
// Directory operations
// ====================
static void js_storage_read_directory(struct mjs* mjs) {
const char* path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);
Storage* storage = JS_GET_CONTEXT(mjs);
File* dir = storage_file_alloc(storage);
@@ -200,30 +255,32 @@ static void js_storage_read_directory(struct mjs* mjs) {
static void js_storage_directory_exists(struct mjs* mjs) {
const char* path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);
Storage* storage = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_dir_exists(storage, path)));
}
static void js_storage_make_directory(struct mjs* mjs) {
const char* path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);
Storage* storage = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_mkdir(storage, path)));
}
// ---=== common ops ===---
// =================
// Common operations
// =================
static void js_storage_file_or_dir_exists(struct mjs* mjs) {
const char* path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);
Storage* storage = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_exists(storage, path)));
}
static void js_storage_stat(struct mjs* mjs) {
const char* path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);
Storage* storage = JS_GET_CONTEXT(mjs);
FileInfo file_info;
uint32_t timestamp;
@@ -244,21 +301,21 @@ static void js_storage_stat(struct mjs* mjs) {
static void js_storage_remove(struct mjs* mjs) {
const char* path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);
Storage* storage = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_remove(storage, path)));
}
static void js_storage_rmrf(struct mjs* mjs) {
const char* path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path);
Storage* storage = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_remove_recursive(storage, path)));
}
static void js_storage_rename(struct mjs* mjs) {
const char *old, *new;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&old), JS_ARG_STR(&new));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &old, &new);
Storage* storage = JS_GET_CONTEXT(mjs);
FS_Error status = storage_common_rename(storage, old, new);
mjs_return(mjs, mjs_mk_boolean(mjs, status == FSE_OK));
@@ -266,7 +323,7 @@ static void js_storage_rename(struct mjs* mjs) {
static void js_storage_copy(struct mjs* mjs) {
const char *source, *dest;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&source), JS_ARG_STR(&dest));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &source, &dest);
Storage* storage = JS_GET_CONTEXT(mjs);
FS_Error status = storage_common_copy(storage, source, dest);
mjs_return(mjs, mjs_mk_boolean(mjs, status == FSE_OK || status == FSE_EXIST));
@@ -274,7 +331,7 @@ static void js_storage_copy(struct mjs* mjs) {
static void js_storage_fs_info(struct mjs* mjs) {
const char* fs;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&fs));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &fs);
Storage* storage = JS_GET_CONTEXT(mjs);
uint64_t total_space, free_space;
if(storage_common_fs_info(storage, fs, &total_space, &free_space) != FSE_OK) {
@@ -290,15 +347,19 @@ static void js_storage_fs_info(struct mjs* mjs) {
}
static void js_storage_next_available_filename(struct mjs* mjs) {
static const JsValueDeclaration js_storage_naf_arg_list[] = {
JS_VALUE_SIMPLE(JsValueTypeString),
JS_VALUE_SIMPLE(JsValueTypeString),
JS_VALUE_SIMPLE(JsValueTypeString),
JS_VALUE_SIMPLE(JsValueTypeInt32),
};
static const JsValueArguments js_storage_naf_args = JS_VALUE_ARGS(js_storage_naf_arg_list);
const char *dir_path, *file_name, *file_ext;
int32_t max_len;
JS_FETCH_ARGS_OR_RETURN(
mjs,
JS_EXACTLY,
JS_ARG_STR(&dir_path),
JS_ARG_STR(&file_name),
JS_ARG_STR(&file_ext),
JS_ARG_INT32(&max_len));
JS_VALUE_PARSE_ARGS_OR_RETURN(
mjs, &js_storage_naf_args, &dir_path, &file_name, &file_ext, &max_len);
Storage* storage = JS_GET_CONTEXT(mjs);
FuriString* next_name = furi_string_alloc();
storage_get_next_filename(storage, dir_path, file_name, file_ext, next_name, max_len);
@@ -306,23 +367,27 @@ static void js_storage_next_available_filename(struct mjs* mjs) {
furi_string_free(next_name);
}
// ---=== path ops ===---
// ===============
// Path operations
// ===============
static void js_storage_are_paths_equal(struct mjs* mjs) {
const char *path1, *path2;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path1), JS_ARG_STR(&path2));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &path1, &path2);
Storage* storage = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_equivalent_path(storage, path1, path2)));
}
static void js_storage_is_subpath_of(struct mjs* mjs) {
const char *parent, *child;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&parent), JS_ARG_STR(&child));
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &parent, &child);
Storage* storage = JS_GET_CONTEXT(mjs);
mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_is_subdir(storage, parent, child)));
}
// ---=== module ctor & dtor ===---
// ==================
// Module ctor & dtor
// ==================
static void* js_storage_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {
UNUSED(modules);
@@ -363,7 +428,9 @@ static void js_storage_destroy(void* data) {
furi_record_close(RECORD_STORAGE);
}
// ---=== boilerplate ===---
// ===========
// Boilerplate
// ===========
static const JsModuleDescriptor js_storage_desc = {
"storage",

View File

@@ -1,4 +1,5 @@
#include "js_plugin_api.h"
#include "../js_modules.h"
/*
* A list of app's private functions and objects to expose for plugins.
* It is used to generate a table of symbols for import resolver to use.
@@ -8,4 +9,16 @@ static constexpr auto app_api_table = sort(create_array_t<sym_entry>(
API_METHOD(js_delay_with_flags, bool, (struct mjs*, uint32_t)),
API_METHOD(js_flags_set, void, (struct mjs*, uint32_t)),
API_METHOD(js_flags_wait, uint32_t, (struct mjs*, uint32_t, uint32_t)),
API_METHOD(js_module_get, void*, (JsModules*, const char*))));
API_METHOD(js_module_get, void*, (JsModules*, const char*)),
API_METHOD(js_value_buffer_size, size_t, (const JsValueParseDeclaration declaration)),
API_METHOD(
js_value_parse,
JsValueParseStatus,
(struct mjs * mjs,
const JsValueParseDeclaration declaration,
JsValueParseFlag flags,
mjs_val_t* buffer,
size_t buf_size,
mjs_val_t* source,
size_t n_c_vals,
...))));

View File

@@ -1,22 +0,0 @@
#pragma once
#include <furi.h>
#include <mjs_core_public.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void JsModules;
bool js_delay_with_flags(struct mjs* mjs, uint32_t time);
void js_flags_set(struct mjs* mjs, uint32_t flags);
uint32_t js_flags_wait(struct mjs* mjs, uint32_t flags, uint32_t timeout);
void* js_module_get(JsModules* modules, const char* name);
#ifdef __cplusplus
}
#endif