Loading...
Searching...
No Matches
js_modules.h
1#pragma once
2
3#include <stdint.h>
4#include "js_thread_i.h"
6#include <flipper_application/plugins/plugin_manager.h>
7#include <flipper_application/plugins/composite_resolver.h>
8
9#define PLUGIN_APP_ID "js"
10#define PLUGIN_API_VERSION 1
11
12#define JS_SDK_VENDOR "flipperdevices"
13#define JS_SDK_MAJOR 0
14#define JS_SDK_MINOR 3
15
19#define JS_GET_INST(mjs, obj) mjs_get_ptr(mjs, mjs_get(mjs, obj, INST_PROP_NAME, ~0))
23#define JS_GET_CONTEXT(mjs) JS_GET_INST(mjs, mjs_get_this(mjs))
24
36#define JS_ASSIGN_MULTI(mjs, object) \
37 for(struct { \
38 struct mjs* mjs; \
39 mjs_val_t val; \
40 int i; \
41 } _ass_multi = {mjs, object, 0}; \
42 _ass_multi.i == 0; \
43 _ass_multi.i++)
44#define JS_FIELD(name, value) mjs_set(_ass_multi.mjs, _ass_multi.val, name, ~0, value)
45
61typedef enum {
62 JsForeignMagicStart = 0x15BAD000,
63 JsForeignMagic_JsEventLoopContract,
64} JsForeignMagic;
65
66// Are you tired of your silly little JS+C glue code functions being 75%
67// argument validation code and 25% actual logic? Introducing: ASS (Argument
68// Schema for Scripts)! ASS is a set of macros that reduce the typical
69// boilerplate code of "check argument count, get arguments, validate arguments,
70// extract C values from arguments" down to just one line!
71
76#define JS_EXACTLY ==
81#define JS_AT_LEAST >=
82
83typedef struct {
84 const char* name;
85 size_t value;
87
88#define JS_ENUM_MAP(var_name, ...) \
89 static const JsEnumMapping var_name##_mapping[] = { \
90 {NULL, sizeof(var_name)}, \
91 __VA_ARGS__, \
92 {NULL, 0}, \
93 };
94
95typedef struct {
96 const char* name;
97 size_t offset;
99
100#define JS_OBJ_MAP(var_name, ...) \
101 static const JsObjectMapping var_name##_mapping[] = { \
102 __VA_ARGS__, \
103 {NULL, 0}, \
104 };
105
106typedef struct {
107 void* out;
108 int (*validator)(mjs_val_t);
109 void (*converter)(struct mjs*, mjs_val_t*, void* out, const void* extra);
110 const char* expected_type;
111 bool (*extended_validator)(struct mjs*, mjs_val_t, const void* extra);
112 const void* extra_data;
114
115static inline void _js_to_int32(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
116 UNUSED(extra);
117 *(int32_t*)out = mjs_get_int32(mjs, *in);
118}
119#define JS_ARG_INT32(out) ((_js_arg_decl){out, mjs_is_number, _js_to_int32, "number", NULL, NULL})
120
121static inline void _js_to_ptr(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
122 UNUSED(extra);
123 *(void**)out = mjs_get_ptr(mjs, *in);
124}
125#define JS_ARG_PTR(out) \
126 ((_js_arg_decl){out, mjs_is_foreign, _js_to_ptr, "opaque pointer", NULL, NULL})
127
128static inline void _js_to_string(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
129 UNUSED(extra);
130 *(const char**)out = mjs_get_string(mjs, in, NULL);
131}
132#define JS_ARG_STR(out) ((_js_arg_decl){out, mjs_is_string, _js_to_string, "string", NULL, NULL})
133
134static inline void _js_to_bool(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
135 UNUSED(extra);
136 *(bool*)out = !!mjs_get_bool(mjs, *in);
137}
138#define JS_ARG_BOOL(out) ((_js_arg_decl){out, mjs_is_boolean, _js_to_bool, "boolean", NULL, NULL})
139
140static inline void _js_passthrough(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
141 UNUSED(extra);
142 UNUSED(mjs);
143 *(mjs_val_t*)out = *in;
144}
145#define JS_ARG_ANY(out) ((_js_arg_decl){out, NULL, _js_passthrough, "any", NULL, NULL})
146#define JS_ARG_OBJ(out) ((_js_arg_decl){out, mjs_is_object, _js_passthrough, "any", NULL, NULL})
147#define JS_ARG_FN(out) \
148 ((_js_arg_decl){out, mjs_is_function, _js_passthrough, "function", NULL, NULL})
149#define JS_ARG_ARR(out) ((_js_arg_decl){out, mjs_is_array, _js_passthrough, "array", NULL, NULL})
150
151static inline bool _js_validate_struct(struct mjs* mjs, mjs_val_t val, const void* extra) {
152 JsForeignMagic expected_magic = (JsForeignMagic)(size_t)extra;
153 JsForeignMagic struct_magic = *(JsForeignMagic*)mjs_get_ptr(mjs, val);
154 return struct_magic == expected_magic;
155}
156#define JS_ARG_STRUCT(type, out) \
157 ((_js_arg_decl){ \
158 out, \
159 mjs_is_foreign, \
160 _js_to_ptr, \
161 #type, \
162 _js_validate_struct, \
163 (void*)JsForeignMagic##_##type})
164
165static inline bool _js_validate_obj_w_struct(struct mjs* mjs, mjs_val_t val, const void* extra) {
166 JsForeignMagic expected_magic = (JsForeignMagic)(size_t)extra;
167 JsForeignMagic struct_magic = *(JsForeignMagic*)JS_GET_INST(mjs, val);
168 return struct_magic == expected_magic;
169}
170#define JS_ARG_OBJ_WITH_STRUCT(type, out) \
171 ((_js_arg_decl){ \
172 out, \
173 mjs_is_object, \
174 _js_passthrough, \
175 #type, \
176 _js_validate_obj_w_struct, \
177 (void*)JsForeignMagic##_##type})
178
179static inline bool _js_validate_enum(struct mjs* mjs, mjs_val_t val, const void* extra) {
180 for(const JsEnumMapping* mapping = (JsEnumMapping*)extra + 1; mapping->name; mapping++)
181 if(strcmp(mapping->name, mjs_get_string(mjs, &val, NULL)) == 0) return true;
182 return false;
183}
184static inline void
185 _js_convert_enum(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) {
186 const JsEnumMapping* mapping = (JsEnumMapping*)extra;
187 size_t size = mapping->value; // get enum size from first entry
188 for(mapping++; mapping->name; mapping++) {
189 if(strcmp(mapping->name, mjs_get_string(mjs, val, NULL)) == 0) {
190 if(size == 1)
191 *(uint8_t*)out = mapping->value;
192 else if(size == 2)
193 *(uint16_t*)out = mapping->value;
194 else if(size == 4)
195 *(uint32_t*)out = mapping->value;
196 else if(size == 8)
197 *(uint64_t*)out = mapping->value;
198 return;
199 }
200 }
201 // unreachable, thanks to _js_validate_enum
202}
203#define JS_ARG_ENUM(var_name, name) \
204 ((_js_arg_decl){ \
205 &var_name, \
206 mjs_is_string, \
207 _js_convert_enum, \
208 name " enum", \
209 _js_validate_enum, \
210 var_name##_mapping})
211
212static inline bool _js_validate_object(struct mjs* mjs, mjs_val_t val, const void* extra) {
213 for(const JsObjectMapping* mapping = (JsObjectMapping*)extra; mapping->name; mapping++)
214 if(mjs_get(mjs, val, mapping->name, ~0) == MJS_UNDEFINED) return false;
215 return true;
216}
217static inline void
218 _js_convert_object(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) {
219 const JsObjectMapping* mapping = (JsObjectMapping*)extra;
220 for(; mapping->name; mapping++) {
221 mjs_val_t field_val = mjs_get(mjs, *val, mapping->name, ~0);
222 *(mjs_val_t*)((uint8_t*)out + mapping->offset) = field_val;
223 }
224}
225#define JS_ARG_OBJECT(var_name, name) \
226 ((_js_arg_decl){ \
227 &var_name, \
228 mjs_is_object, \
229 _js_convert_object, \
230 name " object", \
231 _js_validate_object, \
232 var_name##_mapping})
233
241#define JS_CONVERT_OR_RETURN(mjs, value, decl, source, ...) \
242 if(decl.validator) \
243 if(!decl.validator(*value)) \
244 JS_ERROR_AND_RETURN( \
245 mjs, \
246 MJS_BAD_ARGS_ERROR, \
247 source ": expected %s", \
248 ##__VA_ARGS__, \
249 decl.expected_type); \
250 if(decl.extended_validator) \
251 if(!decl.extended_validator(mjs, *value, decl.extra_data)) \
252 JS_ERROR_AND_RETURN( \
253 mjs, \
254 MJS_BAD_ARGS_ERROR, \
255 source ": expected %s", \
256 ##__VA_ARGS__, \
257 decl.expected_type); \
258 decl.converter(mjs, value, decl.out, decl.extra_data);
259
260//-V:JS_FETCH_ARGS_OR_RETURN:1008
269#define JS_FETCH_ARGS_OR_RETURN(mjs, arg_operator, ...) \
270 _js_arg_decl _js_args[] = {__VA_ARGS__}; \
271 int _js_arg_cnt = COUNT_OF(_js_args); \
272 mjs_val_t _js_arg_vals[_js_arg_cnt]; \
273 if(!(mjs_nargs(mjs) arg_operator _js_arg_cnt)) \
274 JS_ERROR_AND_RETURN( \
275 mjs, \
276 MJS_BAD_ARGS_ERROR, \
277 "expected %s%d arguments, got %d", \
278 #arg_operator, \
279 _js_arg_cnt, \
280 mjs_nargs(mjs)); \
281 for(int _i = 0; _i < _js_arg_cnt; _i++) { \
282 _js_arg_vals[_i] = mjs_arg(mjs, _i); \
283 JS_CONVERT_OR_RETURN(mjs, &_js_arg_vals[_i], _js_args[_i], "argument %d", _i); \
284 }
285
291#define JS_ERROR_AND_RETURN(mjs, error_code, ...) \
292 do { \
293 mjs_prepend_errorf(mjs, error_code, __VA_ARGS__); \
294 mjs_return(mjs, MJS_UNDEFINED); \
295 return; \
296 } while(0)
297
303#define JS_ERROR_AND_RETURN_VAL(mjs, error_code, ret_val, ...) \
304 do { \
305 mjs_prepend_errorf(mjs, error_code, __VA_ARGS__); \
306 mjs_return(mjs, MJS_UNDEFINED); \
307 return ret_val; \
308 } while(0)
309
310typedef struct JsModules JsModules;
311
312typedef void* (*JsModuleConstructor)(struct mjs* mjs, mjs_val_t* object, JsModules* modules);
313typedef void (*JsModuleDestructor)(void* inst);
314
315typedef struct {
316 char* name;
317 JsModuleConstructor create;
318 JsModuleDestructor destroy;
319 const ElfApiInterface* api_interface;
321
322JsModules* js_modules_create(struct mjs* mjs, CompositeApiResolver* resolver);
323
324void js_modules_destroy(JsModules* modules);
325
326mjs_val_t js_module_require(JsModules* modules, const char* name, size_t name_len);
327
334void* js_module_get(JsModules* modules, const char* name);
335
339void js_sdk_compatibility_status(struct mjs* mjs);
340
344void js_is_sdk_compatible(struct mjs* mjs);
345
349void js_check_sdk_compatibility(struct mjs* mjs);
350
354void js_does_sdk_support(struct mjs* mjs);
355
359void js_check_sdk_features(struct mjs* mjs);
Flipper application.
Definition js_modules.h:106
Definition composite_resolver.c:10
Interface for ELF loader to resolve symbols.
Definition elf_api_interface.h:9
Definition js_modules.h:83
Definition js_modules.h:315
Definition js_modules.c:40
Definition js_modules.h:95
Definition mjs_core.h:63