• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kos-lang / kos / 28804370359

06 Jul 2026 01:33PM UTC coverage: 95.093% (-1.3%) from 96.422%
28804370359

push

github

cdragan
net: use poll() instead of select()

select() has a limit of 1024 fd values, for any fds which are higher
than 1024 select() can't work.

7 of 13 new or added lines in 1 file covered. (53.85%)

884 existing lines in 19 files now uncovered.

24707 of 25982 relevant lines covered (95.09%)

939477.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.14
/core/kos_instance.c
1
/* SPDX-License-Identifier: MIT
2
 * SPDX-FileCopyrightText: Copyright (c) 2014-2026 Chris Dragan
3
 */
4

5
#include "../inc/kos_instance.h"
6
#include "../inc/kos_array.h"
7
#include "../inc/kos_atomic.h"
8
#include "../inc/kos_constants.h"
9
#include "../inc/kos_error.h"
10
#include "../inc/kos_malloc.h"
11
#include "../inc/kos_memory.h"
12
#include "../inc/kos_module.h"
13
#include "../inc/kos_object.h"
14
#include "../inc/kos_string.h"
15
#include "../inc/kos_utils.h"
16
#include "kos_config.h"
17
#include "kos_debug.h"
18
#include "kos_heap.h"
19
#include "kos_math.h"
20
#include "kos_misc.h"
21
#include "kos_object_internal.h"
22
#include "kos_perf.h"
23
#include "kos_system_internal.h"
24
#include "kos_threads_internal.h"
25
#include "kos_try.h"
26
#include <assert.h>
27
#include <errno.h>
28
#define __STDC_FORMAT_MACROS
29
#include <inttypes.h>
30
#include <signal.h>
31
#include <stdarg.h>
32
#include <stdio.h>
33
#include <string.h>
34

35
static const char str_format_exception[]        = "Exception: ";
36
static const char str_format_hash[]             = "  #";
37
static const char str_format_line[]             = ":";
38
static const char str_format_function[]         = " in '";
39
static const char str_format_module[]           = "' in ";
40
static const char str_format_offset[]           = "  ";
41
static const char str_format_question_marks[]   = "???";
42

43
KOS_DECLARE_STATIC_CONST_STRING(str_description,               "description");
44
KOS_DECLARE_STATIC_CONST_STRING(str_err_ctrl_c,                "user pressed Ctrl-C");
45
KOS_DECLARE_STATIC_CONST_STRING(str_err_ctrl_c_already_hooked, "Ctrl-C already hooked");
46
KOS_DECLARE_STATIC_CONST_STRING(str_err_generator_close,       "closing generator");
47
KOS_DECLARE_STATIC_CONST_STRING(str_err_generator_end,         "end of generator");
48
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_array,             "object is not an array");
49
KOS_DECLARE_STATIC_CONST_STRING(str_err_panic,                 "detected bytecode corruption");
50
KOS_DECLARE_STATIC_CONST_STRING(str_err_thread_registered,     "thread already registered");
51

52
struct KOS_LIB_LIST_S {
53
    uint32_t       num_libs;
54
    uint32_t       capacity;
55
    KOS_SHARED_LIB libs[1];
56
};
57

58
#ifdef CONFIG_PERF
59
struct KOS_PERF_S kos_perf = {
60
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0 },
61
    0, 0,
62
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
63
    0, 0, 0, 0, 0, 0, 0, 0, 0,
64
    { 0, 0, 0, 0 },
65
    { 0, 0, 0, 0 },
66
    0
67
};
68
#endif
69

70
#if defined(CONFIG_SEQFAIL) || defined(CONFIG_FUZZ)
71
static int                  kos_seq_init      = 0;
72
static KOS_ATOMIC(uint32_t) kos_seq;
73
static uint32_t             kos_seq_threshold = ~0U;
74

75
int kos_seq_fail(void)
34,644,200✔
76
{
77
    if ( ! kos_seq_init) {
34,644,200✔
78

79
        KOS_VECTOR cstr;
80
        int64_t    value = -1;
7,589✔
81

82
        KOS_vector_init(&cstr);
7,589✔
83

84
        if (KOS_get_env("KOSSEQFAIL", &cstr) == KOS_SUCCESS
7,589✔
85
                && cstr.size > 0
7,302✔
86
                && kos_parse_int(cstr.buffer, cstr.buffer + cstr.size - 1, &value) == KOS_SUCCESS)
7,302✔
87
            kos_seq_threshold = (uint32_t)value;
7,302✔
88

89
        KOS_vector_destroy(&cstr);
7,589✔
90

91
        KOS_atomic_write_relaxed_u32(kos_seq, 0);
7,589✔
92

93
        kos_seq_init = 1;
7,589✔
94
    }
95

96
    if ((uint32_t)KOS_atomic_add_i32(kos_seq, 1) >= kos_seq_threshold)
34,644,200✔
97
        return KOS_ERROR_INTERNAL;
19,909✔
98

99
    return KOS_SUCCESS;
34,624,291✔
100
}
101

UNCOV
102
void kos_set_seq_point(int seq_point)
×
103
{
104
    kos_seq_init = seq_point;
×
UNCOV
105
}
×
106
#endif
107

108
static void init_context(KOS_CONTEXT ctx, KOS_INSTANCE *inst)
24,515✔
109
{
110
    ctx->next        = KOS_NULL;
24,515✔
111
    ctx->prev        = KOS_NULL;
24,515✔
112
    ctx->inst        = inst;
24,515✔
113
    ctx->cur_page    = KOS_NULL;
24,515✔
114
    ctx->thread_obj  = KOS_BADPTR;
24,515✔
115
    ctx->exception   = KOS_BADPTR;
24,515✔
116
    ctx->stack       = KOS_BADPTR;
24,515✔
117
    ctx->regs_idx    = 0;
24,515✔
118
    ctx->stack_depth = 0;
24,515✔
119
    ctx->local_list  = KOS_NULL;
24,515✔
120
    ctx->ulocal_list = KOS_NULL;
24,515✔
121
    KOS_atomic_write_relaxed_u32(ctx->gc_state, GC_SUSPENDED);
24,515✔
122
    KOS_atomic_write_relaxed_u32(ctx->event_flags, 0);
24,515✔
123
}
24,515✔
124

125
static int register_thread(KOS_INSTANCE *inst,
15,893✔
126
                           KOS_CONTEXT   ctx)
127
{
128
    int error = KOS_SUCCESS;
15,893✔
129

130
    if (kos_tls_get(inst->threads.thread_key)) {
15,893✔
131
        TRY(KOS_resume_context(ctx));
×
UNCOV
132
        RAISE_EXCEPTION_STR(str_err_thread_registered);
×
133
    }
134

135
    assert( ! kos_tls_get(inst->threads.thread_key));
15,893✔
136

137
    kos_tls_set(inst->threads.thread_key, ctx);
15,893✔
138

139
    TRY(KOS_resume_context(ctx));
15,893✔
140

141
cleanup:
15,893✔
142
    if (error)
15,893✔
UNCOV
143
        kos_heap_release_thread_page(ctx);
×
144

145
    return error;
15,893✔
146
}
147

148
static void unregister_thread(KOS_INSTANCE *inst,
7,254✔
149
                              KOS_CONTEXT   ctx)
150
{
151
    assert(ctx != &inst->threads.main_thread);
7,254✔
152

153
    KOS_suspend_context(ctx);
7,254✔
154

155
    kos_tls_set(inst->threads.thread_key, KOS_NULL);
7,254✔
156

157
    kos_lock_mutex(inst->threads.ctx_mutex);
7,254✔
158

159
    if (ctx->prev)
7,254✔
160
        ctx->prev->next = ctx->next;
7,254✔
161

162
    if (ctx->next)
7,254✔
163
        ctx->next->prev = ctx->prev;
2,889✔
164

165
    kos_unlock_mutex(inst->threads.ctx_mutex);
7,254✔
166
}
7,254✔
167

168
int KOS_instance_register_thread(KOS_INSTANCE *inst,
7,254✔
169
                                 KOS_CONTEXT   ctx)
