@mikrojs/native 0.0.7
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 +198 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/cmake/mikrojs_bytecode.cmake +146 -0
- package/cmake.js +22 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +132 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/include/byteorder_apple.h +11 -0
- package/include/byteorder_windows.h +12 -0
- package/include/mikrojs/cbor_helpers.h +24 -0
- package/include/mikrojs/cutils_wrap.h +59 -0
- package/include/mikrojs/errors.h +144 -0
- package/include/mikrojs/mem.h +11 -0
- package/include/mikrojs/mik_color.h +32 -0
- package/include/mikrojs/mikrojs.h +331 -0
- package/include/mikrojs/platform.h +82 -0
- package/include/mikrojs/private.h +281 -0
- package/include/mikrojs/utils.h +125 -0
- package/package.json +100 -0
- 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/ble/ble.ts +231 -0
- package/runtime/ble/types.ts +194 -0
- package/runtime/ble/uuid.ts +89 -0
- package/runtime/ble/validators.ts +61 -0
- package/runtime/cbor/cbor.ts +1 -0
- package/runtime/cbor/types.ts +8 -0
- package/runtime/console/types.ts +50 -0
- package/runtime/env/env.ts +17 -0
- package/runtime/env/types.ts +12 -0
- package/runtime/format/types.ts +4 -0
- package/runtime/fs/fs.ts +93 -0
- package/runtime/fs/types.ts +92 -0
- package/runtime/globals.d.ts +87 -0
- package/runtime/http/helpers.ts +222 -0
- package/runtime/http/native.ts +151 -0
- package/runtime/http/request.ts +25 -0
- package/runtime/i2c/i2c.ts +35 -0
- package/runtime/i2c/types.ts +55 -0
- package/runtime/inspect/types.ts +10 -0
- package/runtime/internal.d.ts +456 -0
- package/runtime/kv/nvs.ts +17 -0
- package/runtime/kv/rtc.ts +17 -0
- package/runtime/kv/shared.ts +107 -0
- package/runtime/kv/types.ts +150 -0
- package/runtime/neopixel/neopixel.ts +38 -0
- package/runtime/neopixel/types.ts +27 -0
- package/runtime/pin/pin.ts +51 -0
- package/runtime/pin/types.ts +49 -0
- package/runtime/pwm/pwm.ts +32 -0
- package/runtime/pwm/types.ts +29 -0
- package/runtime/reader/reader.ts +167 -0
- package/runtime/reader/types.ts +34 -0
- package/runtime/result/native-result.node-shim.ts +44 -0
- package/runtime/result/result.ts +26 -0
- package/runtime/result/types.ts +60 -0
- package/runtime/schema/schema.ts +321 -0
- package/runtime/schema/types.ts +152 -0
- package/runtime/sleep/sleep.ts +14 -0
- package/runtime/sleep/types.ts +44 -0
- package/runtime/sntp/sntp.ts +54 -0
- package/runtime/sntp/types.ts +38 -0
- package/runtime/spi/spi.ts +31 -0
- package/runtime/spi/types.ts +42 -0
- package/runtime/stdio/stdio.ts +44 -0
- package/runtime/stdio/types.ts +22 -0
- package/runtime/stream/stream.ts +150 -0
- package/runtime/stream/types.ts +47 -0
- package/runtime/sys/sys.ts +90 -0
- package/runtime/sys/types.ts +131 -0
- package/runtime/test/test.ts +595 -0
- package/runtime/test/types.ts +97 -0
- package/runtime/uart/types.ts +75 -0
- package/runtime/uart/uart.ts +51 -0
- package/runtime/wifi/types.ts +156 -0
- package/runtime/wifi/wifi.ts +208 -0
- package/scripts/bundle-runtime.js +149 -0
- package/scripts/compare-minifiers.js +189 -0
- package/scripts/compile-bytecode.sh +38 -0
- package/scripts/copy-prebuild.js +20 -0
- package/scripts/generate-symbol-map.js +146 -0
- package/src/builtins.cpp +82 -0
- package/src/cutils_compat.c +38 -0
- package/src/eval_bytecode.cpp +42 -0
- package/src/fs.cpp +878 -0
- package/src/mem.cpp +63 -0
- package/src/mik_abort.cpp +160 -0
- package/src/mik_app_config.cpp +358 -0
- package/src/mik_cbor.cpp +334 -0
- package/src/mik_color.cpp +46 -0
- package/src/mik_console.cpp +422 -0
- package/src/mik_inspect.cpp +850 -0
- package/src/mik_repl.cpp +1122 -0
- package/src/mik_result.cpp +344 -0
- package/src/mik_stdio.cpp +147 -0
- package/src/mik_sys.cpp +239 -0
- package/src/mik_text_encoding.cpp +443 -0
- package/src/mikrojs.cpp +942 -0
- package/src/modules.cpp +944 -0
- package/src/platform_posix.cpp +134 -0
- package/src/timers.cpp +208 -0
- package/src/utils.cpp +173 -0
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
#include <cinttypes>
|
|
2
|
+
#include <cmath>
|
|
3
|
+
#include <cstdio>
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <vector>
|
|
6
|
+
|
|
7
|
+
#include <quickjs.h>
|
|
8
|
+
|
|
9
|
+
#include "mikrojs/mik_color.h"
|
|
10
|
+
#include "mikrojs/private.h"
|
|
11
|
+
#include "mikrojs/utils.h"
|
|
12
|
+
|
|
13
|
+
/* ── Options ─────────────────────────────────────────────────────── */
|
|
14
|
+
|
|
15
|
+
struct InspectOpts {
|
|
16
|
+
int depth;
|
|
17
|
+
int truncate;
|
|
18
|
+
bool colors;
|
|
19
|
+
bool show_hidden;
|
|
20
|
+
std::vector<void*> seen;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/* ── Helpers ─────────────────────────────────────────────────────── */
|
|
24
|
+
|
|
25
|
+
static const char TRUNCATOR[] = "\xe2\x80\xa6"; // UTF-8 "…"
|
|
26
|
+
|
|
27
|
+
static std::string stylize(const std::string& value, MikThemeToken token, const InspectOpts& opts) {
|
|
28
|
+
if (opts.colors) {
|
|
29
|
+
return mik_colorize(token, value);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static std::string truncate_str(const std::string& str, int max_len) {
|
|
35
|
+
if (max_len <= 0) return TRUNCATOR;
|
|
36
|
+
int slen = static_cast<int>(str.size());
|
|
37
|
+
if (slen <= max_len) return str;
|
|
38
|
+
if (max_len <= 1) return TRUNCATOR;
|
|
39
|
+
return str.substr(0, max_len - 1) + TRUNCATOR;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Check if a key needs quoting */
|
|
43
|
+
static bool is_simple_key(const char* key) {
|
|
44
|
+
if (!key || !*key) return false;
|
|
45
|
+
char c = key[0];
|
|
46
|
+
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) return false;
|
|
47
|
+
for (const char* p = key + 1; *p; p++) {
|
|
48
|
+
c = *p;
|
|
49
|
+
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') ||
|
|
50
|
+
c == '_'))
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static std::string quote_key(const char* key) {
|
|
57
|
+
if (is_simple_key(key)) return key;
|
|
58
|
+
/* Wrap in single quotes with minimal escaping */
|
|
59
|
+
std::string out = "'";
|
|
60
|
+
for (const char* p = key; *p; p++) {
|
|
61
|
+
if (*p == '\'')
|
|
62
|
+
out += "\\'";
|
|
63
|
+
else if (*p == '\\')
|
|
64
|
+
out += "\\\\";
|
|
65
|
+
else
|
|
66
|
+
out += *p;
|
|
67
|
+
}
|
|
68
|
+
out += "'";
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* ── Forward declaration ─────────────────────────────────────────── */
|
|
73
|
+
|
|
74
|
+
static std::string inspect_value(JSContext* ctx, JSValue value, InspectOpts& opts, int depth);
|
|
75
|
+
|
|
76
|
+
/* ── Custom inspect callback state (single-threaded JS) ──────────── */
|
|
77
|
+
|
|
78
|
+
static InspectOpts* s_inspect_opts = nullptr;
|
|
79
|
+
|
|
80
|
+
static JSValue mik__custom_inspect_cb(JSContext* ctx, JSValue /*this_val*/, int argc,
|
|
81
|
+
JSValue* argv) {
|
|
82
|
+
if (argc < 1 || !s_inspect_opts) return JS_NewString(ctx, "undefined");
|
|
83
|
+
int32_t depth = 2;
|
|
84
|
+
if (argc >= 2) JS_ToInt32(ctx, &depth, argv[1]);
|
|
85
|
+
std::string result = inspect_value(ctx, argv[0], *s_inspect_opts, depth);
|
|
86
|
+
return JS_NewString(ctx, result.c_str());
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Call Symbol.for('mikrojs.inspect') on value if present.
|
|
90
|
+
* Returns true and sets `out` if custom inspect was used. */
|
|
91
|
+
static bool try_custom_inspect(JSContext* ctx, JSValue value, InspectOpts& opts, int depth,
|
|
92
|
+
std::string& out) {
|
|
93
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
94
|
+
JSValue symbol_ctor = JS_GetPropertyStr(ctx, global, "Symbol");
|
|
95
|
+
JSValue for_fn = JS_GetPropertyStr(ctx, symbol_ctor, "for");
|
|
96
|
+
|
|
97
|
+
JSValue key_str = JS_NewString(ctx, "mikrojs.inspect");
|
|
98
|
+
JSValue inspect_sym = JS_Call(ctx, for_fn, symbol_ctor, 1, &key_str);
|
|
99
|
+
JS_FreeValue(ctx, key_str);
|
|
100
|
+
JS_FreeValue(ctx, for_fn);
|
|
101
|
+
|
|
102
|
+
bool used = false;
|
|
103
|
+
|
|
104
|
+
if (!JS_IsException(inspect_sym)) {
|
|
105
|
+
JSAtom sym_atom = JS_ValueToAtom(ctx, inspect_sym);
|
|
106
|
+
JSValue custom_fn = JS_GetProperty(ctx, value, sym_atom);
|
|
107
|
+
JS_FreeAtom(ctx, sym_atom);
|
|
108
|
+
|
|
109
|
+
if (JS_IsFunction(ctx, custom_fn)) {
|
|
110
|
+
/* Set up callback state and create the inspect helper */
|
|
111
|
+
InspectOpts* prev_opts = s_inspect_opts;
|
|
112
|
+
s_inspect_opts = &opts;
|
|
113
|
+
|
|
114
|
+
JSValue inspect_fn =
|
|
115
|
+
JS_NewCFunction(ctx, mik__custom_inspect_cb, "inspect", 1);
|
|
116
|
+
JSValue args[2] = {JS_NewInt32(ctx, depth), inspect_fn};
|
|
117
|
+
JSValue result = JS_Call(ctx, custom_fn, value, 2, args);
|
|
118
|
+
JS_FreeValue(ctx, inspect_fn);
|
|
119
|
+
JS_FreeValue(ctx, args[0]);
|
|
120
|
+
|
|
121
|
+
s_inspect_opts = prev_opts;
|
|
122
|
+
|
|
123
|
+
if (JS_IsString(result)) {
|
|
124
|
+
const char* str = JS_ToCString(ctx, result);
|
|
125
|
+
if (str) {
|
|
126
|
+
out = str;
|
|
127
|
+
JS_FreeCString(ctx, str);
|
|
128
|
+
used = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
JS_FreeValue(ctx, result);
|
|
132
|
+
}
|
|
133
|
+
JS_FreeValue(ctx, custom_fn);
|
|
134
|
+
}
|
|
135
|
+
JS_FreeValue(ctx, inspect_sym);
|
|
136
|
+
JS_FreeValue(ctx, symbol_ctor);
|
|
137
|
+
JS_FreeValue(ctx, global);
|
|
138
|
+
|
|
139
|
+
return used;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* ── String escaping ─────────────────────────────────────────────── */
|
|
143
|
+
|
|
144
|
+
static std::string escape_string(const char* str, size_t len) {
|
|
145
|
+
std::string out;
|
|
146
|
+
out.reserve(len + 8);
|
|
147
|
+
for (size_t i = 0; i < len; i++) {
|
|
148
|
+
unsigned char c = static_cast<unsigned char>(str[i]);
|
|
149
|
+
switch (c) {
|
|
150
|
+
case '\b':
|
|
151
|
+
out += "\\b";
|
|
152
|
+
break;
|
|
153
|
+
case '\t':
|
|
154
|
+
out += "\\t";
|
|
155
|
+
break;
|
|
156
|
+
case '\n':
|
|
157
|
+
out += "\\n";
|
|
158
|
+
break;
|
|
159
|
+
case '\f':
|
|
160
|
+
out += "\\f";
|
|
161
|
+
break;
|
|
162
|
+
case '\r':
|
|
163
|
+
out += "\\r";
|
|
164
|
+
break;
|
|
165
|
+
case '\'':
|
|
166
|
+
out += "\\'";
|
|
167
|
+
break;
|
|
168
|
+
case '\\':
|
|
169
|
+
out += "\\\\";
|
|
170
|
+
break;
|
|
171
|
+
default:
|
|
172
|
+
if (c < 0x20 || c == 0x7f) {
|
|
173
|
+
/* ASCII control characters */
|
|
174
|
+
char buf[8];
|
|
175
|
+
snprintf(buf, sizeof(buf), "\\x%02x", c);
|
|
176
|
+
out += buf;
|
|
177
|
+
} else {
|
|
178
|
+
/* Pass through printable ASCII and all multi-byte UTF-8
|
|
179
|
+
* bytes (0x80+) so strings with unicode render correctly */
|
|
180
|
+
out += static_cast<char>(c);
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* ── Type detection via Object.prototype.toString ────────────────── */
|
|
189
|
+
|
|
190
|
+
static std::string get_object_type(JSContext* ctx, JSValue value) {
|
|
191
|
+
/* Call Object.prototype.toString.call(value) */
|
|
192
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
193
|
+
JSValue object_ctor = JS_GetPropertyStr(ctx, global, "Object");
|
|
194
|
+
JSValue prototype = JS_GetPropertyStr(ctx, object_ctor, "prototype");
|
|
195
|
+
JSValue to_string = JS_GetPropertyStr(ctx, prototype, "toString");
|
|
196
|
+
|
|
197
|
+
JSValue result = JS_Call(ctx, to_string, value, 0, nullptr);
|
|
198
|
+
|
|
199
|
+
std::string type;
|
|
200
|
+
if (!JS_IsException(result)) {
|
|
201
|
+
const char* str = JS_ToCString(ctx, result);
|
|
202
|
+
if (str) {
|
|
203
|
+
/* Extract "Foo" from "[object Foo]" */
|
|
204
|
+
std::string_view sv(str);
|
|
205
|
+
if (sv.size() > 9 && sv.substr(0, 8) == "[object " && sv.back() == ']') {
|
|
206
|
+
type = sv.substr(8, sv.size() - 9);
|
|
207
|
+
} else {
|
|
208
|
+
type = sv;
|
|
209
|
+
}
|
|
210
|
+
JS_FreeCString(ctx, str);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
JS_FreeValue(ctx, result);
|
|
215
|
+
JS_FreeValue(ctx, to_string);
|
|
216
|
+
JS_FreeValue(ctx, prototype);
|
|
217
|
+
JS_FreeValue(ctx, object_ctor);
|
|
218
|
+
JS_FreeValue(ctx, global);
|
|
219
|
+
|
|
220
|
+
return type;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* ── Circular reference check ────────────────────────────────────── */
|
|
224
|
+
|
|
225
|
+
static bool is_seen(const InspectOpts& opts, JSValue value) {
|
|
226
|
+
void* ptr = JS_VALUE_GET_PTR(value);
|
|
227
|
+
for (auto* seen_ptr : opts.seen) {
|
|
228
|
+
if (seen_ptr == ptr) return true;
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* ── Inspect list of items ───────────────────────────────────────── */
|
|
234
|
+
|
|
235
|
+
/* Inspect a JS array's elements as a comma-separated list */
|
|
236
|
+
static std::string inspect_array_items(JSContext* ctx, JSValue arr, uint32_t len,
|
|
237
|
+
InspectOpts& opts, int depth) {
|
|
238
|
+
std::string out;
|
|
239
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
240
|
+
if (i > 0) out += ", ";
|
|
241
|
+
JSValue item = JS_GetPropertyUint32(ctx, arr, i);
|
|
242
|
+
out += inspect_value(ctx, item, opts, depth);
|
|
243
|
+
JS_FreeValue(ctx, item);
|
|
244
|
+
}
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* ── Inspect object properties ───────────────────────────────────── */
|
|
249
|
+
|
|
250
|
+
static std::string inspect_properties(JSContext* ctx, JSValue obj, InspectOpts& opts, int depth,
|
|
251
|
+
const char* const* skip_keys = nullptr,
|
|
252
|
+
size_t skip_count = 0) {
|
|
253
|
+
JSPropertyEnum* ptab = nullptr;
|
|
254
|
+
uint32_t plen = 0;
|
|
255
|
+
|
|
256
|
+
int flags = JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY;
|
|
257
|
+
if (opts.show_hidden) {
|
|
258
|
+
flags = JS_GPN_STRING_MASK;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (JS_GetOwnPropertyNames(ctx, &ptab, &plen, obj, flags) != 0) {
|
|
262
|
+
return "";
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
std::string out;
|
|
266
|
+
bool first = true;
|
|
267
|
+
for (uint32_t i = 0; i < plen; i++) {
|
|
268
|
+
const char* key = JS_AtomToCString(ctx, ptab[i].atom);
|
|
269
|
+
if (!key) {
|
|
270
|
+
JS_FreeAtom(ctx, ptab[i].atom);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* Skip specified keys */
|
|
275
|
+
bool skip = false;
|
|
276
|
+
if (skip_keys) {
|
|
277
|
+
for (size_t j = 0; j < skip_count; j++) {
|
|
278
|
+
if (strcmp(key, skip_keys[j]) == 0) {
|
|
279
|
+
skip = true;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (!skip) {
|
|
286
|
+
if (!first) out += ", ";
|
|
287
|
+
first = false;
|
|
288
|
+
|
|
289
|
+
JSValue val = JS_GetPropertyStr(ctx, obj, key);
|
|
290
|
+
out += quote_key(key);
|
|
291
|
+
out += ": ";
|
|
292
|
+
out += inspect_value(ctx, val, opts, depth);
|
|
293
|
+
JS_FreeValue(ctx, val);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
JS_FreeCString(ctx, key);
|
|
297
|
+
JS_FreeAtom(ctx, ptab[i].atom);
|
|
298
|
+
}
|
|
299
|
+
js_free(ctx, ptab);
|
|
300
|
+
return out;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* ── Type-specific inspectors ────────────────────────────────────── */
|
|
304
|
+
|
|
305
|
+
static std::string inspect_number(JSContext* ctx, JSValue value, InspectOpts& opts) {
|
|
306
|
+
double d;
|
|
307
|
+
JS_ToFloat64(ctx, &d, value);
|
|
308
|
+
|
|
309
|
+
if (std::isnan(d)) return stylize("NaN", MIK_TOKEN_NUMBER, opts);
|
|
310
|
+
if (d == INFINITY) return stylize("Infinity", MIK_TOKEN_NUMBER, opts);
|
|
311
|
+
if (d == -INFINITY) return stylize("-Infinity", MIK_TOKEN_NUMBER, opts);
|
|
312
|
+
if (d == 0.0) {
|
|
313
|
+
return stylize(std::signbit(d) ? "-0" : "+0", MIK_TOKEN_NUMBER, opts);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/* Use JS_ToCString which formats numbers correctly (integer or float) */
|
|
317
|
+
const char* str = JS_ToCString(ctx, value);
|
|
318
|
+
std::string result(str ? str : "0");
|
|
319
|
+
if (str) JS_FreeCString(ctx, str);
|
|
320
|
+
return stylize(result, MIK_TOKEN_NUMBER, opts);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
static std::string inspect_string(JSContext* ctx, JSValue value, InspectOpts& opts) {
|
|
324
|
+
size_t len;
|
|
325
|
+
const char* str = JS_ToCStringLen(ctx, &len, value);
|
|
326
|
+
if (!str) return stylize("''", MIK_TOKEN_STRING, opts);
|
|
327
|
+
|
|
328
|
+
std::string escaped = escape_string(str, len);
|
|
329
|
+
JS_FreeCString(ctx, str);
|
|
330
|
+
|
|
331
|
+
if (opts.truncate > 0) {
|
|
332
|
+
escaped = truncate_str(escaped, opts.truncate - 2);
|
|
333
|
+
}
|
|
334
|
+
return stylize("'" + escaped + "'", MIK_TOKEN_STRING, opts);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
static std::string inspect_function(JSContext* ctx, JSValue value, InspectOpts& opts) {
|
|
338
|
+
JSValue name_val = JS_GetPropertyStr(ctx, value, "name");
|
|
339
|
+
const char* name = JS_ToCString(ctx, name_val);
|
|
340
|
+
std::string result;
|
|
341
|
+
|
|
342
|
+
/* Check for Symbol.toStringTag */
|
|
343
|
+
std::string func_type = "Function";
|
|
344
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
345
|
+
JSValue symbol_ctor = JS_GetPropertyStr(ctx, global, "Symbol");
|
|
346
|
+
JSValue tag_sym = JS_GetPropertyStr(ctx, symbol_ctor, "toStringTag");
|
|
347
|
+
if (!JS_IsUndefined(tag_sym)) {
|
|
348
|
+
JSAtom tag_atom = JS_ValueToAtom(ctx, tag_sym);
|
|
349
|
+
JSValue tag_val = JS_GetProperty(ctx, value, tag_atom);
|
|
350
|
+
if (JS_IsString(tag_val)) {
|
|
351
|
+
const char* tag = JS_ToCString(ctx, tag_val);
|
|
352
|
+
if (tag) {
|
|
353
|
+
func_type = tag;
|
|
354
|
+
JS_FreeCString(ctx, tag);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
JS_FreeValue(ctx, tag_val);
|
|
358
|
+
JS_FreeAtom(ctx, tag_atom);
|
|
359
|
+
}
|
|
360
|
+
JS_FreeValue(ctx, tag_sym);
|
|
361
|
+
JS_FreeValue(ctx, symbol_ctor);
|
|
362
|
+
JS_FreeValue(ctx, global);
|
|
363
|
+
|
|
364
|
+
if (name && name[0]) {
|
|
365
|
+
result = "[" + func_type + " " + name + "]";
|
|
366
|
+
} else {
|
|
367
|
+
result = "[" + func_type + "]";
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (name) JS_FreeCString(ctx, name);
|
|
371
|
+
JS_FreeValue(ctx, name_val);
|
|
372
|
+
|
|
373
|
+
return stylize(result, MIK_TOKEN_FUNCTION, opts);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
static std::string inspect_symbol(JSContext* ctx, JSValue value, InspectOpts& opts) {
|
|
377
|
+
const char* str = JS_ToCString(ctx, value);
|
|
378
|
+
std::string result;
|
|
379
|
+
if (str) {
|
|
380
|
+
result = str;
|
|
381
|
+
JS_FreeCString(ctx, str);
|
|
382
|
+
} else {
|
|
383
|
+
result = "Symbol()";
|
|
384
|
+
}
|
|
385
|
+
return stylize(result, MIK_TOKEN_SYMBOL, opts);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
static std::string inspect_bigint(JSContext* ctx, JSValue value, InspectOpts& opts) {
|
|
389
|
+
const char* str = JS_ToCString(ctx, value);
|
|
390
|
+
std::string result;
|
|
391
|
+
if (str) {
|
|
392
|
+
result = std::string(str) + "n";
|
|
393
|
+
JS_FreeCString(ctx, str);
|
|
394
|
+
} else {
|
|
395
|
+
result = "0n";
|
|
396
|
+
}
|
|
397
|
+
return stylize(result, MIK_TOKEN_BIGINT, opts);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
static std::string inspect_date(JSContext* ctx, JSValue value, InspectOpts& opts) {
|
|
401
|
+
JSValue to_json = JS_GetPropertyStr(ctx, value, "toJSON");
|
|
402
|
+
if (JS_IsFunction(ctx, to_json)) {
|
|
403
|
+
JSValue json_val = JS_Call(ctx, to_json, value, 0, nullptr);
|
|
404
|
+
JS_FreeValue(ctx, to_json);
|
|
405
|
+
if (JS_IsNull(json_val) || JS_IsUndefined(json_val)) {
|
|
406
|
+
JS_FreeValue(ctx, json_val);
|
|
407
|
+
return "Invalid Date";
|
|
408
|
+
}
|
|
409
|
+
const char* str = JS_ToCString(ctx, json_val);
|
|
410
|
+
JS_FreeValue(ctx, json_val);
|
|
411
|
+
if (str) {
|
|
412
|
+
std::string result(str);
|
|
413
|
+
JS_FreeCString(ctx, str);
|
|
414
|
+
return stylize(result, MIK_TOKEN_DATE, opts);
|
|
415
|
+
}
|
|
416
|
+
} else {
|
|
417
|
+
JS_FreeValue(ctx, to_json);
|
|
418
|
+
}
|
|
419
|
+
return "Invalid Date";
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
static std::string inspect_regexp(JSContext* ctx, JSValue value, InspectOpts& opts) {
|
|
423
|
+
const char* str = JS_ToCString(ctx, value);
|
|
424
|
+
if (str) {
|
|
425
|
+
std::string result(str);
|
|
426
|
+
JS_FreeCString(ctx, str);
|
|
427
|
+
return stylize(result, MIK_TOKEN_REGEXP, opts);
|
|
428
|
+
}
|
|
429
|
+
return stylize("/(?:)/", MIK_TOKEN_REGEXP, opts);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
static std::string inspect_error(JSContext* ctx, JSValue value, InspectOpts& opts, int depth) {
|
|
433
|
+
/* Get name and message */
|
|
434
|
+
JSValue name_val = JS_GetPropertyStr(ctx, value, "name");
|
|
435
|
+
JSValue msg_val = JS_GetPropertyStr(ctx, value, "message");
|
|
436
|
+
|
|
437
|
+
const char* name = JS_ToCString(ctx, name_val);
|
|
438
|
+
const char* msg = JS_ToCString(ctx, msg_val);
|
|
439
|
+
|
|
440
|
+
std::string result;
|
|
441
|
+
if (name) result = name;
|
|
442
|
+
else result = "Error";
|
|
443
|
+
|
|
444
|
+
if (msg && msg[0]) {
|
|
445
|
+
result += ": ";
|
|
446
|
+
result += msg;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (name) JS_FreeCString(ctx, name);
|
|
450
|
+
if (msg) JS_FreeCString(ctx, msg);
|
|
451
|
+
JS_FreeValue(ctx, name_val);
|
|
452
|
+
JS_FreeValue(ctx, msg_val);
|
|
453
|
+
|
|
454
|
+
/* Inspect extra properties (skip standard error keys) */
|
|
455
|
+
static const char* const error_keys[] = {"stack", "line", "column", "name",
|
|
456
|
+
"message", "fileName", "lineNumber", "columnNumber",
|
|
457
|
+
"number", "description"};
|
|
458
|
+
std::string props =
|
|
459
|
+
inspect_properties(ctx, value, opts, depth, error_keys, countof(error_keys));
|
|
460
|
+
if (!props.empty()) {
|
|
461
|
+
result += " { " + props + " }";
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
static std::string inspect_array(JSContext* ctx, JSValue value, InspectOpts& opts, int depth) {
|
|
468
|
+
JSValue len_val = JS_GetPropertyStr(ctx, value, "length");
|
|
469
|
+
uint32_t len = 0;
|
|
470
|
+
JS_ToUint32(ctx, &len, len_val);
|
|
471
|
+
JS_FreeValue(ctx, len_val);
|
|
472
|
+
|
|
473
|
+
if (len == 0) {
|
|
474
|
+
/* Check for non-index properties */
|
|
475
|
+
JSPropertyEnum* ptab = nullptr;
|
|
476
|
+
uint32_t plen = 0;
|
|
477
|
+
int flags = JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY;
|
|
478
|
+
if (JS_GetOwnPropertyNames(ctx, &ptab, &plen, value, flags) == 0) {
|
|
479
|
+
/* All indexed properties would have numeric keys */
|
|
480
|
+
bool has_non_index = plen > len;
|
|
481
|
+
for (uint32_t i = 0; i < plen; i++) {
|
|
482
|
+
JS_FreeAtom(ctx, ptab[i].atom);
|
|
483
|
+
}
|
|
484
|
+
js_free(ctx, ptab);
|
|
485
|
+
if (!has_non_index) return "[]";
|
|
486
|
+
} else {
|
|
487
|
+
return "[]";
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (is_seen(opts, value)) return "[Circular]";
|
|
492
|
+
|
|
493
|
+
opts.seen.push_back(JS_VALUE_GET_PTR(value));
|
|
494
|
+
|
|
495
|
+
std::string items = inspect_array_items(ctx, value, len, opts, depth);
|
|
496
|
+
|
|
497
|
+
opts.seen.pop_back();
|
|
498
|
+
|
|
499
|
+
return "[ " + items + " ]";
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
static std::string inspect_map(JSContext* ctx, JSValue value, InspectOpts& opts, int depth) {
|
|
503
|
+
JSValue size_val = JS_GetPropertyStr(ctx, value, "size");
|
|
504
|
+
int32_t size = 0;
|
|
505
|
+
JS_ToInt32(ctx, &size, size_val);
|
|
506
|
+
JS_FreeValue(ctx, size_val);
|
|
507
|
+
|
|
508
|
+
if (size <= 0) return "Map{}";
|
|
509
|
+
|
|
510
|
+
if (is_seen(opts, value)) return "[Circular]";
|
|
511
|
+
opts.seen.push_back(JS_VALUE_GET_PTR(value));
|
|
512
|
+
|
|
513
|
+
/* Get entries via entries() iterator */
|
|
514
|
+
JSValue entries_fn = JS_GetPropertyStr(ctx, value, "entries");
|
|
515
|
+
JSValue iterator = JS_Call(ctx, entries_fn, value, 0, nullptr);
|
|
516
|
+
JS_FreeValue(ctx, entries_fn);
|
|
517
|
+
|
|
518
|
+
JSValue next_fn = JS_GetPropertyStr(ctx, iterator, "next");
|
|
519
|
+
|
|
520
|
+
std::string items;
|
|
521
|
+
bool first = true;
|
|
522
|
+
for (int i = 0; i < size; i++) {
|
|
523
|
+
JSValue step = JS_Call(ctx, next_fn, iterator, 0, nullptr);
|
|
524
|
+
JSValue done = JS_GetPropertyStr(ctx, step, "done");
|
|
525
|
+
if (JS_ToBool(ctx, done)) {
|
|
526
|
+
JS_FreeValue(ctx, done);
|
|
527
|
+
JS_FreeValue(ctx, step);
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
JS_FreeValue(ctx, done);
|
|
531
|
+
|
|
532
|
+
JSValue entry = JS_GetPropertyStr(ctx, step, "value");
|
|
533
|
+
JSValue key = JS_GetPropertyUint32(ctx, entry, 0);
|
|
534
|
+
JSValue val = JS_GetPropertyUint32(ctx, entry, 1);
|
|
535
|
+
|
|
536
|
+
if (!first) items += ", ";
|
|
537
|
+
first = false;
|
|
538
|
+
items += inspect_value(ctx, key, opts, depth);
|
|
539
|
+
items += " => ";
|
|
540
|
+
items += inspect_value(ctx, val, opts, depth);
|
|
541
|
+
|
|
542
|
+
JS_FreeValue(ctx, key);
|
|
543
|
+
JS_FreeValue(ctx, val);
|
|
544
|
+
JS_FreeValue(ctx, entry);
|
|
545
|
+
JS_FreeValue(ctx, step);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
JS_FreeValue(ctx, next_fn);
|
|
549
|
+
JS_FreeValue(ctx, iterator);
|
|
550
|
+
|
|
551
|
+
opts.seen.pop_back();
|
|
552
|
+
|
|
553
|
+
return "Map{ " + items + " }";
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
static std::string inspect_set(JSContext* ctx, JSValue value, InspectOpts& opts, int depth) {
|
|
557
|
+
JSValue size_val = JS_GetPropertyStr(ctx, value, "size");
|
|
558
|
+
int32_t size = 0;
|
|
559
|
+
JS_ToInt32(ctx, &size, size_val);
|
|
560
|
+
JS_FreeValue(ctx, size_val);
|
|
561
|
+
|
|
562
|
+
if (size == 0) return "Set{}";
|
|
563
|
+
|
|
564
|
+
if (is_seen(opts, value)) return "[Circular]";
|
|
565
|
+
opts.seen.push_back(JS_VALUE_GET_PTR(value));
|
|
566
|
+
|
|
567
|
+
/* Get values via forEach */
|
|
568
|
+
JSValue for_each_fn = JS_GetPropertyStr(ctx, value, "forEach");
|
|
569
|
+
|
|
570
|
+
/* Build array of values to inspect */
|
|
571
|
+
std::string items;
|
|
572
|
+
JSValue values_fn = JS_GetPropertyStr(ctx, value, "values");
|
|
573
|
+
JSValue iterator = JS_Call(ctx, values_fn, value, 0, nullptr);
|
|
574
|
+
JS_FreeValue(ctx, values_fn);
|
|
575
|
+
JS_FreeValue(ctx, for_each_fn);
|
|
576
|
+
|
|
577
|
+
JSValue next_fn = JS_GetPropertyStr(ctx, iterator, "next");
|
|
578
|
+
bool first = true;
|
|
579
|
+
for (int i = 0; i < size; i++) {
|
|
580
|
+
JSValue step = JS_Call(ctx, next_fn, iterator, 0, nullptr);
|
|
581
|
+
JSValue done = JS_GetPropertyStr(ctx, step, "done");
|
|
582
|
+
if (JS_ToBool(ctx, done)) {
|
|
583
|
+
JS_FreeValue(ctx, done);
|
|
584
|
+
JS_FreeValue(ctx, step);
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
JS_FreeValue(ctx, done);
|
|
588
|
+
|
|
589
|
+
JSValue val = JS_GetPropertyStr(ctx, step, "value");
|
|
590
|
+
if (!first) items += ", ";
|
|
591
|
+
first = false;
|
|
592
|
+
items += inspect_value(ctx, val, opts, depth);
|
|
593
|
+
JS_FreeValue(ctx, val);
|
|
594
|
+
JS_FreeValue(ctx, step);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
JS_FreeValue(ctx, next_fn);
|
|
598
|
+
JS_FreeValue(ctx, iterator);
|
|
599
|
+
|
|
600
|
+
opts.seen.pop_back();
|
|
601
|
+
|
|
602
|
+
return "Set{ " + items + " }";
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
static std::string inspect_typed_array(JSContext* ctx, JSValue value, const std::string& type_name,
|
|
606
|
+
InspectOpts& opts) {
|
|
607
|
+
size_t byte_length;
|
|
608
|
+
size_t byte_offset;
|
|
609
|
+
size_t bytes_per_element;
|
|
610
|
+
JSValue buffer = JS_GetTypedArrayBuffer(ctx, value, &byte_offset, &byte_length, &bytes_per_element);
|
|
611
|
+
JS_FreeValue(ctx, buffer);
|
|
612
|
+
|
|
613
|
+
JSValue len_val = JS_GetPropertyStr(ctx, value, "length");
|
|
614
|
+
uint32_t len = 0;
|
|
615
|
+
JS_ToUint32(ctx, &len, len_val);
|
|
616
|
+
JS_FreeValue(ctx, len_val);
|
|
617
|
+
|
|
618
|
+
if (len == 0) return type_name + "[]";
|
|
619
|
+
|
|
620
|
+
std::string items;
|
|
621
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
622
|
+
if (i > 0) items += ", ";
|
|
623
|
+
JSValue elem = JS_GetPropertyUint32(ctx, value, i);
|
|
624
|
+
const char* str = JS_ToCString(ctx, elem);
|
|
625
|
+
JS_FreeValue(ctx, elem);
|
|
626
|
+
items += stylize(str ? str : "0", MIK_TOKEN_NUMBER, opts);
|
|
627
|
+
if (str) JS_FreeCString(ctx, str);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return type_name + "[ " + items + " ]";
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
static std::string inspect_object(JSContext* ctx, JSValue value, InspectOpts& opts, int depth) {
|
|
634
|
+
if (is_seen(opts, value)) return "[Circular]";
|
|
635
|
+
|
|
636
|
+
/* Check for empty object */
|
|
637
|
+
JSPropertyEnum* ptab = nullptr;
|
|
638
|
+
uint32_t plen = 0;
|
|
639
|
+
int flags = JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY;
|
|
640
|
+
if (opts.show_hidden) {
|
|
641
|
+
flags = JS_GPN_STRING_MASK;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (JS_GetOwnPropertyNames(ctx, &ptab, &plen, value, flags) != 0) {
|
|
645
|
+
return "{}";
|
|
646
|
+
}
|
|
647
|
+
for (uint32_t i = 0; i < plen; i++) {
|
|
648
|
+
JS_FreeAtom(ctx, ptab[i].atom);
|
|
649
|
+
}
|
|
650
|
+
js_free(ctx, ptab);
|
|
651
|
+
|
|
652
|
+
if (plen == 0) return "{}";
|
|
653
|
+
|
|
654
|
+
opts.seen.push_back(JS_VALUE_GET_PTR(value));
|
|
655
|
+
std::string props = inspect_properties(ctx, value, opts, depth);
|
|
656
|
+
opts.seen.pop_back();
|
|
657
|
+
|
|
658
|
+
return "{ " + props + " }";
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
static std::string inspect_class(JSContext* ctx, JSValue value, const std::string& type_name,
|
|
662
|
+
InspectOpts& opts, int depth) {
|
|
663
|
+
/* Get constructor name or toStringTag */
|
|
664
|
+
std::string name = type_name;
|
|
665
|
+
|
|
666
|
+
if (name.empty() || name == "Object") {
|
|
667
|
+
JSValue ctor = JS_GetPropertyStr(ctx, value, "constructor");
|
|
668
|
+
if (JS_IsFunction(ctx, ctor)) {
|
|
669
|
+
JSValue ctor_name = JS_GetPropertyStr(ctx, ctor, "name");
|
|
670
|
+
const char* n = JS_ToCString(ctx, ctor_name);
|
|
671
|
+
if (n && n[0] && strcmp(n, "Object") != 0) {
|
|
672
|
+
name = n;
|
|
673
|
+
}
|
|
674
|
+
if (n) JS_FreeCString(ctx, n);
|
|
675
|
+
JS_FreeValue(ctx, ctor_name);
|
|
676
|
+
}
|
|
677
|
+
JS_FreeValue(ctx, ctor);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
std::string obj_str = inspect_object(ctx, value, opts, depth);
|
|
681
|
+
if (!name.empty() && name != "Object") {
|
|
682
|
+
return name + obj_str;
|
|
683
|
+
}
|
|
684
|
+
return obj_str;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/* ── Main dispatch ───────────────────────────────────────────────── */
|
|
688
|
+
|
|
689
|
+
static std::string inspect_value(JSContext* ctx, JSValue value, InspectOpts& opts, int depth) {
|
|
690
|
+
/* Primitives */
|
|
691
|
+
if (JS_IsUndefined(value)) {
|
|
692
|
+
return stylize("undefined", MIK_TOKEN_UNDEFINED, opts);
|
|
693
|
+
}
|
|
694
|
+
if (JS_IsNull(value)) {
|
|
695
|
+
return stylize("null", MIK_TOKEN_NULL, opts);
|
|
696
|
+
}
|
|
697
|
+
if (JS_IsBool(value)) {
|
|
698
|
+
return stylize(JS_ToBool(ctx, value) ? "true" : "false", MIK_TOKEN_BOOLEAN, opts);
|
|
699
|
+
}
|
|
700
|
+
if (JS_IsNumber(value)) {
|
|
701
|
+
return inspect_number(ctx, value, opts);
|
|
702
|
+
}
|
|
703
|
+
if (JS_IsBigInt(value)) {
|
|
704
|
+
return inspect_bigint(ctx, value, opts);
|
|
705
|
+
}
|
|
706
|
+
if (JS_IsString(value)) {
|
|
707
|
+
return inspect_string(ctx, value, opts);
|
|
708
|
+
}
|
|
709
|
+
if (JS_IsSymbol(value)) {
|
|
710
|
+
return inspect_symbol(ctx, value, opts);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/* Functions */
|
|
714
|
+
if (JS_IsFunction(ctx, value)) {
|
|
715
|
+
return inspect_function(ctx, value, opts);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/* Depth check for objects */
|
|
719
|
+
if (depth <= 0) {
|
|
720
|
+
if (JS_IsArray(value)) {
|
|
721
|
+
JSValue len_val = JS_GetPropertyStr(ctx, value, "length");
|
|
722
|
+
uint32_t len = 0;
|
|
723
|
+
JS_ToUint32(ctx, &len, len_val);
|
|
724
|
+
JS_FreeValue(ctx, len_val);
|
|
725
|
+
char buf[32];
|
|
726
|
+
snprintf(buf, sizeof(buf), "[Array(%u)]", len);
|
|
727
|
+
return buf;
|
|
728
|
+
}
|
|
729
|
+
return "[Object]";
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/* Check for custom inspect: Symbol.for('mikrojs.inspect') */
|
|
733
|
+
std::string custom;
|
|
734
|
+
if (try_custom_inspect(ctx, value, opts, depth, custom)) {
|
|
735
|
+
return custom;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/* Objects — detect type */
|
|
739
|
+
std::string type = get_object_type(ctx, value);
|
|
740
|
+
|
|
741
|
+
if (JS_IsArray(value) || type == "Array") {
|
|
742
|
+
return inspect_array(ctx, value, opts, depth - 1);
|
|
743
|
+
}
|
|
744
|
+
if (JS_IsError(value) || type == "Error") {
|
|
745
|
+
return inspect_error(ctx, value, opts, depth - 1);
|
|
746
|
+
}
|
|
747
|
+
if (type == "Date") {
|
|
748
|
+
return inspect_date(ctx, value, opts);
|
|
749
|
+
}
|
|
750
|
+
if (type == "RegExp") {
|
|
751
|
+
return inspect_regexp(ctx, value, opts);
|
|
752
|
+
}
|
|
753
|
+
if (type == "Map") {
|
|
754
|
+
return inspect_map(ctx, value, opts, depth - 1);
|
|
755
|
+
}
|
|
756
|
+
if (type == "Set") {
|
|
757
|
+
return inspect_set(ctx, value, opts, depth - 1);
|
|
758
|
+
}
|
|
759
|
+
if (type == "Promise") {
|
|
760
|
+
return stylize("Promise{\xe2\x80\xa6}", MIK_TOKEN_IDENTIFIER, opts);
|
|
761
|
+
}
|
|
762
|
+
if (type == "WeakMap") {
|
|
763
|
+
return stylize("WeakMap{\xe2\x80\xa6}", MIK_TOKEN_IDENTIFIER, opts);
|
|
764
|
+
}
|
|
765
|
+
if (type == "WeakSet") {
|
|
766
|
+
return stylize("WeakSet{\xe2\x80\xa6}", MIK_TOKEN_IDENTIFIER, opts);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/* Typed arrays */
|
|
770
|
+
if (type == "Int8Array" || type == "Uint8Array" || type == "Uint8ClampedArray" ||
|
|
771
|
+
type == "Int16Array" || type == "Uint16Array" || type == "Int32Array" ||
|
|
772
|
+
type == "Uint32Array" || type == "Float32Array" || type == "Float64Array") {
|
|
773
|
+
return inspect_typed_array(ctx, value, type, opts);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/* Plain object vs class instance */
|
|
777
|
+
JSValue proto = JS_GetPrototype(ctx, value);
|
|
778
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
779
|
+
JSValue obj_ctor = JS_GetPropertyStr(ctx, global, "Object");
|
|
780
|
+
JSValue obj_proto = JS_GetPropertyStr(ctx, obj_ctor, "prototype");
|
|
781
|
+
|
|
782
|
+
bool is_plain = JS_IsNull(proto) ||
|
|
783
|
+
(JS_VALUE_GET_PTR(proto) == JS_VALUE_GET_PTR(obj_proto));
|
|
784
|
+
|
|
785
|
+
JS_FreeValue(ctx, obj_proto);
|
|
786
|
+
JS_FreeValue(ctx, obj_ctor);
|
|
787
|
+
JS_FreeValue(ctx, global);
|
|
788
|
+
JS_FreeValue(ctx, proto);
|
|
789
|
+
|
|
790
|
+
if (is_plain) {
|
|
791
|
+
return inspect_object(ctx, value, opts, depth - 1);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return inspect_class(ctx, value, type, opts, depth - 1);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/* ── Public API ──────────────────────────────────────────────────── */
|
|
798
|
+
|
|
799
|
+
std::string mik_inspect(JSContext* ctx, JSValue value, int depth, bool colors, bool show_hidden) {
|
|
800
|
+
InspectOpts opts;
|
|
801
|
+
opts.depth = depth;
|
|
802
|
+
opts.truncate = 0; /* 0 = unlimited */
|
|
803
|
+
opts.colors = colors;
|
|
804
|
+
opts.show_hidden = show_hidden;
|
|
805
|
+
return inspect_value(ctx, value, opts, depth);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/* ── JS-callable inspect function (for mikrojs/inspect module) ───── */
|
|
809
|
+
|
|
810
|
+
static JSValue mik__inspect_js(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
811
|
+
if (argc < 1) return JS_NewString(ctx, "undefined");
|
|
812
|
+
|
|
813
|
+
int depth = 2;
|
|
814
|
+
bool colors = false;
|
|
815
|
+
bool show_hidden = false;
|
|
816
|
+
|
|
817
|
+
if (argc >= 2 && JS_IsObject(argv[1])) {
|
|
818
|
+
JSValue d = JS_GetPropertyStr(ctx, argv[1], "depth");
|
|
819
|
+
if (JS_IsNumber(d)) {
|
|
820
|
+
int32_t d32;
|
|
821
|
+
JS_ToInt32(ctx, &d32, d);
|
|
822
|
+
depth = d32;
|
|
823
|
+
}
|
|
824
|
+
JS_FreeValue(ctx, d);
|
|
825
|
+
|
|
826
|
+
JSValue c = JS_GetPropertyStr(ctx, argv[1], "colors");
|
|
827
|
+
if (!JS_IsUndefined(c)) colors = JS_ToBool(ctx, c);
|
|
828
|
+
JS_FreeValue(ctx, c);
|
|
829
|
+
|
|
830
|
+
JSValue h = JS_GetPropertyStr(ctx, argv[1], "showHidden");
|
|
831
|
+
if (!JS_IsUndefined(h)) show_hidden = JS_ToBool(ctx, h);
|
|
832
|
+
JS_FreeValue(ctx, h);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
std::string result = mik_inspect(ctx, argv[0], depth, colors, show_hidden);
|
|
836
|
+
return JS_NewString(ctx, result.c_str());
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/* Module init for 'mikrojs/inspect' native C module */
|
|
840
|
+
int mik__inspect_module_init(JSContext* ctx, JSModuleDef* m) {
|
|
841
|
+
return JS_SetModuleExport(ctx, m, "inspect",
|
|
842
|
+
JS_NewCFunction(ctx, mik__inspect_js, "inspect", 1));
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
void mik__inspect_register(JSContext* ctx) {
|
|
846
|
+
JSModuleDef* m = JS_NewCModule(ctx, "mikrojs/inspect", mik__inspect_module_init);
|
|
847
|
+
if (m) {
|
|
848
|
+
JS_AddModuleExport(ctx, m, "inspect");
|
|
849
|
+
}
|
|
850
|
+
}
|