@mikrojs/native 0.12.0 → 0.14.0-pr-229.g0d8db1b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,418 @@
1
+ /*
2
+ * SPDX-License-Identifier: CC0-1.0
3
+ */
4
+
5
+ /**
6
+ * @ingroup nanocbor
7
+ * @{
8
+ * @file
9
+ * @brief Minimalistic CBOR encoder implementation
10
+ *
11
+ * @author Koen Zandberg <koen@bergzand.net>
12
+ * @}
13
+ */
14
+
15
+ #include <stddef.h>
16
+ #include <stdint.h>
17
+ #include <stdlib.h>
18
+ #include <string.h>
19
+
20
+ #include "nanocbor/config.h"
21
+ #include "nanocbor/nanocbor.h"
22
+
23
+ #include NANOCBOR_BYTEORDER_HEADER
24
+
25
+ /* memarray functions */
26
+ static bool _encoder_mem_fits(nanocbor_encoder_t *enc, void *ctx, size_t len)
27
+ {
28
+ (void)ctx;
29
+ return ((size_t)(enc->end - enc->cur) >= len);
30
+ }
31
+
32
+ static void _encoder_mem_append(nanocbor_encoder_t *enc, void *ctx, const uint8_t *data, size_t len)
33
+ {
34
+ (void)ctx;
35
+ memcpy(enc->cur, data, len);
36
+ enc->cur += len;
37
+ }
38
+
39
+ void nanocbor_encoder_init(nanocbor_encoder_t *enc, uint8_t *buf, size_t len)
40
+ {
41
+ enc->len = 0;
42
+ enc->cur = buf;
43
+ enc->end = buf + len;
44
+ enc->append = _encoder_mem_append;
45
+ enc->fits = _encoder_mem_fits;
46
+ }
47
+
48
+ void nanocbor_encoder_stream_init(nanocbor_encoder_t *enc, void *ctx,
49
+ nanocbor_encoder_append append_func,
50
+ nanocbor_encoder_fits fits_func)
51
+ {
52
+ enc->len = 0;
53
+ enc->append = append_func;
54
+ enc->fits = fits_func;
55
+ enc->context = ctx;
56
+ }
57
+
58
+ size_t nanocbor_encoded_len(nanocbor_encoder_t *enc)
59
+ {
60
+ return enc->len;
61
+ }
62
+
63
+ static inline void _incr_len(nanocbor_encoder_t *enc, size_t len)
64
+ {
65
+ enc->len += len;
66
+ }
67
+
68
+ static inline void _append(nanocbor_encoder_t *enc, const uint8_t *data, size_t len)
69
+ {
70
+ enc->append(enc, enc->context, data, len);
71
+ }
72
+
73
+ static inline int _fits(nanocbor_encoder_t *enc, size_t len)
74
+ {
75
+ return enc->fits(enc, enc->context, len) ? (int)len : NANOCBOR_ERR_END;
76
+ }
77
+
78
+ static int _fmt_single(nanocbor_encoder_t *enc, uint8_t single)
79
+ {
80
+ _incr_len(enc, 1);
81
+ int res = _fits(enc, 1);
82
+
83
+ if (res == 1) {
84
+ _append(enc, &single, 1);
85
+
86
+ }
87
+ return res;
88
+ }
89
+
90
+ int nanocbor_fmt_bool(nanocbor_encoder_t *enc, bool content)
91
+ {
92
+ uint8_t single = NANOCBOR_MASK_FLOAT
93
+ | (content ? NANOCBOR_SIMPLE_TRUE : NANOCBOR_SIMPLE_FALSE);
94
+
95
+ return _fmt_single(enc, single);
96
+ }
97
+
98
+ static int _fmt_uint64(nanocbor_encoder_t *enc, uint64_t num, uint8_t type)
99
+ {
100
+ unsigned extrabytes = 0;
101
+
102
+ if (num < NANOCBOR_SIZE_BYTE) {
103
+ type |= num;
104
+ }
105
+ else {
106
+ if (num > UINT32_MAX) {
107
+ /* Requires long size */
108
+ type |= NANOCBOR_SIZE_LONG;
109
+ extrabytes = sizeof(uint64_t);
110
+ }
111
+ else if (num > UINT16_MAX) {
112
+ /* At least word size */
113
+ type |= NANOCBOR_SIZE_WORD;
114
+ extrabytes = sizeof(uint32_t);
115
+ }
116
+ else if (num > UINT8_MAX) {
117
+ type |= NANOCBOR_SIZE_SHORT;
118
+ extrabytes = sizeof(uint16_t);
119
+ }
120
+ else {
121
+ type |= NANOCBOR_SIZE_BYTE;
122
+ extrabytes = sizeof(uint8_t);
123
+ }
124
+ }
125
+ _incr_len(enc, extrabytes + 1);
126
+ int res = _fits(enc, extrabytes + 1);
127
+ if (res > 0) {
128
+ _append(enc, &type, 1);
129
+
130
+ /* NOLINTNEXTLINE: user supplied function */
131
+ uint64_t benum = NANOCBOR_HTOBE64_FUNC(num);
132
+
133
+
134
+ _append(enc, (uint8_t *)&benum + sizeof(benum) - extrabytes,
135
+ extrabytes);
136
+ }
137
+ return res;
138
+ }
139
+
140
+ int nanocbor_fmt_uint(nanocbor_encoder_t *enc, uint64_t num)
141
+ {
142
+ return _fmt_uint64(enc, num, NANOCBOR_MASK_UINT);
143
+ }
144
+
145
+ int nanocbor_fmt_tag(nanocbor_encoder_t *enc, uint64_t num)
146
+ {
147
+ return _fmt_uint64(enc, num, NANOCBOR_MASK_TAG);
148
+ }
149
+
150
+ int nanocbor_fmt_int(nanocbor_encoder_t *enc, int64_t num)
151
+ {
152
+ if (num < 0) {
153
+ /* Always negative at this point */
154
+ num = -(num + 1);
155
+ return _fmt_uint64(enc, (uint64_t)num, NANOCBOR_MASK_NINT);
156
+ }
157
+ return nanocbor_fmt_uint(enc, (uint64_t)num);
158
+ }
159
+
160
+ int nanocbor_fmt_simple(nanocbor_encoder_t *enc, uint8_t value)
161
+ {
162
+ /* Exclude assigned or reserved simple values between 20 and 31 */
163
+ if (value >= NANOCBOR_SIMPLE_FALSE && value <= NANOCBOR_SIZE_INDEFINITE) {
164
+ return NANOCBOR_ERR_INVALID_TYPE;
165
+ }
166
+ return _fmt_uint64(enc, value, NANOCBOR_MASK_FLOAT);
167
+ }
168
+
169
+ int nanocbor_fmt_bstr(nanocbor_encoder_t *enc, size_t len)
170
+ {
171
+ return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_BSTR);
172
+ }
173
+
174
+ int nanocbor_fmt_tstr(nanocbor_encoder_t *enc, size_t len)
175
+ {
176
+ return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_TSTR);
177
+ }
178
+
179
+ static int _put_bytes(nanocbor_encoder_t *enc, const uint8_t *str, size_t len)
180
+ {
181
+ _incr_len(enc, len);
182
+ int res = _fits(enc, len);
183
+
184
+ if (res >= 0) {
185
+ _append(enc, str, len);
186
+ return NANOCBOR_OK;
187
+ }
188
+ return res;
189
+ }
190
+
191
+ int nanocbor_put_tstr(nanocbor_encoder_t *enc, const char *str)
192
+ {
193
+ size_t len = strlen(str);
194
+
195
+ nanocbor_fmt_tstr(enc, len);
196
+ return _put_bytes(enc, (const uint8_t *)str, len);
197
+ }
198
+
199
+ int nanocbor_put_tstrn(nanocbor_encoder_t *enc, const char *str, size_t len)
200
+ {
201
+ nanocbor_fmt_tstr(enc, len);
202
+ return _put_bytes(enc, (const uint8_t *)str, len);
203
+ }
204
+
205
+ int nanocbor_put_bstr(nanocbor_encoder_t *enc, const uint8_t *str, size_t len)
206
+ {
207
+ nanocbor_fmt_bstr(enc, len);
208
+ return _put_bytes(enc, str, len);
209
+ }
210
+
211
+ int nanocbor_fmt_array(nanocbor_encoder_t *enc, size_t len)
212
+ {
213
+ return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_ARR);
214
+ }
215
+
216
+ int nanocbor_fmt_map(nanocbor_encoder_t *enc, size_t len)
217
+ {
218
+ return _fmt_uint64(enc, (uint64_t)len, NANOCBOR_MASK_MAP);
219
+ }
220
+
221
+ int nanocbor_fmt_array_indefinite(nanocbor_encoder_t *enc)
222
+ {
223
+ return _fmt_single(enc, NANOCBOR_MASK_ARR | NANOCBOR_SIZE_INDEFINITE);
224
+ }
225
+
226
+ int nanocbor_fmt_map_indefinite(nanocbor_encoder_t *enc)
227
+ {
228
+ return _fmt_single(enc, NANOCBOR_MASK_MAP | NANOCBOR_SIZE_INDEFINITE);
229
+ }
230
+
231
+ int nanocbor_fmt_end_indefinite(nanocbor_encoder_t *enc)
232
+ {
233
+ /* End is marked with float major and indefinite minor number */
234
+ return _fmt_single(enc, NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_INDEFINITE);
235
+ }
236
+
237
+ int nanocbor_fmt_null(nanocbor_encoder_t *enc)
238
+ {
239
+ return _fmt_single(enc, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_NULL);
240
+ }
241
+
242
+ int nanocbor_fmt_undefined(nanocbor_encoder_t *enc)
243
+ {
244
+ return _fmt_single(enc, NANOCBOR_MASK_FLOAT | NANOCBOR_SIMPLE_UNDEF);
245
+ }
246
+
247
+ /* Double bit mask related defines */
248
+ #define DOUBLE_EXP_OFFSET (1023U)
249
+ #define DOUBLE_SIZE (64U)
250
+ #define DOUBLE_EXP_POS (52U)
251
+ #define DOUBLE_SIGN_POS (63U)
252
+ #define DOUBLE_EXP_MASK ((uint64_t)0x7FFU)
253
+ #define DOUBLE_SIGN_MASK ((uint64_t)1U << DOUBLE_SIGN_POS)
254
+ #define DOUBLE_EXP_IS_NAN (0x7FFU)
255
+ #define DOUBLE_IS_ZERO (~(DOUBLE_SIGN_MASK))
256
+ #define DOUBLE_FLOAT_LOSS (0x1FFFFFFFU)
257
+
258
+ /* float bit mask related defines */
259
+ #define FLOAT_EXP_OFFSET (127U)
260
+ #define FLOAT_SIZE (32U)
261
+ #define FLOAT_EXP_POS (23U)
262
+ #define FLOAT_EXP_MASK ((uint32_t)0xFFU)
263
+ #define FLOAT_SIGN_POS (31U)
264
+ #define FLOAT_FRAC_MASK (0x7FFFFFU)
265
+ #define FLOAT_SIGN_MASK ((uint32_t)1U << FLOAT_SIGN_POS)
266
+ #define FLOAT_EXP_IS_NAN (0xFFU)
267
+ #define FLOAT_IS_ZERO (~(FLOAT_SIGN_MASK))
268
+ /* Part where a float to halffloat leads to precision loss */
269
+ #define FLOAT_HALF_LOSS (0x1FFFU)
270
+
271
+ /* halffloat bit mask related defines */
272
+ #define HALF_EXP_OFFSET (15U)
273
+ #define HALF_SIZE (16U)
274
+ #define HALF_EXP_POS (10U)
275
+ #define HALF_EXP_MASK (0x1FU)
276
+ #define HALF_SIGN_POS (15U)
277
+ #define HALF_FRAC_MASK (0x3FFU)
278
+ #define HALF_SIGN_MASK ((uint16_t)(1U << HALF_SIGN_POS))
279
+ #define HALF_MASK_HALF (0xFFU)
280
+
281
+ /* Check special cases for single precision floats */
282
+ static bool _single_is_inf_nan(uint8_t exp)
283
+ {
284
+ return exp == FLOAT_EXP_IS_NAN;
285
+ }
286
+
287
+ static bool _single_is_zero(uint32_t num)
288
+ {
289
+ return (num & FLOAT_IS_ZERO) == 0;
290
+ }
291
+
292
+ static bool _single_in_range(uint8_t exp, uint32_t num)
293
+ {
294
+ /* Check if lower 13 bits of fraction are zero, if so we might be able to
295
+ * convert without precision loss */
296
+ if (exp <= (HALF_EXP_OFFSET + FLOAT_EXP_OFFSET)
297
+ && exp >= ((-HALF_EXP_OFFSET + 1) + FLOAT_EXP_OFFSET)
298
+ && ((num & FLOAT_HALF_LOSS) == 0)) {
299
+ return true;
300
+ }
301
+ return false;
302
+ }
303
+
304
+ static int _fmt_halffloat(nanocbor_encoder_t *enc, uint16_t half)
305
+ {
306
+ _incr_len(enc, sizeof(uint16_t) + 1);
307
+ int res = _fits(enc, sizeof(uint16_t) + 1);
308
+ if (res > 0) {
309
+ uint8_t tmp[3] = {
310
+ NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_SHORT,
311
+ (half >> HALF_SIZE / 2),
312
+ half & HALF_MASK_HALF };
313
+ _append(enc, tmp, sizeof(tmp));
314
+ res = sizeof(uint16_t) + 1;
315
+ }
316
+ return res;
317
+ }
318
+
319
+ #if __SIZEOF_DOUBLE__ != __SIZEOF_FLOAT__
320
+ /* Check special cases for single precision floats */
321
+ static bool _double_is_inf_nan(uint16_t exp)
322
+ {
323
+ return (exp == DOUBLE_EXP_IS_NAN);
324
+ }
325
+
326
+ static bool _double_is_zero(uint64_t num)
327
+ {
328
+ return (num & DOUBLE_IS_ZERO) == 0;
329
+ }
330
+
331
+ static bool _double_in_range(uint16_t exp, uint64_t num)
332
+ {
333
+ /* Check if lower 13 bits of fraction are zero, if so we might be able to
334
+ * convert without precision loss */
335
+ if (exp <= (DOUBLE_EXP_OFFSET + FLOAT_EXP_OFFSET)
336
+ && exp >= (DOUBLE_EXP_OFFSET - FLOAT_EXP_OFFSET + 1)
337
+ && ((num & DOUBLE_FLOAT_LOSS) == 0)) { /* First 29 bits must be zero */
338
+ return true;
339
+ }
340
+ return false;
341
+ }
342
+ #endif
343
+
344
+ int nanocbor_fmt_float(nanocbor_encoder_t *enc, float num)
345
+ {
346
+ /* Allow bitwise access to float */
347
+ uint32_t *unum = (uint32_t *)&num;
348
+
349
+ /* Retrieve exponent */
350
+ uint8_t exp = (*unum >> FLOAT_EXP_POS) & FLOAT_EXP_MASK;
351
+ if (_single_is_inf_nan(exp) || _single_is_zero(*unum)
352
+ || _single_in_range(exp, *unum)) {
353
+ /* Copy sign bit */
354
+ uint16_t half = ((*unum >> (FLOAT_SIZE - HALF_SIZE)) & HALF_SIGN_MASK);
355
+ /* Shift exponent */
356
+ if (exp != FLOAT_EXP_IS_NAN && exp != 0) {
357
+ exp = exp + (uint8_t)(HALF_EXP_OFFSET - FLOAT_EXP_OFFSET);
358
+ }
359
+ /* Add exponent */
360
+ half |= ((exp & HALF_EXP_MASK) << HALF_EXP_POS)
361
+ | ((*unum >> (FLOAT_EXP_POS - HALF_EXP_POS)) & HALF_FRAC_MASK);
362
+ return _fmt_halffloat(enc, half);
363
+ }
364
+ /* normal float */
365
+ _incr_len(enc, sizeof(float) + 1);
366
+ int res = _fits(enc, 1 + sizeof(float));
367
+ if (res > 0) {
368
+ const uint8_t tmp = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_WORD;
369
+ _append(enc, &tmp, sizeof(tmp));
370
+ /* NOLINTNEXTLINE: user supplied function */
371
+ uint32_t bnum = NANOCBOR_HTOBE32_FUNC(*unum);
372
+ _append(enc, (uint8_t*)&bnum, sizeof(bnum));
373
+ }
374
+ return res;
375
+ }
376
+
377
+ int nanocbor_fmt_double(nanocbor_encoder_t *enc, double num)
378
+ {
379
+ #if __SIZEOF_DOUBLE__ == __SIZEOF_FLOAT__
380
+ return nanocbor_fmt_float(enc, num);
381
+ #else
382
+ uint64_t *unum = (uint64_t *)&num;
383
+ uint16_t exp = (*unum >> DOUBLE_EXP_POS) & DOUBLE_EXP_MASK;
384
+ if (_double_is_inf_nan(exp) || _double_is_zero(*unum)
385
+ || _double_in_range(exp, *unum)) {
386
+ /* copy sign bit over */
387
+ uint32_t single
388
+ = (*unum >> (DOUBLE_SIZE - FLOAT_SIZE)) & (FLOAT_SIGN_MASK);
389
+ /* Shift exponent */
390
+ if (exp != DOUBLE_EXP_IS_NAN && exp != 0) {
391
+ exp = exp + FLOAT_EXP_OFFSET - DOUBLE_EXP_OFFSET;
392
+ }
393
+ single |= ((exp & FLOAT_EXP_MASK) << FLOAT_EXP_POS)
394
+ | ((*unum >> (DOUBLE_EXP_POS - FLOAT_EXP_POS)) & FLOAT_FRAC_MASK);
395
+ float *fsingle = (float *)&single;
396
+ return nanocbor_fmt_float(enc, *fsingle);
397
+ }
398
+ _incr_len(enc, sizeof(double) + 1);
399
+ int res = _fits(enc, 1 + sizeof(double));
400
+ if (res > 0) {
401
+ const uint8_t tmp = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_LONG;
402
+ _append(enc, &tmp, 1);
403
+ /* NOLINTNEXTLINE: user supplied function */
404
+ uint64_t bnum = NANOCBOR_HTOBE64_FUNC(*unum);
405
+ _append(enc, (uint8_t*)&bnum, sizeof(bnum));
406
+ }
407
+ return res;
408
+ #endif
409
+ }
410
+
411
+ int nanocbor_fmt_decimal_frac(nanocbor_encoder_t *enc, int32_t e, int32_t m)
412
+ {
413
+ int res = nanocbor_fmt_tag(enc, NANOCBOR_TAG_DEC_FRAC);
414
+ res += nanocbor_fmt_array(enc, 2);
415
+ res += nanocbor_fmt_int(enc, e);
416
+ res += nanocbor_fmt_int(enc, m);
417
+ return res;
418
+ }
@@ -0,0 +1,12 @@
1
+ decoder_source = files('decoder.c')
2
+ encoder_source = files('encoder.c')
3
+
4
+ project_sources += decoder_source
5
+ project_sources += encoder_source
6
+
7
+ encoder_lib = static_library('encoder',
8
+ encoder_source,
9
+ include_directories : inc)
10
+ decoder_lib = static_library('decoder',
11
+ decoder_source,
12
+ include_directories : inc)
@@ -86,6 +86,13 @@ void MIK_FreeRuntime(MIKRuntime* mik_rt);
86
86
  * Emits no diagnostic output — callers decide whether and how to log. */