170
{
171
    int error;
172

173
    init_context(ctx, inst);
7,254✔
174

175
#if defined(CONFIG_THREADS) && (CONFIG_THREADS == 0)
176
    {
177
        KOS_DECLARE_STATIC_CONST_STRING(str_no_threads, "Kos was compiled without supports for threads");
178

179
        KOS_raise_exception(ctx, KOS_CONST_ID(str_no_threads));
180
    }
181
    error = KOS_ERROR_EXCEPTION;
182
#else
183

184
    kos_lock_mutex(inst->threads.ctx_mutex);
7,254✔
185

186
    ctx->prev                      = &inst->threads.main_thread;
7,254✔
187
    ctx->next                      = inst->threads.main_thread.next;
7,254✔
188
    inst->threads.main_thread.next = ctx;
7,254✔
189
    if (ctx->next)
7,254✔
190
        ctx->next->prev            = ctx;
4,859✔
191

192
    KOS_atomic_write_relaxed_u32(ctx->event_flags,
7,254✔
193
            KOS_atomic_read_relaxed_u32(inst->threads.main_thread.event_flags));
194

195
    kos_unlock_mutex(inst->threads.ctx_mutex);
7,254✔
196

197
    error = register_thread(inst, ctx);
7,254✔
198

199
    if (error)
7,254✔
UNCOV
200
        unregister_thread(inst, ctx);
×
201
#endif
202

203
    return error;
7,254✔
204
}
205

206
void KOS_instance_unregister_thread(KOS_INSTANCE *inst,
7,254✔
207
                                    KOS_CONTEXT   ctx)
208
{
209
    assert((KOS_CONTEXT)kos_tls_get(inst->threads.thread_key) == ctx);
7,254✔
210

211
    unregister_thread(inst, ctx);
7,254✔
212
}
7,254✔
213

214
static int add_multiple_paths(KOS_CONTEXT ctx, KOS_VECTOR *cpaths)
18✔
215
{
216
    int   error = KOS_SUCCESS;
18✔
217
    char *buf   = cpaths->buffer;
18✔
218

219
    while ( ! error) {
18✔
220
        char *end = strchr(buf, KOS_PATH_LIST_SEPARATOR);
18✔
221

222
        if (end)
18✔
UNCOV
223
            *end = '\0';
×
224

225
        error = KOS_instance_add_path(ctx, buf);
18✔
226

227
        if (end)
18✔
UNCOV
228
            buf = end + 1;
×
229
        else
230
            break;
18✔
231
    }
232

233
    return error;
18✔
234
}
235

236
static int init_search_paths(KOS_CONTEXT ctx)
8,609✔
237
{
238
#ifdef CONFIG_DISABLE_KOSPATH
239
    return KOS_SUCCESS;
240
#else
241
    int        error = KOS_SUCCESS;
8,609✔
242
    KOS_VECTOR cpaths;
243

244
    KOS_vector_init(&cpaths);
8,609✔
245

246
    if (KOS_get_env("KOSPATH", &cpaths) == KOS_SUCCESS)
8,609✔
247
        error = add_multiple_paths(ctx, &cpaths);
18✔
248

249
    KOS_vector_destroy(&cpaths);
8,609✔
250

251
    return error;
8,609✔
252
#endif
253
}
254

255
static void setup_init_module(KOS_MODULE *init_module)
25,873✔
256
{
257
    KOS_DECLARE_STATIC_CONST_STRING(str_init, "init");
258

259
    assert(kos_get_object_type(init_module->header) == OBJ_MODULE);
25,873✔
260

261
    init_module->name           = KOS_CONST_ID(str_init);
25,873✔
262
    init_module->path           = KOS_STR_EMPTY;
25,873✔
263
    init_module->inst           = KOS_NULL;
25,873✔
264
    init_module->constants      = KOS_BADPTR;
25,873✔
265
    init_module->global_names   = KOS_BADPTR;
25,873✔
266
    init_module->globals        = KOS_BADPTR;
25,873✔
267
    init_module->module_names   = KOS_BADPTR;
25,873✔
268
    init_module->priv           = KOS_BADPTR;
25,873✔
269
    init_module->finalize       = KOS_NULL;
25,873✔
270
}
25,873✔
271

272
struct KOS_CONST_MODULE_S {
273
    struct KOS_CONST_OBJECT_ALIGNMENT_S align;
274
    KOS_MODULE                          object;
275
};
276

277
static KOS_OBJ_ID get_init_module(void)
17,261✔
278
{
279
    KOS_DECLARE_ALIGNED(32, static struct KOS_CONST_MODULE_S) init_module;
280

281
    kos_set_object_type_size(init_module.object.header, OBJ_MODULE, 0);
17,261✔
282

283
    setup_init_module(&init_module.object);
17,261✔
284

285
    assert( ! kos_is_heap_object(KOS_CONST_ID(init_module)));
17,261✔
286

287
    return KOS_CONST_ID(init_module);
17,261✔
288
}
289

290
static void clear_instance(KOS_INSTANCE *inst)
17,261✔
291
{
292
    /* Disable GC during early init */
293
    inst->flags = KOS_INST_MANUAL_GC;
17,261✔
294

295
    inst->args                             = KOS_BADPTR;
17,261✔
296
    inst->prototypes.object_proto          = KOS_BADPTR;
17,261✔
297
    inst->prototypes.number_proto          = KOS_BADPTR;
17,261✔
298
    inst->prototypes.integer_proto         = KOS_BADPTR;
17,261✔
299
    inst->prototypes.float_proto           = KOS_BADPTR;
17,261✔
300
    inst->prototypes.string_proto          = KOS_BADPTR;
17,261✔
301
    inst->prototypes.boolean_proto         = KOS_BADPTR;
17,261✔
302
    inst->prototypes.array_proto           = KOS_BADPTR;
17,261✔
303
    inst->prototypes.buffer_proto          = KOS_BADPTR;
17,261✔
304
    inst->prototypes.function_proto        = KOS_BADPTR;
17,261✔
305
    inst->prototypes.class_proto           = KOS_BADPTR;
17,261✔
306
    inst->prototypes.generator_proto       = KOS_BADPTR;
17,261✔
307
    inst->prototypes.exception_proto       = KOS_BADPTR;
17,261✔
308
    inst->prototypes.generator_end_proto   = KOS_BADPTR;
17,261✔
309
    inst->prototypes.generator_close_proto = KOS_BADPTR;
17,261✔
310
    inst->prototypes.panic_proto           = KOS_BADPTR;
17,261✔
311
    inst->prototypes.ctrl_c_proto          = KOS_BADPTR;
17,261✔
312
    inst->prototypes.thread_proto          = KOS_BADPTR;
17,261✔
313
    inst->prototypes.module_proto          = KOS_BADPTR;
17,261✔
314
    inst->modules.search_paths             = KOS_BADPTR;
17,261✔
315
    inst->modules.module_names             = KOS_BADPTR;
17,261✔
316
    inst->modules.modules                  = KOS_BADPTR;
17,261✔
317
    inst->modules.init_module              = get_init_module();
17,261✔
318
    inst->modules.module_inits             = KOS_BADPTR;
17,261✔
319
    inst->modules.libs                     = KOS_NULL;
17,261✔
320
    inst->modules.load_chain               = KOS_NULL;
17,261✔
321
    inst->threads.threads                  = KOS_NULL;
17,261✔
322
    inst->threads.can_create               = 0;
17,261✔
323
    inst->threads.num_threads              = 0;
17,261✔
324
    inst->threads.max_threads              = KOS_MAX_THREADS;
17,261✔
325

326
    init_context(&inst->threads.main_thread, inst);
17,261✔
327
}
17,261✔
328

329
static int init_prototypes(KOS_CONTEXT ctx, struct KOS_PROTOTYPES_S *prototypes)
8,639✔
330
{
331
    int error = KOS_SUCCESS;
8,639✔
332

333
    TRY_OBJID(prototypes->object_proto          = KOS_new_object_with_prototype(ctx, KOS_VOID));
8,639✔
334
    TRY_OBJID(prototypes->number_proto          = KOS_new_object(ctx));
8,636✔
335
    TRY_OBJID(prototypes->integer_proto         = KOS_new_object_with_prototype(ctx, prototypes->number_proto));
8,635✔
336
    TRY_OBJID(prototypes->float_proto           = KOS_new_object_with_prototype(ctx, prototypes->number_proto));
8,634✔
337
    TRY_OBJID(prototypes->string_proto          = KOS_new_object(ctx));
8,633✔
338
    TRY_OBJID(prototypes->boolean_proto         = KOS_new_object(ctx));
8,632✔
339
    TRY_OBJID(prototypes->array_proto           = KOS_new_object(ctx));
8,631✔
340
    TRY_OBJID(prototypes->buffer_proto          = KOS_new_object(ctx));
8,630✔
341
    TRY_OBJID(prototypes->function_proto        = KOS_new_object(ctx));
8,629✔
342
    TRY_OBJID(prototypes->class_proto           = KOS_new_object_with_prototype(ctx, prototypes->function_proto));
8,628✔
343
    TRY_OBJID(prototypes->generator_proto       = KOS_new_object_with_prototype(ctx, prototypes->function_proto));
8,627✔
344
    TRY_OBJID(prototypes->exception_proto       = KOS_new_object(ctx));
8,626✔
345
    TRY_OBJID(prototypes->generator_end_proto   = KOS_new_object(ctx));
8,625✔
346
    TRY_OBJID(prototypes->generator_close_proto = KOS_new_object_with_prototype(ctx, prototypes->generator_end_proto));
8,624✔
347
    TRY_OBJID(prototypes->panic_proto           = KOS_new_object(ctx));
8,623✔
348
    TRY_OBJID(prototypes->ctrl_c_proto          = KOS_new_object(ctx));
8,622✔
349
    TRY_OBJID(prototypes->thread_proto          = KOS_new_object(ctx));
8,621✔
350
    TRY_OBJID(prototypes->module_proto          = KOS_new_object(ctx));
8,620✔
351

352
cleanup:
8,619✔
353
    return error;
8,639✔
354
}
355

