@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.
- package/CMakeLists.txt +3 -1
- package/deps/nanocbor/LICENSE +121 -0
- package/deps/nanocbor/include/nanocbor/config.h +87 -0
- package/deps/nanocbor/include/nanocbor/nanocbor.h +1012 -0
- package/deps/nanocbor/src/decoder.c +706 -0
- package/deps/nanocbor/src/encoder.c +418 -0
- package/deps/nanocbor/src/meson.build +12 -0
- package/include/mikrojs/mikrojs.h +7 -0
- package/include/mikrojs/platform.h +9 -0
- package/include/mikrojs/private.h +32 -5
- package/package.json +5 -2
- package/prebuilds/darwin-arm64/mikrojs.napi.node +0 -0
- package/prebuilds/linux-arm64/mikrojs.napi.node +0 -0
- package/prebuilds/linux-x64/mikrojs.napi.node +0 -0
- package/runtime/abort/abort.ts +120 -0
- package/runtime/internal.d.ts +2 -1
- package/runtime/sys/sys.ts +2 -0
- package/runtime/sys/types.ts +27 -1
- package/src/builtins.cpp +10 -5
- package/src/mik_abort.cpp +40 -107
- package/src/mik_console.cpp +26 -24
- package/src/mik_inspect.cpp +3 -3
- package/src/mik_repl.cpp +4 -0
- package/src/mik_sys.cpp +11 -1
- package/src/mikrojs.cpp +224 -78
- package/src/modules.cpp +7 -16
- package/src/platform_posix.cpp +5 -0
- package/LICENSE +0 -21
package/runtime/internal.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ declare module 'native:observable' {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
declare module 'native:sys' {
|
|
37
|
-
import type {JsMemoryUsage} from './sys/types.js'
|
|
37
|
+
import type {JsMemoryUsage, ResetReason} from './sys/types.js'
|
|
38
38
|
export function evalScript(code: string): Promise<{value: unknown}>
|
|
39
39
|
export function memoryUsage(): {
|
|
40
40
|
heapUsed: number
|
|
@@ -67,6 +67,7 @@ declare module 'native:sys' {
|
|
|
67
67
|
}
|
|
68
68
|
export const firmware: {hash: string; date: string; idfVersion: string | undefined}
|
|
69
69
|
export const deviceId: string
|
|
70
|
+
export const resetReason: ResetReason
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
declare module 'native:fs' {
|
package/runtime/sys/sys.ts
CHANGED
package/runtime/sys/types.ts
CHANGED
|
@@ -73,7 +73,10 @@ export interface JsMemoryUsage {
|
|
|
73
73
|
arrayCount: number
|
|
74
74
|
/** Total element slots across all fast arrays */
|
|
75
75
|
fastArrayElements: number
|
|
76
|
-
/**
|
|
76
|
+
/** Cumulative bytes deserialized via JS_ReadObject over this
|
|
77
|
+
* runtime's lifetime (bytecode of imported modules, builtins, and
|
|
78
|
+
* bjson). A monotonic counter, NOT live memory — it never goes
|
|
79
|
+
* down, so don't read it as retained ArrayBuffer storage. */
|
|
77
80
|
binaryObjectSize: number
|
|
78
81
|
}
|
|
79
82
|
|
|
@@ -122,6 +125,29 @@ export declare const firmware: {
|
|
|
122
125
|
readonly idfVersion: string | undefined
|
|
123
126
|
}
|
|
124
127
|
|
|
128
|
+
/** Why the device last reset. A clean `restart()` reports `'software'`;
|
|
129
|
+
* a crash reports `'panic'`. Host/Node builds always report `'unknown'`. */
|
|
130
|
+
export type ResetReason =
|
|
131
|
+
| 'power-on'
|
|
132
|
+
| 'software'
|
|
133
|
+
| 'panic'
|
|
134
|
+
| 'watchdog'
|
|
135
|
+
| 'interrupt-watchdog'
|
|
136
|
+
| 'task-watchdog'
|
|
137
|
+
| 'brownout'
|
|
138
|
+
| 'deep-sleep'
|
|
139
|
+
| 'external'
|
|
140
|
+
| 'sdio'
|
|
141
|
+
| 'usb'
|
|
142
|
+
| 'jtag'
|
|
143
|
+
| 'efuse'
|
|
144
|
+
| 'power-glitch'
|
|
145
|
+
| 'cpu-lockup'
|
|
146
|
+
| 'unknown'
|
|
147
|
+
|
|
148
|
+
/** Reason the device last reset, determined once at boot. */
|
|
149
|
+
export declare const resetReason: ResetReason
|
|
150
|
+
|
|
125
151
|
export declare function restart(): never
|
|
126
152
|
|
|
127
153
|
/** Reason the device woke up this boot. Set after deep sleep (or first
|
package/src/builtins.cpp
CHANGED
|
@@ -32,11 +32,16 @@ static JSModuleDef* mik__deserialize_builtin(JSContext* ctx, const char* name,
|
|
|
32
32
|
JSValue obj = JS_ReadObject(ctx, data, data_size, JS_READ_OBJ_BYTECODE);
|
|
33
33
|
|
|
34
34
|
if (JS_IsException(obj)) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
/* Peek the exception for the log, then re-throw it so the caller
|
|
36
|
+
* can still propagate the real error (e.g. "Maximum call stack
|
|
37
|
+
* size exceeded") instead of a generic "not available" message. */
|
|
38
|
+
JSValue exc = JS_GetException(ctx);
|
|
39
|
+
const char* msg = JS_ToCString(ctx, exc);
|
|
40
|
+
platform->log(MIK_LOG_ERROR, "mikrojs",
|
|
41
|
+
"Failed to deserialize bytecode for builtin '%s' (%u bytes): %s", name,
|
|
42
|
+
data_size, msg ? msg : "unknown error");
|
|
43
|
+
if (msg) JS_FreeCString(ctx, msg);
|
|
44
|
+
JS_Throw(ctx, exc);
|
|
40
45
|
return NULL;
|
|
41
46
|
}
|
|
42
47
|
|
package/src/mik_abort.cpp
CHANGED
|
@@ -1,110 +1,32 @@
|
|
|
1
1
|
#include <quickjs.h>
|
|
2
2
|
|
|
3
|
+
#include "mikrojs/private.h"
|
|
3
4
|
#include "mikrojs/utils.h"
|
|
4
5
|
|
|
5
|
-
/*
|
|
6
|
+
/* AbortController / AbortSignal globals.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* The implementation lives in runtime/abort/abort.ts and ships as
|
|
9
|
+
* precompiled bytecode in the builtins table ("mikro/abort").
|
|
10
|
+
* Evaluating that module installs AbortController, AbortSignal,
|
|
11
|
+
* AbortError, and TimeoutError on globalThis.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* AbortSignal.any(signals) — aborts when any input signal aborts
|
|
17
|
-
* AbortController: signal, abort(reason)
|
|
18
|
-
*
|
|
19
|
-
* Lazy-initialized: the JS class hierarchy is compiled and installed
|
|
20
|
-
* on first access to any of the four globals (AbortController,
|
|
21
|
-
* AbortSignal, AbortError, TimeoutError).
|
|
13
|
+
* Lazy-initialized: this file installs lazy getters for the four
|
|
14
|
+
* globals; the first access evaluates the bytecode module. Bytecode
|
|
15
|
+
* (rather than JS source evaluated at runtime) keeps the QuickJS
|
|
16
|
+
* parser out of the install path — the parse peak pushed low-memory
|
|
17
|
+
* chips into OOM when the heap was already near full.
|
|
22
18
|
*/
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
(function(g) {
|
|
26
|
-
"use strict";
|
|
27
|
-
|
|
28
|
-
class AbortError extends Error {
|
|
29
|
-
constructor(message) { super(message || "The operation was aborted"); }
|
|
30
|
-
get name() { return "AbortError"; }
|
|
31
|
-
}
|
|
32
|
-
class TimeoutError extends Error {
|
|
33
|
-
constructor(message) { super(message || "The operation timed out"); }
|
|
34
|
-
get name() { return "TimeoutError"; }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const _a = Symbol(), _r = Symbol(), _l = Symbol(), _s = Symbol();
|
|
38
|
-
const ABORT_REASON = () => new AbortError();
|
|
39
|
-
|
|
40
|
-
function doAbort(signal, reason) {
|
|
41
|
-
if (signal[_a]) return;
|
|
42
|
-
signal[_a] = true;
|
|
43
|
-
signal[_r] = reason;
|
|
44
|
-
if (typeof signal.onabort === "function") signal.onabort();
|
|
45
|
-
for (const fn of signal[_l]) fn();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
class AbortSignal {
|
|
49
|
-
constructor() {
|
|
50
|
-
this[_a] = false;
|
|
51
|
-
this[_r] = undefined;
|
|
52
|
-
this[_l] = [];
|
|
53
|
-
this.onabort = null;
|
|
54
|
-
}
|
|
55
|
-
get aborted() { return this[_a]; }
|
|
56
|
-
get reason() { return this[_r]; }
|
|
57
|
-
throwIfAborted() { if (this[_a]) throw this[_r]; }
|
|
58
|
-
addEventListener(type, fn) {
|
|
59
|
-
if (type === "abort" && typeof fn === "function") this[_l].push(fn);
|
|
60
|
-
}
|
|
61
|
-
removeEventListener(type, fn) {
|
|
62
|
-
if (type === "abort") this[_l] = this[_l].filter(f => f !== fn);
|
|
63
|
-
}
|
|
64
|
-
static abort(reason) {
|
|
65
|
-
const s = new AbortSignal();
|
|
66
|
-
doAbort(s, reason !== undefined ? reason : ABORT_REASON());
|
|
67
|
-
return s;
|
|
68
|
-
}
|
|
69
|
-
static timeout(ms) {
|
|
70
|
-
const s = new AbortSignal();
|
|
71
|
-
setTimeout(() => doAbort(s, new TimeoutError()), ms);
|
|
72
|
-
return s;
|
|
73
|
-
}
|
|
74
|
-
static any(signals) {
|
|
75
|
-
const s = new AbortSignal();
|
|
76
|
-
for (const i of signals) {
|
|
77
|
-
if (i.aborted) { doAbort(s, i.reason); return s; }
|
|
78
|
-
}
|
|
79
|
-
for (const i of signals) {
|
|
80
|
-
i.addEventListener("abort", () => doAbort(s, i.reason));
|
|
81
|
-
}
|
|
82
|
-
return s;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
class AbortController {
|
|
87
|
-
constructor() { this[_s] = new AbortSignal(); }
|
|
88
|
-
get signal() { return this[_s]; }
|
|
89
|
-
abort(reason) { doAbort(this[_s], reason !== undefined ? reason : ABORT_REASON()); }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
g.AbortError = AbortError;
|
|
93
|
-
g.TimeoutError = TimeoutError;
|
|
94
|
-
g.AbortSignal = AbortSignal;
|
|
95
|
-
g.AbortController = AbortController;
|
|
96
|
-
})
|
|
97
|
-
)JS";
|
|
98
|
-
|
|
99
|
-
/* Names of the globals this module installs */
|
|
20
|
+
/* Names of the globals the abort module installs */
|
|
100
21
|
static const char* const abort_global_names[] = {"AbortController", "AbortSignal", "AbortError",
|
|
101
22
|
"TimeoutError"};
|
|
102
23
|
static constexpr int ABORT_GLOBAL_COUNT = 4;
|
|
103
24
|
|
|
104
25
|
/**
|
|
105
26
|
* Lazy getter: on first access to any of the four globals, delete all
|
|
106
|
-
* lazy getters, evaluate the
|
|
107
|
-
* the requested one.
|
|
27
|
+
* lazy getters, evaluate the bytecode module to install real values,
|
|
28
|
+
* then return the requested one. The magic parameter identifies which
|
|
29
|
+
* global.
|
|
108
30
|
*
|
|
109
31
|
* Signature must match JS_CFUNC_getter_magic: (ctx, this_val, magic).
|
|
110
32
|
*/
|
|
@@ -112,29 +34,40 @@ static JSValue mik__abort_lazy_get(JSContext* ctx, JSValue this_val, int magic)
|
|
|
112
34
|
|
|
113
35
|
JSValue global_obj = JS_GetGlobalObject(ctx);
|
|
114
36
|
|
|
115
|
-
/* Remove all four lazy getters so the
|
|
37
|
+
/* Remove all four lazy getters so the module's globalThis
|
|
38
|
+
* assignments define plain properties */
|
|
116
39
|
for (int i = 0; i < ABORT_GLOBAL_COUNT; i++) {
|
|
117
40
|
JSAtom prop = JS_NewAtom(ctx, abort_global_names[i]);
|
|
118
41
|
JS_DeleteProperty(ctx, global_obj, prop, 0);
|
|
119
42
|
JS_FreeAtom(ctx, prop);
|
|
120
43
|
}
|
|
121
44
|
|
|
122
|
-
/* Evaluate
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
45
|
+
/* Evaluate the precompiled module; its side effect installs the
|
|
46
|
+
* globals. The module is synchronous, so the returned promise is
|
|
47
|
+
* already settled. */
|
|
48
|
+
JSModuleDef* m = mik__load_builtin(ctx, "mikro/abort");
|
|
49
|
+
if (m == NULL) {
|
|
50
|
+
/* A table miss returns NULL with no pending exception (only
|
|
51
|
+
* deserialize failures throw); without this guard the getter
|
|
52
|
+
* would surface a bare 'null'. Mirrors the module loader's
|
|
53
|
+
* missing-builtin fallback. */
|
|
54
|
+
if (!JS_HasException(ctx)) {
|
|
55
|
+
JS_ThrowReferenceError(ctx,
|
|
56
|
+
"Builtin module 'mikro/abort' is not available in this build");
|
|
57
|
+
}
|
|
128
58
|
JS_FreeValue(ctx, global_obj);
|
|
129
|
-
return
|
|
59
|
+
return JS_EXCEPTION;
|
|
130
60
|
}
|
|
131
|
-
JSValue ret =
|
|
132
|
-
JS_FreeValue(ctx, wrapper);
|
|
61
|
+
JSValue ret = JS_EvalFunction(ctx, JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)));
|
|
133
62
|
if (JS_IsException(ret)) {
|
|
134
|
-
JSValue exc = JS_GetException(ctx);
|
|
135
|
-
JS_FreeValue(ctx, exc);
|
|
136
63
|
JS_FreeValue(ctx, global_obj);
|
|
137
|
-
return
|
|
64
|
+
return JS_EXCEPTION;
|
|
65
|
+
}
|
|
66
|
+
if (JS_PromiseState(ctx, ret) == JS_PROMISE_REJECTED) {
|
|
67
|
+
JSValue err = JS_PromiseResult(ctx, ret);
|
|
68
|
+
JS_FreeValue(ctx, ret);
|
|
69
|
+
JS_FreeValue(ctx, global_obj);
|
|
70
|
+
return JS_Throw(ctx, err);
|
|
138
71
|
}
|
|
139
72
|
JS_FreeValue(ctx, ret);
|
|
140
73
|
|
|
@@ -145,7 +78,7 @@ static JSValue mik__abort_lazy_get(JSContext* ctx, JSValue this_val, int magic)
|
|
|
145
78
|
}
|
|
146
79
|
|
|
147
80
|
void mik__abort_init(JSContext* ctx, JSValue global_obj) {
|
|
148
|
-
/* Install lazy getters instead of eagerly evaluating the
|
|
81
|
+
/* Install lazy getters instead of eagerly evaluating the module.
|
|
149
82
|
* Each getter shares the same C function, distinguished by magic. */
|
|
150
83
|
for (int i = 0; i < ABORT_GLOBAL_COUNT; i++) {
|
|
151
84
|
JSAtom prop = JS_NewAtom(ctx, abort_global_names[i]);
|
package/src/mik_console.cpp
CHANGED
|
@@ -236,31 +236,32 @@ static JSValue mik__console_error_warn(JSContext* ctx, JSValue this_val, int arg
|
|
|
236
236
|
|
|
237
237
|
/* ── Uncaught error reporting ─────────────────────────────────────── */
|
|
238
238
|
|
|
239
|
-
/* Dedup:
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
239
|
+
/* Dedup across report paths: one error can reach this function more than
|
|
240
|
+
* once for a single failure. A failed dynamic import leaves two never-handled
|
|
241
|
+
* promises with the SAME reason (QuickJS runs a module body as an async
|
|
242
|
+
* function and reads its rejected result by value, plus the rejection
|
|
243
|
+
* propagated up the await chain), and at the REPL the throw path reports an
|
|
244
|
+
* error the deferred flush then sees again. Mark the error object the first
|
|
245
|
+
* time it is reported and skip it afterwards. Keying on object identity is
|
|
246
|
+
* exact and time-independent; a recycled address belongs to a fresh object
|
|
247
|
+
* with no marker, so distinct errors are never suppressed. Primitive
|
|
248
|
+
* rejections (e.g. a null OOM rejection) can't hold a marker and are always
|
|
249
|
+
* reported. Returns true if it reported, false if it was a duplicate. */
|
|
250
|
+
bool mik__report_uncaught(JSContext* ctx, JSValue exc, bool in_promise) {
|
|
250
251
|
if (JS_IsObject(exc)) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
252
|
+
JSAtom marker = JS_NewAtom(ctx, "\xff" "mik_reported");
|
|
253
|
+
if (marker != JS_ATOM_NULL) {
|
|
254
|
+
if (JS_HasProperty(ctx, exc, marker) > 0) {
|
|
255
|
+
JS_FreeAtom(ctx, marker);
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
/* Non-enumerable, non-writable, non-configurable so it stays
|
|
259
|
+
* invisible to user code. Best-effort: JS_* calls return error
|
|
260
|
+
* codes (no C++ throw), so under OOM we just report without
|
|
261
|
+
* dedup rather than abort. */
|
|
262
|
+
JS_DefinePropertyValue(ctx, exc, marker, JS_TRUE, 0);
|
|
263
|
+
JS_FreeAtom(ctx, marker);
|
|
259
264
|
}
|
|
260
|
-
last_reported_ptr = ptr;
|
|
261
|
-
last_reported_time = now;
|
|
262
|
-
} else {
|
|
263
|
-
last_reported_ptr = nullptr;
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
/* Fixed-size stack buffer so this function never allocates from the
|
|
@@ -360,11 +361,12 @@ void mik__report_uncaught(JSContext* ctx, JSValue exc, bool in_promise) {
|
|
|
360
361
|
|
|
361
362
|
if (mik__repl_is_protocol_mode()) {
|
|
362
363
|
mik__repl_proto_send_output(MIK_MSG_ERROR, buf, pos);
|
|
363
|
-
return;
|
|
364
|
+
return true;
|
|
364
365
|
}
|
|
365
366
|
|
|
366
367
|
append_cstr("\r\n");
|
|
367
368
|
MIK_GetPlatform()->stderr_write(buf, pos);
|
|
369
|
+
return true;
|
|
368
370
|
}
|
|
369
371
|
|
|
370
372
|
/* ── Test emit ────────────────────────────────────────────────────── */
|
package/src/mik_inspect.cpp
CHANGED
|
@@ -805,7 +805,7 @@ std::string mik_inspect(JSContext* ctx, JSValue value, int depth, bool colors, b
|
|
|
805
805
|
return inspect_value(ctx, value, opts, depth);
|
|
806
806
|
}
|
|
807
807
|
|
|
808
|
-
/* ── JS-callable inspect function (for
|
|
808
|
+
/* ── JS-callable inspect function (for mikro/inspect module) ───── */
|
|
809
809
|
|
|
810
810
|
static JSValue mik__inspect_js(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
811
811
|
if (argc < 1) return JS_NewString(ctx, "undefined");
|
|
@@ -836,14 +836,14 @@ static JSValue mik__inspect_js(JSContext* ctx, JSValue this_val, int argc, JSVal
|
|
|
836
836
|
return JS_NewString(ctx, result.c_str());
|
|
837
837
|
}
|
|
838
838
|
|
|
839
|
-
/* Module init for '
|
|
839
|
+
/* Module init for 'mikro/inspect' native C module */
|
|
840
840
|
int mik__inspect_module_init(JSContext* ctx, JSModuleDef* m) {
|
|
841
841
|
return JS_SetModuleExport(ctx, m, "inspect",
|
|
842
842
|
JS_NewCFunction(ctx, mik__inspect_js, "inspect", 1));
|
|
843
843
|
}
|
|
844
844
|
|
|
845
845
|
void mik__inspect_register(JSContext* ctx) {
|
|
846
|
-
JSModuleDef* m = JS_NewCModule(ctx, "
|
|
846
|
+
JSModuleDef* m = JS_NewCModule(ctx, "mikro/inspect", mik__inspect_module_init);
|
|
847
847
|
if (m) {
|
|
848
848
|
JS_AddModuleExport(ctx, m, "inspect");
|
|
849
849
|
}
|
package/src/mik_repl.cpp
CHANGED
|
@@ -263,6 +263,10 @@ static JSValue repl_eval_and_pump(JSContext* ctx, const char* code, size_t len)
|
|
|
263
263
|
return pr;
|
|
264
264
|
}
|
|
265
265
|
if (state == JS_PROMISE_REJECTED) {
|
|
266
|
+
/* This promise's rejection is about to be reported via the throw
|
|
267
|
+
* path below; drop it from the deferred unhandled-rejection queue
|
|
268
|
+
* so the end-of-turn flush doesn't report it a second time. */
|
|
269
|
+
mik__forget_rejection(ctx, result);
|
|
266
270
|
JSValue reason = JS_PromiseResult(ctx, result);
|
|
267
271
|
JS_FreeValue(ctx, result);
|
|
268
272
|
JS_Throw(ctx, reason);
|
package/src/mik_sys.cpp
CHANGED
|
@@ -260,7 +260,17 @@ void mik__sys_api_init(JSContext* ctx, JSValue ns) {
|
|
|
260
260
|
JS_SetPropertyStr(ctx, ns, "board", mik__sys_board(ctx));
|
|
261
261
|
JS_SetPropertyStr(ctx, ns, "firmware", mik__sys_firmware(ctx));
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
/* Guard the platform function POINTERS, not just their results: a
|
|
264
|
+
* platform that omits an optional hook leaves a NULL pointer, and
|
|
265
|
+
* calling it is a segfault with no diagnostics (the sim was down for
|
|
266
|
+
* weeks because platform_node lacked get_reset_reason). */
|
|
267
|
+
const MIKPlatform* platform = MIK_GetPlatform();
|
|
268
|
+
const char* device_id = platform->get_device_id ? platform->get_device_id() : NULL;
|
|
264
269
|
JS_SetPropertyStr(ctx, ns, "deviceId",
|
|
265
270
|
device_id ? JS_NewString(ctx, device_id) : JS_UNDEFINED);
|
|
271
|
+
|
|
272
|
+
const char* reset_reason =
|
|
273
|
+
platform->get_reset_reason ? platform->get_reset_reason() : NULL;
|
|
274
|
+
JS_SetPropertyStr(ctx, ns, "resetReason",
|
|
275
|
+
JS_NewString(ctx, reset_reason ? reset_reason : "unknown"));
|
|
266
276
|
}
|