87
87
  int MIK_RunEntry(MIKRuntime* mik_rt, const char* entry);
88
88
 
89
+ /* MIK_RunEntry variant that, on -EFAULT, copies the thrown exception's
90
+ * string form (or the sync-rejected promise's rejection value) into
91
+ * err_buf so callers can surface the real failure instead of a generic
92
+ * "evaluation threw". err_buf may be NULL; when given it is always
93
+ * NUL-terminated (empty when no message could be captured). */
94
+ int MIK_RunEntryErr(MIKRuntime* mik_rt, const char* entry, char* err_buf, size_t err_buf_size);
95
+
89
96
  JSContext* MIK_GetJSContext(MIKRuntime* mik_rt);
90
97
  MIKRuntime* MIK_GetRuntime(JSContext* ctx);
91
98
  void MIK_SetFSBasePath(MIKRuntime* mik_rt, const char* base_path);
@@ -64,6 +64,15 @@ typedef struct MIKPlatform {
64
64
  * original 6 MAC bytes. The returned pointer must remain valid for the
65
65
  * lifetime of the platform. */
66
66
  const char* (*get_device_id)(void);
67
+ /** Reason the chip last reset, as a stable lowercase string. On ESP32
68
+ * this maps esp_reset_reason(): "power-on", "software", "panic",
69
+ * "watchdog", "interrupt-watchdog", "task-watchdog", "brownout",
70
+ * "deep-sleep", "external", "sdio", "usb", "jtag", "efuse",
71
+ * "power-glitch", "cpu-lockup", or "unknown". A clean restart()
72
+ * reports "software". Host platforms have no reset concept and return
73
+ * "unknown". The returned pointer must remain valid for the lifetime
74
+ * of the platform. */
75
+ const char* (*get_reset_reason)(void);
67
76
  } MIKPlatform;