356
#ifdef CONFIG_FUZZ
357
KOS_ATOMIC(uint32_t) kos_fuzz_instructions;
358
#endif
359

360
int KOS_instance_init(KOS_INSTANCE *inst,
8,658✔
361
                      uint32_t      flags,
362
                      KOS_CONTEXT  *out_ctx)
363
{
364
    int         error;
365
    int         heap_ok   = 0;
8,658✔
366
    int         thread_ok = 0;
8,658✔
367
    KOS_MODULE *init_module;
368
    KOS_CONTEXT ctx;
369

370
    assert(!kos_is_heap_object(KOS_VOID));
8,658✔
371
    assert(!kos_is_heap_object(KOS_FALSE));
8,658✔
372
    assert(!kos_is_heap_object(KOS_TRUE));
8,658✔
373
    assert(!kos_is_heap_object(KOS_STR_EMPTY));
8,658✔
374
    assert(KOS_get_string_length(KOS_STR_EMPTY) == 0);
8,658✔
375
    assert(!kos_is_heap_object(KOS_STR_OUT_OF_MEMORY));
8,658✔
376
    assert(KOS_get_string_length(KOS_STR_OUT_OF_MEMORY) == 13);
8,658✔
377
    assert(!kos_is_heap_object(KOS_EMPTY_ARRAY));
8,658✔
378
    assert(GET_OBJ_TYPE(KOS_EMPTY_ARRAY) == OBJ_ARRAY);
8,658✔
379
    assert(KOS_get_array_size(KOS_EMPTY_ARRAY) == 0);
8,658✔
380

381
#ifdef CONFIG_FUZZ
382
    KOS_atomic_write_relaxed_u32(kos_fuzz_instructions, 0U);
383
#endif
384

385
    clear_instance(inst);
8,658✔
386

387
    TRY(kos_tls_create(&inst->threads.thread_key));
8,658✔
388
    error = kos_create_mutex(&inst->threads.ctx_mutex);
8,656✔
389
    if (error) {
8,656✔
390
        kos_tls_destroy(inst->threads.thread_key);
2✔
391
        goto cleanup;
2✔
392
    }
393
    error = kos_create_mutex(&inst->threads.new_mutex);
8,654✔
394
    if (error) {
8,654✔
395
        kos_destroy_mutex(&inst->threads.ctx_mutex);
2✔
396
        kos_tls_destroy(inst->threads.thread_key);
2✔
397
        goto cleanup;
2✔
398
    }
399
    thread_ok = 1;
8,652✔
400

401
    TRY(kos_heap_init(inst));
8,652✔
402
    heap_ok = 1;
8,640✔
403

404
    inst->threads.threads = (KOS_ATOMIC(KOS_THREAD *) *)
8,640✔
405
        KOS_malloc(sizeof(KOS_THREAD *) * inst->threads.max_threads);
8,640✔
406
    if ( ! inst->threads.threads)
8,640✔
407
        RAISE_ERROR(KOS_ERROR_OUT_OF_MEMORY);
1✔
408
    memset((void *)inst->threads.threads, 0, sizeof(KOS_THREAD *) * inst->threads.max_threads);
8,639✔
409

410
    TRY(register_thread(inst, &inst->threads.main_thread));
8,639✔
411

412
    ctx = &inst->threads.main_thread;
8,639✔
413

414
    TRY(init_prototypes(ctx, &inst->prototypes));
8,639✔
415

416
    TRY_OBJID(inst->modules.module_names           = KOS_new_object(ctx));
8,619✔
417
    TRY_OBJID(inst->modules.modules                = KOS_new_array(ctx, 0));
8,618✔
418
    TRY_OBJID(inst->modules.search_paths           = KOS_new_array(ctx, 0));
8,617✔
419
    TRY_OBJID(inst->modules.module_inits           = KOS_new_object(ctx));
8,616✔
420
    TRY_OBJID(inst->args                           = KOS_new_array(ctx, 0));
8,615✔
421

422
    init_module = (KOS_MODULE *)kos_alloc_object(ctx, KOS_ALLOC_IMMOVABLE, OBJ_MODULE, sizeof(KOS_MODULE));
8,614✔
423
    if ( ! init_module)
8,614✔
424
        RAISE_ERROR(KOS_ERROR_OUT_OF_MEMORY);
2✔
425
    setup_init_module(init_module);
8,612✔
426

427
    init_module->inst                    = inst;
8,612✔
428
    TRY_OBJID(init_module->globals       = KOS_new_array(ctx, 0));
8,612✔
429
    TRY_OBJID(init_module->global_names  = KOS_new_object(ctx));
8,611✔
430
    TRY_OBJID(init_module->module_names  = KOS_new_object(ctx));
8,610✔
431

432
    inst->modules.init_module = OBJID(MODULE, init_module);
8,609✔
433

434
    TRY(init_search_paths(ctx));
8,609✔
435

436
    TRY(KOS_set_property(ctx, inst->prototypes.generator_end_proto, KOS_CONST_ID(str_description),
8,609✔
437
                         KOS_CONST_ID(str_err_generator_end)));
438
    TRY(KOS_set_property(ctx, inst->prototypes.generator_close_proto, KOS_CONST_ID(str_description),
8,608✔
439
                         KOS_CONST_ID(str_err_generator_close)));
440
    TRY(KOS_set_property(ctx, inst->prototypes.panic_proto, KOS_CONST_ID(str_description),
8,607✔
441
                         KOS_CONST_ID(str_err_panic)));
442
    TRY(KOS_set_property(ctx, inst->prototypes.ctrl_c_proto, KOS_CONST_ID(str_description),
8,606✔
443
                         KOS_CONST_ID(str_err_ctrl_c)));
444

445
    *out_ctx = ctx;
8,605✔
446

447
    /* Set user flags.
448
     * Also, enable automatic GC unless disabled by user */
449
    inst->flags = flags;
8,605✔
450

451
    /* Enable creation of new threads */
452
    inst->threads.can_create = 1U;
8,605✔
453

454
cleanup:
8,658✔
455
    if (error) {
8,658✔
456
        if (heap_ok)
53✔
457
            kos_heap_destroy(inst);
35✔
458

459
        if (thread_ok) {
53✔
460
            kos_tls_destroy(inst->threads.thread_key);
47✔
461
            kos_destroy_mutex(&inst->threads.new_mutex);
47✔
462
            kos_destroy_mutex(&inst->threads.ctx_mutex);
47✔
463
        }
464

465
        if (inst->threads.threads)
53✔
466
            KOS_free((void *)inst->threads.threads);
34✔
467
    }
468

469
    return error;
8,658✔
470
}
471

