aboutsummaryrefslogtreecommitdiff
path: root/src/zjs_util.h
blob: 734ccc1320bcb974bed89db4ec070b1e63fbad68 (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
// Copyright (c) 2016-2017, Intel Corporation.

#ifndef __zjs_util_h__
#define __zjs_util_h__

// The util code is only for the X86 side

#include <stdlib.h>
#include "jerryscript.h"
#include "zjs_common.h"
#include "zjs_error.h"

#define ZJS_UNDEFINED jerry_create_undefined()

/**
 * Call malloc but if it fails, run JerryScript garbage collection and retry
 *
 * @param size  Number of bytes to allocate
 */
void *zjs_malloc_with_retry(size_t size);

#ifdef ZJS_LINUX_BUILD
#define zjs_malloc(sz) malloc(sz)
#define zjs_free(ptr) free(ptr)
#else
#include <zephyr.h>
#ifdef ZJS_TRACE_MALLOC
#define zjs_malloc(sz) ({void *zjs_ptr = zjs_malloc_with_retry(sz); ZJS_PRINT("%s:%d: allocating %lu bytes (%p)\n", __func__, __LINE__, (uint32_t)sz, zjs_ptr); zjs_ptr;})
#define zjs_free(ptr) (ZJS_PRINT("%s:%d: freeing %p\n", __func__, __LINE__, ptr), free(ptr))
#else
#define zjs_malloc(sz) ({void *zjs_ptr = zjs_malloc_with_retry(sz); if (!zjs_ptr) {ERR_PRINT("malloc failed\n");} zjs_ptr;})
#define zjs_free(ptr) free(ptr)
#endif  // ZJS_TRACE_MALLOC
#endif  // ZJS_LINUX_BUILD

void zjs_set_property(const jerry_value_t obj, const char *name,
                      const jerry_value_t prop);
void zjs_set_readonly_property(const jerry_value_t obj, const char *name,
                               const jerry_value_t prop);
jerry_value_t zjs_get_property (const jerry_value_t obj, const char *name);
bool zjs_delete_property(const jerry_value_t obj, const char *str);

typedef struct zjs_native_func {
    void *function;
    const char *name;
} zjs_native_func_t;

/**
 * Add a series of functions described in array funcs to obj.
 *
 * @param obj    JerryScript object to add the functions to
 * @param funcs  Array of zjs_native_func_t, terminated with {NULL, NULL}
 */
void zjs_obj_add_functions(jerry_value_t obj, zjs_native_func_t *funcs);

void zjs_obj_add_boolean(jerry_value_t obj, bool flag, const char *name);
void zjs_obj_add_readonly_boolean(jerry_value_t obj, bool flag,
                                  const char *name);
void zjs_obj_add_function(jerry_value_t obj, void *function, const char *name);
void zjs_obj_add_object(jerry_value_t parent, jerry_value_t child,
                        const char *name);
void zjs_obj_add_string(jerry_value_t obj, const char *str, const char *name);
void zjs_obj_add_readonly_string(jerry_value_t obj, const char *str,
                                 const char *name);
void zjs_obj_add_number(jerry_value_t obj, double num, const char *name);
void zjs_obj_add_readonly_number(jerry_value_t obj, double num,
                                 const char *name);

bool zjs_obj_get_boolean(jerry_value_t obj, const char *name, bool *flag);
bool zjs_obj_get_string(jerry_value_t obj, const char *name, char *buffer,
                        int len);
bool zjs_obj_get_double(jerry_value_t obj, const char *name, double *num);
bool zjs_obj_get_uint32(jerry_value_t obj, const char *name, uint32_t *num);
bool zjs_obj_get_int32(jerry_value_t obj, const char *name, int32_t *num);

/**
 * Copy a JerryScript string into a supplied char * buffer.
 *
 * @param jstr    A JerryScript string value.
 * @param buffer  A char * buffer with at least *maxlen bytes.
 * @param maxlen  Pointer to a maximum size to be written to the buffer. If the
 *                  string size with a null terminator would exceed *maxlen,
 *                  only a null terminator will be written to the buffer and
 *                  *maxlen will be set to 0. If the string is successfully
 *                  copied, *maxlen will be set to the bytes copied (not
 *                  counting the null terminator). If *maxlen is 0, behavior is
 *                  undefined.
 */
void zjs_copy_jstring(jerry_value_t jstr, char *buffer, jerry_size_t *maxlen);

/**
 * Allocate a char * buffer on the heap and copy the JerryScript string to it.
 *
 * @param jstr    A JerryScript string value.
 * @param maxlen  Pointer to a maximum size for the returned string. If NULL or
 *                  pointing to 0, there is no limit to the string size
 *                  returned. If not NULL, the actual length of the string will
 *                  be written to *maxlen. If the call succeeds, the buffer
 *                  returned will be truncated to the given maxlen with a null
 *                  terminator. You can use zjs_copy_jstring if you'd rather
 *                  fail than truncate on error.
 * @return A new null-terminated string (which must be freed with zjs_free) or
 *          NULL on failure.
 */
char *zjs_alloc_from_jstring(jerry_value_t jstr, jerry_size_t *maxlen);

bool zjs_hex_to_byte(const char *buf, uint8_t *byte);

void zjs_default_convert_pin(uint32_t orig, int *dev, int *pin);

uint16_t zjs_compress_32_to_16(uint32_t num);
uint32_t zjs_uncompress_16_to_32(uint16_t num);

void zjs_print_error_message(jerry_value_t error, jerry_value_t func);

/**
 * Macro to declare a standard JerryScript external function in a shorter form
 *
 * @param name  The name of the function to declare
 *
 * Note: This hides the fact that you have function_obj, this, argv, and argc
 *   variables available in the function.
 *
 * Example:
 * static ZJS_DECL_FUNC(zjs_my_api)
 * {
 *     // do something useful; often using those hidden args
 *     return ZJS_UNDEFINED;
 * };
 */
#define ZJS_DECL_FUNC(name)                               \
    jerry_value_t name(const jerry_value_t function_obj,  \
                       const jerry_value_t this,          \
                       const jerry_value_t argv[],        \
                       const jerry_length_t argc)

/**
 * Macro to declare a function that takes the JerryScript args plus more
 *
 * @param name  The name of the function to declare
 * @param ...   List of other arguments to declare after the standard ones
 *
 * Example:
 * static ZJS_DECL_FUNC_ARGS(zjs_my_api_with_args, int mode)
 * {
 *     // do something useful, using mode and often the hidden args
 *     return ZJS_UNDEFINED;
 * };
 */
#define ZJS_DECL_FUNC_ARGS(name, ...)                     \
    jerry_value_t name(const jerry_value_t function_obj,  \
                       const jerry_value_t this,          \
                       const jerry_value_t argv[],        \
                       const jerry_length_t argc,         \
                       __VA_ARGS__)

/**
 * Macro to call a function declared with ZJS_DECL_FUNC_ARGS from another API
 *
 * @param name  The name of the function to call
 * @param ...   List of other arguments to pass
 *
 * Example:
 * jerry_value_t rval = ZJS_CHAIN_FUNC_ARGS(zjs_my_api_with_args, 1);
 */
#define ZJS_CHAIN_FUNC_ARGS(name, ...)                 \
    name(function_obj, this, argv, argc, __VA_ARGS__)

/**
 * Release a jerry_value_t passed by reference
 */
void zjs_free_value(const jerry_value_t *value);

/**
 * Macro to declare a jerry_value_t and have it released automatically
 *
 * If you're going to be returning the value from a function, you usually don't
 * want this, as ownership will transfer to the caller (unless you acquire it on
 * return). However, if you have error return paths that should release the
 * value, then it might still be a good idea.
 *
 * Also, don't use this for a variable that holds a C copy of a value that was
 * passed into you, as those are owned by the caller.
 */
#define ZVAL const jerry_value_t __attribute__ ((__cleanup__(zjs_free_value)))

/**
 * A non-const version of ZVAL
 *
 * This is for when you need to initialize a ZVAL from more than one path. It
 * should be used sparingly, because this is less safe; it's possible to
 * overwrite a value and forget to release the old one.
 */
#define ZVAL_MUTABLE jerry_value_t __attribute__ ((__cleanup__(zjs_free_value)))

//
// ztypes (for argument validation)
//

// Z_OPTIONAL means the argument isn't required, this must come first if present
#define Z_OPTIONAL  "?"

// Z_ANY matches any type (i.e. ignores it) - only makes sense for required arg
#define Z_ANY       "a"
// the rest all match specific type by calling jerry_value_is_* function
#define Z_ARRAY     "b"
#define Z_BOOL      "c"
#define Z_FUNCTION  "d"
#define Z_NULL      "e"
#define Z_NUMBER    "f"
// NOTE: Z_OBJECT will match arrays and functions too, because they are objects
#define Z_OBJECT    "g"
#define Z_STRING    "h"
// NOTE: If this test passes, you're guaranteed zjs_buffer_find will succeed
#define Z_BUFFER    "i"
#define Z_UNDEFINED "j"

enum {
    ZJS_VALID_REQUIRED,
    ZJS_VALID_OPTIONAL,
    ZJS_SKIP_OPTIONAL,
    ZJS_INTERNAL_ERROR = -1,
    ZJS_INVALID_ARG = -2,
    ZJS_INSUFFICIENT_ARGS = -3
};

int zjs_validate_args(const char *expectations[], const jerry_length_t argc,
                      const jerry_value_t argv[]);

/**
 * Macro to validate existing argv based on a list of expected argument types.
 *
 * NOTE: Expects argc and argv to exist as in a JerryScript native function.
 *
 * @param optcount  A pointer to an int to receive count of optional args found,
 *                    or NULL if not needed.
 * @param offset    Integer offset of arg in argv to start with (normally 0)
 * @param typestr   Each remaining comma-separated argument to the macro
 *                    corresponds to an argument in argv; each argument should
 *                    be a space-separated list of "ztypes" from defines above.
 *
 * Example: ZJS_VALIDATE_ARGS(Z_NUMBER, Z_OBJECT Z_NULL, Z_OPTIONAL Z_FUNCTION);
 * This requires argv[0] to be a number type, argv[1] to be an object type
 *   or null,  and argv[2] may or may not exist, but if it does it must be a
 *   function type. (Implicitly, argc will have to be at least 2.) Arguments
 *   beyond what are specified are allowed and ignored.
 */
#define ZJS_VALIDATE_ARGS_FULL(optcount, offset, ...)                       \
    int optcount = zjs_validate_args((const char *[]){ __VA_ARGS__, NULL }, \
                                     argc - offset, argv + offset);         \
    if (optcount <= ZJS_INVALID_ARG) {                                      \
        return TYPE_ERROR("invalid arguments");                             \
    }

// Use this if you need an offset
#define ZJS_VALIDATE_ARGS_OFFSET(offset, ...)           \
    ZJS_VALIDATE_ARGS_FULL(zjs_validate_rval, offset, __VA_ARGS__)

// Use this if you need the number of optional args found
#define ZJS_VALIDATE_ARGS_OPTCOUNT(optcount, ...)       \
    ZJS_VALIDATE_ARGS_FULL(optcount, 0, __VA_ARGS__)

// Normally use this as a shortcut
#define ZJS_VALIDATE_ARGS(...)                      \
    ZJS_VALIDATE_ARGS_FULL(zjs_validate_rval, 0, __VA_ARGS__)

/**
 * Macro to check if the argument list was expected, but not return an error.
 * This is useful if you want to reject a promise based on bad arguments instead
 * of automatically returning an error.
 *
 * NOTE: Expects argc and argv to exist as in a JerryScript native function
 */
#define ZJS_CHECK_ARGS(...) \
    (zjs_validate_args((const char *[]){ __VA_ARGS__, NULL }, argc, argv) \
     <= ZJS_INVALID_ARG) ? 1 : 0

/**
 * Checks for a boolean property and returns it via result.
 *
 * Returns TypeError if property is not a boolean.
 *
 * @param value   A valid JS object.
 * @param prop    A string property name.
 * @param result  Receives result; should already be set to default.
 *
 * @return 0 on success, negative number on failure
 */
int zjs_require_bool_if_prop(jerry_value_t obj, const char *prop, bool *result);

/**
 * Returns an error from calling function if obj.prop exists but is not a
 *   boolean.
 */
#define ZJS_REQUIRE_BOOL_IF_PROP(value, prop, result)         \
    if (zjs_require_bool_if_prop(value, prop, result) < 0) {  \
        return TYPE_ERROR("bool required");                   \
    }

typedef struct str2int {
    const char *first;
    int second;
} str2int_t;

/**
 * Checks for a string property obj.prop and maps it to an int via map array.
 *
 * @param obj     A valid JS object.
 * @param prop    A string property name.
 * @param map     An array of mappings from string to int; final one with NULL.
 * @param maxlen  The max length of string to expect in the property.
 * @param result  Receives results; should already be set to default.
 *
 *  *result will hold the mapped value if a match is found; if no such
 *  property exists, *result will be untouched; if the property is not a
 *  string or holds a value that doesn't match, returns a constant above.
 *
 * @return 0 on success, negative number on failure
 */
int zjs_require_string_if_prop_map(jerry_value_t obj, const char *prop,
                                   str2int_t map[], int maxlen, int *result);

/**
 * Returns an error from calling function if obj.prop exists but is not a
 *   string, or if string is not one of the allowed values in map.
 */
#define ZJS_REQUIRE_STR_IF_PROP_MAP(obj, prop, map, maxlen, result)  \
    {                                                                \
        int rval = zjs_require_string_if_prop_map(obj, prop, map,    \
                                                  maxlen, result);   \
        if (rval < 0) {                                              \
            return TYPE_ERROR("one of specific strings required");   \
        }                                                            \
    }

#ifndef ZJS_LINUX_BUILD
#define LOCK  k_sched_lock
#define UNLOCK k_sched_unlock

#ifndef ZJS_ASHELL
/*
 * Unblock the main loop
 */
void zjs_loop_unblock(void);

/*
 * Block in the main loop for a specified amount of time
 */
void zjs_loop_block(int time);

/*
 * Initialize the main loop blocking semaphore
 */
void zjs_loop_init(void);
#else
#define zjs_loop_unblock() do {} while(0)
#define zjs_loop_block(time) do {} while(0)
#define zjs_loop_init() do {} while(0)
#endif
#else
#define LOCK() do {} while (0)
#define UNLOCK() do {} while (0)
#endif

// Type definition to be used with macros below
// struct list_item {
//     int value;
//     struct list_item *next;
// } list_item_t;
// list_item_t *my_list = NULL;

// Looks for and returns a node found in a list.
// @param type          Struct type of list
// @param list          Pointer to list head
// @param cmp_element   Name of element in node struct to compare
// @param cmp_to        Value to compare node element to

// Example:
//    list_item_t *item = ZJS_FIND_NODE(list_item_t, my_list, value, 42);

// The above will return a list item whos `value` parameter == 42
#define ZJS_LIST_FIND(type, list, cmp_element, cmp_to) \
   ({ \
       type *found = NULL; \
       type *cur = list; \
       while (cur) { \
           if (cur->cmp_element == cmp_to) { \
               found = cur; \
               break; \
           } \
           cur = cur->next; \
       } \
       found; \
   })

// Append a new node to the end of a list
// Example:
//    list_item_t *item = new_list_item(...);
//    ZJS_APPEND_NODE(list_item_t, my_list, item);
#define ZJS_LIST_APPEND(type, list, p) \
    { \
        type **pnext = &list; \
        while (*pnext) { \
            pnext = &(*pnext)->next; \
        } \
        *pnext = p; \
    }

// Prepend a new node to the beginning of a list
// Example:
//    list_item_t *item = new_list_item(...);
//    ZJS_PREPEND_NODE(list_item_t, my_list, item);
#define ZJS_LIST_PREPEND(type, list, p) \
    { \
        p->next = list; \
        list = p; \
    }

// Remove a node from a list. Returns 1 if the node was removed.
// Example:
//     void remove(list_item_t *to_remove) {
//         ZJS_REMOVE_NODE(list_item_t, my_list, to_remove);
//     }
#define ZJS_LIST_REMOVE(type, list, p) \
    ({ \
        uint8_t removed = 0; \
        type *cur = list; \
        if (p == list) { \
            list = p->next; \
        } else { \
            while (cur->next) { \
                if (cur->next == p) { \
                    cur->next = p->next; \
                    removed = 1; \
                    break; \
                } \
                cur = cur->next; \
            } \
        } \
        removed; \
    })

// Free and iterate over a linked list, calling a callback for each list item
// and removing the current item at each iteration.
#define ZJS_LIST_FREE(type, list, callback) \
    { \
        while (list) { \
            type *tmp = list->next; \
            callback(list); \
            list = tmp; \
        } \
    }

// Get the length of a linked list
#define ZJS_LIST_LENGTH(type, list) \
    ({ \
        int ret = 0; \
        type *i = list; \
        while (i) { \
            ret++; \
            i = i->next; \
        } \
        ret; \
    })

#define ZJS_GET_HANDLE(obj, type, var, info) \
    type *var; \
    { \
        void *native; \
        const jerry_object_native_info_t *tmp; \
        if (!jerry_get_object_native_pointer(obj, &native, &tmp)) { \
            return zjs_error("no native handle"); \
        } \
        if (tmp != &info) { \
            return zjs_error("handle was incorrect type"); \
        } \
        var = (type *)native; \
    }

#endif  // __zjs_util_h__