68
77
 
69
78
  /* Log levels (matching ESP-IDF ESP_LOG_xxx values) */
@@ -48,6 +48,15 @@ struct MIKLoopConsumerEntry {
48
48
  MIKLoopDestroyFn destroy_fn;
49
49
  };
50
50
 
51
+ /* A promise that rejected without a handler, awaiting the end-of-turn
52
+ * unhandled-rejection check. Mirrors quickjs-libc's JSRejectedPromiseEntry:
53
+ * we hold a reference to both the promise (matched by identity on `handle`)
54
+ * and its reason (reported at flush time). */
55
+ struct MIKRejectedPromise {
56
+ JSValue promise;
57
+ JSValue reason;
58
+ };
59
+
51
60
  struct MIKRuntime {
52
61
  MIKRunOptions options;
53
62
  MIKConfig config;
@@ -83,10 +92,16 @@ struct MIKRuntime {
83
92
  MIKTimerRegistry* timers;
84
93
  std::vector<MIKNativeModuleEntry> native_modules;
85
94
  std::vector<MIKLoopConsumerEntry> loop_consumers;
86
- struct {
87
- JSValue promise_event_ctor;
88
- JSValue dispatch_event_func;
89
- } builtins;
95
+ /* Promises that rejected without a handler, awaiting the end-of-turn
96
+ * unhandled-rejection check. The host rejection tracker adds a promise
97
+ * here on `reject` and removes it on `handle`; whatever remains after a
98
+ * microtask drain (mik__execute_jobs) is a genuine unhandled rejection
99
+ * and gets reported. This deferral mirrors the HTML/WinterCG algorithm
100
+ * and is what prevents a transiently-unhandled promise (e.g. a module's
101
+ * evaluation promise, rejected before the import loader attaches its
102
+ * .then) from being reported. Entries hold dup'd references owned by this
103
+ * vector and freed when removed or flushed. */
104
+ std::vector<MIKRejectedPromise> pending_rejections;
90
105
  /* Shared prototype for Result objects ({ok, value} / {ok, error}) created
91
106
  * by mik__result_ok/mik__result_err and the native:result ok()/err()
92
107
  * functions. Holds .map/.mapErr/.andThen/.match/.orDefault/.orPanic +
@@ -167,6 +182,10 @@ JSValue mik_new_error(JSContext* ctx, int err);
167
182
  JSValue mik_throw_errno(JSContext* ctx, int err);
168
183
 
169
184
  void mik__execute_jobs(JSContext* ctx);
185
+ /* End-of-turn unhandled-rejection check; called after each microtask drain. */
186
+ void mik__flush_unhandled_rejections(JSContext* ctx);
187
+ /* Drop a promise from the pending-rejection queue without reporting it. */
188
+ void mik__forget_rejection(JSContext* ctx, JSValue promise);
170
189
  JSModuleDef* mik__load_builtin(JSContext* ctx, const char* name);
171
190
  int mik__load_file(JSContext* ctx, DynBuf* dbuf, const char* filename);
172
191
  void mik__resolve_fs_path(JSContext* ctx, const char* module_name, char* out, size_t out_size);
@@ -221,7 +240,10 @@ void mik__stdin_consume(JSContext* ctx);
221
240
 
222
241
  /* Console global (mik_console.cpp) */
223
242
  void mik__console_init(JSContext* ctx, JSValue global_obj);
224
- void mik__report_uncaught(JSContext* ctx, JSValue exc, bool in_promise = false);
243
+ /* Reports an uncaught error/rejection. Dedups by error-object identity:
244
+ * returns true if it reported, false if this same object was already
245
+ * reported. */
246
+ bool mik__report_uncaught(JSContext* ctx, JSValue exc, bool in_promise = false);
225
247
 
226
248
  /* Inspect (mik_inspect.cpp) */
227
249
  std::string mik_inspect(JSContext* ctx, JSValue value, int depth = 2, bool colors = false,
@@ -313,6 +335,11 @@ void mik__repl_set_paused(bool paused);
313
335
  * Device replies with zero-or-more MIK_MSG_FS_CHUNK frames followed by
314
336
  * MIK_MSG_OK on EOF, or MIK_MSG_ERR if the path can't be opened. */
315
337
  #define MIK_CMD_FS_GET 0x2B
338
+ /* Clear the on-device log files (log.txt + log.txt.1). No payload.
339
+ * Device suspends the logger, deletes both files, reopens a fresh
340
+ * log.txt, and replies MIK_MSG_OK. No-op (still OK) when file logging
341
+ * is disabled. */
342
+ #define MIK_CMD_LOG_RESET 0x2C
316
343
 
317
344
  #define MIK_CMD_CONFIG_LIST 0x40
318
345
  #define MIK_CMD_CONFIG_SET 0x41
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikrojs/native",
3
- "version": "0.12.0",
3
+ "version": "0.14.0-pr-229.g0d8db1b",
4
4
  "description": "Mikro.js C++ runtime library and Node.js native addon",
5
5
  "keywords": [
6
6
  "esp32",
@@ -25,6 +25,9 @@
25
25
  "cmake",
26
26
  "cmake.js",
27
27
  "CMakeLists.txt",
28
+ "deps/nanocbor/src",
29
+ "deps/nanocbor/include",
30
+ "deps/nanocbor/LICENSE",
28
31
  "include",
29
32
  "runtime",
30
33
  "scripts",
@@ -80,7 +83,7 @@
80
83
  "cmake-js": "^8.0.0",
81
84
  "node-addon-api": "^8.7.0",
82
85
  "node-gyp-build": "^4.8.4",
83
- "@mikrojs/quickjs": "0.12.0"
86
+ "@mikrojs/quickjs": "0.14.0-pr-229.g0d8db1b"
84
87
  },
85
88
  "devDependencies": {
86
89
  "@swc/core": "^1.15.30",
@@ -0,0 +1,120 @@
1
+ /* Minimal AbortController / AbortSignal implementation.
2
+ *
3
+ * This is a lightweight subset of the WHATWG DOM spec: just enough
4
+ * for fetch timeouts and cooperative cancellation. No EventTarget
5
+ * dependency: listeners are stored in a simple array.
6
+ *
7
+ * Evaluating this module installs AbortController, AbortSignal,
8
+ * AbortError, and TimeoutError on globalThis. It is loaded lazily by
9
+ * the native lazy getters in mik_abort.cpp on first access to any of
10
+ * the four globals; shipping it as precompiled bytecode keeps the
11
+ * QuickJS parser out of the install path (parsing at runtime caused
12
+ * OOM on low-memory chips).
13
+ */
14
+
15
+ class AbortError extends Error {
16
+ constructor(message?: string) {
17
+ super(message || 'The operation was aborted')
18
+ }
19
+ get name() {
20
+ return 'AbortError'
21
+ }
22
+ }
23
+
24
+ class TimeoutError extends Error {
25
+ constructor(message?: string) {
26
+ super(message || 'The operation timed out')
27
+ }
28
+ get name() {
29
+ return 'TimeoutError'
30
+ }
31
+ }
32
+
33
+ const _a: unique symbol = Symbol()
34
+ const _r: unique symbol = Symbol()
35
+ const _l: unique symbol = Symbol()
36
+ const _s: unique symbol = Symbol()
37
+
38
+ function doAbort(signal: AbortSignal, reason: unknown): void {
39
+ if (signal[_a]) return
40
+ signal[_a] = true
41
+ signal[_r] = reason
42
+ if (typeof signal.onabort === 'function') signal.onabort()
43
+ for (const fn of signal[_l]) fn()
44
+ }
45
+
46
+ class AbortSignal {
47
+ [_a] = false;
48
+ [_r]: unknown = undefined;
49
+ [_l]: (() => void)[] = []
50
+ onabort: (() => void) | null = null
51
+
52
+ get aborted(): boolean {
53
+ return this[_a]
54
+ }
55
+ get reason(): unknown {
56
+ return this[_r]
57
+ }
58
+ throwIfAborted(): void {
59
+ if (this[_a]) throw this[_r]
60
+ }
61
+ addEventListener(type: 'abort', fn: () => void): void {
62
+ if (type === 'abort' && typeof fn === 'function') this[_l].push(fn)
63
+ }
64
+ removeEventListener(type: 'abort', fn: () => void): void {
65
+ if (type === 'abort') this[_l] = this[_l].filter((f) => f !== fn)
66
+ }
67
+ static abort(reason?: unknown): AbortSignal {
68
+ const s = new AbortSignal()
69
+ doAbort(s, reason !== undefined ? reason : new AbortError())
70
+ return s
71
+ }
72
+ static timeout(ms: number): AbortSignal {
73
+ const s = new AbortSignal()
74
+ setTimeout(() => doAbort(s, new TimeoutError()), ms)
75
+ return s
76
+ }
77
+ static any(signals: AbortSignal[]): AbortSignal {
78
+ const s = new AbortSignal()
79
+ for (const i of signals) {
80
+ if (i.aborted) {
81
+ doAbort(s, i.reason)
82
+ return s
83
+ }
84
+ }
85
+ for (const i of signals) {
86
+ i.addEventListener('abort', () => doAbort(s, i.reason))
87
+ }
88
+ return s
89
+ }
90
+ }
91
+
92
+ class AbortController {
93
+ [_s] = new AbortSignal()
94
+
95
+ get signal(): AbortSignal {
96
+ return this[_s]
97
+ }
98
+ abort(reason?: unknown): void {
99
+ doAbort(this[_s], reason !== undefined ? reason : new AbortError())
100
+ }
101
+ }
102
+
103
+ /* Object.defineProperties, NOT Object.assign: when this module is
104
+ * evaluated via a direct `import 'mikro/abort'` (it is in the builtins
105
+ * table), the four lazy getters installed by mik_abort.cpp are still
106
+ * present as setter-less accessors, and Object.assign's [[Set]] would
107
+ * throw "no setter for property". Defining replaces the accessors with
108
+ * plain data properties, matching what the lazy-getter path produces. */
109
+ const descriptor = (value: unknown) => ({
110
+ value,
111
+ writable: true,
112
+ enumerable: true,
113
+ configurable: true,
114
+ })
115
+ Object.defineProperties(globalThis, {
116
+ AbortError: descriptor(AbortError),
117
+ TimeoutError: descriptor(TimeoutError),
118
+ AbortSignal: descriptor(AbortSignal),
119
+ AbortController: descriptor(AbortController),
120
+ })