472
void KOS_instance_destroy(KOS_INSTANCE *inst)
8,603✔
473
{
474
    int         error;
475
    uint32_t    i;
476
    uint32_t    num_modules = KOS_get_array_size(inst->modules.modules);
8,603✔
477
    KOS_CONTEXT ctx         = &inst->threads.main_thread;
8,603✔
478
    KOS_VECTOR  finalizers;
479

480
    kos_validate_context(ctx);
8,603✔
481

482
    error = kos_join_finished_threads(ctx, KOS_JOIN_ALL);
8,603✔
483

484
    if (error == KOS_ERROR_EXCEPTION)
8,603✔
485
        KOS_print_exception(ctx, KOS_STDERR);
1✔
486
    else if (error) {
8,602✔
UNCOV
487
        assert(error == KOS_ERROR_OUT_OF_MEMORY);
×
UNCOV
488
        fprintf(stderr, "Out of memory\n");
×
489
    }
490

491
    KOS_vector_init(&finalizers);
8,603✔
492

493
    for (i = 0; i < num_modules; i++) {
21,403✔
494
        KOS_OBJ_ID module_obj = KOS_array_read(ctx, inst->modules.modules, (int)i);
12,800✔
495
        if (IS_BAD_PTR(module_obj))
12,800✔
496
            KOS_clear_exception(ctx);
11,242✔
497
        else if (GET_OBJ_TYPE(module_obj) == OBJ_MODULE) {
1,558✔
498

499
            /* Copy away module finalizers and run them after heap is destroyed
500
             * in case objects created by the module have private data which
501
             * depends on module initialization. */
502
            if (OBJPTR(MODULE, module_obj)->finalize) {
423✔
503
                const size_t old_size = finalizers.size;
4✔
504
                const size_t new_size = old_size + sizeof(KOS_MODULE_FINALIZE);
4✔
505
                if ( ! KOS_vector_resize(&finalizers, new_size)) {
4✔
506
                    memcpy(&finalizers.buffer[old_size], &OBJPTR(MODULE, module_obj)->finalize, sizeof(KOS_MODULE_FINALIZE));
4✔
507
                    memset(&OBJPTR(MODULE, module_obj)->finalize, 0, sizeof(KOS_MODULE_FINALIZE));
4✔
508
                }
509
            }
510
        }
511
        else {
512
            /* failed e.g. during compilation */
513
            assert(GET_OBJ_TYPE(module_obj) == OBJ_VOID);
1,135✔
514
        }
515
    }
516

517
    kos_heap_destroy(inst);
8,603✔
518

519
    if (finalizers.size) {
8,603✔
520
        KOS_MODULE_FINALIZE       *finalize = (KOS_MODULE_FINALIZE *)finalizers.buffer;
4✔
521
        KOS_MODULE_FINALIZE *const end      = (KOS_MODULE_FINALIZE *)(finalizers.buffer + finalizers.size);
4✔
522

523
        for ( ; finalize < end; ++finalize)
8✔
524
            (*finalize)();
4✔
525
    }
526

527
    KOS_vector_destroy(&finalizers);
8,603✔
528

529
    if (inst->modules.libs) {
8,603✔
530
        uint32_t      idx;
531
        KOS_LIB_LIST *libs = inst->modules.libs;
1,273✔
532

533
        for (idx = libs->num_libs; idx > 0; ) {
2,551✔
534
            const KOS_SHARED_LIB lib = libs->libs[--idx];
1,278✔
535

536
            assert(lib);
1,278✔
537
            KOS_unload_library(lib);
1,278✔
538
        }
539

540
        KOS_free((void *)libs);
1,273✔
541
    }
542

543
    kos_tls_destroy(inst->threads.thread_key);
8,603✔
544

545
    kos_destroy_mutex(&inst->threads.new_mutex);
8,603✔
546
    kos_destroy_mutex(&inst->threads.ctx_mutex);
8,603✔
547

548
    KOS_free((void *)inst->threads.threads);
8,603✔
549

550
    clear_instance(inst);
8,603✔
551

552
#ifdef CONFIG_PERF
553
#   define PERF_RATIO(a) do {                                                      \
554
        const uint64_t va = KOS_atomic_read_relaxed_u64(kos_perf.a##_success);     \
555
        const uint64_t vb = KOS_atomic_read_relaxed_u64(kos_perf.a##_fail);        \
556
        uint64_t       total = va + vb;                                            \
557
        if (total == 0) total = 1;                                                 \
558
        fprintf(stderr, "    " #a "\t%" PRIu64 " / %" PRIu64 " (%" PRIu64 "%%)\n", \
559
                va, total, va * 100 / total);                                      \
560
    } while (0)
561
#   define PERF_VALUE_NAME(name, a) do {                             \
562
        const uint64_t va = KOS_atomic_read_relaxed_u64(kos_perf.a); \
563
        fprintf(stderr, "    " #name "\t%" PRIu64 " \n", va);        \
564
    } while (0)
565
#   define PERF_VALUE(a) PERF_VALUE_NAME(a, a)
566
    fprintf(stderr, "Performance stats:\n");
567
    PERF_VALUE(object_key_diff_hash);
568
    PERF_RATIO(object_key_compare);
569
    PERF_RATIO(object_get);
570
    PERF_RATIO(object_set);
571
    PERF_RATIO(object_delete);
572
    PERF_RATIO(object_resize);
573
    PERF_RATIO(object_salvage);
574
    PERF_VALUE(object_collision[0]);
575
    PERF_VALUE(object_collision[1]);
576
    PERF_VALUE(object_collision[2]);
577
    PERF_VALUE(object_collision[3]);
578
    PERF_RATIO(array_salvage);
579
    PERF_VALUE(alloc_object);
580
    PERF_VALUE_NAME(new_object_integer,        new_object[0]);
581
    PERF_VALUE_NAME(new_object_float,          new_object[1]);
582
    PERF_VALUE_NAME(new_object_void,           new_object[2]);
583
    PERF_VALUE_NAME(new_object_boolean,        new_object[3]);
584
    PERF_VALUE_NAME(new_object_string,         new_object[4]);
585
    PERF_VALUE_NAME(new_object_object,         new_object[5]);
586
    PERF_VALUE_NAME(new_object_array,          new_object[6]);
587
    PERF_VALUE_NAME(new_object_buffer,         new_object[7]);
588
    PERF_VALUE_NAME(new_object_function,       new_object[8]);
589
    PERF_VALUE_NAME(new_object_class,          new_object[9]);
590
    PERF_VALUE_NAME(new_object_module,         new_object[10]);
591
    PERF_VALUE_NAME(new_object_opaque,         new_object[11]);
592
    PERF_VALUE_NAME(new_object_huge_tracker,   new_object[12]);
593
    PERF_VALUE_NAME(new_object_object_storage, new_object[13]);
594
    PERF_VALUE_NAME(new_object_array_storage,  new_object[14]);
595
    PERF_VALUE_NAME(new_object_buffer_storage, new_object[15]);
596
    PERF_VALUE_NAME(new_object_dynamic_prop,   new_object[16]);
597
    PERF_VALUE_NAME(new_object_iterator,       new_object[17]);
598
    PERF_VALUE_NAME(new_object_stack,          new_object[18]);
599
    PERF_VALUE_NAME(alloc_object_32,        alloc_object_size[0]);
600
    PERF_VALUE_NAME(alloc_object_64_128,    alloc_object_size[1]);
601
    PERF_VALUE_NAME(alloc_object_160_256,   alloc_object_size[2]);
602
    PERF_VALUE_NAME(alloc_object_288_512,   alloc_object_size[3]);
603
    PERF_VALUE(alloc_huge_object);
604
    PERF_VALUE(non_full_seek);
605
    PERF_VALUE(non_full_seek_max);
606
    PERF_VALUE(alloc_new_page);
607
    PERF_VALUE(alloc_free_page);
608
    PERF_VALUE(gc_cycles);
609
    PERF_VALUE(mark_groups_alloc);
610
    PERF_VALUE(mark_groups_sched);
611
    PERF_VALUE_NAME(evac_object_32,         evac_object_size[0]);
612
    PERF_VALUE_NAME(evac_object_64_128,     evac_object_size[1]);
613
    PERF_VALUE_NAME(evac_object_160_256,    evac_object_size[2]);
614
    PERF_VALUE_NAME(evac_object_288_512,    evac_object_size[3]);
615
    PERF_VALUE(instructions);
616
#endif
617
}
8,603✔
618

619
int KOS_instance_add_path(KOS_CONTEXT ctx, const char *module_search_path)
7,474✔
620
{
621
    int           error;
622
    KOS_OBJ_ID    path_str;
623
    KOS_INSTANCE *inst = ctx->inst;
7,474✔
624

625
    path_str = KOS_new_cstring(ctx, module_search_path);
7,474✔
626
    TRY_OBJID(path_str);
7,474✔
627

628
    TRY(KOS_array_push(ctx, inst->modules.search_paths, path_str, KOS_NULL));
7,472✔
629

630
cleanup:
7,471✔
631
    return error;
7,474✔
632
}
633

634
#ifndef CONFIG_MODULE_PATH
635
#   ifdef _WIN32
636
#       define CONFIG_MODULE_PATH "modules"
637
#   else
638
#       define CONFIG_MODULE_PATH "../share/kos/modules"
639
#   endif
640
#endif
641

642
int KOS_instance_add_default_path(KOS_CONTEXT ctx, const char *argv0)
7,474✔
643
{
644
    int               error      = KOS_ERROR_NOT_FOUND;
7,474✔
645
    KOS_VECTOR        cstr;
646
    KOS_VECTOR        cpath;
647
    size_t            pos;
648
    static const char rel_path[] = CONFIG_MODULE_PATH;
649

650
    KOS_vector_init(&cstr);
7,474✔
651
    KOS_vector_init(&cpath);
7,474✔
652

653
    if (argv0) {
7,474✔
654

655
        size_t len = strlen(argv0);
18✔
656

657
        if ( ! len)
18✔
658
            goto cleanup;
1✔
659

660
        /* Absolute or relative path */
661
        if (strchr(argv0, KOS_PATH_SEPARATOR)) {
17✔
662

663
            if ( ! KOS_does_file_exist(argv0))
12✔
664
                RAISE_ERROR(KOS_ERROR_NOT_FOUND);
10✔
665

666
            len += 1;
2✔
667
            TRY(KOS_vector_resize(&cstr, len));
2✔
668

669
            memcpy(cstr.buffer, argv0, len);
2✔
670
        }
671
        /* Just executable name, scan PATH */
672
        else {
673

674
            char *buf;
675

676
            TRY(KOS_get_env("PATH", &cpath));
5✔
677

678
            buf = cpath.buffer;
5✔
679

680
            TRY(KOS_vector_reserve(&cstr, cpath.size + len + 1));
5✔
681

682
            cstr.size = 0;
5✔
683

684
            while ((size_t)(buf - cpath.buffer + 1) < cpath.size) {
9✔
685

686
                char  *end = strchr(buf, KOS_PATH_LIST_SEPARATOR);
7✔
687
                size_t base_len;
688

689
                if ( ! end)
7✔
690
                    end = cpath.buffer + cpath.size - 1;
3✔
691

692
                base_len = end - buf;
7✔
693

694
                TRY(KOS_vector_resize(&cstr, base_len + 1 + len + 1));
7✔
695

696
                memcpy(cstr.buffer, buf, base_len);
7✔
697
                cstr.buffer[base_len] = KOS_PATH_SEPARATOR;
7✔
698
                memcpy(&cstr.buffer[base_len + 1], argv0, len);
7✔
699
                cstr.buffer[base_len + 1 + len] = 0;
7✔
700

701
                if (KOS_does_file_exist(cstr.buffer))
7✔
702
                    break;
3✔
703

704
                cstr.size = 0;
4✔
705

706
                buf = end + 1;
4✔
707
            }
708

709
            if (cstr.size == 0)
5✔
710
                RAISE_ERROR(KOS_ERROR_NOT_FOUND);
2✔
711
        }
712
    }
713
    else {
714
        if (kos_seq_fail())
7,456✔
715
            RAISE_ERROR(KOS_ERROR_NOT_FOUND);
1✔
716

717
        TRY(kos_executable_path(&cstr));
7,455✔
718
    }
719

720
    TRY(KOS_get_absolute_path(&cstr));
7,456✔
721

722
    assert(cstr.size > 0);
7,456✔
723

724
    for (pos = cstr.size - 1; pos > 0 && cstr.buffer[pos] != KOS_PATH_SEPARATOR; --pos);
37,325✔
725

726
    if ( ! pos)
7,456✔
UNCOV
727
        RAISE_ERROR(KOS_ERROR_NOT_FOUND);
×
728

729
    TRY(KOS_vector_resize(&cstr, pos + 1 + sizeof(rel_path)));
7,456✔
730

731
    memcpy(&cstr.buffer[pos + 1], rel_path, sizeof(rel_path));
7,456✔
732

733
    TRY(KOS_instance_add_path(ctx, cstr.buffer));
7,456✔
734

735
cleanup:
7,453✔
736
    KOS_vector_destroy(&cpath);
7,474✔
737
    KOS_vector_destroy(&cstr);
7,474✔
738

739
    return error;
7,474✔
740
}
741

742
int KOS_instance_set_args(KOS_CONTEXT  ctx,
7,467✔
743
                          int          argc,
744
                          const char **argv)
745
{
746
    int           error;
747
    int           i;
748
    KOS_INSTANCE *inst = ctx->inst;
7,467✔
749

750
    assert(argc >= 0);
7,467✔
751

752
    if (argc <= 0)
7,467✔
UNCOV
753
        return KOS_SUCCESS;
×
754

755
    TRY(KOS_array_resize(ctx, inst->args, (uint32_t)argc));
7,467✔
756

757
    for (i = 0; i < argc; i++) {
36,646✔
758
        KOS_OBJ_ID arg_str = KOS_new_cstring(ctx, argv[i]);
29,185✔
759
        TRY_OBJID(arg_str);
29,185✔
760

761
        TRY(KOS_array_write(ctx, inst->args, i, arg_str));
29,180✔
762
    }
763

764
cleanup:
7,461✔
765
    return error;
7,467✔
766
}
767

768
static int save_module_lib(KOS_CONTEXT ctx, KOS_SHARED_LIB lib)
53,509✔
769
{
770
    KOS_LIB_LIST *libs;
771

772
    if ( ! lib)
53,509✔
773
        return KOS_SUCCESS;
52,227✔
774

775
    libs = ctx->inst->modules.libs;
1,282✔
776

777
    if ( ! libs) {
1,282✔
778
        libs = (KOS_LIB_LIST *)KOS_malloc(sizeof(KOS_LIB_LIST) + 7 * sizeof(KOS_SHARED_LIB));
1,276✔
779

780
        if ( ! libs)
1,276✔
781
            return KOS_ERROR_OUT_OF_MEMORY;
1✔
782

783
        libs->num_libs = 0;
1,275✔
784
        libs->capacity = 8;
1,275✔
785

786
        ctx->inst->modules.libs = libs;
1,275✔
787
    }
788

789
    if (libs->num_libs >= libs->capacity) {
1,281✔
790
        const uint32_t new_capacity = KOS_min(libs->capacity + 8, 0xFFFFu);
×
UNCOV
791
        KOS_LIB_LIST  *new_libs     = (KOS_LIB_LIST *)KOS_realloc(libs,
×
792
                sizeof(KOS_LIB_LIST) + (new_capacity - 1) * sizeof(KOS_SHARED_LIB));
×
793

794
        if ( ! new_libs)
×
UNCOV
795
            return KOS_ERROR_OUT_OF_MEMORY;
×
796

UNCOV
797
        libs                    = new_libs;
×
UNCOV
798
        libs->capacity          = new_capacity;
×
UNCOV
799
        ctx->inst->modules.libs = libs;
×
800
    }
801

802
    libs->libs[libs->num_libs++] = lib;
1,281✔
803

804
    return KOS_SUCCESS;
1,281✔
805
}
806

807
KOS_OBJ_ID kos_register_module_init(KOS_CONTEXT      ctx,
53,520✔
808
                                    KOS_OBJ_ID       module_name_obj,
809
                                    KOS_SHARED_LIB   lib,
810
                                    KOS_BUILTIN_INIT init,
811
                                    unsigned         flags)
812
{
813
    struct KOS_MODULE_INIT_S *mod_init_ptr;
814
    KOS_INSTANCE       *const inst  = ctx->inst;
53,520✔
815
    KOS_LOCAL                 module_name;
816
    KOS_LOCAL                 mod_init;
817

818
#ifdef CONFIG_FUZZ
819
    flags &= ~KOS_MODULE_NEEDS_KOS_SOURCE;
820
#endif
821

822
    KOS_init_local(     ctx, &mod_init);
53,520✔
823
    KOS_init_local_with(ctx, &module_name, module_name_obj);
53,520✔
824

825
    mod_init_ptr = (struct KOS_MODULE_INIT_S *)kos_alloc_object(ctx,
53,520✔
826
                                                                KOS_ALLOC_MOVABLE,
827
                                                                OBJ_OPAQUE,
828
                                                                sizeof(struct KOS_MODULE_INIT_S));
829

830
    if ( ! mod_init_ptr)
53,520✔
831
        goto cleanup;
8✔
832

833
    mod_init_ptr->lib   = lib;
53,512✔
834
    mod_init_ptr->init  = init;
53,512✔
835
    mod_init_ptr->flags = flags;
53,512✔
836

837
    mod_init.o = OBJID(OPAQUE, (KOS_OPAQUE *)mod_init_ptr);
53,512✔
838

839
    if (KOS_set_property(ctx,
53,512✔
840
                         inst->modules.module_inits,
841
                         module_name.o,
842
                         mod_init.o) == KOS_SUCCESS) {
843

844
        if (save_module_lib(ctx, lib) != KOS_SUCCESS) {
53,509✔
845
            assert( ! KOS_is_exception_pending(ctx));
1✔
846

847
            mod_init_ptr = (struct KOS_MODULE_INIT_S *)OBJPTR(OPAQUE, mod_init.o);
1✔
848
            mod_init_ptr->lib  = KOS_NULL;
1✔
849
            mod_init_ptr->init = KOS_NULL;
1✔
850

851
            mod_init.o = KOS_BADPTR;
1✔
852

853
            KOS_raise_exception(ctx, KOS_STR_OUT_OF_MEMORY);
1✔
854
        }
855
    }
856
    else
857
        mod_init.o = KOS_BADPTR;
3✔
858

859
cleanup:
53,520✔
860
    if (IS_BAD_PTR(mod_init.o) && lib)
53,520✔
861
        KOS_unload_library(lib);
2✔
862

863
    return KOS_destroy_top_locals(ctx, &module_name, &mod_init);
53,520✔
864
}
865

866
int KOS_instance_register_builtin(KOS_CONTEXT      ctx,
52,244✔
867
                                  const char      *module,
868
                                  KOS_BUILTIN_INIT init,
869
                                  unsigned         flags)
870
{
871
    const KOS_OBJ_ID module_name = KOS_new_cstring(ctx, module);
52,244✔
872

873
    if (IS_BAD_PTR(module_name))
52,244✔
874
        return KOS_ERROR_EXCEPTION;
7✔
875

876
    return IS_BAD_PTR(kos_register_module_init(ctx, module_name, KOS_NULL, init, flags))
52,237✔
877
           ? KOS_ERROR_EXCEPTION : KOS_SUCCESS;
52,237✔
878
}
879

880
#ifndef NDEBUG
881
void kos_validate_context(KOS_CONTEXT ctx)
11,767,873✔
882
{
883
    KOS_INSTANCE *inst = ctx->inst;
11,767,873✔
884
    KOS_CONTEXT   thread_ctx;
885

886
    assert(inst);
11,767,873✔
887

888
    thread_ctx = (KOS_CONTEXT)kos_tls_get(inst->threads.thread_key);
11,767,873✔
889

890
    assert(thread_ctx);
11,767,873✔
891
    assert(thread_ctx == ctx);
11,767,873✔
892
}
11,767,873✔
893
#endif
894

895
void KOS_raise_exception(KOS_CONTEXT ctx,
654,901✔
896
                         KOS_OBJ_ID  exception_obj)
897
{
898
    /* Nested exceptions are not allowed. */
899
    /* This can only happen if there is a bug and an exception has been ignored. */
900
    assert(IS_BAD_PTR(ctx->exception));
654,901✔
901

902
    assert(GET_OBJ_TYPE_GC_SAFE(exception_obj) <= OBJ_LAST_TYPE ||
654,901✔
903
           GET_OBJ_TYPE_GC_SAFE(exception_obj) == OBJ_DYNAMIC_PROP);
904

905
#ifdef CONFIG_MAD_GC
906
    if ( ! kos_gc_active(ctx)) {
907
        KOS_LOCAL saved_exception;
908

909
        KOS_init_local_with(ctx, &saved_exception, exception_obj);
910
        (void)kos_trigger_mad_gc(ctx);
911
        exception_obj = KOS_destroy_top_local(ctx, &saved_exception);
912
    }
913
#endif
914

915
    if (IS_BAD_PTR(ctx->exception))
654,901✔
916
        ctx->exception = exception_obj;
654,901✔
917
}
654,901✔
918

919
void KOS_raise_exception_cstring(KOS_CONTEXT ctx,
51✔
920
                                 const char *cstr)
921
{
922
    const KOS_OBJ_ID str = KOS_new_const_ascii_cstring(ctx, cstr);
51✔
923

924
    if ( ! IS_BAD_PTR(str))
51✔
925
        KOS_raise_exception(ctx, str);
51✔
926

927
    assert( ! IS_BAD_PTR(ctx->exception));
51✔
928
}
51✔
929

930
KOS_OBJ_ID KOS_format_exception(KOS_CONTEXT ctx,
506✔
931
                                KOS_OBJ_ID  exception)
932
{
933
    int        error;
934
    unsigned   i;
935
    unsigned   depth;
936
    KOS_LOCAL  value;
937
    KOS_LOCAL  backtrace;
938
    KOS_LOCAL  frame_desc;
939
    KOS_LOCAL  array;
940
    KOS_OBJ_ID str;
941
    KOS_VECTOR cstr;
942

943
    KOS_vector_init(&cstr);
506✔
944

945
    KOS_init_locals(ctx, &value, &backtrace, &frame_desc, &array, kos_end_locals);
506✔
946

947
    value.o = KOS_get_property(ctx, exception, KOS_STR_VALUE);
506✔
948
    TRY_OBJID(value.o);
506✔
949

950
    backtrace.o = KOS_get_property(ctx, exception, KOS_STR_BACKTRACE);
506✔
951
    TRY_OBJID(backtrace.o);
506✔
952

953
    if (GET_OBJ_TYPE(backtrace.o) != OBJ_ARRAY)
506✔
UNCOV
954
        RAISE_EXCEPTION_STR(str_err_not_array);
×
955

956
    depth   = KOS_get_array_size(backtrace.o);
506✔
957
    array.o = KOS_new_array(ctx, 1 + depth);
506✔
958
    TRY_OBJID(array.o);
506✔
959

960
    if (GET_OBJ_TYPE(value.o) == OBJ_OBJECT) {
504✔
961
        str = KOS_get_property(ctx, value.o, KOS_CONST_ID(str_description));
219✔
962

963
        if (IS_BAD_PTR(str))
219✔
964
            KOS_clear_exception(ctx);
219✔
965
        else
UNCOV
966
            value.o = str;
×
967
    }
968

969
    if (KOS_vector_reserve(&cstr, 80) != KOS_SUCCESS) {
504✔
970
        KOS_raise_exception(ctx, KOS_STR_OUT_OF_MEMORY);
4✔
971
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
4✔
972
    }
973
    TRY(KOS_append_cstr(ctx, &cstr, str_format_exception, sizeof(str_format_exception) - 1));
500✔
974
    TRY(KOS_object_to_string_or_cstr_vec(ctx, value.o, KOS_DONT_QUOTE, KOS_NULL, &cstr));
500✔
975

976
    str = KOS_new_string(ctx, cstr.buffer, (unsigned)(cstr.size - 1));
498✔
977
    TRY_OBJID(str);
498✔
978

979
    TRY(KOS_array_write(ctx, array.o, 0, str));
496✔
980

981
    for (i = 0; i < depth; i++) {
4,829✔
982

983
        char     cbuf[19];
984
        unsigned len;
985

986
        frame_desc.o = KOS_array_read(ctx, backtrace.o, (int)i);
4,343✔
987
        TRY_OBJID(frame_desc.o);
4,351✔
988

989
        cstr.size = 0;
4,341✔
990

991
        TRY(KOS_append_cstr(ctx, &cstr, str_format_hash,
4,341✔
992
                            sizeof(str_format_hash) - 1));
993

994
        len = (unsigned)snprintf(cbuf, sizeof(cbuf), "%u", i);
4,341✔
995
        TRY(KOS_append_cstr(ctx, &cstr, cbuf, KOS_min(len, (unsigned)(sizeof(cbuf) - 1))));
4,341✔
996

997
        TRY(KOS_append_cstr(ctx, &cstr, str_format_offset,
4,341✔
998
                            sizeof(str_format_offset) - 1));
999

1000
        str = KOS_get_property(ctx, frame_desc.o, KOS_STR_OFFSET);
4,341✔
1001
        TRY_OBJID(str);
4,341✔
1002
        if (IS_NUMERIC_OBJ(str)) {
8,682✔
1003
            int64_t int_value;
1004

1005
            TRY(KOS_get_integer(ctx, str, &int_value));
4,341✔
1006
            len = (unsigned)snprintf(cbuf, sizeof(cbuf), "0x%" PRIX64, int_value);
4,341✔
1007
            TRY(KOS_append_cstr(ctx, &cstr, cbuf, KOS_min(len, (unsigned)(sizeof(cbuf) - 1))));
4,341✔
1008
        }
1009
        else
UNCOV
1010
            TRY(KOS_append_cstr(ctx, &cstr, str_format_question_marks,
×
1011
                                sizeof(str_format_question_marks) - 1));
1012

1013
        TRY(KOS_append_cstr(ctx, &cstr, str_format_function,
4,341✔
1014
                            sizeof(str_format_function) - 1));
1015

1016
        str = KOS_get_property(ctx, frame_desc.o, KOS_STR_FUNCTION);
4,341✔
1017
        TRY_OBJID(str);
4,341✔
1018
        TRY(KOS_object_to_string_or_cstr_vec(ctx, str, KOS_DONT_QUOTE, KOS_NULL, &cstr));
4,341✔
1019

1020
        TRY(KOS_append_cstr(ctx, &cstr, str_format_module,
4,341✔
1021
                            sizeof(str_format_module) - 1));
1022

1023
        str = KOS_get_property(ctx, frame_desc.o, KOS_STR_FILE);
4,341✔
1024
        TRY_OBJID(str);
4,341✔
1025
        str = KOS_get_file_name(ctx, str);
4,341✔
1026
        TRY_OBJID(str);
4,341✔
1027
        TRY(KOS_object_to_string_or_cstr_vec(ctx, str, KOS_DONT_QUOTE, KOS_NULL, &cstr));
4,339✔
1028

1029
        TRY(KOS_append_cstr(ctx, &cstr, str_format_line,
4,339✔
1030
                            sizeof(str_format_line) - 1));
1031

1032
        str = KOS_get_property(ctx, frame_desc.o, KOS_STR_LINE);
4,339✔
1033
        TRY_OBJID(str);
4,339✔
1034
        TRY(KOS_object_to_string_or_cstr_vec(ctx, str, KOS_DONT_QUOTE, KOS_NULL, &cstr));
4,339✔
1035

1036
        str = KOS_new_string(ctx, cstr.buffer, (unsigned)(cstr.size - 1));
4,337✔
1037
        TRY_OBJID(str);
4,337✔
1038

1039
        TRY(KOS_array_write(ctx, array.o, 1+(int)i, str));
4,333✔
1040
    }
1041

1042
cleanup:
486✔
1043
    array.o = KOS_destroy_top_locals(ctx, &value, &array);
506✔
1044

1045
    KOS_vector_destroy(&cstr);
506✔
1046

1047
    return error ? KOS_BADPTR : array.o;
506✔
1048
}
1049

1050
static void raise_internal_exception(KOS_CONTEXT ctx, KOS_OBJ_ID base)
120✔
1051
{
1052
    const KOS_OBJ_ID exception = KOS_new_object_with_prototype(ctx, base);
120✔
1053

1054
    if ( ! IS_BAD_PTR(exception))
120✔
1055
        KOS_raise_exception(ctx, exception);
120✔
1056
}
120✔
1057

1058
void KOS_raise_generator_end(KOS_CONTEXT ctx)
114✔
1059
{
1060
    raise_internal_exception(ctx, ctx->inst->prototypes.generator_end_proto);
114✔
1061
}
114✔
1062

1063
void kos_raise_generator_close(KOS_CONTEXT ctx)
6✔
1064
{
1065
    raise_internal_exception(ctx, ctx->inst->prototypes.generator_close_proto);
6✔
1066
}
6✔
1067

UNCOV
1068
void kos_raise_panic(KOS_CONTEXT ctx)
×
1069
{
UNCOV
1070
    raise_internal_exception(ctx, ctx->inst->prototypes.panic_proto);
×
UNCOV
1071
}
×
1072

1073
#ifdef NDEBUG
1074
#define check_local_list(ctx, local) ((void)0)
1075
#else
1076
static void check_local_list(KOS_LOCAL *list, KOS_LOCAL *local)
37,826,364✔
1077
{
1078
    while (list) {
442,346,517✔
1079
        assert(list != local);
404,520,153✔
1080
        list = list->next;
404,520,153✔
1081
    }
1082
}
37,826,364✔
1083
#endif
1084

1085
void KOS_init_local_with(KOS_CONTEXT ctx, KOS_LOCAL *local, KOS_OBJ_ID obj_id)
28,730,122✔
1086
{
1087
    KOS_LOCAL *next = ctx->local_list;
28,730,122✔
1088

1089
    check_local_list(next, local);
28,730,122✔
1090

1091
    local->next     = next;
28,730,122✔
1092
    local->o        = obj_id;
28,730,122✔
1093
    ctx->local_list = local;
28,730,122✔
1094
}
28,730,122✔
1095

1096
void KOS_init_ulocal(KOS_CONTEXT ctx, KOS_ULOCAL *local)
866✔
1097
{
1098
    KOS_ULOCAL *next = ctx->ulocal_list;
866✔
1099

1100
    check_local_list((KOS_LOCAL *)next, (KOS_LOCAL *)local);
866✔
1101

1102
    local->next      = next;
866✔
1103
    local->prev      = KOS_NULL;
866✔
1104
    local->o         = KOS_BADPTR;
866✔
1105
    ctx->ulocal_list = local;
866✔
1106
    if (next)
866✔
1107
        next->prev   = local;
825✔
1108
}
866✔
1109

1110
void KOS_init_locals(KOS_CONTEXT ctx, ...)
1,241,539✔
1111
{
1112
    va_list     args;
1113
    KOS_LOCAL  *head     = KOS_NULL;
1,241,539✔
1114
    KOS_LOCAL **tail_ptr = &head;
1,241,539✔
1115

1116
    va_start(args, ctx);
1,241,539✔
1117

1118
    for (;;) {
4,547,688✔
1119
        KOS_LOCAL *local = (KOS_LOCAL *)va_arg(args, KOS_LOCAL *);
5,789,227✔
1120

1121
        if ( ! local)
5,789,227✔
1122
            break;
1,241,539✔
1123

1124
        check_local_list(head, local);
4,547,688✔
1125
        check_local_list(ctx->local_list, local);
4,547,688✔
1126

1127
        *tail_ptr = local;
4,547,688✔
1128
        tail_ptr  = &local->next;
4,547,688✔
1129

1130
        local->o = KOS_BADPTR;
4,547,688✔
1131
#ifndef NDEBUG
1132
        local->next = KOS_NULL;
4,547,688✔
1133
#endif
1134
    }
1135

1136
    *tail_ptr       = ctx->local_list;
1,241,539✔
1137
    ctx->local_list = head;
1,241,539✔
1138

1139
    va_end(args);
1,241,539✔
1140
}
1,241,539✔
1141

1142
KOS_OBJ_ID KOS_destroy_ulocal(KOS_CONTEXT ctx, KOS_ULOCAL *local)
889✔
1143
{
1144
    KOS_OBJ_ID ret = KOS_BADPTR;
889✔
1145

1146
    if (ctx) {
889✔
1147
        KOS_ULOCAL *prev = local->prev;
866✔
1148
        KOS_ULOCAL *next = local->next;
866✔
1149

1150
        KOS_ULOCAL **prev_hookup = prev ? &prev->next : &ctx->ulocal_list;
866✔
1151

1152
        assert(ctx->ulocal_list);
866✔
1153
        if (prev) {
866✔
1154
            assert(ctx->ulocal_list != local);
463✔
1155
        }
1156
        else {
1157
            assert(ctx->ulocal_list == local);
403✔
1158
        }
1159

1160
        *prev_hookup = next;
866✔
1161
        if (next)
866✔
1162
            next->prev = prev;
812✔
1163

1164
        ret = local->o;
866✔
1165

1166
#ifndef NDEBUG
1167
        local->next = KOS_NULL;
866✔
1168
        local->prev = KOS_NULL;
866✔
1169
        local->o    = KOS_BADPTR;
866✔
1170
#endif
1171
    }
1172

1173
    return ret;
889✔
1174
}
1175

1176
KOS_OBJ_ID KOS_destroy_top_local(KOS_CONTEXT ctx, KOS_LOCAL *local)
6,807,518✔
1177
{
1178
    KOS_OBJ_ID ret = KOS_BADPTR;
6,807,518✔
1179

1180
    assert(ctx->local_list == local);
6,807,518✔
1181

1182
    ctx->local_list = local->next;
6,807,518✔
1183

1184
    ret = local->o;
6,807,518✔
1185

1186
#ifndef NDEBUG
1187
    local->next = KOS_NULL;
6,807,518✔
1188
    local->o    = KOS_BADPTR;
6,807,518✔
1189
#endif
1190

1191
    return ret;
6,807,518✔
1192
}
1193

1194
KOS_OBJ_ID KOS_destroy_top_locals(KOS_CONTEXT ctx, KOS_LOCAL *first, KOS_LOCAL *last)
9,399,838✔
1195
{
1196
    KOS_LOCAL **hookup = &ctx->local_list;
9,399,838✔
1197
    KOS_OBJ_ID  ret    = last->o;
9,399,838✔
1198

1199
    assert(ctx->local_list == first);
9,399,838✔
1200

1201
    *hookup = last->next;
9,399,838✔
1202

1203
#ifndef NDEBUG
1204
    for (;;) {
17,069,904✔
1205
        KOS_LOCAL *next = first->next;
26,469,742✔
1206

1207
        first->next = KOS_NULL;
26,469,742✔
1208
        first->o    = KOS_BADPTR;
26,469,742✔
1209

1210
        if (first == last)
26,469,742✔
1211
            break;
9,399,838✔
1212

1213
        first = next;
17,069,904✔
1214
    }
1215
#endif
1216

1217
    return ret;
9,399,838✔
1218
}
1219

1220
void kos_set_global_event(KOS_CONTEXT ctx, enum KOS_GLOBAL_EVENT_E event)
3,746✔
1221
{
1222
    KOS_INSTANCE* const inst = ctx->inst;
3,746✔
1223
    KOS_CONTEXT         walk_ctx;
1224

1225
    kos_lock_mutex(inst->threads.ctx_mutex);
3,746✔
1226

1227
    walk_ctx = &inst->threads.main_thread;
3,746✔
1228

1229
    for ( ; walk_ctx; walk_ctx = walk_ctx->next) {
11,635✔
1230
        uint32_t flags;
1231

1232
        do {
1233
            flags = KOS_atomic_read_relaxed_u32(walk_ctx->event_flags);
7,889✔
1234
        } while ( ! KOS_atomic_cas_weak_u32(walk_ctx->event_flags, flags, flags | event));
7,889✔
1235
    }
1236

1237
    kos_unlock_mutex(inst->threads.ctx_mutex);
3,746✔
1238
}
3,746✔
1239

1240
void kos_clear_global_event(KOS_CONTEXT ctx, enum KOS_GLOBAL_EVENT_E event)
3,818✔
1241
{
1242
    KOS_INSTANCE* const inst = ctx->inst;
3,818✔
1243
    KOS_CONTEXT         walk_ctx;
1244

1245
    kos_lock_mutex(inst->threads.ctx_mutex);
3,818✔
1246

1247
    walk_ctx = &inst->threads.main_thread;
3,818✔
1248

1249
    for ( ; walk_ctx; walk_ctx = walk_ctx->next) {
12,743✔
1250
        uint32_t flags;
1251

1252
        do {
1253
            flags = KOS_atomic_read_relaxed_u32(walk_ctx->event_flags);
8,925✔
1254
        } while ( ! KOS_atomic_cas_weak_u32(walk_ctx->event_flags, flags, flags & ~event));
8,925✔
1255
    }
1256

1257
    kos_unlock_mutex(inst->threads.ctx_mutex);
3,818✔
1258
}
3,818✔
1259

1260
int KOS_handle_global_event(KOS_CONTEXT ctx)
5,071,798✔
1261
{
1262
    uint32_t event;
1263

1264
    kos_validate_context(ctx);
5,071,798✔
1265

1266
    event = KOS_atomic_read_relaxed_u32(ctx->event_flags);
5,071,798✔
1267

1268
    if ( ! event)
5,071,798✔
1269
        return KOS_SUCCESS;
5,071,737✔
1270

1271
    if (event & KOS_EVENT_PANIC) {
61✔
UNCOV
1272
        kos_raise_panic(ctx);
×
UNCOV
1273
        return KOS_ERROR_EXCEPTION;
×
1274
    }
1275

1276
    if (event & KOS_EVENT_CTRL_C) {
61✔
UNCOV
1277
        raise_internal_exception(ctx, ctx->inst->prototypes.ctrl_c_proto);
×
1278

UNCOV
1279
        return KOS_ERROR_EXCEPTION;
×
1280
    }
1281

1282
    if (event & KOS_EVENT_GC)
61✔
1283
        kos_help_gc(ctx);
61✔
1284

1285
    return KOS_SUCCESS;
61✔
1286
}
1287

1288
#ifdef _WIN32
1289
typedef void (* signal_handler)(int);
1290

1291
static int set_signal(int sig, void (* handler)(int), signal_handler *old_action)
1292
{
1293
    errno = 0;
1294

1295
    return (kos_seq_fail() || ((*old_action = signal(sig, handler)) == SIG_ERR)) ? KOS_ERROR_ERRNO : KOS_SUCCESS;
1296
}
1297

1298
static void restore_signal(int sig, signal_handler *old_action)
1299
{
1300
    signal(sig, *old_action);
1301
}
1302
#else
1303
typedef struct sigaction signal_handler;
1304

1305
static int set_signal(int sig, void (* handler)(int), signal_handler *old_action)
7,533✔
1306
{
1307
    signal_handler sa;
1308

1309
    memset(&sa, 0, sizeof(sa));
7,533✔
1310
    sa.sa_handler = handler;
7,533✔
1311
    sigemptyset(&sa.sa_mask);
7,533✔
1312

1313
    errno = 0;
7,533✔
1314

1315
    return (kos_seq_fail() || sigaction(sig, &sa, old_action) != 0) ? KOS_ERROR_ERRNO : KOS_SUCCESS;
7,533✔
1316
}
1317

1318
static void restore_signal(int sig, signal_handler *old_action)
72✔
1319
{
1320
    signal_handler prev_handler;
1321

1322
    sigaction(sig, old_action, &prev_handler);
72✔
1323
}
72✔
1324
#endif
1325

1326
struct KOS_CTRL_C {
1327
    KOS_ATOMIC(KOS_INSTANCE *) instance;
1328
    signal_handler             old_handler;
1329
};
1330

1331
static struct KOS_CTRL_C ctrl_c;
1332

1333
static void instance_ctrlc_signal_handler(int sig)
×
1334
{
UNCOV
1335
    KOS_INSTANCE *const instance = (KOS_INSTANCE *)KOS_atomic_read_relaxed_ptr(ctrl_c.instance);
×
1336

UNCOV
1337
    kos_set_global_event(&instance->threads.main_thread, KOS_EVENT_CTRL_C);
×
UNCOV
1338
}
×
1339

1340
int KOS_hook_ctrl_c(KOS_CONTEXT ctx)
7,533✔
1341
{
1342
    if ( ! KOS_atomic_cas_strong_ptr(ctrl_c.instance, (KOS_INSTANCE *)KOS_NULL, ctx->inst)) {
7,533✔
UNCOV
1343
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_ctrl_c_already_hooked));
×
UNCOV
1344
        return KOS_ERROR_EXCEPTION;
×
1345
    }
1346

1347
    if (set_signal(SIGINT, instance_ctrlc_signal_handler, &ctrl_c.old_handler)) {
7,533✔
1348
        KOS_raise_errno(ctx, "signal");
1✔
1349
        return KOS_ERROR_EXCEPTION;
1✔
1350
    }
1351

1352
    return KOS_SUCCESS;
7,532✔
1353
}
1354

1355
int KOS_unhook_ctrl_c(KOS_CONTEXT ctx)
72✔
1356
{
1357
    if (KOS_atomic_read_acquire_ptr(ctrl_c.instance) == ctx->inst) {
72✔
1358

1359
        restore_signal(SIGINT, &ctrl_c.old_handler);
72✔
1360
        memset(&ctrl_c.old_handler, 0, sizeof(ctrl_c.old_handler));
72✔
1361

1362
        kos_clear_global_event(ctx, KOS_EVENT_CTRL_C);
72✔
1363

1364
        KOS_atomic_write_release_ptr(ctrl_c.instance, (KOS_INSTANCE *)KOS_NULL);
72✔
1365
    }
1366

1367
    return KOS_SUCCESS;
72✔
1368
}
1369

1370
KOS_OBJ_ID KOS_get_exception_value(KOS_CONTEXT ctx)
8,659✔
1371
{
1372
    KOS_INSTANCE *const inst = ctx->inst;
8,659✔
1373
    KOS_LOCAL exception;
1374

1375
    KOS_init_local_with(ctx, &exception, KOS_get_exception(ctx));
8,659✔
1376

1377
    if ( ! IS_BAD_PTR(exception.o) &&
8,659✔
1378
        KOS_get_prototype(ctx, exception.o) == inst->prototypes.exception_proto) {
8,659✔
1379

1380
        KOS_clear_exception(ctx);
8,639✔
1381

1382
        const KOS_OBJ_ID value = KOS_get_property(ctx, exception.o, KOS_STR_VALUE);
8,639✔
1383

1384
        if (IS_BAD_PTR(value)) {
8,639✔
1385
            KOS_clear_exception(ctx);
3✔
1386
            KOS_raise_exception(ctx, exception.o);
3✔
1387
        }
1388
        else {
1389
            const KOS_OBJ_ID restore = exception.o;
8,636✔
1390
            exception.o = value;
8,636✔
1391
            KOS_raise_exception(ctx, restore);
8,636✔
1392
        }
1393
    }
1394

1395
    return KOS_destroy_top_local(ctx, &exception);
8,659✔
1396
}
1397

1398
void KOS_clear_ctrl_c_event(KOS_CONTEXT ctx)
8,633✔
1399
{
1400
    const KOS_OBJ_ID exception = KOS_get_exception_value(ctx);
8,633✔
1401

1402
    if (KOS_has_prototype(ctx, exception, ctx->inst->prototypes.ctrl_c_proto))
8,633✔
UNCOV
1403
        kos_clear_global_event(ctx, KOS_EVENT_CTRL_C);
×
1404
}
8,633✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc