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

kos-lang / kos / 12875849342

20 Jan 2025 09:20PM UTC coverage: 96.443% (-0.03%) from 96.473%
12875849342

push

github

cdragan
interpreter: fix spurious error when packaging on Linux

24620 of 25528 relevant lines covered (96.44%)

872051.69 hits per line

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

98.4
/modules/kos_mod_base.c
1
/* SPDX-License-Identifier: MIT
2
 * SPDX-FileCopyrightText: Copyright (c) 2014-2024 Chris Dragan
3
 */
4

5
#include "../inc/kos_array.h"
6
#include "../inc/kos_buffer.h"
7
#include "../inc/kos_constants.h"
8
#include "../inc/kos_error.h"
9
#include "../inc/kos_object.h"
10
#include "../inc/kos_malloc.h"
11
#include "../inc/kos_memory.h"
12
#include "../inc/kos_module.h"
13
#include "../inc/kos_string.h"
14
#include "../inc/kos_utils.h"
15
#include "../core/kos_object_internal.h"
16
#include "../core/kos_math.h"
17
#include "../core/kos_misc.h"
18
#include "../core/kos_try.h"
19
#include <assert.h>
20
#define __STDC_FORMAT_MACROS
21
#include <inttypes.h>
22
#include <limits.h>
23
#include <memory.h>
24
#include <stdio.h>
25

26
KOS_DECLARE_STATIC_CONST_STRING(str_args,                         "args");
27
KOS_DECLARE_STATIC_CONST_STRING(str_array,                        "array");
28
KOS_DECLARE_STATIC_CONST_STRING(str_begin,                        "begin");
29
KOS_DECLARE_STATIC_CONST_STRING(str_chars,                        "chars");
30
KOS_DECLARE_STATIC_CONST_STRING(str_count,                        "count");
31
KOS_DECLARE_STATIC_CONST_STRING(str_default_value,                "default_value");
32
KOS_DECLARE_STATIC_CONST_STRING(str_end,                          "end");
33
KOS_DECLARE_STATIC_CONST_STRING(str_err_already_joined,           "thread already joined");
34
KOS_DECLARE_STATIC_CONST_STRING(str_err_args_not_array,           "function arguments are not an array");
35
KOS_DECLARE_STATIC_CONST_STRING(str_err_bad_number,               "number parse failed");
36
KOS_DECLARE_STATIC_CONST_STRING(str_err_cannot_convert_to_array,  "unsupported type passed to array class");
37
KOS_DECLARE_STATIC_CONST_STRING(str_err_cannot_convert_to_buffer, "unsupported type passed to buffer class");
38
KOS_DECLARE_STATIC_CONST_STRING(str_err_cannot_convert_to_string, "unsupported type passed to string class");
39
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_array_size,       "array size out of range");
40
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_buffer_size,      "buffer size out of range");
41
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_byte_value,       "buffer element value out of range");
42
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_char_code,        "invalid character code");
43
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_key_type,         "invalid key type, must be function or void");
44
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_reverse_type,     "invalid reverse type, must be boolean");
45
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_string,           "invalid string");
46
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_string_idx,       "string index is out of range");
47
KOS_DECLARE_STATIC_CONST_STRING(str_err_join_self,                "thread cannot join itself");
48
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_array,                "object is not an array");
49
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_boolean,              "object is not a boolean");
50
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_buffer,               "object is not a buffer");
51
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_class,                "object is not a class");
52
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_function,             "object is not a function");
53
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_generator,            "object is not a generator");
54
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_module,               "object is not a module");
55
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_string,               "object is not a string");
56
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_thread,               "object is not a thread");
57
KOS_DECLARE_STATIC_CONST_STRING(str_err_object_arg_not_iterable,  "argument passed to object class is not iterable");
58
KOS_DECLARE_STATIC_CONST_STRING(str_err_too_many_repeats,         "invalid string repeat count");
59
KOS_DECLARE_STATIC_CONST_STRING(str_err_unsup_operand_types,      "unsupported operand types");
60
KOS_DECLARE_STATIC_CONST_STRING(str_err_use_async,                "use async to launch threads");
61
KOS_DECLARE_STATIC_CONST_STRING(str_err_use_load,                 "use module.load() to import modules");
62
KOS_DECLARE_STATIC_CONST_STRING(str_format,                       "format");
63
KOS_DECLARE_STATIC_CONST_STRING(str_gen_active,                   "active");
64
KOS_DECLARE_STATIC_CONST_STRING(str_gen_done,                     "done");
65
KOS_DECLARE_STATIC_CONST_STRING(str_gen_init,                     "init");
66
KOS_DECLARE_STATIC_CONST_STRING(str_gen_ready,                    "ready");
67
KOS_DECLARE_STATIC_CONST_STRING(str_gen_running,                  "running");
68
KOS_DECLARE_STATIC_CONST_STRING(str_inclusive,                    "inclusive");
69
KOS_DECLARE_STATIC_CONST_STRING(str_key,                          "key");
70
KOS_DECLARE_STATIC_CONST_STRING(str_keys,                         "keys");
71
KOS_DECLARE_STATIC_CONST_STRING(str_name,                         "name");
72
KOS_DECLARE_STATIC_CONST_STRING(str_new_value,                    "new_value");
73
KOS_DECLARE_STATIC_CONST_STRING(str_obj,                          "obj");
74
KOS_DECLARE_STATIC_CONST_STRING(str_old_value,                    "old_value");
75
KOS_DECLARE_STATIC_CONST_STRING(str_pos,                          "pos");
76
KOS_DECLARE_STATIC_CONST_STRING(str_reverse,                      "reverse");
77
KOS_DECLARE_STATIC_CONST_STRING(str_size,                         "size");
78
KOS_DECLARE_STATIC_CONST_STRING(str_source,                       "source");
79
KOS_DECLARE_STATIC_CONST_STRING(str_str,                          "str");
80
KOS_DECLARE_STATIC_CONST_STRING(str_substr,                       "substr");
81
KOS_DECLARE_STATIC_CONST_STRING(str_this_obj,                     "this_obj");
82
KOS_DECLARE_STATIC_CONST_STRING(str_value,                        "value");
83
KOS_DECLARE_STATIC_CONST_STRING(str_values,                       "values");
84

85
#define TRY_CREATE_CONSTRUCTOR(name, module, args)         \
86
do {                                                       \
87
    KOS_DECLARE_STATIC_CONST_STRING(str_ctr_##name, #name);\
88
    TRY(create_class(ctx,                                  \
89
                     module,                               \
90
                     KOS_CONST_ID(str_ctr_##name),         \
91
                     name##_constructor,                   \
92
                     (args),                               \
93
                     ctx->inst->prototypes.name##_proto)); \
94
} while (0)
95

96
#define PROTO(type) (ctx->inst->prototypes.type##_proto)
97

98
/* @item base print()
99
 *
100
 *     print(values...)
101
 *
102
 * Converts all arguments to printable strings and prints them on stdout.
103
 *
104
 * Accepts zero or more arguments to print.
105
 *
106
 * Printed values are separated with a single space.
107
 *
108
 * After printing all values prints an EOL character.  If no values are
109
 * provided, just prints an EOL character.
110
 */
111
static KOS_OBJ_ID print(KOS_CONTEXT ctx,
229✔
112
                        KOS_OBJ_ID  this_obj,
113
                        KOS_OBJ_ID  args_obj)
114
{
115
    int        error = KOS_SUCCESS;
229✔
116
    KOS_VECTOR cstr;
117

118
    KOS_vector_init(&cstr);
229✔
119

120
    TRY(KOS_print_to_cstr_vec(ctx, args_obj, KOS_DONT_QUOTE, &cstr, " ", 1));
229✔
121

122
    KOS_suspend_context(ctx);
225✔
123

124
#ifndef CONFIG_FUZZ
125
    if (cstr.size) {
225✔
126
        cstr.buffer[cstr.size - 1] = '\n';
222✔
127
        fwrite(cstr.buffer, 1, cstr.size, stdout);
222✔
128
    }
129
    else
130
        printf("\n");
3✔
131
#endif
132

133
    KOS_resume_context(ctx);
225✔
134

135
cleanup:
229✔
136
    KOS_vector_destroy(&cstr);
229✔
137

138
    return error ? KOS_BADPTR : KOS_VOID;
229✔
139
}
140

141
static KOS_OBJ_ID object_iterator(KOS_CONTEXT      ctx,
815✔
142
                                  KOS_OBJ_ID       regs_obj,
143
                                  enum KOS_DEPTH_E depth)
144
{
145
    int        error;
146
    KOS_OBJ_ID ret = KOS_BADPTR;
815✔
147
    KOS_LOCAL  regs;
148
    KOS_LOCAL  array;
149
    KOS_LOCAL  walk;
150
    KOS_LOCAL  value;
151

152
    KOS_init_locals(ctx, &regs, &array, &walk, &value, kos_end_locals);
815✔
153

154
    regs.o = regs_obj;
815✔
155

156
    assert( ! IS_BAD_PTR(regs.o));
815✔
157
    assert(GET_OBJ_TYPE(regs.o) == OBJ_ARRAY);
815✔
158
    assert(KOS_get_array_size(regs.o) > 0);
815✔
159

160
    walk.o = KOS_array_read(ctx, regs.o, 0);
815✔
161
    TRY_OBJID(walk.o);
815✔
162

163
    if (GET_OBJ_TYPE(walk.o) != OBJ_ITERATOR) {
812✔
164
        walk.o = KOS_new_iterator(ctx, walk.o, depth);
280✔
165
        TRY_OBJID(walk.o);
280✔
166

167
        TRY(KOS_array_write(ctx, regs.o, 0, walk.o));
279✔
168
    }
169

170
    {
171
        array.o = KOS_new_array(ctx, 2);
811✔
172
        TRY_OBJID(array.o);
811✔
173

174
        error = KOS_iterator_next(ctx, walk.o);
808✔
175

176
        if ( ! error) {
808✔
177
            value.o = KOS_get_walk_value(walk.o);
552✔
178

179
            assert( ! IS_BAD_PTR(KOS_get_walk_key(walk.o)));
552✔
180
            assert( ! IS_BAD_PTR(value.o));
552✔
181

182
            if (GET_OBJ_TYPE(value.o) == OBJ_DYNAMIC_PROP) {
552✔
183
                value.o = KOS_call_function(ctx,
4✔
184
                                            OBJPTR(DYNAMIC_PROP, value.o)->getter,
185
                                            OBJPTR(ITERATOR, walk.o)->obj,
186
                                            KOS_EMPTY_ARRAY);
187
                if (IS_BAD_PTR(value.o)) {
4✔
188
                    assert(KOS_is_exception_pending(ctx));
1✔
189
                    KOS_clear_exception(ctx);
1✔
190

191
                    value.o = OBJPTR(DYNAMIC_PROP, KOS_get_walk_value(walk.o))->getter;
1✔
192
                }
193
            }
194

195
            TRY(KOS_array_write(ctx, array.o, 0, KOS_get_walk_key(walk.o)));
552✔
196
            TRY(KOS_array_write(ctx, array.o, 1, value.o));
552✔
197

198
            ret = array.o;
552✔
199
        }
200
    }
201

202
cleanup:
256✔
203
    KOS_destroy_top_locals(ctx, &regs, &value);
815✔
204

205
    return ret;
815✔
206
}
207

208
/* @item base shallow()
209
 *
210
 *     shallow(obj)
211
 *
212
 * A generator which produces properties of an object in a shallow manner,
213
 * i.e. without descending into prototypes.
214
 *
215
 * Returns an iterator function, which yields 2-element arrays, which are
216
 * [key, value] pairs of subsequent properties of the `obj` object.
217
 *
218
 * The order of the elements yielded is unspecified.
219
 *
220
 * Example:
221
 *
222
 *     > [ shallow({x:0, y:1}) ... ]
223
 *     [["y", 1], ["x", 0]]
224
 */
225
static KOS_OBJ_ID shallow(KOS_CONTEXT ctx,
777✔
226
                          KOS_OBJ_ID  regs_obj,
227
                          KOS_OBJ_ID  args_obj)
228
{
229
    return object_iterator(ctx, regs_obj, KOS_SHALLOW);
777✔
230
}
231

232
/* @item base deep()
233
 *
234
 *     deep(obj)
235
 *
236
 * A generator which produces properties of an object and all its prototypes.
237
 *
238
 * Returns an iterator function, which yields 2-element arrays, which are
239
 * [key, value] pairs of subsequent properties of the `obj` object.
240
 *
241
 * The order of the elements yielded is unspecified.
242
 *
243
 * Example:
244
 *
245
 *     > [ deep({x:0, y:1}) ... ]
246
 *     [["any", <function>], ["all", <function>], ["filter", <function>],
247
 *      ["count", <function>], ["reduce", <function>], ["iterator", <function>],
248
 *      ["map", <function>], ["y", 1], ["x", 0]]
249
 */
250
static const KOS_CONVERT deep_args[2] = {
251
    KOS_DEFINE_MANDATORY_ARG(str_obj),
252
    KOS_DEFINE_TAIL_ARG()
253
};
254

255
static KOS_OBJ_ID deep(KOS_CONTEXT ctx,
38✔
256
                       KOS_OBJ_ID  regs_obj,
257
                       KOS_OBJ_ID  args_obj)
258
{
259
    return object_iterator(ctx, regs_obj, KOS_DEEP);
38✔
260
}
261

262
static int create_class(KOS_CONTEXT          ctx,
110,247✔
263
                        KOS_OBJ_ID           module_obj,
264
                        KOS_OBJ_ID           class_name,
265
                        KOS_FUNCTION_HANDLER constructor,
266
                        const KOS_CONVERT   *args,
267
                        KOS_OBJ_ID           prototype)
268
{
269
    int        error    = KOS_SUCCESS;
110,247✔
270
    KOS_OBJ_ID func_obj = KOS_BADPTR;
110,247✔
271
    KOS_LOCAL  module;
272
    KOS_LOCAL  proto;
273

274
    KOS_init_local_with(ctx, &module, module_obj);
110,247✔
275
    KOS_init_local_with(ctx, &proto,  prototype);
110,247✔
276

277
    func_obj = KOS_new_builtin_class(ctx, class_name, constructor, args);
110,247✔
278
    TRY_OBJID(func_obj);
110,247✔
279

280
    OBJPTR(CLASS, func_obj)->prototype = proto.o;
110,126✔
281
    OBJPTR(CLASS, func_obj)->module    = module.o;
110,126✔
282

283
    TRY(KOS_module_add_global(ctx,
110,126✔
284
                              module.o,
285
                              class_name,
286
                              func_obj,
287
                              KOS_NULL));
288

289
cleanup:
110,116✔
290
    KOS_destroy_top_locals(ctx, &proto, &module);
110,247✔
291
    return error;
110,247✔
292
}
293

294
/* @item base number()
295
 *
296
 *     number(value = 0)
297
 *
298
 * Numeric type class.
299
 *
300
 * The optional `value` argument can be an integer, a float or a string.
301
 *
302
 * If `value` is not provided, returns 0.
303
 *
304
 * If `value` is an integer or a float, returns `value`.
305
 *
306
 * If `value` is a string, parses it in the same manner numeric literals are
307
 * parsed by the interpreter and returns the number as either an integer or
308
 * a float, depending on the parsing result.
309
 * Throws an exception if the string cannot be parsed.
310
 *
311
 * The prototype of `number.prototype` is `object.prototype`.
312
 *
313
 * Examples:
314
 *
315
 *     > number()
316
 *     0
317
 *     > number(10)
318
 *     10
319
 *     > number(10.0)
320
 *     10.0
321
 *     > number("123.000")
322
 *     123.0
323
 *     > number("0x100")
324
 *     256
325
 */
326
static KOS_OBJ_ID number_constructor(KOS_CONTEXT ctx,
30✔
327
                                     KOS_OBJ_ID  this_obj,
328
                                     KOS_OBJ_ID  args_obj)
329
{
330
    const uint32_t num_args = KOS_get_array_size(args_obj);
30✔
331
    KOS_OBJ_ID     ret      = KOS_BADPTR;
30✔
332

333
    if (num_args == 0)
30✔
334
        ret = TO_SMALL_INT(0);
1✔
335
    else {
336
        KOS_OBJ_ID arg = KOS_array_read(ctx, args_obj, 0);
29✔
337

338
        if (IS_NUMERIC_OBJ(arg))
29✔
339
            ret = arg;
3✔
340
        else if (READ_OBJ_TYPE(arg) == OBJ_STRING) {
26✔
341

342
            KOS_VECTOR cstr;
343

344
            KOS_vector_init(&cstr);
21✔
345

346
            if (KOS_SUCCESS == KOS_string_to_cstr_vec(ctx, arg, &cstr)) {
21✔
347

348
                const char *begin = cstr.buffer;
21✔
349
                const char *end   = begin + cstr.size - 1;
21✔
350
                KOS_NUMERIC numeric;
351

352
                assert(begin <= end);
21✔
353

354
                if (KOS_SUCCESS == kos_parse_numeric(begin, end, &numeric)) {
21✔
355

356
                    if (numeric.type == KOS_INTEGER_VALUE)
14✔
357
                        ret = KOS_new_int(ctx, numeric.u.i);
8✔
358
                    else {
359
                        assert(numeric.type == KOS_FLOAT_VALUE);
6✔
360
                        ret = KOS_new_float(ctx, numeric.u.d);
6✔
361
                    }
362
                }
363
                else
364
                    KOS_raise_exception(ctx, KOS_CONST_ID(str_err_bad_number));
7✔
365
            }
366

367
            KOS_vector_destroy(&cstr);
21✔
368
        }
369
        else
370
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_unsup_operand_types));
5✔
371
    }
372

373
    return ret;
30✔
374
}
375

376
/* @item base integer()
377
 *
378
 *     integer(value = 0)
379
 *
380
 * Integer type class.
381
 *
382
 * The optional `value` argument can be an integer, a float or a string.
383
 *
384
 * If `value` is not provided, returns 0.
385
 *
386
 * If `value` is an integer, returns `value`.
387
 *
388
 * If `value` is a float, converts it to integer using floor mode and returns the
389
 * converted value.
390
 *
391
 * If `value` is a string, parses it in the same manner numeric literals are
392
 * parsed by the interpreter, requiring that the string is an integer literal.
393
 * Throws an exception if the string is a floating-point literal or cannot be
394
 * parsed.
395
 *
396
 * The prototype of `integer.prototype` is `number.prototype`.
397
 *
398
 * Examples:
399
 *
400
 *     > integer()
401
 *     0
402
 *     > integer(10)
403
 *     10
404
 *     > integer(4.2)
405
 *     4
406
 *     > integer("123")
407
 *     123
408
 */
409
static KOS_OBJ_ID integer_constructor(KOS_CONTEXT ctx,
829✔
410
                                      KOS_OBJ_ID  this_obj,
411
                                      KOS_OBJ_ID  args_obj)
412
{
413
    const uint32_t num_args = KOS_get_array_size(args_obj);
829✔
414
    KOS_OBJ_ID     ret      = KOS_BADPTR;
829✔
415

416
    if (num_args == 0)
829✔
417
        ret = TO_SMALL_INT(0);
4✔
418
    else {
419
        int64_t    value;
420
        KOS_OBJ_ID arg = KOS_array_read(ctx, args_obj, 0);
825✔
421

422
        if (IS_NUMERIC_OBJ(arg)) {
825✔
423
            if (KOS_get_integer(ctx, arg, &value) == KOS_SUCCESS)
787✔
424
                ret = KOS_new_int(ctx, value);
787✔
425
        }
426
        else if (READ_OBJ_TYPE(arg) == OBJ_STRING) {
38✔
427

428
            KOS_VECTOR cstr;
429

430
            KOS_vector_init(&cstr);
32✔
431

432
            if (KOS_SUCCESS == KOS_string_to_cstr_vec(ctx, arg, &cstr)) {
32✔
433

434
                const char *begin = cstr.buffer;
32✔
435
                const char *end   = begin + cstr.size - 1;
32✔
436
                int         error;
437

438
                assert(begin <= end);
32✔
439

440
                error = kos_parse_int(begin, end, &value);
32✔
441

442
                if (error)
32✔
443
                    KOS_raise_exception(ctx, KOS_CONST_ID(str_err_bad_number));
11✔
444
                else
445
                    ret = KOS_new_int(ctx, value);
21✔
446
            }
447

448
            KOS_vector_destroy(&cstr);
32✔
449
        }
450
        else if ( ! IS_BAD_PTR(arg))
6✔
451
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_unsup_operand_types));
6✔
452
    }
453

454
    return ret;
829✔
455
}
456

457
/* @item base float()
458
 *
459
 *     float(value = 0.0)
460
 *
461
 * Float type class.
462
 *
463
 * The optional `value` argument can be an integer, a float or a string.
464
 *
465
 * If `value` is not provided, returns `0.0`.
466
 *
467
 * If `value` is an integer, converts it to a float and returns the converted value.
468
 *
469
 * If `value` is a float, returns `value`.
470
 *
471
 * If `value` is a string, parses it in the same manner numeric literals are
472
 * parsed by the interpreter, assuming it is a floating-point literal.
473
 * Throws an exception if the string cannot be parsed.
474
 *
475
 * The prototype of `float.prototype` is `number.prototype`.
476
 *
477
 * Examples:
478
 *
479
 *     > float()
480
 *     0.0
481
 *     > float(10)
482
 *     10.0
483
 *     > float("123.5")
484
 *     123.5
485
 */
486
static KOS_OBJ_ID float_constructor(KOS_CONTEXT ctx,
253✔
487
                                    KOS_OBJ_ID  this_obj,
488
                                    KOS_OBJ_ID  args_obj)
489
{
490
    const uint32_t num_args = KOS_get_array_size(args_obj);
253✔
491
    KOS_OBJ_ID     ret      = KOS_BADPTR;
253✔
492
    KOS_OBJ_ID     arg;
493

494
    if (num_args == 0)
253✔
495
        return KOS_new_float(ctx, 0);
3✔
496

497
    arg = KOS_array_read(ctx, args_obj, 0);
250✔
498

499
    if (IS_BAD_PTR(arg))
250✔
500
        return arg;
1✔
501

502
    if (IS_SMALL_INT(arg))
249✔
503
        return KOS_new_float(ctx, (double)GET_SMALL_INT(arg));
225✔
504

505
    switch (READ_OBJ_TYPE(arg)) {
24✔
506

507
        case OBJ_INTEGER:
1✔
508
            ret = KOS_new_float(ctx, (double)(OBJPTR(INTEGER, arg)->value));
1✔
509
            break;
1✔
510

511
        case OBJ_FLOAT:
2✔
512
            ret = arg;
2✔
513
            break;
2✔
514

515
        case OBJ_STRING: {
16✔
516

517
            KOS_VECTOR cstr;
518

519
            KOS_vector_init(&cstr);
16✔
520

521
            if (KOS_string_to_cstr_vec(ctx, arg, &cstr) == KOS_SUCCESS) {
16✔
522

523
                const char *begin = cstr.buffer;
16✔
524
                const char *end   = begin + cstr.size - 1;
16✔
525
                double      value;
526
                int         error;
527

528
                assert(begin <= end);
16✔
529

530
                error = kos_parse_double(begin, end, &value);
16✔
531

532
                if (error)
16✔
533
                    KOS_raise_exception(ctx, KOS_CONST_ID(str_err_bad_number));
9✔
534
                else
535
                    ret = KOS_new_float(ctx, value);
7✔
536
            }
537

538
            KOS_vector_destroy(&cstr);
16✔
539
            break;
16✔
540
        }
541

542
        default:
5✔
543
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_unsup_operand_types));
5✔
544
            break;
5✔
545
    }
546

547
    return ret;
24✔
548
}
549

550
/* @item base boolean()
551
 *
552
 *     boolean(value = false)
553
 *
554
 * Boolean type class.
555
 *
556
 * Returns the value converted to a boolean using standard truth detection
557
 * rules.
558
 *
559
 * If `value` is `false`, `void`, integer `0` or float `0.0` returns `false`.
560
 * Otherwise returns `true`.
561
 *
562
 * If `value` is not provided, returns `false`.
563
 *
564
 * The prototype of `boolean.prototype` is `object.prototype`.
565
 *
566
 * Examples:
567
 *
568
 *     > boolean()
569
 *     false
570
 *     > boolean(0)
571
 *     false
572
 *     > boolean([])
573
 *     true
574
 *     > boolean("")
575
 *     true
576
 *     > boolean("false")
577
 *     true
578
 */
579
static KOS_OBJ_ID boolean_constructor(KOS_CONTEXT ctx,
5✔
580
                                      KOS_OBJ_ID  this_obj,
581
                                      KOS_OBJ_ID  args_obj)
582
{
583
    const uint32_t num_args = KOS_get_array_size(args_obj);
5✔
584
    KOS_OBJ_ID     ret      = KOS_BADPTR;
5✔
585

586
    if (num_args > 0) {
5✔
587
        KOS_OBJ_ID arg = KOS_array_read(ctx, args_obj, 0);
4✔
588

589
        if ( ! IS_BAD_PTR(arg))
4✔
590
            ret = KOS_BOOL(kos_is_truthy(arg));
4✔
591
    }
592
    else
593
        ret = KOS_FALSE;
1✔
594

595
    return ret;
5✔
596
}
597

598
/* @item base string()
599
 *
600
 *     string(args...)
601
 *
602
 * String type class.
603
 *
604
 * Returns a new string created from converting all arguments to strings
605
 * and concatenating them.
606
 *
607
 * If no arguments are provided, returns an empty string.
608
 *
609
 * For multiple arguments, constructs a string which is a concatenation of
610
 * strings created from each argument.  The following argument types are
611
 * supported:
612
 *
613
 *  * array    - The array must contain numbers from 0 to 0x1FFFFF, inclusive.
614
 *               Float numbers are converted to integers using floor operation.
615
 *               Any other types of array elements trigger an exception.  The
616
 *               array's elements are code points from which a new string is
617
 *               created.  The new string's length is equal to the length of
618
 *               the array.
619
 *  * buffer   - A buffer is treated as an UTF-8 sequence and it is decoded
620
 *               into a string.
621
 *  * integer  - An integer is converted to its string representation.
622
 *  * float    - An float is converted to its string representation.
623
 *  * function - If the function is an iterator (a primed generator),
624
 *               subsequent elements are obtained from it and added to the
625
 *               string.  The acceptable types of values returned from the
626
 *               iterator are: number from 0 to 0x1FFFFF inclusive, which
627
 *               is treated as a code point, array of numbers from 0 to
628
 *               0x1FFFFF, each treated as a code point, buffer treated
629
 *               as a UTF-8 sequence and string.  All elements returned
630
 *               by the iterator are concatenated in the order they are
631
 *               returned.
632
 *               If the function is not an iterator, an exception is thrown.
633
 *  * string   - No conversion is performed.
634
 *
635
 * The prototype of `string.prototype` is `object.prototype`.
636
 *
637
 * Examples:
638
 *
639
 *     > string(10.1)
640
 *     "10.1"
641
 *     > string("kos", [108, 97, 110, 103], 32)
642
 *     "koslang32"
643
 */
644
static KOS_OBJ_ID string_constructor(KOS_CONTEXT ctx,
15,111✔
645
                                     KOS_OBJ_ID  this_obj,
646
                                     KOS_OBJ_ID  args_obj)
647
{
648
    int            error      = KOS_SUCCESS;
15,111✔
649
    const uint32_t num_args   = KOS_get_array_size(args_obj);
15,111✔
650
    KOS_LOCAL      args;
651
    KOS_LOCAL      obj;
652
    KOS_LOCAL      codes;
653
    KOS_LOCAL      substrings;
654
    KOS_LOCAL      ret;
655

656
    KOS_init_locals(ctx, &args, &obj, &codes, &substrings, &ret, kos_end_locals);
15,111✔
657

658
    args.o = args_obj;
15,111✔
659

660
    if (num_args == 0)
15,111✔
661
        ret.o = KOS_new_string(ctx, 0, 0);
10✔
662

663
    else {
664

665
        uint32_t i;
666

667
        for (i = 0; i < num_args; i++) {
70,457✔
668
            obj.o = KOS_array_read(ctx, args.o, (int)i);
55,385✔
669
            TRY_OBJID(obj.o);
55,385✔
670

671
            if (IS_NUMERIC_OBJ(obj.o))
55,383✔
672
                obj.o = KOS_object_to_string(ctx, obj.o);
21✔
673

674
            else switch (READ_OBJ_TYPE(obj.o)) {
55,362✔
675

676
                case OBJ_STRING:
55,115✔
677
                    break;
55,115✔
678

679
                case OBJ_ARRAY:
55✔
680
                    obj.o = KOS_new_string_from_codes(ctx, obj.o);
55✔
681
                    break;
55✔
682

683
                case OBJ_BUFFER:
164✔
684
                    obj.o = KOS_new_string_from_buffer(ctx, obj.o, 0, KOS_get_buffer_size(obj.o));
164✔
685
                    break;
164✔
686

687

688
                case OBJ_FUNCTION: {
18✔
689
                    KOS_FUNCTION_STATE state;
690

691
                    if ( ! KOS_is_generator(obj.o, &state))
18✔
692
                        RAISE_EXCEPTION_STR(str_err_cannot_convert_to_string);
11✔
693

694
                    if (IS_BAD_PTR(substrings.o)) {
13✔
695
                        substrings.o = KOS_new_array(ctx, 32);
10✔
696
                        TRY_OBJID(substrings.o);
10✔
697
                    }
698

699
                    TRY(KOS_array_resize(ctx, substrings.o, 0));
13✔
700

701
                    if (state != KOS_GEN_DONE) {
13✔
702

703
                        for (;;) {
14✔
704
                            KOS_TYPE type;
705

706
                            ret.o = KOS_call_generator(ctx, obj.o, KOS_VOID, KOS_EMPTY_ARRAY);
27✔
707
                            if (IS_BAD_PTR(ret.o)) { /* end of iterator */
27✔
708
                                if (KOS_is_exception_pending(ctx))
8✔
709
                                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
710
                                break;
7✔
711
                            }
712

713
                            type = GET_OBJ_TYPE(ret.o);
19✔
714

715
                            switch (type) {
716

717
                                case OBJ_SMALL_INTEGER:
9✔
718
                                    /* fall through */
719
                                case OBJ_INTEGER:
720
                                    /* fall through */
721
                                case OBJ_FLOAT: {
722
                                    int64_t value;
723
                                    TRY(KOS_get_integer(ctx, ret.o, &value));
11✔
724

725
                                    if (value < 0 || value > 0x1FFFFF)
9✔
726
                                        RAISE_EXCEPTION_STR(str_err_invalid_char_code);
2✔
727

728
                                    if (IS_BAD_PTR(codes.o)) {
7✔
729
                                        codes.o = KOS_new_array(ctx, 128);
3✔
730
                                        TRY_OBJID(codes.o);
3✔
731
                                        TRY(KOS_array_resize(ctx, codes.o, 0));
3✔
732
                                    }
733

734
                                    TRY(KOS_array_push(ctx, codes.o, TO_SMALL_INT((int)value), KOS_NULL));
7✔
735
                                    break;
7✔
736
                                }
737

738
                                case OBJ_ARRAY:
7✔
739
                                    /* fall through */
740
                                case OBJ_STRING:
741
                                    /* fall through */
742
                                case OBJ_BUFFER:
743

744
                                    if ( ! IS_BAD_PTR(codes.o) && KOS_get_array_size(codes.o)) {
7✔
745
                                        KOS_OBJ_ID str = KOS_new_string_from_codes(ctx, codes.o);
1✔
746
                                        TRY_OBJID(str);
1✔
747

748
                                        TRY(KOS_array_push(ctx, substrings.o, str, KOS_NULL));
1✔
749

750
                                        TRY(KOS_array_resize(ctx, codes.o, 0));
1✔
751
                                    }
752

753
                                    if (type == OBJ_ARRAY) {
7✔
754
                                        ret.o = KOS_new_string_from_codes(ctx, ret.o);
3✔
755
                                        TRY_OBJID(ret.o);
3✔
756
                                    }
757
                                    else if (type == OBJ_BUFFER) {
4✔
758
                                        ret.o = KOS_new_string_from_buffer(ctx, ret.o, 0, KOS_get_buffer_size(ret.o));
1✔
759
                                        TRY_OBJID(ret.o);
1✔
760
                                    }
761

762
                                    TRY(KOS_array_push(ctx, substrings.o, ret.o, KOS_NULL));
7✔
763
                                    break;
7✔
764

765
                                default:
3✔
766
                                    RAISE_EXCEPTION_STR(str_err_cannot_convert_to_string);
3✔
767
                            }
768
                        }
769

770
                        if ( ! IS_BAD_PTR(codes.o) && KOS_get_array_size(codes.o)) {
7✔
771
                            KOS_OBJ_ID str = KOS_new_string_from_codes(ctx, codes.o);
2✔
772
                            TRY_OBJID(str);
2✔
773

774
                            TRY(KOS_array_push(ctx, substrings.o, str, KOS_NULL));
2✔
775

776
                            TRY(KOS_array_resize(ctx, codes.o, 0));
2✔
777
                        }
778

779
                        obj.o = KOS_string_add(ctx, substrings.o);
7✔
780
                    }
781
                    break;
7✔
782
                }
783

784
                default:
10✔
785
                    RAISE_EXCEPTION_STR(str_err_cannot_convert_to_string);
10✔
786
            }
787

788
            TRY_OBJID(obj.o);
55,362✔
789

790
            TRY(KOS_array_write(ctx, args.o, (int)i, obj.o));
55,356✔
791
        }
792

793
        if (i == num_args)
15,072✔
794
            ret.o = KOS_string_add(ctx, args.o);
15,072✔
795
    }
796

797
cleanup:
×
798
    ret.o = KOS_destroy_top_locals(ctx, &args, &ret);
15,111✔
799

800
    return error ? KOS_BADPTR : ret.o;
15,111✔
801
}
802

803
/* @item base stringify()
804
 *
805
 *     stringify(args...)
806
 *
807
 * Converts values to human-readable string representation.
808
 *
809
 * Returns a new string created from converting all arguments to strings
810
 * and concatenating them.
811
 *
812
 * If no arguments are provided, returns an empty string.
813
 *
814
 * `stringify()` is implicitly invoked during string interpolation, so
815
 * the result of `stringify()` is the same as the result of string
816
 * interpolation.
817
 *
818
 * String arguments are treated literally without any conversion.
819
 *
820
 * Integer, float, boolean and void arguments are converted to their
821
 * string representation, which is the same as in source code.
822
 *
823
 * Array and object arguments are converted to a human-readable representation
824
 * similar to their apperance in source code.  Strings inside arrays
825
 * and objects are double-quoted.
826
 *
827
 * Buffer arguments are converted to the form of `<xx xx ...>`, where `xx` are
828
 * two hexadecimal digits representing every byte in the buffer.
829
 *
830
 * Function arguments are converted to the form of `<function nnn @ xxx>`,
831
 * where `nnn` is the function name and `xxx` is the bytecode offset of the
832
 * function's entry point.
833
 *
834
 * Example:
835
 *
836
 *     > stringify(true, "true", 42, [10, "str"])
837
 *     "truetrue42[10, str]"
838
 */
839
static KOS_OBJ_ID stringify(KOS_CONTEXT ctx,
1,999✔
840
                            KOS_OBJ_ID  this_obj,
841
                            KOS_OBJ_ID  args_obj)
842
{
843
    int            error    = KOS_SUCCESS;
1,999✔
844
    const uint32_t num_args = KOS_get_array_size(args_obj);
1,999✔
845
    KOS_OBJ_ID     ret      = KOS_BADPTR;
1,999✔
846
    KOS_LOCAL      args;
847

848
    KOS_init_local_with(ctx, &args, args_obj);
1,999✔
849

850
    if (num_args == 0)
1,999✔
851
        ret = KOS_new_string(ctx, KOS_NULL, 0);
1✔
852

853
    else {
854

855
        uint32_t i;
856

857
        for (i = 0; i < num_args; i++) {
8,306✔
858

859
            KOS_OBJ_ID obj = KOS_array_read(ctx, args.o, (int)i);
6,358✔
860
            TRY_OBJID(obj);
6,358✔
861

862
            obj = KOS_object_to_string(ctx, obj);
6,346✔
863
            TRY_OBJID(obj);
6,346✔
864

865
            TRY(KOS_array_write(ctx, args.o, (int)i, obj));
6,308✔
866
        }
867

868
        if (i == num_args)
1,948✔
869
            ret = KOS_string_add(ctx, args.o);
1,948✔
870
    }
871

872
cleanup:
×
873
    KOS_destroy_top_local(ctx, &args);
1,999✔
874

875
    return error ? KOS_BADPTR : ret;
1,999✔
876
}
877

878
/* @item base object()
879
 *
880
 *     object()
881
 *     object(iterable)
882
 *     object(keys, values)
883
 *
884
 * Object type class.
885
 *
886
 * If no arguments are provided, returns a new empty object.  Equivalent to empty object literal `{}`.
887
 *
888
 * If one argument is provided - `iterable`, it is iterated over and elements extracted from
889
 * it must be key-value pairs.  The new object is then filled with these keys and values.
890
 * For example, `iterable` can be an array of 2-element arrays, another object or a generator
891
 * which yields key-balue pairs.
892
 *
893
 * If two arguments are provided, subsequent values extracted from `keys` and `values` are used
894
 * to create properties of the new object.  Both `key` and `values` can be generators.  The values
895
 * extracted from `keys` must be strings.
896
 *
897
 * `object.prototype` is directly or indirectly the prototype for all object types.
898
 *
899
 * Examples:
900
 *
901
 *     > object()
902
 *     {}
903
 *     > object([["a", 1], ["b", 2], ["c", 3]])
904
 *     {"a": 1, "b": 2, "c": 3}
905
 *     > object(["a", "b", "c"], [1, 2, 3])
906
 *     {"a": 1, "b": 2, "c": 3}
907
 */
908
static const KOS_CONVERT object_args[3] = {
909
    KOS_DEFINE_OPTIONAL_ARG(str_keys,   KOS_VOID),
910
    KOS_DEFINE_OPTIONAL_ARG(str_values, KOS_VOID),
911
    KOS_DEFINE_TAIL_ARG()
912
};
913

914
static KOS_OBJ_ID object_constructor(KOS_CONTEXT ctx,
34✔
915
                                     KOS_OBJ_ID  this_obj,
916
                                     KOS_OBJ_ID  args_obj)
917
{
918
    KOS_LOCAL keys;
919
    KOS_LOCAL values;
920
    KOS_LOCAL key;
921
    KOS_LOCAL elem;
922
    KOS_LOCAL obj;
923
    int       error = KOS_SUCCESS;
34✔
924

925
    assert(KOS_get_array_size(args_obj) >= 2);
34✔
926

927
    KOS_init_locals(ctx, &keys, &values, &key, &elem, &obj, kos_end_locals);
34✔
928

929
    values.o = args_obj;
34✔
930

931
    keys.o = KOS_array_read(ctx, args_obj, 0);
34✔
932
    TRY_OBJID(keys.o);
34✔
933

934
    values.o = KOS_array_read(ctx, values.o, 1);
34✔
935
    TRY_OBJID(values.o);
34✔
936

937
    obj.o = KOS_new_object(ctx);
34✔
938
    TRY_OBJID(obj.o);
34✔
939

940
    if (values.o == KOS_VOID) {
34✔
941
        keys.o = KOS_new_iterator(ctx, keys.o, KOS_CONTENTS);
28✔
942
        TRY_OBJID(keys.o);
28✔
943

944
        while ( ! KOS_iterator_next(ctx, keys.o)) {
36✔
945

946
            assert( ! IS_BAD_PTR(KOS_get_walk_key(keys.o)));
23✔
947
            assert( ! IS_BAD_PTR(KOS_get_walk_value(keys.o)));
23✔
948

949
            elem.o = KOS_get_walk_key(keys.o);
23✔
950

951
            if (GET_OBJ_TYPE(elem.o) == OBJ_STRING)
23✔
952
                TRY(KOS_set_property(ctx, obj.o, elem.o, KOS_get_walk_value(keys.o)));
3✔
953
            else {
954
                int64_t idx;
955

956
                if (GET_OBJ_TYPE(elem.o) > OBJ_INTEGER)
20✔
957
                    RAISE_EXCEPTION_STR(str_err_object_arg_not_iterable);
15✔
958

959
                TRY(KOS_get_integer(ctx, elem.o, &idx));
19✔
960

961
                elem.o = KOS_get_walk_value(keys.o);
19✔
962

963
                if (GET_OBJ_TYPE(elem.o) != OBJ_ARRAY) {
19✔
964
                    KOS_raise_printf(ctx,
8✔
965
                                     "element %" PRId64 " passed to object class is %s, but expected array",
966
                                     idx,
967
                                     KOS_get_type_name(GET_OBJ_TYPE(elem.o)));
8✔
968
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
8✔
969
                }
970

971
                if (KOS_get_array_size(elem.o) < 2) {
11✔
972
                    KOS_raise_printf(ctx,
4✔
973
                                     "element %" PRId64 " is an array with %u element%s, but expected 2 elements",
974
                                     idx,
975
                                     KOS_get_array_size(elem.o),
4✔
976
                                     KOS_get_array_size(elem.o) == 0 ? "s" : "");
4✔
977
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
4✔
978
                }
979

980
                key.o = KOS_array_read(ctx, elem.o, 0);
7✔
981
                TRY_OBJID(key.o);
7✔
982

983
                elem.o = KOS_array_read(ctx, elem.o, 1);
7✔
984
                TRY_OBJID(elem.o);
7✔
985

986
                if (GET_OBJ_TYPE(key.o) != OBJ_STRING) {
7✔
987
                    KOS_raise_printf(ctx,
2✔
988
                                     "element %" PRId64 " has key which is %s, but expected string",
989
                                     idx,
990
                                     KOS_get_type_name(GET_OBJ_TYPE(key.o)));
2✔
991
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
992
                }
993

994
                TRY(KOS_set_property(ctx, obj.o, key.o, elem.o));
5✔
995
            }
996
        }
997

998
        if (KOS_is_exception_pending(ctx))
13✔
999
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
1000
    }
1001
    else {
1002

1003
        const KOS_TYPE type = GET_OBJ_TYPE(keys.o);
6✔
1004

1005
        if ((type != OBJ_ARRAY) && (type != OBJ_STRING) && (type != OBJ_FUNCTION)) {
6✔
1006
            KOS_raise_printf(ctx,
2✔
1007
                             "'keys' argument is %s, unable to extract object keys",
1008
                             KOS_get_type_name(type));
1009
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
1010
        }
1011

1012
        keys.o = KOS_new_iterator(ctx, keys.o, KOS_CONTENTS);
4✔
1013
        TRY_OBJID(keys.o);
4✔
1014

1015
        values.o = KOS_new_iterator(ctx, values.o, KOS_CONTENTS);
4✔
1016
        TRY_OBJID(values.o);
4✔
1017

1018
        while ( ! KOS_iterator_next(ctx, keys.o) && ! KOS_iterator_next(ctx, values.o)) {
11✔
1019

1020
            key.o = KOS_get_walk_value(keys.o);
7✔
1021

1022
            elem.o = KOS_get_walk_key(values.o);
7✔
1023

1024
            if (GET_OBJ_TYPE(elem.o) == OBJ_STRING) {
7✔
1025
                elem.o = KOS_new_array(ctx, 2);
1✔
1026
                TRY_OBJID(elem.o);
1✔
1027

1028
                TRY(KOS_array_write(ctx, elem.o, 0, KOS_get_walk_key(values.o)));
1✔
1029
                TRY(KOS_array_write(ctx, elem.o, 1, KOS_get_walk_value(values.o)));
1✔
1030
            }
1031
            else
1032
                elem.o = KOS_get_walk_value(values.o);
6✔
1033

1034
            TRY(KOS_set_property(ctx, obj.o, key.o, elem.o));
7✔
1035
        }
1036

1037
        if (KOS_is_exception_pending(ctx))
4✔
1038
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
1039
    }
1040

1041
cleanup:
3✔
1042
    obj.o = KOS_destroy_top_locals(ctx, &keys, &obj);
34✔
1043

1044
    return error ? KOS_BADPTR : obj.o;
34✔
1045
}
1046

1047
static int make_room_in_array(KOS_CONTEXT ctx,
12✔
1048
                              KOS_OBJ_ID *array_obj_ptr,
1049
                              uint32_t    size)
1050
{
1051
    if (size) {
12✔
1052

1053
        KOS_OBJ_ID array_obj = *array_obj_ptr;
12✔
1054

1055
        if (IS_BAD_PTR(array_obj)) {
12✔
1056

1057
            array_obj = KOS_new_array(ctx, size);
8✔
1058
            if (IS_BAD_PTR(array_obj))
8✔
1059
                return KOS_ERROR_EXCEPTION;
×
1060

1061
            *array_obj_ptr = array_obj;
8✔
1062
        }
1063
        else
1064
            return KOS_array_resize(ctx, array_obj, KOS_get_array_size(array_obj) + size);
4✔
1065
    }
1066

1067
    return KOS_SUCCESS;
8✔
1068
}
1069

1070
/* @item base array()
1071
 *
1072
 *     array(size = 0, value = void)
1073
 *     array(arg, ...)
1074
 *
1075
 * Array type class.
1076
 *
1077
 * The first variant constructs an array of the specified size.  `size` defaults
1078
 * to 0.  `value` is the value to fill the array with if `size` is greater than
1079
 * 0.
1080
 *
1081
 * The second variant constructs an from one or more non-numeric objects.
1082
 * Each of these input arguments is converted to an array and the resulting
1083
 * arrays are concatenated, producing the final array, which is returned
1084
 * by the class.  The following argument types are supported:
1085
 *
1086
 *  * array    - The array is simply appended to the new array without conversion.
1087
 *               This can be used to make a copy of an array.
1088
 *  * buffer   - Buffer's bytes are appended to the new array as integers.
1089
 *  * function - If the function is an iterator (a primed generator), subsequent
1090
 *               elements are obtained from it and appended to the array.
1091
 *               For non-iterator functions an exception is thrown.
1092
 *  * object   - Object's elements are extracted using shallow operation, i.e.
1093
 *               without traversing its prototypes, then subsequent properties
1094
 *               are appended to the array as two-element arrays containing
1095
 *               the property name (key) and property value.
1096
 *  * string   - All characters in the string are converted to code points (integers)
1097
 *               and then each code point is subsequently appended to the new array.
1098
 *
1099
 * The prototype of `array.prototype` is `object.prototype`.
1100
 *
1101
 * Examples:
1102
 *
1103
 *     > array()
1104
 *     []
1105
 *     > array(3, "abc")
1106
 *     ["abc", "abc", "abc"]
1107
 *     > array("hello")
1108
 *     [104, 101, 108, 108, 111]
1109
 *     > array(range(5))
1110
 *     [0, 1, 2, 3, 4]
1111
 *     > array({ one: 1, two: 2, three: 3 })
1112
 *     [["one", 1], ["two", 2], ["three", 3]]
1113
 */
1114
static KOS_OBJ_ID array_constructor(KOS_CONTEXT ctx,
121,567✔
1115
                                    KOS_OBJ_ID  this_obj,
1116
                                    KOS_OBJ_ID  args_obj)
1117
{
1118
    int            error      = KOS_SUCCESS;
121,567✔
1119
    const uint32_t num_args   = KOS_get_array_size(args_obj);
121,567✔
1120
    uint32_t       i_arg      = 0;
121,567✔
1121
    uint32_t       cur_size   = 0;
121,567✔
1122
    KOS_LOCAL      args;
1123
    KOS_LOCAL      arg;
1124
    KOS_LOCAL      gen_args;
1125
    KOS_LOCAL      walk;
1126
    KOS_LOCAL      walk_val;
1127
    KOS_LOCAL      gen_ret;
1128
    KOS_LOCAL      ret;
1129

1130
    if (num_args == 0)
121,567✔
1131
        return KOS_new_array(ctx, 0);
3✔
1132

1133
    KOS_init_locals(ctx, &args, &arg, &gen_args, &walk, &walk_val, &gen_ret, &ret, kos_end_locals);
121,564✔
1134

1135
    args.o = args_obj;
121,564✔
1136

1137
    arg.o = KOS_array_read(ctx, args.o, 0);
121,564✔
1138
    TRY_OBJID(arg.o);
121,564✔
1139

1140
    if (num_args < 3 && IS_NUMERIC_OBJ(arg.o)) {
121,564✔
1141

1142
        int64_t size;
1143

1144
        TRY(KOS_get_integer(ctx, arg.o, &size));
121,539✔
1145

1146
        if (size < 0 || size > INT_MAX)
121,539✔
1147
            RAISE_EXCEPTION_STR(str_err_invalid_array_size);
4✔
1148

1149
        if (num_args == 2) {
121,535✔
1150
            arg.o = KOS_array_read(ctx, args.o, 1);
25✔
1151
            TRY_OBJID(arg.o);
25✔
1152

1153
            ++i_arg;
25✔
1154
        }
1155

1156
        assert(IS_BAD_PTR(ret.o));
121,535✔
1157

1158
        ret.o = KOS_new_array(ctx, (uint32_t)size);
121,535✔
1159
        TRY_OBJID(ret.o);
121,535✔
1160

1161
        if (size && num_args == 2)
121,532✔
1162
            TRY(KOS_array_fill(ctx, ret.o, 0, size, arg.o));
18✔
1163

1164
        goto cleanup;
121,532✔
1165
    }
1166

1167
    do {
1168

1169
        if (i_arg) {
32✔
1170
            arg.o = KOS_array_read(ctx, args.o, (int)i_arg);
7✔
1171
            TRY_OBJID(arg.o);
7✔
1172
        }
1173

1174
        switch (GET_OBJ_TYPE(arg.o)) {
32✔
1175

1176
            case OBJ_ARRAY: {
7✔
1177
                const uint32_t src_size = KOS_get_array_size(arg.o);
7✔
1178

1179
                if (src_size) {
7✔
1180

1181
                    TRY(make_room_in_array(ctx, &ret.o, src_size));
6✔
1182

1183
                    TRY(KOS_array_insert(ctx, ret.o, cur_size, cur_size + src_size,
6✔
1184
                                         arg.o, 0, src_size));
1185

1186
                    cur_size += src_size;
6✔
1187
                }
1188
                break;
7✔
1189
            }
1190

1191
            case OBJ_STRING: {
6✔
1192
                const uint32_t str_len = KOS_get_string_length(arg.o);
6✔
1193

1194
                if (str_len) {
6✔
1195

1196
                    uint32_t i;
1197

1198
                    TRY(make_room_in_array(ctx, &ret.o, str_len));
4✔
1199

1200
                    for (i = 0; i < str_len; i++) {
22✔
1201

1202
                        const unsigned ch_code = KOS_string_get_char_code(ctx, arg.o, i);
18✔
1203

1204
                        KOS_OBJ_ID value = KOS_new_int(ctx, (int64_t)ch_code);
18✔
1205
                        TRY_OBJID(value);
18✔
1206

1207
                        TRY(KOS_array_write(ctx, ret.o, cur_size + i, value));
18✔
1208
                    }
1209

1210
                    cur_size += str_len;
4✔
1211
                }
1212
                break;
6✔
1213
            }
1214

1215
            case OBJ_BUFFER: {
4✔
1216
                const uint32_t buf_size = KOS_get_buffer_size(arg.o);
4✔
1217

1218
                if (buf_size) {
4✔
1219

1220
                    uint32_t i;
1221

1222
                    TRY(make_room_in_array(ctx, &ret.o, buf_size));
2✔
1223

1224
                    for (i = 0; i < buf_size; i++) {
14✔
1225

1226
                        const uint8_t value = KOS_buffer_data_const(arg.o)[i];
12✔
1227

1228
                        TRY(KOS_array_write(ctx, ret.o,
12✔
1229
                                            cur_size + i, TO_SMALL_INT((int)value)));
1230
                    }
1231

1232
                    cur_size += buf_size;
2✔
1233
                }
1234
                break;
4✔
1235
            }
1236

1237
            case OBJ_OBJECT: {
3✔
1238
                walk.o = KOS_new_iterator(ctx, arg.o, KOS_SHALLOW);
3✔
1239
                TRY_OBJID(walk.o);
3✔
1240

1241
                while ( ! KOS_iterator_next(ctx, walk.o)) {
20✔
1242

1243
                    walk_val.o = KOS_get_walk_value(walk.o);
17✔
1244

1245
                    assert( ! IS_BAD_PTR(KOS_get_walk_key(walk.o)));
17✔
1246
                    assert( ! IS_BAD_PTR(walk_val.o));
17✔
1247

1248
                    if (GET_OBJ_TYPE(walk_val.o) == OBJ_DYNAMIC_PROP) {
17✔
1249
                        walk_val.o = KOS_call_function(ctx,
1✔
1250
                                                       OBJPTR(DYNAMIC_PROP, walk_val.o)->getter,
1251
                                                       OBJPTR(ITERATOR, walk.o)->obj,
1252
                                                       KOS_EMPTY_ARRAY);
1253
                        if (IS_BAD_PTR(walk_val.o)) {
1✔
1254
                            assert(KOS_is_exception_pending(ctx));
1✔
1255
                            KOS_clear_exception(ctx);
1✔
1256

1257
                            walk_val.o = OBJPTR(DYNAMIC_PROP, KOS_get_walk_value(walk.o))->getter;
1✔
1258
                        }
1259
                    }
1260

1261
                    gen_args.o = KOS_new_array(ctx, 2);
17✔
1262
                    TRY_OBJID(gen_args.o);
17✔
1263

1264
                    TRY(KOS_array_write(ctx, gen_args.o, 0, KOS_get_walk_key(walk.o)));
17✔
1265
                    TRY(KOS_array_write(ctx, gen_args.o, 1, walk_val.o));
17✔
1266

1267
                    if (IS_BAD_PTR(ret.o)) {
17✔
1268
                        ret.o = KOS_new_array(ctx, 1);
3✔
1269
                        TRY_OBJID(ret.o);
3✔
1270

1271
                        TRY(KOS_array_write(ctx, ret.o, 0, gen_args.o));
3✔
1272
                    }
1273
                    else
1274
                        TRY(KOS_array_push(ctx, ret.o, gen_args.o, KOS_NULL));
14✔
1275

1276
                    ++cur_size;
17✔
1277
                }
1278

1279
                if (KOS_is_exception_pending(ctx))
3✔
1280
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1281
                break;
3✔
1282
            }
1283

1284
            case OBJ_FUNCTION: {
9✔
1285
                KOS_FUNCTION_STATE state;
1286

1287
                if ( ! KOS_is_generator(arg.o, &state))
9✔
1288
                    RAISE_EXCEPTION_STR(str_err_cannot_convert_to_array);
2✔
1289

1290
                if (state != KOS_GEN_DONE) {
8✔
1291

1292
                    for (;;) {
1293

1294
                        gen_ret.o = KOS_call_generator(ctx, arg.o, KOS_VOID, KOS_EMPTY_ARRAY);
24✔
1295
                        if (IS_BAD_PTR(gen_ret.o)) { /* end of iterator */
24✔
1296
                            if (KOS_is_exception_pending(ctx))
8✔
1297
                                RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
1298
                            break;
7✔
1299
                        }
1300

1301
                        if (IS_BAD_PTR(ret.o)) {
16✔
1302
                            ret.o = KOS_new_array(ctx, 1);
6✔
1303
                            TRY_OBJID(ret.o);
6✔
1304

1305
                            TRY(KOS_array_write(ctx, ret.o, 0, gen_ret.o));
6✔
1306
                        }
1307
                        else
1308
                            TRY(KOS_array_push(ctx, ret.o, gen_ret.o, KOS_NULL));
10✔
1309

1310
                        gen_ret.o = KOS_BADPTR;
16✔
1311

1312
                        ++cur_size;
16✔
1313
                    }
1314
                }
1315
                break;
7✔
1316
            }
1317

1318
            default:
3✔
1319
                RAISE_EXCEPTION_STR(str_err_cannot_convert_to_array);
3✔
1320
        }
1321

1322
        ++i_arg;
27✔
1323

1324
    } while (i_arg < num_args);
27✔
1325

1326
    if ( ! cur_size) {
20✔
1327
        assert(IS_BAD_PTR(ret.o));
4✔
1328
        ret.o = KOS_new_array(ctx, 0);
4✔
1329
    }
1330

1331
cleanup:
16✔
1332
    ret.o = KOS_destroy_top_locals(ctx, &args, &ret);
121,564✔
1333

1334
    return error ? KOS_BADPTR : ret.o;
121,564✔
1335
}
1336

1337
/* @item base buffer()
1338
 *
1339
 *     buffer(size = 0, value = 0)
1340
 *     buffer(arg, ...)
1341
 *
1342
 * Buffer type class.
1343
 *
1344
 * The first variant constructs a buffer of the specified size.  `size` defaults
1345
 * to 0.  `value` is the value to fill the buffer with is `size` is greater than
1346
 * 0.  `value` must be a number from 0 to 255 (floor operation is applied to
1347
 * floats), it defaults to 0 if it's not specified.
1348
 *
1349
 * The second variant constructs a buffer from one or more non-numeric objects.
1350
 * Each of these input arguments is converted to a buffer and the resulting
1351
 * buffers are concatenated, producing the final buffer, which is returned
1352
 * by the class.  The following argument types are supported:
1353
 *
1354
 *  * array    - The array must contain numbers from 0 to 255 (floor operation
1355
 *               is applied to floats).  Any other array elements trigger an
1356
 *               exception.  The array is converted to a buffer containing
1357
 *               bytes with values from the array.
1358
 *  * buffer   - A buffer is simply concatenated with other input arguments without
1359
 *               any transformation.
1360
 *               This can be used to make a copy of a buffer.
1361
 *  * function - If the function is an iterator (a primed generator), subsequent
1362
 *               elements are obtained from it and added to the buffer.  The
1363
 *               values returned by the iterator must be numbers from 0 to 255
1364
 *               (floor operation is applied to floats), any other values trigger
1365
 *               an exception.
1366
 *               For non-iterator functions an exception is thrown.
1367
 *  * string   - The string is converted to an UTF-8 representation stored
1368
 *               into a buffer.
1369
 *
1370
 * The prototype of `buffer.prototype` is `object.prototype`.
1371
 *
1372
 * Examples:
1373
 *
1374
 *     > buffer()
1375
 *     <>
1376
 *     > buffer(5)
1377
 *     <00 00 00 00 00>
1378
 *     > buffer("hello")
1379
 *     <68 65 6c 6c 6f>
1380
 *     > buffer(range(4))
1381
 *     <00 01 02 03>
1382
 */
1383
static KOS_OBJ_ID buffer_constructor(KOS_CONTEXT ctx,
1,599✔
1384
                                     KOS_OBJ_ID  this_obj,
1385
                                     KOS_OBJ_ID  args_obj)
1386
{
1387
    int            error    = KOS_SUCCESS;
1,599✔
1388
    const uint32_t num_args = KOS_get_array_size(args_obj);
1,599✔
1389
    uint32_t       i_arg;
1390
    KOS_LOCAL      args;
1391
    KOS_LOCAL      arg;
1392
    KOS_LOCAL      buffer;
1393

1394
    KOS_init_locals(ctx, &args, &arg, &buffer, kos_end_locals);
1,599✔
1395

1396
    args.o = args_obj;
1,599✔
1397

1398
    buffer.o = KOS_new_buffer(ctx, 0);
1,599✔
1399
    TRY_OBJID(buffer.o);
1,598✔
1400

1401
    for (i_arg = 0; i_arg < num_args; i_arg++) {
3,031✔
1402

1403
        const uint32_t cur_size = KOS_get_buffer_size(buffer.o);
1,500✔
1404

1405
        arg.o = KOS_array_read(ctx, args.o, (int)i_arg);
1,500✔
1406
        TRY_OBJID(arg.o);
1,501✔
1407

1408
        if (i_arg == 0 && num_args < 3 && IS_NUMERIC_OBJ(arg.o)) {
1,499✔
1409
            int64_t size;
1410
            int64_t value = 0;
1,193✔
1411

1412
            TRY(KOS_get_integer(ctx, arg.o, &size));
1,242✔
1413

1414
            if (size < 0 || size > INT_MAX)
1,193✔
1415
                RAISE_EXCEPTION_STR(str_err_invalid_buffer_size);
1✔
1416

1417
            if (num_args == 2) {
1,192✔
1418
                arg.o = KOS_array_read(ctx, args.o, 1);
5✔
1419
                TRY_OBJID(arg.o);
5✔
1420

1421
                TRY(KOS_get_integer(ctx, arg.o, &value));
5✔
1422

1423
                if (value < 0 || value > 255)
5✔
1424
                    RAISE_EXCEPTION_STR(str_err_cannot_convert_to_buffer);
2✔
1425

1426
                ++i_arg;
3✔
1427
            }
1428

1429
            if (size) {
1,190✔
1430
                uint8_t *buf;
1431

1432
                TRY(KOS_buffer_resize(ctx, buffer.o, (uint32_t)size));
1,189✔
1433

1434
                buf = KOS_buffer_data_volatile(ctx, buffer.o);
1,143✔
1435
                if ( ! buf)
1,143✔
1436
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1437

1438
                memset(buf, (int)value, (size_t)size);
1,143✔
1439
            }
1440

1441
            continue;
1,144✔
1442
        }
1443

1444
        switch (GET_OBJ_TYPE(arg.o)) {
306✔
1445

1446
            case OBJ_ARRAY: {
279✔
1447
                const uint32_t size = KOS_get_array_size(arg.o);
279✔
1448
                uint32_t       i;
1449
                uint8_t       *data = KOS_NULL;
279✔
1450

1451
                if ( ! size)
279✔
1452
                    break;
5✔
1453

1454
                TRY(KOS_buffer_resize(ctx, buffer.o, cur_size + size));
274✔
1455

1456
                data = KOS_buffer_data_volatile(ctx, buffer.o);
272✔
1457
                if ( ! data)
272✔
1458
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1459
                data += cur_size;
272✔
1460

1461
                for (i = 0; i < size; i++) {
913✔
1462
                    int64_t value;
1463

1464
                    KOS_OBJ_ID elem = KOS_array_read(ctx, arg.o, i);
647✔
1465
                    TRY_OBJID(elem);
649✔
1466

1467
                    TRY(KOS_get_integer(ctx, elem, &value));
643✔
1468

1469
                    if (value < 0 || value > 255)
643✔
1470
                        RAISE_EXCEPTION_STR(str_err_invalid_byte_value);
2✔
1471

1472
                    *(data++) = (uint8_t)(uint64_t)value;
641✔
1473
                }
1474
                break;
266✔
1475
            }
1476

1477
            case OBJ_STRING: {
11✔
1478
                const uint32_t size = KOS_string_to_utf8(arg.o, KOS_NULL, 0);
11✔
1479
                uint8_t       *data;
1480

1481
                if (size == ~0U)
11✔
1482
                    RAISE_EXCEPTION_STR(str_err_invalid_string);
1✔
1483

1484
                TRY(KOS_buffer_resize(ctx, buffer.o, cur_size + size));
10✔
1485

1486
                data = KOS_buffer_data_volatile(ctx, buffer.o);
10✔
1487
                if ( ! data)
10✔
1488
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1489
                data += cur_size;
10✔
1490

1491
                KOS_string_to_utf8(arg.o, data, size);
10✔
1492
                break;
10✔
1493
            }
1494

1495
            case OBJ_BUFFER: {
2✔
1496
                const uint32_t size = KOS_get_buffer_size(arg.o);
2✔
1497
                uint8_t       *data;
1498

1499
                TRY(KOS_buffer_resize(ctx, buffer.o, cur_size + size));
2✔
1500

1501
                data = KOS_buffer_data_volatile(ctx, buffer.o);
2✔
1502
                if ( ! data)
2✔
1503
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1504
                data += cur_size;
2✔
1505

1506
                memcpy(data, KOS_buffer_data_const(arg.o), size);
2✔
1507
                break;
2✔
1508
            }
1509

1510
            case OBJ_FUNCTION: {
10✔
1511
                KOS_FUNCTION_STATE state;
1512

1513
                if ( ! KOS_is_generator(arg.o, &state))
10✔
1514
                    RAISE_EXCEPTION_STR(str_err_cannot_convert_to_buffer);
2✔
1515

1516
                if (state != KOS_GEN_DONE) {
9✔
1517
                    uint32_t size     = cur_size;
9✔
1518
                    uint32_t capacity = cur_size;
9✔
1519

1520
                    if (cur_size < 64) {
9✔
1521
                        TRY(KOS_buffer_resize(ctx, buffer.o, 64));
9✔
1522
                        capacity = 64;
9✔
1523
                    }
1524

1525
                    for (;;) {
2,831✔
1526
                        int64_t  value;
1527
                        uint8_t *data;
1528

1529
                        KOS_OBJ_ID ret_val = KOS_call_generator(ctx, arg.o, KOS_VOID, KOS_EMPTY_ARRAY);
2,840✔
1530
                        if (IS_BAD_PTR(ret_val)) { /* end of iterator */
2,840✔
1531
                            if (KOS_is_exception_pending(ctx))
8✔
1532
                                RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
1533
                            break;
8✔
1534
                        }
1535

1536
                        TRY(KOS_get_integer(ctx, ret_val, &value));
2,832✔
1537

1538
                        if (value < 0 || value > 255)
2,832✔
1539
                            RAISE_EXCEPTION_STR(str_err_invalid_byte_value);
1✔
1540

1541
                        if (size >= capacity) {
2,831✔
1542
                            capacity *= 2;
13✔
1543
                            TRY(KOS_buffer_resize(ctx, buffer.o, capacity));
13✔
1544
                        }
1545

1546
                        data = KOS_buffer_data_volatile(ctx, buffer.o);
2,831✔
1547
                        if ( ! data)
2,831✔
1548
                            RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1549
                        data += size;
2,831✔
1550

1551
                        *data = (uint8_t)(uint64_t)value;
2,831✔
1552
                        ++size;
2,831✔
1553
                    }
1554

1555
                    TRY(KOS_buffer_resize(ctx, buffer.o, size));
8✔
1556
                }
1557
                break;
8✔
1558
            }
1559

1560
            default:
4✔
1561
                RAISE_EXCEPTION_STR(str_err_cannot_convert_to_buffer);
4✔
1562
        }
1563
    }
1564

1565
cleanup:
1,531✔
1566
    buffer.o = KOS_destroy_top_locals(ctx, &args, &buffer);
1,599✔
1567

1568
    return error ? KOS_BADPTR : buffer.o;
1,599✔
1569
}
1570

1571
/* @item base function()
1572
 *
1573
 *     function(func)
1574
 *
1575
 * Function type class.
1576
 *
1577
 * The argument is a function object.
1578
 *
1579
 *  * For regular functions, returns the same function object which was
1580
 *    passed.
1581
 *  * For classes (constuctor functions), returns a copy of
1582
 *    the function object without copying any properties,
1583
 *    not even the prototype.
1584
 *  * For generator functions (not instantiated), returns the same generator
1585
 *    function which was passed.
1586
 *  * For instantiated generator functions (iterators), returns a copy of
1587
 *    the generator function object, uninstantiated.
1588
 *
1589
 * Throws an exception if the argument is not a function.
1590
 *
1591
 * The prototype of `function.prototype` is `object.prototype`.
1592
 */
1593
static KOS_OBJ_ID function_constructor(KOS_CONTEXT ctx,
12✔
1594
                                       KOS_OBJ_ID  this_obj,
1595
                                       KOS_OBJ_ID  args_obj)
1596
{
1597
    KOS_OBJ_ID ret = KOS_BADPTR;
12✔
1598

1599
    if (KOS_get_array_size(args_obj) != 1)
12✔
1600
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
1✔
1601

1602
    else {
1603

1604
        ret = KOS_array_read(ctx, args_obj, 0);
11✔
1605
        if ( ! IS_BAD_PTR(ret)) {
11✔
1606
            const KOS_TYPE type = GET_OBJ_TYPE(ret);
11✔
1607

1608
            if (type != OBJ_FUNCTION && type != OBJ_CLASS) {
11✔
1609
                KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
5✔
1610
                ret = KOS_BADPTR;
5✔
1611
            }
1612
            else {
1613
                const KOS_FUNCTION_STATE state = (KOS_FUNCTION_STATE)
1614
                    KOS_atomic_read_relaxed_u32(OBJPTR(FUNCTION, ret)->state);
6✔
1615

1616
                switch (state) {
6✔
1617

1618
                    case KOS_FUN:
3✔
1619
                        /* fall through */
1620
                    case KOS_GEN_INIT:
1621
                        break;
3✔
1622

1623
                    default:
3✔
1624
                        ret = kos_copy_function(ctx, ret);
3✔
1625

1626
                        if (state > KOS_GEN_INIT && ! IS_BAD_PTR(ret))
3✔
1627
                            OBJPTR(FUNCTION, ret)->state = KOS_GEN_INIT;
1✔
1628
                }
1629
            }
1630
        }
1631
    }
1632

1633
    return ret;
12✔
1634
}
1635

1636
/* @item base class()
1637
 *
1638
 *     class(func)
1639
 *
1640
 * Class type class.
1641
 *
1642
 * Because `class` is a keyword, this class can only be referenced
1643
 * indirectly via the base module, it cannot be referenced if it is imported
1644
 * directly into the current module.
1645
 *
1646
 * Returns a copy of the `func` class object without copying any properties,
1647
 * not even the prototype.
1648
 *
1649
 * Throws an exception if the `func` argument is not a class.
1650
 *
1651
 * The prototype of `class.prototype` is `function.prototype`.
1652
 */
1653
static KOS_OBJ_ID class_constructor(KOS_CONTEXT ctx,
11✔
1654
                                    KOS_OBJ_ID  this_obj,
1655
                                    KOS_OBJ_ID  args_obj)
1656
{
1657
    KOS_OBJ_ID ret = KOS_BADPTR;
11✔
1658

1659
    if (KOS_get_array_size(args_obj) != 1)
11✔
1660
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_class));
2✔
1661

1662
    else {
1663

1664
        ret = KOS_array_read(ctx, args_obj, 0);
9✔
1665
        if ( ! IS_BAD_PTR(ret)) {
9✔
1666
            if (GET_OBJ_TYPE(ret) == OBJ_CLASS)
9✔
1667
                ret = kos_copy_function(ctx, ret);
2✔
1668
            else {
1669
                KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_class));
7✔
1670
                ret = KOS_BADPTR;
7✔
1671
            }
1672
        }
1673
    }
1674

1675
    return ret;
11✔
1676
}
1677

1678
/* @item base generator()
1679
 *
1680
 *     generator(func)
1681
 *
1682
 * Generator function class.
1683
 *
1684
 * This class can be used with the `instanceof` operator to detect generator
1685
 * functions.
1686
 *
1687
 * The `func` argument must be a generator function.
1688
 *
1689
 *  * For generator functions (not instantiated), returns the same generator
1690
 *    function which was passed.
1691
 *  * For instantiated generator functions (iterators), returns a copy of
1692
 *    the generator function object, uninstantiated.
1693
 *
1694
 * Throws an exception if the `func` argument is not a generator.
1695
 *
1696
 * The prototype of `generator.prototype` is `function.prototype`.
1697
 */
1698
static KOS_OBJ_ID generator_constructor(KOS_CONTEXT ctx,
11✔
1699
                                        KOS_OBJ_ID  this_obj,
1700
                                        KOS_OBJ_ID  args_obj)
1701
{
1702
    KOS_OBJ_ID ret = KOS_BADPTR;
11✔
1703

1704
    if (KOS_get_array_size(args_obj) != 1)
11✔
1705
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_generator));
2✔
1706

1707
    else {
1708

1709
        ret = KOS_array_read(ctx, args_obj, 0);
9✔
1710
        if ( ! IS_BAD_PTR(ret)) {
9✔
1711
            const KOS_TYPE type = GET_OBJ_TYPE(ret);
9✔
1712

1713
            if (type != OBJ_FUNCTION) {
9✔
1714
                KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_generator));
6✔
1715
                ret = KOS_BADPTR;
6✔
1716
            }
1717
            else {
1718
                const KOS_FUNCTION_STATE state = (KOS_FUNCTION_STATE)
1719
                    KOS_atomic_read_relaxed_u32(OBJPTR(FUNCTION, ret)->state);
3✔
1720

1721
                switch (state) {
3✔
1722

1723
                    case KOS_FUN:
1✔
1724
                        /* fall through */
1725
                    case KOS_CTOR:
1726
                        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_generator));
1✔
1727
                        ret = KOS_BADPTR;
1✔
1728
                        break;
1✔
1729

1730
                    case KOS_GEN_INIT:
1✔
1731
                        break;
1✔
1732

1733
                    default:
1✔
1734
                        ret = kos_copy_function(ctx, ret);
1✔
1735

1736
                        if (state > KOS_GEN_INIT && ! IS_BAD_PTR(ret))
1✔
1737
                            OBJPTR(FUNCTION, ret)->state = KOS_GEN_INIT;
1✔
1738
                }
1739
            }
1740
        }
1741
    }
1742

1743
    return ret;
11✔
1744
}
1745

1746
/* @item base exception()
1747
 *
1748
 *     exception(value = void)
1749
 *
1750
 * Exception object class.
1751
 *
1752
 * All caught exception objects have `exception.prototype` as their prototype.
1753
 * This class gives access to that prototype.
1754
 *
1755
 * Calling this class throws an exception, it does not return
1756
 * an exception object.  The thrown exception's `value` property can be set
1757
 * to the optional `value` argument.  In other words, calling this class
1758
 * is equivalent to throwing `value`.
1759
 *
1760
 * If `value` is not specified, `void` is thrown.
1761
 *
1762
 * The prototype of `exception.prototype` is `object.prototype`.
1763
 */
1764
static KOS_OBJ_ID exception_constructor(KOS_CONTEXT ctx,
2✔
1765
                                        KOS_OBJ_ID  this_obj,
1766
                                        KOS_OBJ_ID  args_obj)
1767
{
1768
    KOS_OBJ_ID     exception = KOS_VOID;
2✔
1769
    const uint32_t num_args  = KOS_get_array_size(args_obj);
2✔
1770

1771
    if (num_args > 0)
2✔
1772
        exception = KOS_array_read(ctx, args_obj, 0);
1✔
1773

1774
    KOS_raise_exception(ctx, exception);
2✔
1775
    return KOS_BADPTR;
2✔
1776
}
1777

1778
/* @item base generator_end()
1779
 *
1780
 *     generator_end()
1781
 *
1782
 * Generator end object class.
1783
 *
1784
 * A generator end object is typically thrown when an iterator function is
1785
 * called but has no more values to yield.  In other words, a thrown generator
1786
 * end object indicates end of a generator.  The generator end object can
1787
 * be caught and it becomes the `value` of the exception object caught.
1788
 *
1789
 * Calling this class throws an exception.
1790
 *
1791
 * The prototype of `generator_end.prototype` is `object.prototype`.
1792
 */
1793
static KOS_OBJ_ID generator_end_constructor(KOS_CONTEXT ctx,
1✔
1794
                                            KOS_OBJ_ID  this_obj,
1795
                                            KOS_OBJ_ID  args_obj)
1796
{
1797
    KOS_raise_generator_end(ctx);
1✔
1798
    return KOS_BADPTR;
1✔
1799
}
1800

1801
/* @item base thread()
1802
 *
1803
 *     thread()
1804
 *
1805
 * Thread object class.
1806
 *
1807
 * Thread objects are created by calling `function.prototype.async()`.
1808
 *
1809
 * The purpose of this class is to be used with the `instanceof`
1810
 * operator to detect thread objects.
1811
 *
1812
 * Calling this class directly throws an exception.
1813
 *
1814
 * The prototype of `thread.prototype` is `object.prototype`.
1815
 */
1816
static KOS_OBJ_ID thread_constructor(KOS_CONTEXT ctx,
1✔
1817
                                     KOS_OBJ_ID  this_obj,
1818
                                     KOS_OBJ_ID  args_obj)
1819
{
1820
    KOS_raise_exception(ctx, KOS_CONST_ID(str_err_use_async));
1✔
1821
    return KOS_BADPTR;
1✔
1822
}
1823

1824
/* @item base function.prototype.apply()
1825
 *
1826
 *     function.prototype.apply(this_obj = void, args = [])
1827
 *
1828
 * Invokes a function with the specified this object and arguments.
1829
 *
1830
 * Returns the value returned by the function.
1831
 *
1832
 * The `this_obj` argument is the object which is bound to the function as
1833
 * `this` for the invocation.  It can be any object or `void`.
1834
 *
1835
 * The `args` argument is an array (can be empty) containing arguments for
1836
 * the function.
1837
 *
1838
 * Example:
1839
 *
1840
 *     > fun f(a) { return this + a }
1841
 *     > f.apply(1, [2])
1842
 *     3
1843
 */
1844
static KOS_OBJ_ID apply(KOS_CONTEXT ctx,
404✔
1845
                        KOS_OBJ_ID  this_obj,
1846
                        KOS_OBJ_ID  args_obj)
1847
{
1848
    int        error = KOS_SUCCESS;
404✔
1849
    KOS_OBJ_ID ret   = KOS_BADPTR;
404✔
1850
    KOS_LOCAL  func;
1851
    KOS_LOCAL  arg_this;
1852
    KOS_LOCAL  arg_args;
1853

1854
    KOS_init_locals(ctx, &func, &arg_this, &arg_args, kos_end_locals);
404✔
1855

1856
    func.o     = this_obj;
404✔
1857
    arg_args.o = args_obj;
404✔
1858

1859
    arg_this.o = KOS_array_read(ctx, arg_args.o, 0);
404✔
1860
    TRY_OBJID(arg_this.o);
404✔
1861

1862
    arg_args.o = KOS_array_read(ctx, arg_args.o, 1);
403✔
1863
    TRY_OBJID(arg_args.o);
403✔
1864

1865
    arg_args.o = KOS_array_slice(ctx, arg_args.o, 0, MAX_INT64);
402✔
1866
    TRY_OBJID(arg_args.o);
402✔
1867

1868
    ret = KOS_apply_function(ctx, func.o, arg_this.o, arg_args.o);
399✔
1869

1870
cleanup:
404✔
1871
    KOS_destroy_top_locals(ctx, &func, &arg_args);
404✔
1872
    return error ? KOS_BADPTR : ret;
404✔
1873
}
1874

1875
static void thread_finalize(KOS_CONTEXT ctx,
421✔
1876
                            void       *priv)
1877
{
1878
    if (priv)
421✔
1879
        kos_thread_disown((KOS_THREAD *)priv);
83✔
1880
}
421✔
1881

1882
KOS_DECLARE_PRIVATE_CLASS(thread_priv_class);
1883

1884
/* @item base function.prototype.async()
1885
 *
1886
 *     function.prototype.async(this_obj = void, args = [])
1887
 *
1888
 * Invokes a function asynchronously on a new thread.
1889
 *
1890
 * Returns the created thread object.
1891
 *
1892
 * The `this_obj` argument is the object which is bound to the function as
1893
 * `this` for the invocation.  It can be any object or `void`.
1894
 *
1895
 * The `args` argument is an array (can be empty) containing arguments for
1896
 * the function.
1897
 *
1898
 * Example:
1899
 *
1900
 *     > fun f(a, b) { return a + b }
1901
 *     > const t = f.async(void, [1, 2])
1902
 *     > t.wait()
1903
 *     3
1904
 */
1905
static KOS_OBJ_ID async(KOS_CONTEXT ctx,
424✔
1906
                        KOS_OBJ_ID  this_obj,
1907
                        KOS_OBJ_ID  args_obj)
1908
{
1909
    int         error = KOS_SUCCESS;
424✔
1910
    KOS_LOCAL   func;
1911
    KOS_LOCAL   arg_this;
1912
    KOS_LOCAL   arg_args;
1913
    KOS_LOCAL   thread_obj;
1914
    KOS_THREAD *thread;
1915

1916
    if (GET_OBJ_TYPE(this_obj) != OBJ_FUNCTION) {
424✔
1917
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
3✔
1918
        return KOS_BADPTR;
3✔
1919
    }
1920

1921
    KOS_init_locals(ctx, &func, &arg_this, &arg_args, &thread_obj, kos_end_locals);
421✔
1922

1923
    func.o     = this_obj;
420✔
1924
    arg_args.o = args_obj;
420✔
1925

1926
    thread_obj.o = KOS_new_object_with_private(ctx,
840✔
1927
                                               ctx->inst->prototypes.thread_proto,
420✔
1928
                                               &thread_priv_class,
1929
                                               thread_finalize);
1930
    TRY_OBJID(thread_obj.o);
420✔
1931

1932
    KOS_object_set_private_ptr(thread_obj.o, (void *)KOS_NULL);
420✔
1933

1934
    arg_this.o = KOS_array_read(ctx, arg_args.o, 0);
420✔
1935
    TRY_OBJID(arg_this.o);
419✔
1936

1937
    arg_args.o = KOS_array_read(ctx, arg_args.o, 1);
419✔
1938
    TRY_OBJID(arg_args.o);
420✔
1939
    if (GET_OBJ_TYPE(arg_args.o) != OBJ_ARRAY)
420✔
1940
        RAISE_EXCEPTION_STR(str_err_args_not_array);
5✔
1941

1942
    thread = kos_thread_create(ctx, func.o, arg_this.o, arg_args.o);
415✔
1943
    if ( ! thread) {
417✔
1944
        error = KOS_ERROR_EXCEPTION;
4✔
1945
        goto cleanup;
4✔
1946
    }
1947

1948
    kos_thread_add_ref(thread);
413✔
1949

1950
    KOS_object_set_private_ptr(thread_obj.o, thread);
408✔
1951

1952
cleanup:
416✔
1953
    thread_obj.o = KOS_destroy_top_locals(ctx, &func, &thread_obj);
416✔
1954

1955
    return error ? KOS_BADPTR : thread_obj.o;
416✔
1956
}
1957

1958
/* @item base thread.prototype.wait()
1959
 *
1960
 *     thread.prototype.wait()
1961
 *
1962
 * Waits for thread to complete.
1963
 *
1964
 * Returns the return value returned from the thread function.
1965
 *
1966
 * If the thread function ended with an exception, rethrows that exception
1967
 * on the current thread.
1968
 *
1969
 * Example:
1970
 *
1971
 *     > fun f { return 42 }
1972
 *     > const t = f.async()
1973
 *     > t.wait()
1974
 *     42
1975
 */
1976
static KOS_OBJ_ID wait(KOS_CONTEXT ctx,
331✔
1977
                       KOS_OBJ_ID  this_obj,
1978
                       KOS_OBJ_ID  args_obj)
1979
{
1980
    KOS_THREAD *thread;
1981
    KOS_OBJ_ID  retval;
1982

1983
    if (GET_OBJ_TYPE(this_obj) != OBJ_OBJECT) {
331✔
1984
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_thread));
1✔
1985
        return KOS_BADPTR;
1✔
1986
    }
1987

1988
    thread = (KOS_THREAD *)KOS_object_get_private(this_obj, &thread_priv_class);
330✔
1989

1990
    if (thread && kos_is_current_thread(thread)) {
324✔
1991
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_join_self));
1✔
1992
        return KOS_BADPTR;
1✔
1993
    }
1994

1995
    thread = (KOS_THREAD *)KOS_object_swap_private(this_obj, &thread_priv_class, (void *)KOS_NULL);
327✔
1996

1997
    if ( ! thread) {
328✔
1998
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_already_joined));
2✔
1999
        return KOS_BADPTR;
2✔
2000
    }
2001

2002
    retval = kos_thread_join(ctx, thread);
326✔
2003

2004
    kos_thread_disown(thread);
330✔
2005

2006
    return retval;
330✔
2007
}
2008

2009
/* @item base string.prototype.slice()
2010
 *
2011
 *     string.prototype.slice(begin, end)
2012
 *
2013
 * Extracts substring from a string.
2014
 *
2015
 * Returns a new string, unless the entire string was selected, in which
2016
 * case returns the same string object.  (Note: strings are immutable.)
2017
 *
2018
 * `begin` and `end` specify the range of characters to extract in a new
2019
 * string.  `begin` is the index of the first character and `end` is the index
2020
 * of the character trailing the last character to extract.
2021
 * A negative index is an offset from the end, such that `-1` indicates the
2022
 * last character of the string.
2023
 * If `begin` is `void`, it is equivalent to `0`.  If `end` is `void`, it is
2024
 * equivalent to string size.
2025
 *
2026
 * This function is invoked by the slice operator.
2027
 *
2028
 * Examples:
2029
 *
2030
 *     > "language".slice(0, 4)
2031
 *     "lang"
2032
 *     > "language".slice(void, void)
2033
 *     "language"
2034
 *     > "language".slice(-5, -1)
2035
 *     "guag"
2036
 */
2037

2038
/* @item base array.prototype.slice()
2039
 *
2040
 *     array.prototype.slice(begin, end)
2041
 *
2042
 * Extracts a range of elements from an array.
2043
 *
2044
 * Returns a new array.
2045
 *
2046
 * It can be used to create a flat copy of an array.
2047
 *
2048
 * `begin` and `end` specify the range of elements to extract in a new
2049
 * array.  `begin` is the index of the first element and `end` is the index
2050
 * of the element trailing the last element to extract.
2051
 * A negative index is an offset from the end, such that `-1` indicates the
2052
 * last element of the array.
2053
 * If `begin` is `void`, it is equivalent to `0`.  If `end` is `void`, it is
2054
 * equivalent to array size.
2055
 *
2056
 * This function is invoked by the slice operator.
2057
 *
2058
 * Examples:
2059
 *
2060
 *     > [1, 2, 3, 4, 5, 6, 7, 8].slice(0, 4)
2061
 *     [1, 2, 3, 4]
2062
 *     > [1, 2, 3, 4, 5, 6, 7, 8].slice(void, void)
2063
 *     [1, 2, 3, 4, 5, 6, 7, 8]
2064
 *     > [1, 2, 3, 4, 5, 6, 7, 8].slice(-5, -1)
2065
 *     [4, 5, 6, 7]
2066
 */
2067

2068
/* @item base buffer.prototype.slice()
2069
 *
2070
 *     buffer.prototype.slice(begin, end)
2071
 *
2072
 * Extracts a range of elements from a buffer.
2073
 *
2074
 * Returns a new buffer.
2075
 *
2076
 * It can be used to create a flat copy of a buffer.
2077
 *
2078
 * `begin` and `end` specify the range of elements to extract in a new
2079
 * buffer.  `begin` is the index of the first element and `end` is the index
2080
 * of the element trailing the last element to extract.
2081
 * A negative index is an offset from the end, such that `-1` indicates the
2082
 * last element of the buffer.
2083
 * If `begin` is `void`, it is equivalent to `0`.  If `end` is `void`, it is
2084
 * equivalent to buffer size.
2085
 *
2086
 * This function is invoked by the slice operator.
2087
 *
2088
 * Examples:
2089
 *
2090
 *     > buffer([1, 2, 3, 4, 5, 6, 7, 8]).slice(0, 4)
2091
 *     <1, 2, 3, 4>
2092
 *     > buffer([1, 2, 3, 4, 5, 6, 7, 8]).slice(void, void)
2093
 *     <1, 2, 3, 4, 5, 6, 7, 8>
2094
 *     > buffer([1, 2, 3, 4, 5, 6, 7, 8]).slice(-5, -1)
2095
 *     <4, 5, 6, 7>
2096
 */
2097
static const KOS_CONVERT slice_args[3] = {
2098
    KOS_DEFINE_MANDATORY_ARG(str_begin),
2099
    KOS_DEFINE_MANDATORY_ARG(str_end  ),
2100
    KOS_DEFINE_TAIL_ARG()
2101
};
2102

2103
static KOS_OBJ_ID slice(KOS_CONTEXT ctx,
16✔
2104
                        KOS_OBJ_ID  this_obj,
2105
                        KOS_OBJ_ID  args_obj)
2106
{
2107
    int        error;
2108
    KOS_OBJ_ID ret   = KOS_BADPTR;
16✔
2109
    KOS_OBJ_ID a_obj;
2110
    KOS_OBJ_ID b_obj;
2111
    int64_t    idx_a = 0;
16✔
2112
    int64_t    idx_b = 0;
16✔
2113

2114
    a_obj = KOS_array_read(ctx, args_obj, 0);
16✔
2115
    TRY_OBJID(a_obj);
16✔
2116

2117
    b_obj = KOS_array_read(ctx, args_obj, 1);
16✔
2118
    TRY_OBJID(b_obj);
16✔
2119

2120
    if (IS_NUMERIC_OBJ(a_obj))
16✔
2121
        TRY(KOS_get_integer(ctx, a_obj, &idx_a));
11✔
2122
    else if (READ_OBJ_TYPE(a_obj) == OBJ_VOID)
5✔
2123
        idx_a = 0;
4✔
2124
    else {
2125
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_unsup_operand_types));
1✔
2126
        return KOS_BADPTR;
1✔
2127
    }
2128

2129
    if (IS_NUMERIC_OBJ(b_obj))
15✔
2130
        TRY(KOS_get_integer(ctx, b_obj, &idx_b));
9✔
2131
    else if (READ_OBJ_TYPE(b_obj) == OBJ_VOID)
6✔
2132
        idx_b = MAX_INT64;
5✔
2133
    else {
2134
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_unsup_operand_types));
1✔
2135
        return KOS_BADPTR;
1✔
2136
    }
2137

2138
    if (GET_OBJ_TYPE(this_obj) == OBJ_STRING)
14✔
2139
        ret = KOS_string_slice(ctx, this_obj, idx_a, idx_b);
6✔
2140
    else if (GET_OBJ_TYPE(this_obj) == OBJ_BUFFER)
8✔
2141
        ret = KOS_buffer_slice(ctx, this_obj, idx_a, idx_b);
3✔
2142
    else
2143
        ret = KOS_array_slice(ctx, this_obj, idx_a, idx_b);
5✔
2144

2145
cleanup:
14✔
2146
    return ret;
14✔
2147
}
2148

2149
static KOS_OBJ_ID expand_for_sort(KOS_CONTEXT ctx,
24✔
2150
                                  KOS_OBJ_ID  iterable_obj,
2151
                                  KOS_OBJ_ID  key_func_obj)
2152
{
2153
    int            error  = KOS_SUCCESS;
24✔
2154
    uint32_t       i      = 0;
24✔
2155
    uint32_t       i_dest = 0;
24✔
2156
    uint32_t       size;
2157
    const uint32_t step   = (key_func_obj == KOS_VOID) ? 2 : 3;
24✔
2158
    KOS_LOCAL      iterable;
2159
    KOS_LOCAL      key_func;
2160
    KOS_LOCAL      key_args;
2161
    KOS_LOCAL      src;
2162
    KOS_LOCAL      dest;
2163
    KOS_LOCAL      val;
2164
    KOS_LOCAL      expanded;
2165

2166
    assert(GET_OBJ_TYPE(iterable_obj) == OBJ_ARRAY);
24✔
2167

2168
    KOS_init_locals(ctx, &iterable, &key_func, &key_args, &src, &dest, &val, &expanded, kos_end_locals);
24✔
2169

2170
    iterable.o = iterable_obj;
24✔
2171
    key_func.o = key_func_obj;
24✔
2172

2173
    size  = KOS_get_array_size(iterable.o);
24✔
2174
    src.o = kos_get_array_storage(iterable.o);
24✔
2175

2176
    expanded.o = KOS_new_array(ctx, size * step);
24✔
2177
    TRY_OBJID(expanded.o);
24✔
2178

2179
    dest.o = kos_get_array_storage(expanded.o);
24✔
2180

2181
    if (key_func.o != KOS_VOID) {
24✔
2182
        key_args.o = KOS_new_array(ctx, 1);
7✔
2183
        TRY_OBJID(key_args.o);
7✔
2184
    }
2185

2186
    while (i < size) {
124✔
2187

2188
        val.o = KOS_atomic_read_relaxed_obj(OBJPTR(ARRAY_STORAGE, src.o)->buf[i]);
101✔
2189

2190
        if (key_func.o == KOS_VOID) {
101✔
2191
            KOS_atomic_write_relaxed_ptr(OBJPTR(ARRAY_STORAGE, dest.o)->buf[i_dest],     val.o);
57✔
2192
            KOS_atomic_write_relaxed_ptr(OBJPTR(ARRAY_STORAGE, dest.o)->buf[i_dest + 1], TO_SMALL_INT(i));
57✔
2193
        }
2194
        else {
2195
            KOS_OBJ_ID key;
2196

2197
            TRY(KOS_array_write(ctx, key_args.o, 0, val.o));
44✔
2198
            key = KOS_call_function(ctx, key_func.o, KOS_VOID, key_args.o);
44✔
2199
            TRY_OBJID(key);
44✔
2200

2201
            KOS_atomic_write_relaxed_ptr(OBJPTR(ARRAY_STORAGE, dest.o)->buf[i_dest],     key);
43✔
2202
            KOS_atomic_write_relaxed_ptr(OBJPTR(ARRAY_STORAGE, dest.o)->buf[i_dest + 1], TO_SMALL_INT(i));
43✔
2203
            KOS_atomic_write_relaxed_ptr(OBJPTR(ARRAY_STORAGE, dest.o)->buf[i_dest + 2], val.o);
43✔
2204
        }
2205

2206
        ++i;
100✔
2207
        i_dest += step;
100✔
2208
    }
2209

2210
cleanup:
23✔
2211
    expanded.o = KOS_destroy_top_locals(ctx, &iterable, &expanded);
24✔
2212

2213
    return error ? KOS_BADPTR : expanded.o;
24✔
2214
}
2215

2216
static int is_less_for_sort(KOS_OBJ_ID         left_key,
224✔
2217
                            KOS_OBJ_ID         left_idx,
2218
                            KOS_COMPARE_RESULT lt,
2219
                            KOS_COMPARE_RESULT gt,
2220
                            KOS_OBJ_ID         right_key,
2221
                            KOS_OBJ_ID         right_idx)
2222
{
2223
    const KOS_COMPARE_RESULT cmp = KOS_compare(left_key, right_key);
224✔
2224

2225
    if (cmp == lt)
224✔
2226
        return 1;
91✔
2227

2228
    if (cmp == gt)
133✔
2229
        return 0;
63✔
2230

2231
    if (lt == KOS_LESS_THAN)
70✔
2232
        return (intptr_t)left_idx < (intptr_t)right_idx;
48✔
2233
    else
2234
        return (intptr_t)left_idx > (intptr_t)right_idx;
22✔
2235
}
2236

2237
static void sort_range(KOS_ATOMIC(KOS_OBJ_ID) *begin,
63✔
2238
                       KOS_ATOMIC(KOS_OBJ_ID) *end,
2239
                       int                     step,
2240
                       int                     reverse)
2241
{
2242
    const KOS_OBJ_ID pivot_key = KOS_atomic_read_relaxed_obj(*(end - step));
63✔
2243
    const KOS_OBJ_ID pivot_idx = KOS_atomic_read_relaxed_obj(*(end - step + 1));
63✔
2244

2245
    KOS_ATOMIC(KOS_OBJ_ID)  *mid = begin - step;
63✔
2246
    KOS_ATOMIC(KOS_OBJ_ID)  *p   = begin;
63✔
2247
    const KOS_COMPARE_RESULT lt  = reverse ? KOS_GREATER_THAN : KOS_LESS_THAN;
63✔
2248
    const KOS_COMPARE_RESULT gt  = reverse ? KOS_LESS_THAN : KOS_GREATER_THAN;
63✔
2249

2250
    end -= step;
63✔
2251

2252
    while (p < end) {
224✔
2253
        const KOS_OBJ_ID key = KOS_atomic_read_relaxed_obj(p[0]);
161✔
2254
        const KOS_OBJ_ID idx = KOS_atomic_read_relaxed_obj(p[1]);
161✔
2255

2256
        if (is_less_for_sort(key, idx, lt, gt, pivot_key, pivot_idx)) {
161✔
2257

2258
            mid += step;
84✔
2259

2260
            KOS_atomic_write_relaxed_ptr(p[0], KOS_atomic_read_relaxed_obj(mid[0]));
84✔
2261
            KOS_atomic_write_relaxed_ptr(p[1], KOS_atomic_read_relaxed_obj(mid[1]));
84✔
2262

2263
            KOS_atomic_write_relaxed_ptr(mid[0], key);
84✔
2264
            KOS_atomic_write_relaxed_ptr(mid[1], idx);
84✔
2265

2266
            if (step == 3) {
84✔
2267
                const KOS_OBJ_ID val = KOS_atomic_read_relaxed_obj(p[2]);
42✔
2268

2269
                KOS_atomic_write_relaxed_ptr(p[2], KOS_atomic_read_relaxed_obj(mid[2]));
42✔
2270
                KOS_atomic_write_relaxed_ptr(mid[2], val);
42✔
2271
            }
2272
        }
2273

2274
        p += step;
161✔
2275
    }
2276

2277
    mid += step;
63✔
2278

2279
    {
2280
        const KOS_OBJ_ID key = KOS_atomic_read_relaxed_obj(mid[0]);
63✔
2281
        const KOS_OBJ_ID idx = KOS_atomic_read_relaxed_obj(mid[1]);
63✔
2282

2283
        if (is_less_for_sort(pivot_key, pivot_idx, lt, gt, key, idx)) {
63✔
2284
            KOS_atomic_write_relaxed_ptr(end[0], key);
37✔
2285
            KOS_atomic_write_relaxed_ptr(end[1], idx);
37✔
2286

2287
            KOS_atomic_write_relaxed_ptr(mid[0], pivot_key);
37✔
2288
            KOS_atomic_write_relaxed_ptr(mid[1], pivot_idx);
37✔
2289

2290
            if (step == 3) {
37✔
2291
                const KOS_OBJ_ID pivot_val = KOS_atomic_read_relaxed_obj(end[2]);
20✔
2292

2293
                KOS_atomic_write_relaxed_ptr(end[2], KOS_atomic_read_relaxed_obj(mid[2]));
20✔
2294
                KOS_atomic_write_relaxed_ptr(mid[2], pivot_val);
20✔
2295
            }
2296
        }
2297
    }
2298

2299
    if (begin + step < mid)
63✔
2300
        sort_range(begin, mid, step, reverse);
22✔
2301
    if (mid + step < end)
63✔
2302
        sort_range(mid + step, end + step, step, reverse);
18✔
2303
}
63✔
2304

2305
static void copy_sort_results(KOS_CONTEXT ctx,
23✔
2306
                              KOS_OBJ_ID  ret,
2307
                              KOS_OBJ_ID  sorted,
2308
                              uint32_t    step)
2309
{
2310
    KOS_ATOMIC(KOS_OBJ_ID) *src;
2311
    KOS_ATOMIC(KOS_OBJ_ID) *src_end;
2312
    KOS_ATOMIC(KOS_OBJ_ID) *dest;
2313

2314
    assert(GET_OBJ_TYPE(ret) == OBJ_ARRAY);
23✔
2315
    assert(GET_OBJ_TYPE(sorted) == OBJ_ARRAY);
23✔
2316
    assert(step == 2 || step == 3);
23✔
2317

2318
    src     = kos_get_array_buffer(OBJPTR(ARRAY, sorted));
23✔
2319
    src_end = src + KOS_get_array_size(sorted);
23✔
2320
    dest    = kos_get_array_buffer(OBJPTR(ARRAY, ret));
23✔
2321

2322
    assert(KOS_get_array_size(ret) * step == KOS_get_array_size(sorted));
23✔
2323

2324
    if (step == 3)
23✔
2325
        src += 2;
6✔
2326

2327
    while (src < src_end) {
123✔
2328

2329
        const KOS_OBJ_ID val = KOS_atomic_read_relaxed_obj(*src);
100✔
2330
        KOS_atomic_write_relaxed_ptr(*dest, val);
100✔
2331

2332
        src += step;
100✔
2333
        ++dest;
100✔
2334
    }
2335
}
23✔
2336

2337
/* @item base array.prototype.sort()
2338
 *
2339
 *     array.prototype.sort(key = void, reverse = false)
2340
 *
2341
 * Sorts array in-place.
2342
 *
2343
 * Returns the array being sorted (`this`).
2344
 *
2345
 * Uses a stable sorting algorithm, which preserves order of elements for
2346
 * which sorting keys compare as equal.
2347
 *
2348
 * `key` is a single-argument function which produces a sorting key for each
2349
 * element of the array.  The array elements are then sorted by the keys using
2350
 * the `<` operator.  By default `key` is `void` and the elements themselves
2351
 * are used as sorting keys.
2352
 *
2353
 * `reverse` defaults to `false`.  If `reverse` is specified as `true`,
2354
 * the array elements are sorted in reverse order, i.e. in a descending key
2355
 * order.
2356
 *
2357
 * Example:
2358
 *
2359
 *     > [8, 5, 6, 0, 10, 2].sort()
2360
 *     [0, 2, 5, 6, 8, 10]
2361
 */
2362
static const KOS_CONVERT sort_args[3] = {
2363
    KOS_DEFINE_OPTIONAL_ARG(str_key,     KOS_VOID ),
2364
    KOS_DEFINE_OPTIONAL_ARG(str_reverse, KOS_FALSE),
2365
    KOS_DEFINE_TAIL_ARG()
2366
};
2367

2368
static KOS_OBJ_ID sort(KOS_CONTEXT ctx,
30✔
2369
                       KOS_OBJ_ID  this_obj,
2370
                       KOS_OBJ_ID  args_obj)
2371
{
2372
    int                     error      = KOS_SUCCESS;
30✔
2373
    KOS_OBJ_ID              key        = KOS_VOID;
30✔
2374
    KOS_OBJ_ID              reverse_id = KOS_FALSE;
30✔
2375
    KOS_ATOMIC(KOS_OBJ_ID) *src;
2376
    KOS_LOCAL               to_expand;
2377
    KOS_LOCAL               expanded;
2378
    KOS_LOCAL               sort_key;
2379
    KOS_TYPE                type;
2380

2381
    assert(KOS_get_array_size(args_obj) >= 2);
30✔
2382

2383
    KOS_init_locals(ctx, &to_expand, &expanded, &sort_key, kos_end_locals);
30✔
2384

2385
    if (GET_OBJ_TYPE(this_obj) != OBJ_ARRAY)
30✔
2386
        RAISE_EXCEPTION_STR(str_err_not_array);
1✔
2387

2388
    key = KOS_array_read(ctx, args_obj, 0);
29✔
2389
    TRY_OBJID(key);
29✔
2390

2391
    type = GET_OBJ_TYPE(key);
29✔
2392

2393
    if (type != OBJ_VOID && type != OBJ_FUNCTION && type != OBJ_CLASS)
29✔
2394
        RAISE_EXCEPTION_STR(str_err_invalid_key_type);
1✔
2395

2396
    reverse_id = KOS_array_read(ctx, args_obj, 1);
28✔
2397
    TRY_OBJID(reverse_id);
28✔
2398

2399
    if (reverse_id != KOS_TRUE && reverse_id != KOS_FALSE)
28✔
2400
        RAISE_EXCEPTION_STR(str_err_invalid_reverse_type);
1✔
2401

2402
    if (KOS_get_array_size(this_obj) > 1) {
27✔
2403
        to_expand.o = this_obj;
24✔
2404
        sort_key.o  = key;
24✔
2405

2406
        expanded.o = expand_for_sort(ctx, to_expand.o, sort_key.o);
24✔
2407
        TRY_OBJID(expanded.o);
24✔
2408

2409
        src = kos_get_array_buffer(OBJPTR(ARRAY, expanded.o));
23✔
2410

2411
        sort_range(src,
23✔
2412
                   src + KOS_get_array_size(expanded.o),
23✔
2413
                   (sort_key.o == KOS_VOID) ? 2 : 3,
23✔
2414
                   (int)KOS_get_bool(reverse_id));
23✔
2415

2416
        copy_sort_results(ctx, to_expand.o, expanded.o, (sort_key.o == KOS_VOID) ? 2 : 3);
23✔
2417

2418
        this_obj = to_expand.o;
23✔
2419
    }
2420

2421
cleanup:
3✔
2422
    KOS_destroy_top_locals(ctx, &to_expand, &sort_key);
30✔
2423

2424
    return error ? KOS_BADPTR : this_obj;
30✔
2425
}
2426

2427
/* @item base array.prototype.size
2428
 *
2429
 *     array.prototype.size
2430
 *
2431
 * Read-only size of the array (integer).
2432
 *
2433
 * Example:
2434
 *
2435
 *     > [1, 10, 100].size
2436
 *     3
2437
 */
2438
static KOS_OBJ_ID get_array_size(KOS_CONTEXT ctx,
13,998✔
2439
                                 KOS_OBJ_ID  this_obj,
2440
                                 KOS_OBJ_ID  args_obj)
2441
{
2442
    KOS_OBJ_ID ret;
2443

2444
    assert( ! IS_BAD_PTR(this_obj));
13,998✔
2445

2446
    if (GET_OBJ_TYPE(this_obj) == OBJ_ARRAY)
13,998✔
2447
        ret = KOS_new_int(ctx, (int64_t)KOS_get_array_size(this_obj));
13,994✔
2448
    else {
2449
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
4✔
2450
        ret = KOS_BADPTR;
4✔
2451
    }
2452

2453
    return ret;
13,998✔
2454
}
2455

2456
/* @item base buffer.prototype.size
2457
 *
2458
 *     buffer.prototype.size
2459
 *
2460
 * Read-only size of the buffer (integer).
2461
 *
2462
 * Example:
2463
 *
2464
 *     > buffer([1, 10, 100]).size
2465
 *     3
2466
 */
2467
static KOS_OBJ_ID get_buffer_size(KOS_CONTEXT ctx,
198✔
2468
                                  KOS_OBJ_ID  this_obj,
2469
                                  KOS_OBJ_ID  args_obj)
2470
{
2471
    KOS_OBJ_ID ret = KOS_BADPTR;
198✔
2472

2473
    assert( ! IS_BAD_PTR(this_obj));
198✔
2474

2475
    if (GET_OBJ_TYPE(this_obj) == OBJ_BUFFER)
198✔
2476
        ret = KOS_new_int(ctx, (int64_t)KOS_get_buffer_size(this_obj));
197✔
2477
    else {
2478
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_buffer));
1✔
2479
        ret = KOS_BADPTR;
1✔
2480
    }
2481

2482
    return ret;
198✔
2483
}
2484

2485
/* @item base array.prototype.resize()
2486
 *
2487
 *     array.prototype.resize(size, value = void)
2488
 *
2489
 * Resizes an array.
2490
 *
2491
 * Returns the array being resized (`this`).
2492
 *
2493
 * `size` is the new size of the array.
2494
 *
2495
 * If `size` is greater than the current array size, `value` elements are
2496
 * appended to expand the array.
2497
 *
2498
 * Example:
2499
 *
2500
 *     > const a = []
2501
 *     > a.resize(5)
2502
 *     [void, void, void, void, void]
2503
 */
2504
static const KOS_CONVERT resize_array_args[3] = {
2505
    KOS_DEFINE_MANDATORY_ARG(str_size           ),
2506
    KOS_DEFINE_OPTIONAL_ARG( str_value, KOS_VOID),
2507
    KOS_DEFINE_TAIL_ARG()
2508
};
2509

2510
/* @item base buffer.prototype.resize()
2511
 *
2512
 *     buffer.prototype.resize(size, value = 0)
2513
 *
2514
 * Resizes a buffer.
2515
 *
2516
 * Returns the buffer being resized (`this`).
2517
 *
2518
 * `size` is the new size of the buffer.
2519
 *
2520
 * If `size` is greater than the current buffer size, `value` elements are
2521
 * appended to expand the buffer.
2522
 *
2523
 * Example:
2524
 *
2525
 *     > const a = buffer()
2526
 *     > b.resize(5)
2527
 *     <00 00 00 00 00>
2528
 */
2529
static const KOS_CONVERT resize_buffer_args[3] = {
2530
    KOS_DEFINE_MANDATORY_ARG(str_size                  ),
2531
    KOS_DEFINE_OPTIONAL_ARG( str_value, TO_SMALL_INT(0)),
2532
    KOS_DEFINE_TAIL_ARG()
2533
};
2534

2535
static KOS_OBJ_ID resize(KOS_CONTEXT ctx,
199✔
2536
                         KOS_OBJ_ID  this_obj,
2537
                         KOS_OBJ_ID  args_obj)
2538
{
2539
    int        error    = KOS_SUCCESS;
199✔
2540
    int64_t    size;
2541
    KOS_OBJ_ID size_obj = KOS_VOID;
199✔
2542
    KOS_LOCAL  args;
2543
    KOS_LOCAL  value;
2544
    KOS_LOCAL  array;
2545

2546
    assert(KOS_get_array_size(args_obj) >= 2);
199✔
2547

2548
    KOS_init_locals(ctx, &args, &value, &array, kos_end_locals);
199✔
2549

2550
    array.o = this_obj;
199✔
2551
    args.o  = args_obj;
199✔
2552

2553
    size_obj = KOS_array_read(ctx, args.o, 0);
199✔
2554
    TRY_OBJID(size_obj);
199✔
2555

2556
    TRY(KOS_get_integer(ctx, size_obj, &size));
199✔
2557

2558
    value.o = KOS_array_read(ctx, args.o, 1);
199✔
2559
    TRY_OBJID(value.o);
199✔
2560

2561
    if (GET_OBJ_TYPE(array.o) == OBJ_BUFFER) {
303✔
2562
        const uint32_t old_size  = KOS_get_buffer_size(array.o);
109✔
2563
        int64_t        int_value = 0;
109✔
2564

2565
        if (size < 0 || size > INT_MAX)
109✔
2566
            RAISE_EXCEPTION_STR(str_err_invalid_buffer_size);
5✔
2567

2568
        if (!IS_NUMERIC_OBJ(value.o))
108✔
2569
            RAISE_EXCEPTION_STR(str_err_cannot_convert_to_buffer);
2✔
2570

2571
        TRY(KOS_get_integer(ctx, value.o, &int_value));
106✔
2572

2573
        if (int_value < 0 || int_value > 255)
106✔
2574
            RAISE_EXCEPTION_STR(str_err_cannot_convert_to_buffer);
2✔
2575

2576
        TRY(KOS_buffer_resize(ctx, array.o, (uint32_t)size));
104✔
2577

2578
        if (size > old_size) {
104✔
2579
            uint8_t *data = KOS_buffer_data_volatile(ctx, array.o);
4✔
2580

2581
            if ( ! data)
4✔
2582
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
2583
            data += old_size;
4✔
2584

2585
            memset(data, (int)int_value, (uint32_t)(size - old_size));
4✔
2586
        }
2587
    }
2588
    else {
2589
        uint32_t old_size;
2590

2591
        if (GET_OBJ_TYPE(array.o) != OBJ_ARRAY)
90✔
2592
            RAISE_EXCEPTION_STR(str_err_not_array);
1✔
2593

2594
        if (size < 0 || size > INT_MAX)
89✔
2595
            RAISE_EXCEPTION_STR(str_err_invalid_array_size);
1✔
2596

2597
        old_size = KOS_get_array_size(array.o);
88✔
2598

2599
        TRY(KOS_array_resize(ctx, array.o, (uint32_t)size));
88✔
2600

2601
        if ((uint32_t)size > old_size && value.o != KOS_VOID)
71✔
2602
            TRY(KOS_array_fill(ctx, array.o, old_size, size, value.o));
3✔
2603
    }
2604

2605
cleanup:
71✔
2606
    array.o = KOS_destroy_top_locals(ctx, &args, &array);
199✔
2607

2608
    return error ? KOS_BADPTR : array.o;
199✔
2609
}
2610

2611
/* @item base array.prototype.fill()
2612
 *
2613
 *     array.prototype.fill(value, begin = 0, end = void)
2614
 *
2615
 * Fills specified portion of the array with a value.
2616
 *
2617
 * Returns the array object being filled (`this`).
2618
 *
2619
 * `value` is the object to fill the array with.
2620
 *
2621
 * `begin` is the index at which to start filling the array.  `begin` defaults
2622
 * to `void`.  `void` is equivalent to index `0`.  If `begin` is negative, it
2623
 * is an offset from the end of the array.
2624
 *
2625
 * `end` is the index at which to stop filling the array, the element at this
2626
 * index will not be overwritten.  `end` defaults to `void`.  `void` is
2627
 * equivalent to the size of the array.  If `end` is negative, it is an offset
2628
 * from the end of the array.
2629
 *
2630
 * Example:
2631
 *
2632
 *     > const a = array(5)
2633
 *     > a.fill("foo")
2634
 *     ["foo", "foo", "foo", "foo", "foo"]
2635
 */
2636

2637
/* @item base buffer.prototype.fill()
2638
 *
2639
 *     buffer.prototype.fill(value, begin = 0, end = void)
2640
 *
2641
 * Fills specified portion of the buffer with a value.
2642
 *
2643
 * Returns the buffer object being filled (`this`).
2644
 *
2645
 * `value` is the byte value to fill the buffer with.  It must be a number from
2646
 * `0` to `255`, inclusive.  Float numbers are rounded using floor mode.
2647
 *
2648
 * `begin` is the index at which to start filling the buffer.  `begin` defaults
2649
 * to `void`.  `void` is equivalent to index `0`.  If `begin` is negative, it
2650
 * is an offset from the end of the buffer.
2651
 *
2652
 * `end` is the index at which to stop filling the buffer, the element at this
2653
 * index will not be overwritten.  `end` defaults to `void`.  `void` is
2654
 * equivalent to the size of the buffer.  If `end` is negative, it is an offset
2655
 * from the end of the buffer.
2656
 *
2657
 * Example:
2658
 *
2659
 *     > const b = buffer(5)
2660
 *     > b.fill(0x20)
2661
 *     <20 20 20 20 20>
2662
 */
2663
static const KOS_CONVERT fill_args[4] = {
2664
    KOS_DEFINE_MANDATORY_ARG(str_value                 ),
2665
    KOS_DEFINE_OPTIONAL_ARG( str_begin, TO_SMALL_INT(0)),
2666
    KOS_DEFINE_OPTIONAL_ARG( str_end,   KOS_VOID       ),
2667
    KOS_DEFINE_TAIL_ARG()
2668
};
2669

2670
static KOS_OBJ_ID fill(KOS_CONTEXT ctx,
23✔
2671
                       KOS_OBJ_ID  this_obj,
2672
                       KOS_OBJ_ID  args_obj)
2673
{
2674
    KOS_OBJ_ID     arg   = KOS_array_read(ctx, args_obj, 0);
23✔
2675
    int            begin = 0;
23✔
2676
    int            end   = 0;
23✔
2677
    int            error = KOS_SUCCESS;
23✔
2678
    int            len;
2679
    const KOS_TYPE type  = GET_OBJ_TYPE(this_obj);
23✔
2680

2681
    assert(KOS_get_array_size(args_obj) >= 3);
23✔
2682

2683
    switch (type) {
23✔
2684

2685
        case OBJ_ARRAY:
3✔
2686
            len = (int)KOS_get_array_size(this_obj);
3✔
2687
            break;
3✔
2688

2689
        case OBJ_BUFFER:
19✔
2690
            len = (int)KOS_get_buffer_size(this_obj);
19✔
2691
            break;
19✔
2692

2693
        default:
1✔
2694
            RAISE_EXCEPTION_STR(str_err_not_array);
1✔
2695
    }
2696

2697
    TRY(KOS_get_index_arg(ctx, args_obj, 1, 0,     len, KOS_VOID_INDEX_IS_BEGIN, &begin));
22✔
2698
    TRY(KOS_get_index_arg(ctx, args_obj, 2, begin, len, KOS_VOID_INDEX_IS_END,   &end));
20✔
2699

2700
    if (type == OBJ_ARRAY)
19✔
2701
        error = KOS_array_fill(ctx, this_obj, begin, end, arg);
3✔
2702

2703
    else {
2704

2705
        int64_t        value;
2706

2707
        TRY(KOS_get_integer(ctx, arg, &value));
17✔
2708

2709
        if (value < 0 || value > 255)
16✔
2710
            RAISE_EXCEPTION_STR(str_err_invalid_byte_value);
1✔
2711

2712
        error = KOS_buffer_fill(ctx, this_obj, begin, end, (uint8_t)value);
15✔
2713
    }
2714

2715
cleanup:
23✔
2716
    return error ? KOS_BADPTR : this_obj;
23✔
2717
}
2718

2719
struct KOS_PACK_FORMAT_S {
2720
    KOS_LOCAL fmt_str;
2721
    KOS_LOCAL data;
2722
    int       idx;
2723
    int       big_end;
2724
};
2725

2726
typedef int (*KOS_PACK_FORMAT_FUNC)(KOS_CONTEXT               ctx,
2727
                                    struct KOS_PACK_FORMAT_S *fmt,
2728
                                    unsigned                  fmt_offs,
2729
                                    KOS_OBJ_ID                buffer_obj,
2730
                                    char                      value_fmt,
2731
                                    unsigned                  size,
2732
                                    unsigned                  count);
2733

2734
static int is_whitespace(unsigned char_code)
317✔
2735
{
2736
    return char_code == 32;
317✔
2737
}
2738

2739
static int need_hex_char_print(unsigned char_code)
7✔
2740
{
2741
    return (char_code < 0x20) || (char_code >= 0x7F);
7✔
2742
}
2743

2744
static const char* get_type_str(KOS_OBJ_ID obj_id)
14✔
2745
{
2746
    const KOS_TYPE type = GET_OBJ_TYPE(obj_id);
14✔
2747

2748
    return (type <= OBJ_LAST_TYPE) ? KOS_get_type_name(type) : "unknown";
14✔
2749
}
2750

2751
static void pack_format_skip_spaces(KOS_CONTEXT ctx,
214✔
2752
                                    KOS_OBJ_ID  fmt_str,
2753
                                    unsigned   *i_ptr)
2754
{
2755
    const unsigned size = KOS_get_string_length(fmt_str);
214✔
2756
    unsigned       i    = *i_ptr;
214✔
2757
    unsigned       c;
2758

2759
    if (i >= size)
214✔
2760
        return;
2✔
2761

2762
    do {
2763
        c = KOS_string_get_char_code(ctx, fmt_str, (int)i++);
317✔
2764
        assert(c != ~0U);
317✔
2765
    } while (i < size && is_whitespace(c));
317✔
2766

2767
    if (i < size || ! is_whitespace(c))
212✔
2768
        i--;
211✔
2769

2770
    *i_ptr = i;
212✔
2771
}
2772

2773
static unsigned pack_format_get_count(KOS_CONTEXT ctx,
125✔
2774
                                      KOS_OBJ_ID  fmt_str,
2775
                                      unsigned   *i_ptr)
2776
{
2777
    const unsigned size  = KOS_get_string_length(fmt_str);
125✔
2778
    unsigned       i     = *i_ptr;
125✔
2779
    unsigned       c;
2780
    unsigned       count;
2781

2782
    assert(i < size);
125✔
2783

2784
    c = KOS_string_get_char_code(ctx, fmt_str, (int)i++);
125✔
2785

2786
    assert(c >= '0' && c <= '9');
125✔
2787

2788
    count = c - (unsigned)'0';
125✔
2789

2790
    while (i < size) {
151✔
2791

2792
        c = KOS_string_get_char_code(ctx, fmt_str, (int)i++);
104✔
2793

2794
        assert(c != ~0U);
104✔
2795

2796
        if (c < '0' || c > '9') {
104✔
2797
            i--;
76✔
2798
            break;
76✔
2799
        }
2800

2801
        if (count >= 429496729) {
28✔
2802
            i--;
2✔
2803
            count = ~0U;
2✔
2804
            break;
2✔
2805
        }
2806

2807
        count = count * 10 + (c - (unsigned)'0');
26✔
2808
    }
2809

2810
    *i_ptr = i;
125✔
2811
    return count;
125✔
2812
}
2813

2814
static int process_pack_format(KOS_CONTEXT               ctx,
74✔
2815
                               KOS_OBJ_ID                buffer_obj,
2816
                               KOS_PACK_FORMAT_FUNC      handler,
2817
                               struct KOS_PACK_FORMAT_S *fmt)
2818
{
2819
    int            error    = KOS_SUCCESS;
74✔
2820
    const unsigned fmt_size = KOS_get_string_length(fmt->fmt_str.o);
74✔
2821
    unsigned       i_fmt    = 0;
74✔
2822
    KOS_LOCAL      buffer;
2823

2824
    KOS_init_local_with(ctx, &buffer, buffer_obj);
74✔
2825

2826
    while (i_fmt < fmt_size) {
228✔
2827

2828
        unsigned count = 1;
195✔
2829
        unsigned size  = 1;
195✔
2830
        unsigned c;
2831
        unsigned offs;
2832

2833
        pack_format_skip_spaces(ctx, fmt->fmt_str.o, &i_fmt);
195✔
2834

2835
        if (i_fmt >= fmt_size)
195✔
2836
            break;
1✔
2837

2838
        offs = i_fmt;
194✔
2839

2840
        c = KOS_string_get_char_code(ctx, fmt->fmt_str.o, (int)i_fmt++);
194✔
2841
        assert(c != ~0U);
194✔
2842

2843
        if (c >= '0' && c <= '9') {
194✔
2844
            --i_fmt;
20✔
2845
            count = pack_format_get_count(ctx, fmt->fmt_str.o, &i_fmt);
20✔
2846
            if (count == ~0U) {
20✔
2847
                KOS_raise_printf(ctx, "invalid count at position %u", offs + 1);
1✔
2848
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
2849
            }
2850

2851
            pack_format_skip_spaces(ctx, fmt->fmt_str.o, &i_fmt);
19✔
2852

2853
            if (i_fmt >= fmt_size) {
19✔
2854
                KOS_raise_printf(ctx,
2✔
2855
                    "missing format character at the end of format string after count %u at position %u",
2856
                    count, offs + 1);
2857
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
2858
            }
2859

2860
            offs = i_fmt;
17✔
2861
            c = KOS_string_get_char_code(ctx, fmt->fmt_str.o, (int)i_fmt++);
17✔
2862
            assert(c != ~0U);
17✔
2863
        }
2864

2865
        switch (c) {
191✔
2866

2867
            case '<':
30✔
2868
                fmt->big_end = 0;
30✔
2869
                break;
30✔
2870

2871
            case '>':
31✔
2872
                fmt->big_end = 1;
31✔
2873
                break;
31✔
2874

2875
            case 'x':
6✔
2876
                break;
6✔
2877

2878
            case 'u':
122✔
2879
                /* fall through */
2880
            case 'i':
2881
                /* fall through */
2882
            case 'f':
2883
                /* fall through */
2884
            case 'b':
2885
                /* fall through */
2886
            case 's': {
2887
                unsigned next_c = ~0U;
122✔
2888

2889
                if (i_fmt >= fmt_size) {
122✔
2890
                    if (c != 's') {
10✔
2891
                        KOS_raise_printf(ctx,
2✔
2892
                            "missing size for format character '%c' at position %u\n",
2893
                            (char)c, offs + 1);
2✔
2894
                        RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
2895
                    }
2896
                }
2897
                else
2898
                    next_c = KOS_string_get_char_code(ctx, fmt->fmt_str.o, (int)i_fmt);
112✔
2899

2900
                if (next_c >= '0' && next_c <= '9') {
224✔
2901
                    const unsigned size_offs = i_fmt;
105✔
2902
                    size = pack_format_get_count(ctx, fmt->fmt_str.o, &i_fmt);
105✔
2903
                    if (size == ~0U) {
105✔
2904
                        KOS_raise_printf(ctx,
1✔
2905
                            "invalid size for format character '%c' at position %u",
2906
                            (char)c, size_offs + 1);
1✔
2907
                        RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
2908
                    }
2909
                }
2910
                else if (c == 's') {
15✔
2911
                    size = ~0U;
10✔
2912
                }
2913
                else {
2914
                    if (need_hex_char_print(next_c))
5✔
2915
                        KOS_raise_printf(ctx,
1✔
2916
                            "unexpected character '\\x{%x}' at position %u, expected size",
2917
                            next_c, i_fmt + 1);
2918
                    else
2919
                        KOS_raise_printf(ctx,
4✔
2920
                            "unexpected character '%c' at position %u, expected size",
2921
                            next_c, i_fmt + 1);
2922
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
5✔
2923
                }
2924
                break;
114✔
2925
            }
2926

2927
            default:
2✔
2928
                if (need_hex_char_print(c))
2✔
2929
                    KOS_raise_printf(ctx,
1✔
2930
                        "invalid format character '\\x{%x}' at position %u",
2931
                        c, i_fmt);
2932
                else
2933
                    KOS_raise_printf(ctx,
1✔
2934
                        "invalid format character '%c' at position %u",
2935
                        (char)c, i_fmt);
1✔
2936
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
2937
        }
2938

2939
        if (c != '<' && c != '>')
181✔
2940
            TRY(handler(ctx, fmt, offs, buffer.o, (char)c, size, count));
120✔
2941
    }
2942

2943
cleanup:
33✔
2944
    KOS_destroy_top_local(ctx, &buffer);
74✔
2945

2946
    return error;
74✔
2947
}
2948

2949
static int pack_format(KOS_CONTEXT               ctx,
73✔
2950
                       struct KOS_PACK_FORMAT_S *fmt,
2951
                       unsigned                  fmt_offs,
2952
                       KOS_OBJ_ID                buffer_obj,
2953
                       char                      value_fmt,
2954
                       unsigned                  size,
2955
                       unsigned                  count)
2956
{
2957
    int        error  = KOS_SUCCESS;
73✔
2958
    int        big_end;
2959
    uint8_t   *dst    = KOS_NULL;
73✔
2960
    KOS_VECTOR str_buf;
2961
    KOS_LOCAL  buffer;
2962

2963
    KOS_vector_init(&str_buf);
73✔
2964

2965
    KOS_init_local_with(ctx, &buffer, buffer_obj);
73✔
2966

2967
    if (fmt->idx < 0) {
73✔
2968
        KOS_OBJ_ID obj = fmt->data.o;
41✔
2969

2970
        fmt->idx = 1;
41✔
2971

2972
        if (KOS_get_array_size(obj) > 1) {
41✔
2973

2974
            obj = KOS_array_read(ctx, obj, 1);
40✔
2975
            TRY_OBJID(obj);
40✔
2976

2977
            if (GET_OBJ_TYPE(obj) == OBJ_ARRAY) {
40✔
2978
                fmt->data.o = obj;
2✔
2979
                fmt->idx    = 0;
2✔
2980
            }
2981
        }
2982
    }
2983

2984
    assert(size != ~0U || value_fmt == 's');
73✔
2985

2986
    if (size != ~0U && size && count) {
73✔
2987
        dst = KOS_buffer_make_room(ctx, buffer.o, size * count);
65✔
2988
        if ( ! dst)
65✔
2989
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
2990
    }
2991

2992
    big_end = fmt->big_end;
73✔
2993

2994
    switch (value_fmt) {
73✔
2995

2996
        case 'x':
3✔
2997
            assert(size == 1);
3✔
2998

2999
            if (dst && count)
3✔
3000
                memset(dst, 0, count);
3✔
3001
            break;
3✔
3002

3003
        case 'u':
27✔
3004
            /* fall through */
3005
        case 'i': {
3006

3007
            assert(size != ~0U);
27✔
3008
            if (size != 1 && size != 2 && size != 4 && size != 8) {
27✔
3009
                KOS_raise_printf(ctx, "invalid size in '%c%u' at position %u",
2✔
3010
                                 (char)value_fmt, size, fmt_offs + 1);
3011
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
3012
            }
3013

3014
            if ((unsigned)fmt->idx + count > KOS_get_array_size(fmt->data.o)) {
25✔
3015
                KOS_raise_printf(ctx,
2✔
3016
                    "not enough values to pack '%c%u' count %u at position %u; input has %u elements but required %u",
3017
                    (char)value_fmt, size, count, fmt_offs + 1,
3018
                    KOS_get_array_size(fmt->data.o), (unsigned)fmt->idx + count);
2✔
3019
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
3020
            }
3021

3022
            for ( ; count; count--) {
45✔
3023
                unsigned   i;
3024
                int64_t    value;
3025
                KOS_OBJ_ID value_obj = KOS_array_read(ctx, fmt->data.o, fmt->idx++);
23✔
3026

3027
                TRY_OBJID(value_obj);
24✔
3028

3029
                if ( ! IS_NUMERIC_OBJ(value_obj)) {
23✔
3030
                    KOS_raise_printf(ctx,
1✔
3031
                        "expected numeric value at index %u for '%c%u' at position %u, but got element of type '%s'",
3032
                        fmt->idx - 1, (char)value_fmt, size, fmt_offs + 1, get_type_str(value_obj));
1✔
3033
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
3034
                }
3035

3036
                TRY(KOS_get_integer(ctx, value_obj, &value));
22✔
3037

3038
                for (i = 0; i < size; i++) {
81✔
3039
                    const unsigned offs = big_end ? (size - 1U - i) : i;
59✔
3040
                    dst[offs] = (uint8_t)(value & 0xFF);
59✔
3041
                    value >>= 8;
59✔
3042
                }
3043

3044
                dst += size;
22✔
3045
            }
3046
            break;
22✔
3047
        }
3048

3049
        case 'f': {
11✔
3050

3051
            assert(size != ~0U);
11✔
3052
            if (size != 4 && size != 8) {
11✔
3053
                KOS_raise_printf(ctx, "invalid size in '%c%u' at position %u",
1✔
3054
                                 (char)value_fmt, size, fmt_offs + 1);
3055
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
3056
            }
3057

3058
            if ((unsigned)fmt->idx + count > KOS_get_array_size(fmt->data.o)) {
10✔
3059
                KOS_raise_printf(ctx,
1✔
3060
                    "not enough values to pack '%c%u' count %u at position %u; input has %u elements but required %u",
3061
                    (char)value_fmt, size, count, fmt_offs + 1,
3062
                    KOS_get_array_size(fmt->data.o), (unsigned)fmt->idx + count);
1✔
3063
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
3064
            }
3065

3066
            for ( ; count; count--) {
17✔
3067
                unsigned   i;
3068
                KOS_OBJ_ID value_obj = KOS_array_read(ctx, fmt->data.o, fmt->idx++);
9✔
3069
                double     value     = 0;
9✔
3070
                uint64_t   out_val;
3071

3072
                TRY_OBJID(value_obj);
9✔
3073

3074
                if (IS_SMALL_INT(value_obj))
9✔
3075
                    value = (double)GET_SMALL_INT(value_obj);
1✔
3076

3077
                else switch (READ_OBJ_TYPE(value_obj)) {
8✔
3078

3079
                    case OBJ_INTEGER:
1✔
3080
                        value = (double)(OBJPTR(INTEGER, value_obj)->value);
1✔
3081
                        break;
1✔
3082

3083
                    case OBJ_FLOAT:
6✔
3084
                        value = OBJPTR(FLOAT, value_obj)->value;
6✔
3085
                        break;
6✔
3086

3087
                    default:
1✔
3088
                        KOS_raise_printf(ctx,
1✔
3089
                            "expected numeric value at index %u for '%c%u' at position %u, but got element of type '%s'",
3090
                            fmt->idx - 1, (char)value_fmt, size, fmt_offs + 1, get_type_str(value_obj));
1✔
3091
                        RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
3092
                        break;
3093
                }
3094

3095
                if (size == 4)
8✔
3096
                    out_val = kos_float_to_uint32_t((float)value);
5✔
3097
                else
3098
                    out_val = kos_double_to_uint64_t(value);
3✔
3099

3100
                for (i = 0; i < size; i++) {
52✔
3101
                    const unsigned offs = big_end ? (size - 1U - i) : i;
44✔
3102
                    dst[offs] = (uint8_t)(out_val & 0xFFU);
44✔
3103
                    out_val >>= 8;
44✔
3104
                }
3105

3106
                dst += size;
8✔
3107
            }
3108
            break;
8✔
3109
        }
3110

3111
        case 'b': {
8✔
3112

3113
            assert(size != ~0U);
8✔
3114
            if ((unsigned)fmt->idx + count > KOS_get_array_size(fmt->data.o)) {
8✔
3115
                KOS_raise_printf(ctx,
2✔
3116
                    "not enough values to pack '%c%u' count %u at position %u; input has %u elements but required %u",
3117
                    (char)value_fmt, size, count, fmt_offs + 1,
3118
                    KOS_get_array_size(fmt->data.o), (unsigned)fmt->idx + count);
2✔
3119
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
3120
            }
3121

3122
            for ( ; count; count--) {
12✔
3123
                KOS_OBJ_ID     value_obj = KOS_array_read(ctx, fmt->data.o, fmt->idx++);
7✔
3124
                const uint8_t *src       = KOS_NULL;
7✔
3125
                uint32_t       src_size;
3126
                uint32_t       copy_size;
3127

3128
                TRY_OBJID(value_obj);
7✔
3129

3130
                if (GET_OBJ_TYPE(value_obj) != OBJ_BUFFER) {
7✔
3131
                    KOS_raise_printf(ctx,
1✔
3132
                        "expected buffer at index %u for '%c%u' at position %u, but got element of type '%s'",
3133
                        fmt->idx - 1, (char)value_fmt, size, fmt_offs + 1, get_type_str(value_obj));
1✔
3134
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
3135
                }
3136

3137
                src_size = KOS_get_buffer_size(value_obj);
6✔
3138
                if (src_size)
6✔
3139
                    src = KOS_buffer_data_const(value_obj);
6✔
3140

3141
                copy_size = size > src_size ? src_size : size;
6✔
3142

3143
                if (copy_size) {
6✔
3144
                    const uint8_t *const src_end = src + copy_size;
6✔
3145

3146
                    if (src_end > dst && src < dst)
6✔
3147
                        copy_size = (uint32_t)(dst - src);
1✔
3148

3149
                    memcpy(dst, src, copy_size);
6✔
3150
                }
3151

3152
                if (copy_size < size)
6✔
3153
                    memset(dst + copy_size, 0, size - copy_size);
2✔
3154

3155
                dst += size;
6✔
3156
            }
3157
            break;
5✔
3158
        }
3159

3160
        case 's':
24✔
3161
            /* fall through */
3162
        default: {
3163

3164
            assert(value_fmt == 's');
24✔
3165

3166
            if ((unsigned)fmt->idx + count > KOS_get_array_size(fmt->data.o)) {
24✔
3167
                KOS_raise_printf(ctx,
1✔
3168
                    "not enough values to pack '%c%u' count %u at position %u; input has %u elements but required %u",
3169
                    (char)value_fmt, size, count, fmt_offs + 1,
3170
                    KOS_get_array_size(fmt->data.o), (unsigned)fmt->idx + count);
1✔
3171
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
3172
            }
3173

3174
            for ( ; count; count--) {
36✔
3175
                KOS_OBJ_ID value_obj = KOS_array_read(ctx, fmt->data.o, fmt->idx++);
24✔
3176
                uint32_t   copy_size;
3177

3178
                TRY_OBJID(value_obj);
24✔
3179

3180
                if (GET_OBJ_TYPE(value_obj) != OBJ_STRING) {
24✔
3181
                    KOS_raise_printf(ctx,
11✔
3182
                        "expected string at index %u for '%c%u' at position %u, but got element of type '%s'",
3183
                        fmt->idx - 1, (char)value_fmt, size, fmt_offs + 1, get_type_str(value_obj));
11✔
3184
                    RAISE_ERROR(KOS_ERROR_EXCEPTION);
11✔
3185
                }
3186

3187
                TRY(KOS_string_to_cstr_vec(ctx, value_obj, &str_buf));
13✔
3188

3189
                copy_size = size > str_buf.size-1 ? (uint32_t)str_buf.size-1 : size;
13✔
3190

3191
                if (size == ~0U) {
13✔
3192
                    dst = KOS_buffer_make_room(ctx, buffer.o, copy_size);
5✔
3193
                    if ( ! dst)
5✔
3194
                        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
3195
                }
3196
                else {
3197
                    assert(dst || ! copy_size);
8✔
3198
                }
3199

3200
                if (copy_size)
13✔
3201
                    memcpy(dst, str_buf.buffer, copy_size);
12✔
3202

3203
                if (size != ~0U) {
13✔
3204
                    if (copy_size < size)
8✔
3205
                        memset(dst + copy_size, 0, size - copy_size);
2✔
3206

3207
                    dst += size;
8✔
3208
                }
3209
            }
3210
            break;
12✔
3211
        }
3212
    }
3213

3214
cleanup:
73✔
3215
    KOS_destroy_top_local(ctx, &buffer);
73✔
3216
    KOS_vector_destroy(&str_buf);
73✔
3217
    return error;
73✔
3218
}
3219

3220
static int unpack_format(KOS_CONTEXT               ctx,
47✔
3221
                         struct KOS_PACK_FORMAT_S *fmt,
3222
                         unsigned                  fmt_offs,
3223
                         KOS_OBJ_ID                buffer_obj,
3224
                         char                      value_fmt,
3225
                         unsigned                  size,
3226
                         unsigned                  count)
3227
{
3228
    int            error     = KOS_SUCCESS;
47✔
3229
    int            offs;
3230
    const uint32_t data_size = KOS_get_buffer_size(buffer_obj);
47✔
3231
    int            big_end   = fmt->big_end;
47✔
3232
    KOS_OBJ_ID     obj;
3233
    KOS_LOCAL      buffer;
3234

3235
    KOS_init_local_with(ctx, &buffer, buffer_obj);
47✔
3236

3237
    if (size == ~0U) {
47✔
3238
        assert(value_fmt == 's');
5✔
3239
        if (count != 1) {
5✔
3240
            KOS_raise_printf(ctx,
1✔
3241
                "invalid count %u for format character 's' without size at position %u, expected count 1",
3242
                count, fmt_offs + 1);
3243
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
3244
        }
3245

3246
        size = data_size - fmt->idx;
4✔
3247
    }
3248

3249
    if (fmt->idx + size * count > data_size) {
46✔
3250
        KOS_raise_printf(ctx,
2✔
3251
            "buffer with size %u too short to unpack data for format character "
3252
            "'%c%u' at position %u, need size to be at least %u",
3253
            data_size, (char)value_fmt, size, fmt_offs + 1, fmt->idx + size * count);
2✔
3254
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
3255
    }
3256

3257
    if ( ! count)
44✔
3258
        goto cleanup;
1✔
3259

3260
    assert(data_size || ! size);
43✔
3261
    assert( ! size || KOS_buffer_data_const(buffer.o));
43✔
3262

3263
    offs = fmt->idx;
43✔
3264

3265
    switch (value_fmt) {
43✔
3266

3267
        case 'x':
2✔
3268
            assert(size == 1);
2✔
3269
            offs += size * count;
2✔
3270
            break;
2✔
3271

3272
        case 'f':
35✔
3273
            /* fall through */
3274
        case 'i':
3275
            /* fall through */
3276
        case 'u': {
3277
            if ((value_fmt == 'f' && size != 4 && size != 8) ||
35✔
3278
                (size != 1 && size != 2 && size != 4 && size != 8)) {
27✔
3279
                KOS_raise_printf(ctx, "invalid size in '%c%u' at position %u",
1✔
3280
                                 (char)value_fmt, size, fmt_offs + 1);
3281
                RAISE_ERROR(KOS_ERROR_EXCEPTION);
1✔
3282
            }
3283

3284
            for ( ; count; count--) {
68✔
3285
                uint64_t value = 0;
34✔
3286
                unsigned i;
3287

3288
                for (i = 0; i < size; i++) {
162✔
3289
                    const unsigned rel_offs = big_end ? i : (size - 1 - i);
128✔
3290
                    value = (value << 8) | KOS_buffer_data_const(buffer.o)[offs + rel_offs];
128✔
3291
                }
3292

3293
                if (value_fmt == 'i' && size < 8) {
50✔
3294
                    const unsigned shift = 64 - 8 * size;
16✔
3295
                    const int64_t  ival  = (int64_t)(value << shift);
16✔
3296
                    obj = KOS_new_int(ctx, ival >> shift);
16✔
3297
                }
3298
                else if (value_fmt == 'f') {
18✔
3299
                    double fvalue;
3300
                    if (size == 4) {
4✔
3301
                        union {
3302
                            float    f;
3303
                            uint32_t u;
3304
                        } u2f;
3305
                        u2f.u  = (uint32_t)value;
2✔
3306
                        fvalue = (double)u2f.f;
2✔
3307
                    }
3308
                    else {
3309
                        union {
3310
                            double   f;
3311
                            uint64_t u;
3312
                        } u2f;
3313
                        u2f.u  = value;
2✔
3314
                        fvalue = u2f.f;
2✔
3315
                    }
3316
                    obj = KOS_new_float(ctx, fvalue);
4✔
3317
                }
3318
                else
3319
                    obj = KOS_new_int(ctx, (int64_t)value);
14✔
3320

3321
                TRY_OBJID(obj);
34✔
3322

3323
                TRY(KOS_array_push(ctx, fmt->data.o, obj, KOS_NULL));
34✔
3324

3325
                offs += size;
34✔
3326
            }
3327
            break;
34✔
3328
        }
3329

3330
        case 'b': {
1✔
3331
            for ( ; count; count--) {
3✔
3332
                obj = KOS_new_buffer(ctx, size);
2✔
3333

3334
                TRY_OBJID(obj);
2✔
3335

3336
                if (size) {
2✔
3337
                    uint8_t *data = KOS_buffer_data_volatile(ctx, obj);
2✔
3338

3339
                    if ( ! data)
2✔
3340
                        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
3341

3342
                    memcpy(data, &KOS_buffer_data_const(buffer.o)[offs], size);
2✔
3343
                }
3344

3345
                TRY(KOS_array_push(ctx, fmt->data.o, obj, KOS_NULL));
2✔
3346

3347
                offs += size;
2✔
3348
            }
3349
            break;
1✔
3350
        }
3351

3352
        case 's':
5✔
3353
            /* fall through */
3354
        default: {
3355
            assert(value_fmt == 's');
5✔
3356
            for ( ; count; count--) {
10✔
3357
                if (size)
5✔
3358
                    obj = KOS_new_string_from_buffer(ctx, buffer.o, offs, offs + size);
4✔
3359
                else
3360
                    obj = KOS_new_string(ctx, KOS_NULL, 0);
1✔
3361

3362
                TRY_OBJID(obj);
5✔
3363

3364
                TRY(KOS_array_push(ctx, fmt->data.o, obj, KOS_NULL));
5✔
3365

3366
                offs += size;
5✔
3367
            }
3368
            break;
5✔
3369
        }
3370
    }
3371

3372
    fmt->idx = offs;
42✔
3373

3374
cleanup:
47✔
3375
    KOS_destroy_top_local(ctx, &buffer);
47✔
3376
    return error;
47✔
3377
}
3378

3379
/* @item base buffer.prototype.pack()
3380
 *
3381
 *     buffer.prototype.pack(format, args...)
3382
 *
3383
 * Converts parameters to binary form and appends them at the end of the buffer.
3384
 *
3385
 * Returns the buffer which has been modified.
3386
 *
3387
 * `format` is a string, which describes how subsequent values are to be packed.
3388
 * Formatting characters in the `format` string indicate how coresponding
3389
 * subsequent values will be packed.
3390
 *
3391
 * The following table lists available formatting characters:
3392
 *
3393
 * | Fmt | Value in buffer                   | Argument in `pack()` | Returned from `unpack()` |
3394
 * |-----|-----------------------------------|----------------------|--------------------------|
3395
 * | <   | Switch to little endian (default) |                      |                          |
3396
 * | >   | Switch to big endian              |                      |                          |
3397
 * | u1  | 8-bit unsigned integer            | integer or float     | integer                  |
3398
 * | u2  | 16-bit unsigned integer           | integer or float     | integer                  |
3399
 * | u4  | 32-bit unsigned integer           | integer or float     | integer                  |
3400
 * | u8  | 64-bit unsigned integer           | integer or float     | integer                  |
3401
 * | i1  | 8-bit signed integer              | integer or float     | integer                  |
3402
 * | i2  | 16-bit signed integer             | integer or float     | integer                  |
3403
 * | i4  | 32-bit signed integer             | integer or float     | integer                  |
3404
 * | i8  | 64-bit signed integer             | integer or float     | integer                  |
3405
 * | f4  | 32-bit floating point             | integer or float     | float                    |
3406
 * | f8  | 64-bit floating point             | integer or float     | float                    |
3407
 * | s   | UTF-8 string (no size)            | string               | string                   |
3408
 * | s#  | UTF-8 string with size `#`        | string               | string                   |
3409
 * | b#  | Sequence of bytes with size `#`   | buffer               | buffer                   |
3410
 * | x   | Padding byte                      | zero byte written    | ignored                  |
3411
 *
3412
 * The `<` and `>` characters switch to little endian and big endian mode,
3413
 * respectively, for unsigned, signed and floating point values.  They apply to
3414
 * formatting characters following them and can be used multiple times if needed.
3415
 * If neither `<` nor `>` is specified as the first formatting character, then
3416
 * initial unsigned, signed and floating point values are formatted as little
3417
 * endian until the first `>` formatting character is encountered.
3418
 *
3419
 * `u#` and `i#` produce integer values, unsigned and signed, respectively.
3420
 * The available sizes for these values are 1, 2, 4 and 8 and correspond to
3421
 * how many bytes are written for each number.  The values are formatted as
3422
 * little endian or big endian, depending on the current mode.  If a number
3423
 * doesn't fit in the number of bytes specified, then it is simply truncated.
3424
 * If a value of type float is provided instead of an integer, it is converted
3425
 * to integer using "floor" operation.  The signedness does not matter for
3426
 * `pack()`, it only makes a difference for `unpack()`.
3427
 *
3428
 * `f4` and `f8` produce floating point values of 4 and 8 bytes in size.
3429
 * The values are formatted as little endian or big endian, depending on the
3430
 * current mode.  If an integer value is provided, it is converted to floating
3431
 * point.
3432
 *
3433
 * `s` takes a string argument, converts it to UTF-8 and writes as many bytes
3434
 * as specified in the size to the buffer.  If the resulting UTF-8 sequence is
3435
 * too short, zero (NUL) bytes are written to fill it up to the specified
3436
 * string size.
3437
 *
3438
 * If `s` is not followed by size, then the string is converted to UTF-8 byte
3439
 * sequence and the entire sequence is written to the buffer.
3440
 *
3441
 * `b` takes a buffer argument and writes as many bytes as specified.  If the
3442
 * buffer argument is too short, zero bytes are written to satisfy the requested
3443
 * size.
3444
 *
3445
 * `x` is a padding byte character.  A zero is written for each `x` padding byte
3446
 * and no arguments are consumed.
3447
 *
3448
 * Multiple formatting characters can be optionally separated by spaces for
3449
 * better readability.
3450
 *
3451
 * Every formatting character can be preceded by an optional count number,
3452
 * which specifies how many times this value occurs.
3453
 *
3454
 * All formatting character must have corresponding arguments passed to `pack()`.
3455
 * If not enough arguments are passed to match the number of formatting characters,
3456
 * `pack()` throws an exception.
3457
 *
3458
 * Examples:
3459
 *
3460
 *     > buffer().pack("u4", 0x1234)
3461
 *     <34 12 00 00>
3462
 *     > buffer().pack(">u4", 0x1234)
3463
 *     <00 00 12 34>
3464
 *     > buffer().pack("s10", "hello")
3465
 *     <68 65 6c 6c 6f 00 00 00 00 00>
3466
 *     > buffer().pack("s", "hello")
3467
 *     <68 65 6c 6c 6f>
3468
 *     > buffer().pack("b3", buffer([1,2,3,4,5,6,7,8,9]))
3469
 *     <01 02 03>
3470
 *     > buffer().pack("u1 x i1 x f4", -3, -3, 1)
3471
 *     <fd 00 fd 00 00 00 80 3f>
3472
 *     > buffer().pack("> 3 u2", 0x100F, 0x200F, 0x300F)
3473
 *     <10 0F 20 0F 30 0F>
3474
 */
3475
static const KOS_CONVERT pack_args[2] = {
3476
    KOS_DEFINE_MANDATORY_ARG(str_format),
3477
    KOS_DEFINE_TAIL_ARG()
3478
};
3479

3480
static KOS_OBJ_ID pack(KOS_CONTEXT ctx,
56✔
3481
                       KOS_OBJ_ID  this_obj,
3482
                       KOS_OBJ_ID  args_obj)
3483
{
3484
    int                      error;
3485
    struct KOS_PACK_FORMAT_S fmt;
3486
    KOS_LOCAL                buffer;
3487

3488
    KOS_init_locals(ctx, &fmt.fmt_str, &fmt.data, &buffer, kos_end_locals);
56✔
3489

3490
    buffer.o = this_obj;
56✔
3491

3492
    fmt.fmt_str.o = KOS_array_read(ctx, args_obj, 0);
56✔
3493
    fmt.data.o    = args_obj;
56✔
3494
    fmt.idx       = -1;
56✔
3495
    fmt.big_end   = 0;
56✔
3496

3497
    assert( ! IS_BAD_PTR(fmt.fmt_str.o));
56✔
3498

3499
    if (GET_OBJ_TYPE(fmt.fmt_str.o) == OBJ_STRING)
56✔
3500
        error = process_pack_format(ctx, buffer.o, pack_format, &fmt);
55✔
3501
    else {
3502
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
1✔
3503
        error = KOS_ERROR_EXCEPTION;
1✔
3504
    }
3505

3506
    buffer.o = KOS_destroy_top_locals(ctx, &fmt.fmt_str, &buffer);
56✔
3507

3508
    return error ? KOS_BADPTR : buffer.o;
56✔
3509
}
3510

3511
/* @item base buffer.prototype.unpack()
3512
 *
3513
 *     buffer.prototype.unpack(format, pos = 0)
3514
 *
3515
 * Unpacks values from their binary form from a buffer.
3516
 *
3517
 * Returns an array containing values unpacked from the buffer.
3518
 *
3519
 * `pos` is the position in the buffer at which to start extracting the values.
3520
 * `pos` defaults to `0`.
3521
 *
3522
 * `format` is a string, which describes how values are to be unpacked.
3523
 *
3524
 * Refer to [buffer.prototype.pack()](#bufferprototypeunpack) for description
3525
 * of the contents of the `format` string.
3526
 *
3527
 * Differences in behavior from `pack()`:
3528
 *
3529
 *  * Unpacking signed `i#` and unsigned `u#` values results in different
3530
 *    values returned depending on the most significant bit for sizes 1, 2 and 4.
3531
 *  * Formatting character `s` must always have a size, unless it is the last
3532
 *    formatting character in the format string.  If `s` does not have a size,
3533
 *    all remaining bytes from a buffer are converted into a string.
3534
 *  * Padding bytes `x` just skip over bytes in the buffer and do not produce
3535
 *    any returned values.
3536
 *
3537
 * If the buffer does not contain enough bytes as required by the formatting
3538
 * string, `unpack()` throws an exception.
3539
 *
3540
 * Examples:
3541
 *
3542
 *     > buffer([1,2, 0x3f,0x80,0,0, 0x41,0x42,0x43]).unpack("u2 >f4 s")
3543
 *     [513, 1.0, "ABC"]
3544
 */
3545
static const KOS_CONVERT unpack_args[3] = {
3546
    KOS_DEFINE_MANDATORY_ARG(str_format              ),
3547
    KOS_DEFINE_OPTIONAL_ARG( str_pos, TO_SMALL_INT(0)),
3548
    KOS_DEFINE_TAIL_ARG()
3549
};
3550

3551
static KOS_OBJ_ID unpack(KOS_CONTEXT ctx,
21✔
3552
                         KOS_OBJ_ID  this_obj,
3553
                         KOS_OBJ_ID  args_obj)
3554
{
3555
    struct KOS_PACK_FORMAT_S fmt;
3556
    KOS_LOCAL                buffer;
3557
    KOS_LOCAL                args;
3558
    int                      error;
3559

3560
    KOS_init_locals(ctx, &buffer, &args, &fmt.fmt_str, &fmt.data, kos_end_locals);
21✔
3561

3562
    buffer.o = this_obj;
21✔
3563
    args.o   = args_obj;
21✔
3564

3565
    fmt.fmt_str.o = KOS_BADPTR;
21✔
3566
    fmt.data.o    = KOS_BADPTR;
21✔
3567
    fmt.idx       = 0;
21✔
3568
    fmt.big_end   = 0;
21✔
3569

3570
    assert( ! IS_BAD_PTR(buffer.o));
21✔
3571

3572
    if (GET_OBJ_TYPE(buffer.o) != OBJ_BUFFER)
21✔
3573
        RAISE_EXCEPTION_STR(str_err_not_buffer);
1✔
3574

3575
    fmt.fmt_str.o = KOS_array_read(ctx, args.o, 0);
20✔
3576
    TRY_OBJID(fmt.fmt_str.o);
20✔
3577

3578
    if (GET_OBJ_TYPE(fmt.fmt_str.o) != OBJ_STRING)
20✔
3579
        RAISE_EXCEPTION_STR(str_err_not_string);
1✔
3580

3581
    fmt.data.o = KOS_new_array(ctx, 0);
19✔
3582
    TRY_OBJID(fmt.data.o);
19✔
3583

3584
    TRY(KOS_get_index_arg(ctx, args.o, 1, 0, (int)KOS_get_buffer_size(buffer.o), KOS_VOID_INDEX_IS_BEGIN, &fmt.idx));
19✔
3585

3586
    TRY(process_pack_format(ctx, buffer.o, unpack_format, &fmt));
19✔
3587

3588
cleanup:
14✔
3589
    fmt.data.o = KOS_destroy_top_locals(ctx, &buffer, &fmt.data);
21✔
3590

3591
    return error ? KOS_BADPTR : fmt.data.o;
21✔
3592
}
3593

3594
/* @item base buffer.prototype.copy_buffer()
3595
 *
3596
 *     buffer.prototype.copy_buffer(pos, source)
3597
 *     buffer.prototype.copy_buffer(pos, source, begin)
3598
 *     buffer.prototype.copy_buffer(pos, source, begin, end)
3599
 *
3600
 * Copies a range of bytes from source buffer to the buffer on which it is invoked.
3601
 *
3602
 * Returns the destination buffer being modified (`this`).
3603
 *
3604
 * Stops copying once the last byte in the destination buffer is overwritten,
3605
 * the destination buffer is not grown even if more bytes from the source
3606
 * buffer could be copied.
3607
 *
3608
 * `pos` is the position at which to start placing bytes from the source
3609
 * buffer.  If it is `void`, it is equivalent to `0`.  If it is negative,
3610
 * it is an offset from the end of the destination buffer.
3611
 *
3612
 * `source` is the source buffer to copy from.
3613
 *
3614
 * `begin` is the offset of the first byte in the source buffer to start
3615
 * copying from.  `begin` defaults to `0`.  If it is `void`, it is
3616
 * equivalent to `0`.  If it is negative, it is an offset from the end of
3617
 * the source buffer.
3618
 *
3619
 * `end` is the offset of the byte at which to stop copying from the
3620
 * source buffer.  This byte is not copied.  `end` defaults to the size
3621
 * of the source buffer.  If it is `void`, it is equivalent to the size
3622
 * of the source buffer.  If it is negative, it is an offset from the end
3623
 * of the source buffer.
3624
 *
3625
 * Example:
3626
 *
3627
 *     > const dst = buffer([1, 1, 1, 1, 1])
3628
 *     > const src = buffer([2, 2, 2, 2, 2])
3629
 *     > dst.copy_buffer(2, src)
3630
 *     <01 01 02 02 02>
3631
 */
3632
static const KOS_CONVERT copy_buffer_args[5] = {
3633
    KOS_DEFINE_MANDATORY_ARG(str_pos                    ),
3634
    KOS_DEFINE_MANDATORY_ARG(str_source                 ),
3635
    KOS_DEFINE_OPTIONAL_ARG( str_begin,  TO_SMALL_INT(0)),
3636
    KOS_DEFINE_OPTIONAL_ARG( str_end,    KOS_VOID       ),
3637
    KOS_DEFINE_TAIL_ARG()
3638
};
3639

3640
static KOS_OBJ_ID copy_buffer(KOS_CONTEXT ctx,
30✔
3641
                              KOS_OBJ_ID  this_obj,
3642
                              KOS_OBJ_ID  args_obj)
3643
{
3644
    int64_t    dest_begin = 0;
30✔
3645
    int64_t    src_begin  = 0;
30✔
3646
    int64_t    src_end    = MAX_INT64;
30✔
3647
    KOS_OBJ_ID arg;
3648
    KOS_OBJ_ID src;
3649
    int        error      = KOS_SUCCESS;
30✔
3650

3651
    assert(KOS_get_array_size(args_obj) > 3);
30✔
3652

3653
    arg = KOS_array_read(ctx, args_obj, 0);
30✔
3654

3655
    if (IS_NUMERIC_OBJ(arg))
30✔
3656
        TRY(KOS_get_integer(ctx, arg, &dest_begin));
23✔
3657
    else if (READ_OBJ_TYPE(arg) != OBJ_VOID)
7✔
3658
        RAISE_EXCEPTION_STR(str_err_unsup_operand_types);
3✔
3659

3660
    src = KOS_array_read(ctx, args_obj, 1);
27✔
3661
    TRY_OBJID(src);
27✔
3662

3663
    arg = KOS_array_read(ctx, args_obj, 2);
27✔
3664
    TRY_OBJID(arg);
27✔
3665

3666
    if (IS_NUMERIC_OBJ(arg))
27✔
3667
        TRY(KOS_get_integer(ctx, arg, &src_begin));
22✔
3668
    else if (READ_OBJ_TYPE(arg) != OBJ_VOID)
5✔
3669
        RAISE_EXCEPTION_STR(str_err_unsup_operand_types);
2✔
3670

3671
    arg = KOS_array_read(ctx, args_obj, 3);
25✔
3672
    TRY_OBJID(arg);
25✔
3673

3674
    if (IS_NUMERIC_OBJ(arg))
25✔
3675
        TRY(KOS_get_integer(ctx, arg, &src_end));
3✔
3676
    else if (READ_OBJ_TYPE(arg) != OBJ_VOID)
22✔
3677
        RAISE_EXCEPTION_STR(str_err_unsup_operand_types);
1✔
3678

3679
    error = KOS_buffer_copy(ctx, this_obj, dest_begin, src, src_begin, src_end);
24✔
3680

3681
cleanup:
30✔
3682
    return error ? KOS_BADPTR : this_obj;
30✔
3683
}
3684

3685
/* @item base array.prototype.reserve()
3686
 *
3687
 *     array.prototype.reserve(size)
3688
 *
3689
 * Allocate array storage without resizing the array.
3690
 *
3691
 * The function has no visible effect, but can be used for optimization
3692
 * to avoid reallocating array storage when resizing it or continuously
3693
 * adding more elements.
3694
 *
3695
 * Returns the array object itself (`this`).
3696
 */
3697

3698
/* @item base buffer.prototype.reserve()
3699
 *
3700
 *     buffer.prototype.reserve(size)
3701
 *
3702
 * Allocate buffer storage without resizing the buffer.
3703
 *
3704
 * The function has no visible effect, but can be used for optimization
3705
 * to avoid reallocating buffer storage when resizing it.
3706
 *
3707
 * Returns the buffer object itself (`this`).
3708
 */
3709
static const KOS_CONVERT reserve_args[2] = {
3710
    KOS_DEFINE_MANDATORY_ARG(str_size),
3711
    KOS_DEFINE_TAIL_ARG()
3712
};
3713

3714
static KOS_OBJ_ID reserve(KOS_CONTEXT ctx,
9✔
3715
                          KOS_OBJ_ID  this_obj,
3716
                          KOS_OBJ_ID  args_obj)
3717
{
3718
    int        error = KOS_SUCCESS;
9✔
3719
    int64_t    size;
3720
    KOS_OBJ_ID size_obj;
3721
    KOS_LOCAL  self;
3722

3723
    assert(KOS_get_array_size(args_obj) >= 1);
9✔
3724

3725
    KOS_init_local_with(ctx, &self, this_obj);
9✔
3726

3727
    size_obj = KOS_array_read(ctx, args_obj, 0);
9✔
3728
    TRY_OBJID(size_obj);
9✔
3729

3730
    TRY(KOS_get_integer(ctx, size_obj, &size));
9✔
3731

3732
    if (GET_OBJ_TYPE(self.o) == OBJ_BUFFER) {
8✔
3733
        if (size < 0 || size > INT_MAX)
4✔
3734
            RAISE_EXCEPTION_STR(str_err_invalid_buffer_size);
1✔
3735

3736
        TRY(KOS_buffer_reserve(ctx, self.o, (uint32_t)size));
3✔
3737
    }
3738
    else {
3739
        if (size < 0 || size > INT_MAX)
4✔
3740
            RAISE_EXCEPTION_STR(str_err_invalid_array_size);
1✔
3741

3742
        TRY(KOS_array_reserve(ctx, self.o, (uint32_t)size));
3✔
3743
    }
3744

3745
cleanup:
3✔
3746
    self.o = KOS_destroy_top_local(ctx, &self);
9✔
3747

3748
    return error ? KOS_BADPTR : self.o;
9✔
3749
}
3750

3751
/* @item base array.prototype.cas()
3752
 *
3753
 *     array.prototype.cas(pos, old_value, new_value)
3754
 *
3755
 * Atomic compare-and-swap for an array element.
3756
 *
3757
 * If array element at index `pos` equals to `old_value`, it is swapped
3758
 * with element `new_value`.  If the current array element at that index
3759
 * is not `old_value`, it is left unchanged.
3760
 *
3761
 * The compare-and-swap operation is performed atomically, but without any ordering
3762
 * guarantees.
3763
 *
3764
 * The element comparison is done by comparing object reference, not contents.
3765
 *
3766
 * Returns the original element stored in the array at index `pos` before the `cas`
3767
 * operation.  If `cas` failed, returns the value stored at the time of the comparison.
3768
 * If `cas` succeeded, returns `old_value`.
3769
 */
3770
static const KOS_CONVERT array_cas_args[4] = {
3771
    KOS_DEFINE_MANDATORY_ARG(str_pos      ),
3772
    KOS_DEFINE_MANDATORY_ARG(str_old_value),
3773
    KOS_DEFINE_MANDATORY_ARG(str_new_value),
3774
    KOS_DEFINE_TAIL_ARG()
3775
};
3776

3777
static KOS_OBJ_ID array_cas(KOS_CONTEXT ctx,
10✔
3778
                            KOS_OBJ_ID  this_obj,
3779
                            KOS_OBJ_ID  args_obj)
3780
{
3781
    int            error = KOS_SUCCESS;
10✔
3782
    int64_t        pos   = 0;
10✔
3783
    KOS_OBJ_ID     ret   = KOS_BADPTR;
10✔
3784
    KOS_OBJ_ID     pos_obj;
3785
    KOS_OBJ_ID     old_val;
3786
    KOS_OBJ_ID     new_val;
3787

3788
    if (GET_OBJ_TYPE(this_obj) != OBJ_ARRAY)
10✔
3789
        RAISE_EXCEPTION_STR(str_err_not_array);
1✔
3790

3791
    pos_obj = KOS_array_read(ctx, args_obj, 0);
9✔
3792
    TRY_OBJID(pos_obj);
9✔
3793

3794
    old_val = KOS_array_read(ctx, args_obj, 1);
9✔
3795
    TRY_OBJID(pos_obj);
9✔
3796

3797
    new_val = KOS_array_read(ctx, args_obj, 2);
9✔
3798
    TRY_OBJID(pos_obj);
9✔
3799

3800
    if (!IS_NUMERIC_OBJ(pos_obj))
9✔
3801
        RAISE_EXCEPTION_STR(str_err_unsup_operand_types);
1✔
3802

3803
    TRY(KOS_get_integer(ctx, pos_obj, &pos));
8✔
3804

3805
    ret = KOS_array_cas(ctx, this_obj, (int)pos, old_val, new_val);
8✔
3806

3807
cleanup:
10✔
3808
    return error ? KOS_BADPTR : ret;
10✔
3809
}
3810

3811
/* @item base array.prototype.insert_array()
3812
 *
3813
 *     array.prototype.insert_array(begin, end, array)
3814
 *
3815
 * Inserts elements from one array into `this` array, possibly replacing
3816
 * existing elements.
3817
 *
3818
 * This function is identical in behavior to `array.prototype.insert()`.  In
3819
 * most circumstances `array.prototype.insert()` is recommended instead.
3820
 * `array.prototype.insert_array()` requires the iterable argument to be
3821
 * an array.
3822
 */
3823
static const KOS_CONVERT insert_array_args[4] = {
3824
    KOS_DEFINE_MANDATORY_ARG(str_begin),
3825
    KOS_DEFINE_MANDATORY_ARG(str_end  ),
3826
    KOS_DEFINE_MANDATORY_ARG(str_array),
3827
    KOS_DEFINE_TAIL_ARG()
3828
};
3829

3830
static KOS_OBJ_ID insert_array(KOS_CONTEXT ctx,
82✔
3831
                               KOS_OBJ_ID  this_obj,
3832
                               KOS_OBJ_ID  args_obj)
3833
{
3834
    KOS_OBJ_ID begin_obj;
3835
    KOS_OBJ_ID end_obj;
3836
    KOS_OBJ_ID src_obj;
3837
    KOS_LOCAL  args;
3838
    KOS_LOCAL  self;
3839
    int64_t    src_len;
3840
    int64_t    begin = 0;
82✔
3841
    int64_t    end   = 0;
82✔
3842
    int        error = KOS_SUCCESS;
82✔
3843

3844
    assert(KOS_get_array_size(args_obj) >= 3);
82✔
3845

3846
    KOS_init_local_with(ctx, &self, this_obj);
82✔
3847
    KOS_init_local_with(ctx, &args, args_obj);
82✔
3848

3849
    begin_obj = KOS_array_read(ctx, args.o, 0);
82✔
3850
    TRY_OBJID(begin_obj);
82✔
3851

3852
    end_obj = KOS_array_read(ctx, args.o, 1);
82✔
3853
    TRY_OBJID(end_obj);
82✔
3854

3855
    src_obj = KOS_array_read(ctx, args.o, 2);
82✔
3856
    TRY_OBJID(src_obj);
82✔
3857

3858
    if (GET_OBJ_TYPE(self.o) != OBJ_ARRAY ||
82✔
3859
        GET_OBJ_TYPE(src_obj)  != OBJ_ARRAY)
81✔
3860
        RAISE_EXCEPTION_STR(str_err_not_array);
1✔
3861

3862
    if (IS_NUMERIC_OBJ(begin_obj))
81✔
3863
        TRY(KOS_get_integer(ctx, begin_obj, &begin));
64✔
3864
    else if (READ_OBJ_TYPE(begin_obj) == OBJ_VOID)
17✔
3865
        begin = 0;
16✔
3866
    else
3867
        RAISE_EXCEPTION_STR(str_err_unsup_operand_types);
1✔
3868

3869
    if (IS_NUMERIC_OBJ(end_obj))
80✔
3870
        TRY(KOS_get_integer(ctx, end_obj, &end));
58✔
3871
    else if (READ_OBJ_TYPE(end_obj) == OBJ_VOID)
22✔
3872
        end = MAX_INT64;
21✔
3873
    else
3874
        RAISE_EXCEPTION_STR(str_err_unsup_operand_types);
1✔
3875

3876
    src_len = MAX_INT64;
79✔
3877

3878
    TRY(KOS_array_insert(ctx, self.o, begin, end, src_obj, 0, src_len));
79✔
3879

3880
cleanup:
79✔
3881
    self.o = KOS_destroy_top_locals(ctx, &args, &self);
82✔
3882

3883
    return error ? KOS_BADPTR : self.o;
82✔
3884
}
3885

3886
/* @item base array.prototype.pop()
3887
 *
3888
 *     array.prototype.pop(num_elements = void)
3889
 *
3890
 * Removes elements from the end of array.
3891
 *
3892
 * `num_elements` is the number of elements to remove and it defaults to `void`.
3893
 *
3894
 * If `num_elements` is `void`, returns the last element removed from the array.
3895
 * If `num_elements` is `0`, returns `void`.
3896
 * If `num_elements` is a number greater than `0`, returns an array
3897
 * containing the elements removed.
3898
 *
3899
 * Throws if the array is empty or if more elements are being removed
3900
 * than the array already contains.
3901
 *
3902
 * Examples:
3903
 *
3904
 *     > [1, 2, 3, 4, 5].pop()
3905
 *     5
3906
 *     > [1, 2, 3, 4, 5].pop(1)
3907
 *     [5]
3908
 *     > [1, 2, 3, 4, 5].pop(2)
3909
 *     [4, 5]
3910
 */
3911
KOS_DECLARE_STATIC_CONST_STRING(str_num_elements, "num_elements");
3912

3913
static const KOS_CONVERT pop_args[2] = {
3914
    KOS_DEFINE_OPTIONAL_ARG(str_num_elements, KOS_VOID),
3915
    KOS_DEFINE_TAIL_ARG()
3916
};
3917

3918
static KOS_OBJ_ID pop(KOS_CONTEXT ctx,
36✔
3919
                      KOS_OBJ_ID  this_obj,
3920
                      KOS_OBJ_ID  args_obj)
3921
{
3922
    KOS_LOCAL self;
3923
    KOS_LOCAL arg;
3924
    KOS_LOCAL new_array;
3925
    int64_t   num      = 1;
36✔
3926
    int       idx;
3927
    int       just_one = 0;
36✔
3928
    int       error    = KOS_SUCCESS;
36✔
3929

3930
    assert(KOS_get_array_size(args_obj) >= 1);
36✔
3931

3932
    KOS_init_locals(ctx, &self, &arg, &new_array, kos_end_locals);
36✔
3933

3934
    self.o = this_obj;
36✔
3935

3936
    arg.o = KOS_array_read(ctx, args_obj, 0);
36✔
3937
    TRY_OBJID(arg.o);
36✔
3938

3939
    if (arg.o == KOS_VOID)
36✔
3940
        just_one = 1;
16✔
3941
    else {
3942
        TRY(KOS_get_integer(ctx, arg.o, &num));
20✔
3943

3944
        if (num < 0 || num > INT_MAX)
20✔
3945
            RAISE_EXCEPTION_STR(str_err_invalid_array_size);
1✔
3946
    }
3947

3948
    if (num == 0)
35✔
3949
        new_array.o = KOS_VOID;
1✔
3950
    else if (just_one)
34✔
3951
        new_array.o = KOS_array_pop(ctx, self.o);
16✔
3952
    else {
3953
        new_array.o = KOS_new_array(ctx, (unsigned)num);
18✔
3954
        TRY_OBJID(new_array.o);
18✔
3955

3956
        for (idx = (int)(num - 1); idx >= 0; idx--) {
40✔
3957
            arg.o = KOS_array_pop(ctx, self.o);
24✔
3958
            TRY_OBJID(arg.o);
24✔
3959

3960
            TRY(KOS_array_write(ctx, new_array.o, idx, arg.o));
22✔
3961
        }
3962
    }
3963

3964
cleanup:
16✔
3965
    new_array.o = KOS_destroy_top_locals(ctx, &self, &new_array);
36✔
3966

3967
    return error ? KOS_BADPTR : new_array.o;
36✔
3968
}
3969

3970
/* @item base array.prototype.push()
3971
 *
3972
 *     array.prototype.push(values...)
3973
 *
3974
 * Appends every value argument to the array.
3975
 *
3976
 * Returns the old array size before the first element was inserted.
3977
 * If one or more elements are specified to insert, the returned value
3978
 * is equivalent to the index of the first element inserted.
3979
 *
3980
 * Example:
3981
 *
3982
 *     > [1, 1, 1].push(10, 20)
3983
 *     3
3984
 */
3985
static KOS_OBJ_ID push(KOS_CONTEXT ctx,
1,260✔
3986
                       KOS_OBJ_ID  this_obj,
3987
                       KOS_OBJ_ID  args_obj)
3988
{
3989
    int            error    = KOS_SUCCESS;
1,260✔
3990
    const uint32_t num_args = KOS_get_array_size(args_obj);
1,260✔
3991
    uint32_t       i;
3992
    KOS_LOCAL      self;
3993
    KOS_LOCAL      args;
3994
    KOS_LOCAL      old_size;
3995

3996
    KOS_init_local(ctx, &old_size);
1,260✔
3997
    KOS_init_local_with(ctx, &args, args_obj);
1,260✔
3998
    KOS_init_local_with(ctx, &self, this_obj);
1,260✔
3999

4000
    if (GET_OBJ_TYPE(self.o) != OBJ_ARRAY)
1,260✔
4001
        RAISE_EXCEPTION_STR(str_err_not_array);
2✔
4002

4003
    old_size.o = KOS_new_int(ctx, (int64_t)KOS_get_array_size(self.o));
1,258✔
4004
    TRY_OBJID(old_size.o);
1,258✔
4005

4006
    if (num_args > 1)
1,258✔
4007
        TRY(KOS_array_reserve(ctx,
1✔
4008
                              self.o,
4009
                              KOS_get_array_size(self.o) + num_args));
4010

4011
    for (i = 0; i < num_args; i++) {
2,495✔
4012
        uint32_t   idx      = ~0U;
1,259✔
4013
        KOS_OBJ_ID elem_obj = KOS_array_read(ctx, args.o, (int)i);
1,259✔
4014
        TRY_OBJID(elem_obj);
1,280✔
4015

4016
        TRY(KOS_array_push(ctx, self.o, elem_obj, &idx));
1,259✔
4017

4018
        if (i == 0) {
1,237✔
4019
            old_size.o = KOS_new_int(ctx, (int64_t)idx);
1,236✔
4020
            TRY_OBJID(old_size.o);
1,236✔
4021
        }
4022
    }
4023

4024
cleanup:
1,236✔
4025
    old_size.o = KOS_destroy_top_locals(ctx, &self, &old_size);
1,259✔
4026

4027
    return error ? KOS_BADPTR : old_size.o;
1,260✔
4028
}
4029

4030
/* @item base string.prototype.ends_with()
4031
 *
4032
 *     string.prototype.ends_with(str)
4033
 *
4034
 * Determines if a string ends with `str`.
4035
 *
4036
 * `str` is a string which is matched against the end of the current string
4037
 * (`this`).
4038
 *
4039
 * Returns `true` if the current string ends with `str` or `false` otherwise.
4040
 *
4041
 * Examples:
4042
 *
4043
 *     > "foobar".ends_with("bar")
4044
 *     true
4045
 *     > "foobar".ends_with("foo")
4046
 *     false
4047
 */
4048
static const KOS_CONVERT ends_with_args[2] = {
4049
    KOS_DEFINE_MANDATORY_ARG(str_str),
4050
    KOS_DEFINE_TAIL_ARG()
4051
};
4052

4053
static KOS_OBJ_ID ends_with(KOS_CONTEXT ctx,
14✔
4054
                            KOS_OBJ_ID  this_obj,
4055
                            KOS_OBJ_ID  args_obj)
4056
{
4057
    int        error = KOS_SUCCESS;
14✔
4058
    KOS_OBJ_ID arg;
4059
    KOS_OBJ_ID ret   = KOS_BADPTR;
14✔
4060
    unsigned   this_len;
4061
    unsigned   arg_len;
4062

4063
    arg = KOS_array_read(ctx, args_obj, 0);
14✔
4064
    TRY_OBJID(arg);
14✔
4065

4066
    if (GET_OBJ_TYPE(this_obj) != OBJ_STRING || GET_OBJ_TYPE(arg) != OBJ_STRING)
14✔
4067
        RAISE_EXCEPTION_STR(str_err_not_string);
2✔
4068

4069
    this_len = KOS_get_string_length(this_obj);
12✔
4070
    arg_len  = KOS_get_string_length(arg);
12✔
4071

4072
    if (arg_len > this_len)
12✔
4073
        ret = KOS_FALSE;
1✔
4074
    else
4075
        ret = KOS_BOOL( ! KOS_string_compare_slice(this_obj,
11✔
4076
                                                   this_len - arg_len,
4077
                                                   this_len,
4078
                                                   arg,
4079
                                                   0,
4080
                                                   arg_len));
4081

4082
cleanup:
14✔
4083
    return error ? KOS_BADPTR : ret;
14✔
4084
}
4085

4086
/* @item base string.prototype.repeats()
4087
 *
4088
 *     string.prototype.repeats(count)
4089
 *
4090
 * Creates a repeated string.
4091
 *
4092
 * `count` is a non-negative number of times to repeat the string.
4093
 *
4094
 * If `count` is a float, it is converted to integer using floor mode.
4095
 *
4096
 * Examples:
4097
 *
4098
 *     > "-".repeats(10)
4099
 *     "----------"
4100
 *     > "foo".repeats(5)
4101
 *     "foofoofoofoofoo"
4102
 */
4103
static const KOS_CONVERT repeats_args[2] = {
4104
    KOS_DEFINE_MANDATORY_ARG(str_count),
4105
    KOS_DEFINE_TAIL_ARG()
4106
};
4107

4108
static KOS_OBJ_ID repeats(KOS_CONTEXT ctx,
42✔
4109
                          KOS_OBJ_ID  this_obj,
4110
                          KOS_OBJ_ID  args_obj)
4111
{
4112
    int        error = KOS_SUCCESS;
42✔
4113
    KOS_OBJ_ID arg   = KOS_array_read(ctx, args_obj, 0);
42✔
4114
    KOS_OBJ_ID ret   = KOS_BADPTR;
42✔
4115
    int64_t    num;
4116
    unsigned   text_len;
4117

4118
    TRY_OBJID(arg);
42✔
4119

4120
    if (GET_OBJ_TYPE(this_obj) != OBJ_STRING)
41✔
4121
        RAISE_EXCEPTION_STR(str_err_not_string);
1✔
4122

4123
    TRY(KOS_get_integer(ctx, arg, &num));
40✔
4124

4125
    text_len = KOS_get_string_length(this_obj);
40✔
4126

4127
    if (num < 0 || num > 0xFFFFU || (num * text_len) > 0xFFFFU)
40✔
4128
        RAISE_EXCEPTION_STR(str_err_too_many_repeats);
1✔
4129

4130
    ret = KOS_string_repeat(ctx, this_obj, (unsigned)num);
39✔
4131

4132
cleanup:
42✔
4133
    return error ? KOS_BADPTR : ret;
42✔
4134
}
4135

4136
/* @item base string.prototype.find()
4137
 *
4138
 *     string.prototype.find(substr, pos = 0)
4139
 *
4140
 * Searches for a substring in a string from left to right.
4141
 *
4142
 * Returns index of the first substring found or `-1` if the substring was not
4143
 * found.
4144
 *
4145
 * `substr` is the substring to search for.  The search is case sensitive and
4146
 * an exact match must be found.
4147
 *
4148
 * `pos` is the index in the string at which to begin the search.  It defaults
4149
 * to `0`.  If it is a float, it is converted to integer using floor mode.
4150
 * If it is negative, it is an offset from the end of the string.
4151
 *
4152
 * Examples:
4153
 *
4154
 *     > "kos".find("foo")
4155
 *     -1
4156
 *     > "language".find("gu")
4157
 *     3
4158
 *     > "language".find("g", -3)
4159
 *     6
4160
 */
4161
static const KOS_CONVERT find_args[3] = {
4162
    KOS_DEFINE_MANDATORY_ARG(str_substr              ),
4163
    KOS_DEFINE_OPTIONAL_ARG( str_pos, TO_SMALL_INT(0)),
4164
    KOS_DEFINE_TAIL_ARG()
4165
};
4166

4167
static KOS_OBJ_ID find_dir(KOS_CONTEXT         ctx,
75✔
4168
                           KOS_OBJ_ID          this_obj,
4169
                           KOS_OBJ_ID          args_obj,
4170
                           enum KOS_FIND_DIR_E reverse)
4171
{
4172
    KOS_OBJ_ID pattern = KOS_array_read(ctx, args_obj, 0);
75✔
4173
    int        pos     = 0;
75✔
4174
    int        error   = KOS_SUCCESS;
75✔
4175
    uint32_t   this_len;
4176

4177
    assert(KOS_get_array_size(args_obj) >= 2);
75✔
4178

4179
    TRY_OBJID(pattern);
75✔
4180

4181
    if (GET_OBJ_TYPE(this_obj) != OBJ_STRING || GET_OBJ_TYPE(pattern) != OBJ_STRING)
75✔
4182
        RAISE_EXCEPTION_STR(str_err_not_string);
4✔
4183

4184
    this_len = KOS_get_string_length(this_obj);
71✔
4185

4186
    TRY(KOS_get_index_arg(ctx,
71✔
4187
                          args_obj,
4188
                          1,
4189
                          reverse ? -1 : 0,
4190
                          reverse ? KOS_max((int)(this_len - KOS_get_string_length(pattern)), 0) : (int)this_len,
4191
                          reverse ? KOS_VOID_INDEX_IS_END : KOS_VOID_INDEX_IS_BEGIN,
4192
                          &pos));
4193

4194
    TRY(KOS_string_find(ctx, this_obj, pattern, reverse, &pos));
71✔
4195

4196
cleanup:
71✔
4197
    return error ? KOS_BADPTR : TO_SMALL_INT(pos);
75✔
4198
}
4199

4200
static KOS_OBJ_ID find(KOS_CONTEXT ctx,
53✔
4201
                       KOS_OBJ_ID  this_obj,
4202
                       KOS_OBJ_ID  args_obj)
4203
{
4204
    return find_dir(ctx, this_obj, args_obj, KOS_FIND_FORWARD);
53✔
4205
}
4206

4207
/* @item base string.prototype.rfind()
4208
 *
4209
 *     string.prototype.rfind(substr, pos = void)
4210
 *
4211
 * Performs a reverse search for a substring in a string, i.e. from right to
4212
 * left.
4213
 *
4214
 * Returns index of the first substring found or `-1` if the substring was not
4215
 * found.
4216
 *
4217
 * `substr` is the substring to search for.  The search is case sensitive and
4218
 * an exact match must be found.
4219
 *
4220
 * `pos` is the index in the string at which to begin the search.  It defaults
4221
 * to `void`, which means the search by default starts from the last character of
4222
 * the string.  If `pos` is a float, it is converted to integer using floor
4223
 * mode.  If it is negative, it is an offset from the end of the string.
4224
 *
4225
 * Examples:
4226
 *
4227
 *     > "kos".rfind("foo")
4228
 *     -1
4229
 *     > "language".rfind("a")
4230
 *     5
4231
 *     > "language".find("a", 4)
4232
 *     1
4233
 */
4234
static const KOS_CONVERT rfind_args[3] = {
4235
    KOS_DEFINE_MANDATORY_ARG(str_substr       ),
4236
    KOS_DEFINE_OPTIONAL_ARG( str_pos, KOS_VOID),
4237
    KOS_DEFINE_TAIL_ARG()
4238
};
4239

4240
static KOS_OBJ_ID rfind(KOS_CONTEXT ctx,
22✔
4241
                        KOS_OBJ_ID  this_obj,
4242
                        KOS_OBJ_ID  args_obj)
4243
{
4244
    return find_dir(ctx, this_obj, args_obj, KOS_FIND_REVERSE);
22✔
4245
}
4246

4247
/* @item base string.prototype.scan()
4248
 *
4249
 *     string.prototype.scan(chars, pos = 0, inclusive = true)
4250
 *
4251
 * Scans the string for any matching characters from left to right.
4252
 *
4253
 * Returns the position of the first matching character found or `-1` if no
4254
 * matching characters were found.
4255
 *
4256
 * `chars` is a string containing zero or more characters to be matched.
4257
 * The search starts at position `pos` and stops as soon as any character
4258
 * from `chars` is found.
4259
 *
4260
 * `pos` is the index in the string at which to begin the search.  It defaults
4261
 * to `0`.  If it is a float, it is converted to integer using floor mode.
4262
 * If it is negative, it is an offset from the end of the string.
4263
 *
4264
 * If `inclusive` is `true` (the default), characters in `chars` are sought.
4265
 * If `inclusive` is `false`, then the search stops as soon as any character
4266
 * *not* in `chars` is found.
4267
 *
4268
 * Examples:
4269
 *
4270
 *     > "kos".scan("")
4271
 *     0
4272
 *     > "kos".scan("s")
4273
 *     2
4274
 *     > "language".scan("uga", -5, false)
4275
 *     7
4276
 */
4277
static const KOS_CONVERT scan_args[4] = {
4278
    KOS_DEFINE_MANDATORY_ARG(str_chars                     ),
4279
    KOS_DEFINE_OPTIONAL_ARG( str_pos,       TO_SMALL_INT(0)),
4280
    KOS_DEFINE_OPTIONAL_ARG( str_inclusive, KOS_TRUE       ),
4281
    KOS_DEFINE_TAIL_ARG()
4282
};
4283

4284
static KOS_OBJ_ID scan_dir(KOS_CONTEXT         ctx,
295✔
4285
                           KOS_OBJ_ID          this_obj,
4286
                           KOS_OBJ_ID          args_obj,
4287
                           enum KOS_FIND_DIR_E reverse)
4288
{
4289
    KOS_OBJ_ID              pattern = KOS_array_read(ctx, args_obj, 0);
295✔
4290
    KOS_OBJ_ID              inclusive;
4291
    int                     error   = KOS_SUCCESS;
295✔
4292
    int                     pos     = 0;
295✔
4293
    int                     this_len;
4294
    enum KOS_SCAN_INCLUDE_E include;
4295

4296
    assert(KOS_get_array_size(args_obj) >= 3);
295✔
4297

4298
    TRY_OBJID(pattern);
295✔
4299

4300
    if (GET_OBJ_TYPE(this_obj) != OBJ_STRING || GET_OBJ_TYPE(pattern) != OBJ_STRING)
295✔
4301
        RAISE_EXCEPTION_STR(str_err_not_string);
4✔
4302

4303
    this_len = (int)KOS_get_string_length(this_obj);
291✔
4304

4305
    TRY(KOS_get_index_arg(ctx,
291✔
4306
                          args_obj,
4307
                          1,
4308
                          reverse ? -1 : 0,
4309
                          this_len,
4310
                          reverse ? KOS_VOID_INDEX_IS_END : KOS_VOID_INDEX_IS_BEGIN,
4311
                          &pos));
4312

4313
    if (reverse && (pos == this_len))
291✔
4314
        --pos;
45✔
4315

4316
    inclusive = KOS_array_read(ctx, args_obj, 2);
291✔
4317
    TRY_OBJID(inclusive);
291✔
4318

4319
    if (GET_OBJ_TYPE(inclusive) != OBJ_BOOLEAN)
291✔
4320
        RAISE_EXCEPTION_STR(str_err_not_boolean);
2✔
4321

4322
    include = KOS_get_bool(inclusive) ? KOS_SCAN_INCLUDE : KOS_SCAN_EXCLUDE;
289✔
4323

4324
    TRY(KOS_string_scan(ctx, this_obj, pattern, reverse, include, &pos));
289✔
4325

4326
cleanup:
289✔
4327
    return error ? KOS_BADPTR : TO_SMALL_INT(pos);
295✔
4328
}
4329

4330
static KOS_OBJ_ID scan(KOS_CONTEXT ctx,
222✔
4331
                       KOS_OBJ_ID  this_obj,
4332
                       KOS_OBJ_ID  args_obj)
4333
{
4334
    return scan_dir(ctx, this_obj, args_obj, KOS_FIND_FORWARD);
222✔
4335
}
4336

4337
/* @item base string.prototype.rscan()
4338
 *
4339
 *     string.prototype.rscan(chars, inclusive)
4340
 *     string.prototype.rscan(chars, pos = void, inclusive = true)
4341
 *
4342
 * Scans the string for any matching characters in reverse direction, i.e. from
4343
 * right to left.
4344
 *
4345
 * Returns the position of the first matching character found or `-1` if no
4346
 * matching characters were found.
4347
 *
4348
 * `chars` is a string containing zero or more characters to be matched.
4349
 * The search starts at position `pos` and stops as soon as any character
4350
 * from `chars` is found.
4351
 *
4352
 * `pos` is the index in the string at which to begin the search.  It defaults
4353
 * to `void`, which means the search by default starts from the last character of
4354
 * the string.  If `pos` is a float, it is converted to integer using floor
4355
 * mode.  If it is negative, it is an offset from the end of the string.
4356
 *
4357
 * If `inclusive` is `true` (the default), characters in `chars` are sought.
4358
 * If `inclusive` is `false`, then the search stops as soon as any character
4359
 * *not* in `chars` is found.
4360
 *
4361
 * Examples:
4362
 *
4363
 *     > "language".rscan("g")
4364
 *     6
4365
 *     > "language".rscan("uga", -2, false)
4366
 *     2
4367
 */
4368
static const KOS_CONVERT rscan_args[4] = {
4369
    KOS_DEFINE_MANDATORY_ARG(str_chars              ),
4370
    KOS_DEFINE_OPTIONAL_ARG( str_pos,       KOS_VOID),
4371
    KOS_DEFINE_OPTIONAL_ARG( str_inclusive, KOS_TRUE),
4372
    KOS_DEFINE_TAIL_ARG()
4373
};
4374

4375
static KOS_OBJ_ID rscan(KOS_CONTEXT ctx,
73✔
4376
                        KOS_OBJ_ID  this_obj,
4377
                        KOS_OBJ_ID  args_obj)
4378
{
4379
    return scan_dir(ctx, this_obj, args_obj, KOS_FIND_REVERSE);
73✔
4380
}
4381

4382
/* @item base string.prototype.code()
4383
 *
4384
 *     string.prototype.code(pos = 0)
4385
 *
4386
 * Returns code point of a character at a given position in a string.
4387
 *
4388
 * `pos` is the position of the character for which the code point is returned.
4389
 * `pos` defaults to `0`.  If `pos` is a float, it is converted to integer
4390
 * using floor method.  If `pos` is negative, it is an offset from the end of
4391
 * the string.
4392
 *
4393
 * Examples:
4394
 *
4395
 *     > "a".code()
4396
 *     97
4397
 *     > "kos".code(2)
4398
 *     115
4399
 *     > "language".code(-2)
4400
 *     103
4401
 */
4402
static const KOS_CONVERT code_args[2] = {
4403
    KOS_DEFINE_OPTIONAL_ARG(str_pos, TO_SMALL_INT(0)),
4404
    KOS_DEFINE_TAIL_ARG()
4405
};
4406

4407
static KOS_OBJ_ID code(KOS_CONTEXT ctx,
66✔
4408
                       KOS_OBJ_ID  this_obj,
4409
                       KOS_OBJ_ID  args_obj)
4410
{
4411
    int64_t    idx       = 0;
66✔
4412
    KOS_OBJ_ID arg;
4413
    unsigned   char_code = 0;
66✔
4414
    int        error     = KOS_SUCCESS;
66✔
4415

4416
    arg = KOS_array_read(ctx, args_obj, 0);
66✔
4417
    TRY_OBJID(arg);
66✔
4418

4419
    TRY(KOS_get_integer(ctx, arg, &idx));
66✔
4420

4421
    if (idx < INT_MIN || idx > INT_MAX)
66✔
4422
        RAISE_EXCEPTION_STR(str_err_invalid_string_idx);
1✔
4423

4424
    char_code = KOS_string_get_char_code(ctx, this_obj, (int)idx);
65✔
4425
    if (char_code == ~0U)
65✔
4426
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
2✔
4427

4428
cleanup:
63✔
4429
    return error ? KOS_BADPTR : KOS_new_int(ctx, (int64_t)char_code);
66✔
4430
}
4431

4432
/* @item base string.prototype.starts_with()
4433
 *
4434
 *     string.prototype.starts_with(str)
4435
 *
4436
 * Determines if a string begins with `str`.
4437
 *
4438
 * `str` is a string which is matched against the beginning of the current
4439
 * string (`this`).
4440
 *
4441
 * Returns `true` if the current string begins with `str` or `false` otherwise.
4442
 *
4443
 * Examples:
4444
 *
4445
 *     > "foobar".starts_with("foo")
4446
 *     true
4447
 *     > "foobar".starts_with("bar")
4448
 *     false
4449
 */
4450
static KOS_OBJ_ID starts_with(KOS_CONTEXT ctx,
284✔
4451
                              KOS_OBJ_ID  this_obj,
4452
                              KOS_OBJ_ID  args_obj)
4453
{
4454
    int        error = KOS_SUCCESS;
284✔
4455
    KOS_OBJ_ID arg;
4456
    KOS_OBJ_ID ret   = KOS_BADPTR;
284✔
4457
    unsigned   this_len;
4458
    unsigned   arg_len;
4459

4460
    arg = KOS_array_read(ctx, args_obj, 0);
284✔
4461
    TRY_OBJID(arg);
284✔
4462

4463
    if (GET_OBJ_TYPE(this_obj) != OBJ_STRING || GET_OBJ_TYPE(arg) != OBJ_STRING)
281✔
4464
        RAISE_EXCEPTION_STR(str_err_not_string);
2✔
4465

4466
    this_len = KOS_get_string_length(this_obj);
279✔
4467
    arg_len  = KOS_get_string_length(arg);
279✔
4468

4469
    if (arg_len > this_len)
279✔
4470
        ret = KOS_FALSE;
1✔
4471
    else
4472
        ret = KOS_BOOL( ! KOS_string_compare_slice(this_obj,
278✔
4473
                                                   0,
4474
                                                   arg_len,
4475
                                                   arg,
4476
                                                   0,
4477
                                                   arg_len));
4478

4479
cleanup:
284✔
4480
    return error ? KOS_BADPTR : ret;
284✔
4481
}
4482

4483
/* @item base string.prototype.lowercase()
4484
 *
4485
 *     string.prototype.lowercase()
4486
 *
4487
 * Returns a copy of the string with all alphabetical characters converted to
4488
 * lowercase.
4489
 *
4490
 * Examples:
4491
 *
4492
 *     > "Kos".lowercase()
4493
 *     "kos"
4494
 *     > "Text 123 stRIng".lowercase()
4495
 *     "text 123 string"
4496
 */
4497
static KOS_OBJ_ID lowercase(KOS_CONTEXT ctx,
25✔
4498
                            KOS_OBJ_ID  this_obj,
4499
                            KOS_OBJ_ID  args_obj)
4500
{
4501
    return KOS_string_lowercase(ctx, this_obj);
25✔
4502
}
4503

4504
/* @item base string.prototype.uppercase()
4505
 *
4506
 *     string.prototype.uppercase()
4507
 *
4508
 * Returns a copy of the string with all alphabetical characters converted to
4509
 * uppercase.
4510
 *
4511
 * Examples:
4512
 *
4513
 *     > "Kos".uppercase()
4514
 *     "KOS"
4515
 *     > "Text 123 stRIng".uppercase()
4516
 *     "TEXT 123 STRING"
4517
 */
4518
static KOS_OBJ_ID uppercase(KOS_CONTEXT ctx,
30✔
4519
                            KOS_OBJ_ID  this_obj,
4520
                            KOS_OBJ_ID  args_obj)
4521
{
4522
    return KOS_string_uppercase(ctx, this_obj);
30✔
4523
}
4524

4525
/* @item base string.prototype.size
4526
 *
4527
 *     string.prototype.size
4528
 *
4529
 * Read-only size of the string (integer).
4530
 *
4531
 * Example:
4532
 *
4533
 *     > "rain\x{2601}".size
4534
 *     5
4535
 */
4536
static KOS_OBJ_ID get_string_size(KOS_CONTEXT ctx,
326✔
4537
                                  KOS_OBJ_ID  this_obj,
4538
                                  KOS_OBJ_ID  args_obj)
4539
{
4540
    KOS_OBJ_ID ret;
4541

4542
    if (GET_OBJ_TYPE(this_obj) == OBJ_STRING)
326✔
4543
        ret = KOS_new_int(ctx, (int64_t)KOS_get_string_length(this_obj));
324✔
4544
    else {
4545
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
2✔
4546
        ret = KOS_BADPTR;
2✔
4547
    }
4548

4549
    return ret;
326✔
4550
}
4551

4552
/* @item base string.prototype.ascii
4553
 *
4554
 *     string.prototype.ascii
4555
 *
4556
 * Read-only flag indicating whether this is an ASCII string (boolean).
4557
 *
4558
 * Example:
4559
 *
4560
 *     > "kos".ascii
4561
 *     true
4562
 */
4563
static KOS_OBJ_ID get_string_ascii(KOS_CONTEXT ctx,
×
4564
                                   KOS_OBJ_ID  this_obj,
4565
                                   KOS_OBJ_ID  args_obj)
4566
{
4567
    KOS_OBJ_ID ret;
4568

4569
    if (GET_OBJ_TYPE(this_obj) == OBJ_STRING)
×
4570
        ret = KOS_BOOL(OBJPTR(STRING, this_obj)->header.flags & KOS_STRING_ASCII);
×
4571
    else {
4572
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
×
4573
        ret = KOS_BADPTR;
×
4574
    }
4575

4576
    return ret;
×
4577
}
4578

4579
/* @item base string.prototype.reverse()
4580
 *
4581
 *     string.prototype.reverse()
4582
 *
4583
 * Returns a reversed string.
4584
 *
4585
 * Example:
4586
 *
4587
 *     > "kos".reverse()
4588
 *     "sok"
4589
 */
4590
static KOS_OBJ_ID reverse(KOS_CONTEXT ctx,
13✔
4591
                          KOS_OBJ_ID  this_obj,
4592
                          KOS_OBJ_ID  args_obj)
4593
{
4594
    return KOS_string_reverse(ctx, this_obj);
13✔
4595
}
4596

4597
/* @item base function.prototype.line
4598
 *
4599
 *     function.prototype.line
4600
 *
4601
 * Read-only line at which the function was defined in the source code.
4602
 */
4603
static KOS_OBJ_ID get_function_line(KOS_CONTEXT ctx,
3✔
4604
                                    KOS_OBJ_ID  this_obj,
4605
                                    KOS_OBJ_ID  args_obj)
4606
{
4607
    KOS_OBJ_ID     ret  = KOS_BADPTR;
3✔
4608
    const KOS_TYPE type = GET_OBJ_TYPE(this_obj);
3✔
4609

4610
    if (type == OBJ_FUNCTION || type == OBJ_CLASS) {
3✔
4611

4612
        const uint32_t def_line = KOS_function_get_def_line(this_obj);
2✔
4613

4614
        ret = TO_SMALL_INT((int64_t)def_line);
2✔
4615
    }
4616
    else
4617
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
1✔
4618

4619
    return ret;
3✔
4620
}
4621

4622
/* @item base function.prototype.module
4623
 *
4624
 *     function.prototype.module
4625
 *
4626
 * Read-only module to which the function belongs.
4627
 *
4628
 * Example:
4629
 *
4630
 *     > count.module.name
4631
 *     "base"
4632
 */
4633
static KOS_OBJ_ID get_function_module(KOS_CONTEXT ctx,
4✔
4634
                                      KOS_OBJ_ID  this_obj,
4635
                                      KOS_OBJ_ID  args_obj)
4636
{
4637
    KOS_OBJ_ID     ret  = KOS_BADPTR;
4✔
4638
    const KOS_TYPE type = GET_OBJ_TYPE(this_obj);
4✔
4639

4640
    if (type == OBJ_FUNCTION || type == OBJ_CLASS)
4✔
4641
        ret = OBJPTR(FUNCTION, this_obj)->module;
3✔
4642
    else
4643
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
1✔
4644

4645
    return ret;
4✔
4646
}
4647

4648
/* @item base function.prototype.name
4649
 *
4650
 *     function.prototype.name
4651
 *
4652
 * Read-only function name.
4653
 *
4654
 * Example:
4655
 *
4656
 *     > count.name
4657
 *     "count"
4658
 */
4659
static KOS_OBJ_ID get_function_name(KOS_CONTEXT ctx,
6✔
4660
                                    KOS_OBJ_ID  this_obj,
4661
                                    KOS_OBJ_ID  args_obj)
4662
{
4663
    KOS_OBJ_ID     ret  = KOS_BADPTR;
6✔
4664
    const KOS_TYPE type = GET_OBJ_TYPE(this_obj);
6✔
4665

4666
    if (type == OBJ_FUNCTION || type == OBJ_CLASS)
6✔
4667
        ret = OBJPTR(FUNCTION, this_obj)->name;
5✔
4668
    else
4669
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
1✔
4670

4671
    return ret;
6✔
4672
}
4673

4674
/* @item base function.prototype.instructions
4675
 *
4676
 *     function.prototype.instructions
4677
 *
4678
 * Read-only number of bytecode instructions generated for this function.
4679
 *
4680
 * Zero, if this is a built-in function.
4681
 *
4682
 * Example:
4683
 *
4684
 *     > count.instructions
4685
 *     26
4686
 */
4687
static KOS_OBJ_ID get_instructions(KOS_CONTEXT ctx,
57✔
4688
                                   KOS_OBJ_ID  this_obj,
4689
                                   KOS_OBJ_ID  args_obj)
4690
{
4691
    KOS_OBJ_ID     ret  = KOS_BADPTR;
57✔
4692
    const KOS_TYPE type = GET_OBJ_TYPE(this_obj);
57✔
4693

4694
    if (type == OBJ_FUNCTION || type == OBJ_CLASS) {
113✔
4695

4696
        const uint32_t num_instr = KOS_function_get_num_instr(this_obj);
56✔
4697

4698
        ret = KOS_new_int(ctx, (int64_t)num_instr);
56✔
4699
    }
4700
    else
4701
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
1✔
4702

4703
    return ret;
57✔
4704
}
4705

4706
/* @item base function.prototype.size
4707
 *
4708
 *     function.prototype.size
4709
 *
4710
 * Read-only size of bytecode generated for this function, in bytes.
4711
 *
4712
 * Zero, if this is a built-in function.
4713
 *
4714
 * Example:
4715
 *
4716
 *     > count.size
4717
 *     133
4718
 */
4719
static KOS_OBJ_ID get_code_size(KOS_CONTEXT ctx,
58✔
4720
                                KOS_OBJ_ID  this_obj,
4721
                                KOS_OBJ_ID  args_obj)
4722
{
4723
    KOS_OBJ_ID     ret  = KOS_BADPTR;
58✔
4724
    const KOS_TYPE type = GET_OBJ_TYPE(this_obj);
58✔
4725

4726
    if (type == OBJ_FUNCTION || type == OBJ_CLASS) {
115✔
4727

4728
        const uint32_t code_size = KOS_function_get_code_size(this_obj);
57✔
4729

4730
        ret = KOS_new_int(ctx, (int64_t)code_size);
57✔
4731
    }
4732
    else
4733
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
1✔
4734

4735
    return ret;
58✔
4736
}
4737

4738
/* @item base generator.prototype.state
4739
 *
4740
 *     generator.prototype.state
4741
 *
4742
 * Read-only state of the generator function.
4743
 *
4744
 * This is a string describing the current state of the generator function:
4745
 *
4746
 *  * "init" - the generator function,
4747
 *  * "ready" - the generator function has been instantiated, but not invoked,
4748
 *  * "active" - the generator function has been instantiated and invoked, but not finished,
4749
 *  * "running" - the generator function is currently running (e.g. when inside the function),
4750
 *  * "done" - the generator has finished and exited.
4751
 *
4752
 * Example:
4753
 *
4754
 *     > range.state
4755
 *     init
4756
 *     > range(10).state
4757
 *     ready
4758
 *     > const it = range(10) ; it() ; it.state
4759
 *     active
4760
 */
4761
static KOS_OBJ_ID get_gen_state(KOS_CONTEXT ctx,
14✔
4762
                                KOS_OBJ_ID  this_obj,
4763
                                KOS_OBJ_ID  args_obj)
4764
{
4765
    KOS_OBJ_ID ret = KOS_BADPTR;
14✔
4766

4767
    if (GET_OBJ_TYPE(this_obj) == OBJ_FUNCTION) {
14✔
4768

4769
        switch (KOS_atomic_read_relaxed_u32(OBJPTR(FUNCTION, this_obj)->state)) {
13✔
4770

4771
            case KOS_GEN_INIT:
3✔
4772
                ret = KOS_CONST_ID(str_gen_init);
3✔
4773
                break;
3✔
4774

4775
            case KOS_GEN_READY:
2✔
4776
                ret = KOS_CONST_ID(str_gen_ready);
2✔
4777
                break;
2✔
4778

4779
            case KOS_GEN_ACTIVE:
5✔
4780
                ret = KOS_CONST_ID(str_gen_active);
5✔
4781
                break;
5✔
4782

4783
            case KOS_GEN_RUNNING:
2✔
4784
                ret = KOS_CONST_ID(str_gen_running);
2✔
4785
                break;
2✔
4786

4787
            case KOS_GEN_DONE:
1✔
4788
                ret = KOS_CONST_ID(str_gen_done);
1✔
4789
                break;
1✔
4790

4791
            default:
×
4792
                KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_generator));
×
4793
                break;
×
4794
        }
4795
    }
4796
    else
4797
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
1✔
4798

4799
    return ret;
14✔
4800
}
4801

4802
/* @item base class.prototype.prototype
4803
 *
4804
 *     class.prototype.prototype
4805
 *
4806
 * Allows reading and setting prototype on class objects.
4807
 *
4808
 * The prototype set or retrieved is the prototype used when creating
4809
 * new objects of this class.
4810
 */
4811

4812
/* @item base function.prototype.registers
4813
 *
4814
 *     function.prototype.registers
4815
 *
4816
 * Read-only number of registers used by the function.
4817
 *
4818
 * Zero, if this is a built-in function.
4819
 *
4820
 * Example:
4821
 *
4822
 *     > count.registers
4823
 *     5
4824
 */
4825
static KOS_OBJ_ID get_registers(KOS_CONTEXT ctx,
58✔
4826
                                KOS_OBJ_ID  this_obj,
4827
                                KOS_OBJ_ID  args_obj)
4828
{
4829
    KOS_OBJ_ID     ret  = KOS_BADPTR;
58✔
4830
    const KOS_TYPE type = GET_OBJ_TYPE(this_obj);
58✔
4831

4832
    if (type == OBJ_FUNCTION || type == OBJ_CLASS) {
115✔
4833

4834
        KOS_FUNCTION *func = OBJPTR(FUNCTION, this_obj);
57✔
4835

4836
        ret = KOS_new_int(ctx, (int64_t)func->opts.num_regs);
57✔
4837
    }
4838
    else
4839
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_function));
1✔
4840

4841
    return ret;
58✔
4842
}
4843

4844
/* @item base exception.prototype.print()
4845
 *
4846
 *     exception.prototype.print()
4847
 *
4848
 * Prints the exception object on stdout.
4849
 */
4850
static KOS_OBJ_ID print_exception(KOS_CONTEXT ctx,
612✔
4851
                                  KOS_OBJ_ID  this_obj,
4852
                                  KOS_OBJ_ID  args_obj)
4853
{
4854
    int       error = KOS_SUCCESS;
612✔
4855
    KOS_LOCAL self;
4856

4857
    KOS_init_local_with(ctx, &self, this_obj);
612✔
4858

4859
    KOS_raise_exception(ctx, self.o);
612✔
4860

4861
    KOS_print_exception(ctx, KOS_STDOUT);
612✔
4862

4863
    if (KOS_is_exception_pending(ctx))
612✔
4864
        error = KOS_ERROR_EXCEPTION;
18✔
4865

4866
    self.o = KOS_destroy_top_local(ctx, &self);
612✔
4867

4868
    return error ? KOS_BADPTR : self.o;
612✔
4869
}
4870

4871
/* @item base module()
4872
 *
4873
 *     module()
4874
 *
4875
 * Module object class.
4876
 *
4877
 * Module objects are obtained by calling `module.load()`.
4878
 *
4879
 * The purpose of this class is to be used with the `instanceof`
4880
 * operator to detect thread objects.
4881
 *
4882
 * Calling this class directly throws an exception.
4883
 *
4884
 * The prototype of `module.prototype` is `object.prototype`.
4885
 */
4886
static KOS_OBJ_ID module_constructor(KOS_CONTEXT ctx,
5✔
4887
                                     KOS_OBJ_ID  this_obj,
4888
                                     KOS_OBJ_ID  args_obj)
4889
{
4890
    KOS_raise_exception(ctx, KOS_CONST_ID(str_err_use_load));
5✔
4891
    return KOS_BADPTR;
5✔
4892
}
4893

4894
/* @item base module.prototype.get()
4895
 *
4896
 *     module.prototype.get(name, default_value = void)
4897
 *
4898
 * Retrieves public global by `name` from a module.
4899
 *
4900
 * If the global does not exist, returns `default_value`.
4901
 *
4902
 * Example:
4903
 *
4904
 *     > module.load("base").get("args")
4905
 *     ["script.kos"]
4906
 */
4907
static const KOS_CONVERT module_global_args[3] = {
4908
    KOS_DEFINE_MANDATORY_ARG(str_name),
4909
    KOS_DEFINE_OPTIONAL_ARG( str_default_value, KOS_VOID),
4910
    KOS_DEFINE_TAIL_ARG()
4911
};
4912

4913
static KOS_OBJ_ID get_module_global(KOS_CONTEXT ctx,
6✔
4914
                                    KOS_OBJ_ID  this_obj,
4915
                                    KOS_OBJ_ID  args_obj)
4916
{
4917
    KOS_LOCAL  this_;
4918
    KOS_LOCAL  args;
4919
    KOS_OBJ_ID name_obj;
4920
    KOS_OBJ_ID ret   = KOS_BADPTR;
6✔
4921
    int        error = KOS_SUCCESS;
6✔
4922

4923
    assert(KOS_get_array_size(args_obj) >= 2);
6✔
4924

4925
    KOS_init_local_with(ctx, &this_, this_obj);
6✔
4926
    KOS_init_local_with(ctx, &args,  args_obj);
6✔
4927

4928
    name_obj = KOS_array_read(ctx, args.o, 0);
6✔
4929
    TRY_OBJID(name_obj);
6✔
4930

4931
    if (KOS_module_get_global(ctx, this_.o, name_obj, &ret, KOS_NULL) != KOS_SUCCESS) {
6✔
4932

4933
        KOS_clear_exception(ctx);
2✔
4934

4935
        ret = KOS_array_read(ctx, args.o, 1);
2✔
4936
        TRY_OBJID(ret);
2✔
4937
    }
4938

4939
cleanup:
6✔
4940
    KOS_destroy_top_locals(ctx, &args, &this_);
6✔
4941

4942
    return error ? KOS_BADPTR : ret;
6✔
4943
}
4944

4945
/* @item base module.load()
4946
 *
4947
 *     module.load(name)
4948
 *
4949
 * Imports a module or returns existing module if it's already been imported.
4950
 *
4951
 * Returns module object.
4952
 *
4953
 * If the module has already been imported, e.g. with the `import` statement, returns that
4954
 * module object.  Otherwise imports the module and then returns it.
4955
 */
4956
static const KOS_CONVERT module_load_args[2] = {
4957
    KOS_DEFINE_MANDATORY_ARG(str_name),
4958
    KOS_DEFINE_TAIL_ARG()
4959
};
4960

4961
static KOS_OBJ_ID module_load(KOS_CONTEXT ctx,
213✔
4962
                              KOS_OBJ_ID  this_obj,
4963
                              KOS_OBJ_ID  args_obj)
4964
{
4965
    KOS_LOCAL  name;
4966
    KOS_LOCAL  module;
4967
    KOS_VECTOR path_cstr;
4968
    int        error = KOS_SUCCESS;
213✔
4969

4970
    assert(GET_OBJ_TYPE(args_obj) == OBJ_ARRAY);
213✔
4971
    assert(KOS_get_array_size(args_obj) >= 1);
213✔
4972

4973
    KOS_vector_init(&path_cstr);
213✔
4974
    KOS_init_locals(ctx, &name, &module, kos_end_locals);
213✔
4975

4976
    name.o = KOS_array_read(ctx, args_obj, 0);
213✔
4977
    TRY_OBJID(name.o);
213✔
4978

4979
    if (GET_OBJ_TYPE(name.o) != OBJ_STRING)
212✔
4980
        RAISE_EXCEPTION_STR(str_err_not_string);
2✔
4981

4982
    module.o = KOS_get_property_shallow(ctx, ctx->inst->modules.module_names, name.o);
210✔
4983
    if (IS_BAD_PTR(module.o) || ! IS_SMALL_INT(module.o)) {
210✔
4984
        KOS_clear_exception(ctx);
3✔
4985

4986
        TRY(KOS_string_to_cstr_vec(ctx, name.o, &path_cstr));
3✔
4987

4988
        module.o = KOS_load_module(ctx, path_cstr.buffer, (uint32_t)(path_cstr.size - 1));
3✔
4989
        TRY_OBJID(module.o);
3✔
4990

4991
        TRY_OBJID(KOS_run_module(ctx, module.o));
1✔
4992
    }
4993
    else {
4994

4995
        module.o = KOS_array_read(ctx, ctx->inst->modules.modules, (int)GET_SMALL_INT(module.o));
207✔
4996
        if (IS_BAD_PTR(module.o)) {
207✔
4997
            KOS_clear_exception(ctx);
1✔
4998
            RAISE_EXCEPTION_STR(str_err_not_module);
1✔
4999
        }
5000
    }
5001

5002
cleanup:
206✔
5003
    module.o = KOS_destroy_top_locals(ctx, &name, &module);
213✔
5004
    KOS_vector_destroy(&path_cstr);
213✔
5005

5006
    return error ? KOS_BADPTR : module.o;
213✔
5007
}
5008

5009
/* @item base module.prototype.exports
5010
 *
5011
 *     module.prototype.exports
5012
 *
5013
 * An object with keys being names of public globals and values being
5014
 * indices of these globals.
5015
 *
5016
 * The indices of the globals are valid only for the current run of Kos
5017
 * and can change in subsequent runs.
5018
 *
5019
 * Example:
5020
 *
5021
 *     > module.load("base").exports
5022
 *     { "integer": 1, "string": 2, ... }
5023
 */
5024
static KOS_OBJ_ID get_module_exports(KOS_CONTEXT ctx,
1✔
5025
                                     KOS_OBJ_ID  this_obj,
5026
                                     KOS_OBJ_ID  args_obj)
5027
{
5028
    KOS_LOCAL ret;
5029
    KOS_LOCAL iter;
5030
    int       error = KOS_SUCCESS;
1✔
5031

5032
    assert( ! IS_BAD_PTR(this_obj));
1✔
5033

5034
    KOS_init_locals(ctx, &iter, &ret, kos_end_locals);
1✔
5035

5036
    if (GET_OBJ_TYPE(this_obj) != OBJ_MODULE)
1✔
5037
        RAISE_EXCEPTION_STR(str_err_not_module);
×
5038

5039
    iter.o = KOS_new_iterator(ctx, OBJPTR(MODULE, this_obj)->global_names, KOS_SHALLOW);
1✔
5040
    TRY_OBJID(iter.o);
1✔
5041

5042
    ret.o = KOS_new_object(ctx);
1✔
5043
    TRY_OBJID(ret.o);
1✔
5044

5045
    while ( ! KOS_iterator_next(ctx, iter.o)) {
43✔
5046
        const KOS_OBJ_ID key   = KOS_get_walk_key(iter.o);
42✔
5047
        const KOS_OBJ_ID value = KOS_get_walk_value(iter.o);
42✔
5048

5049
        TRY(KOS_set_property(ctx, ret.o, key, value));
42✔
5050
    }
5051

5052
    if (KOS_is_exception_pending(ctx))
1✔
5053
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
5054

5055
cleanup:
1✔
5056
    ret.o = KOS_destroy_top_locals(ctx, &iter, &ret);
1✔
5057

5058
    return error ? KOS_BADPTR : ret.o;
1✔
5059
}
5060

5061
/* @item base module.prototype.name
5062
 *
5063
 *     module.prototype.name
5064
 *
5065
 * Read-only name of the module (string).
5066
 */
5067
static KOS_OBJ_ID get_module_name(KOS_CONTEXT ctx,
10✔
5068
                                  KOS_OBJ_ID  this_obj,
5069
                                  KOS_OBJ_ID  args_obj)
5070
{
5071
    KOS_OBJ_ID ret;
5072

5073
    assert( ! IS_BAD_PTR(this_obj));
10✔
5074

5075
    if (GET_OBJ_TYPE(this_obj) == OBJ_MODULE)
10✔
5076
        ret = OBJPTR(MODULE, this_obj)->name;
10✔
5077
    else {
5078
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_module));
×
5079
        ret = KOS_BADPTR;
×
5080
    }
5081

5082
    return ret;
10✔
5083
}
5084

5085
/* @item base module.prototype.path
5086
 *
5087
 *     module.prototype.path
5088
 *
5089
 * Read-only path of the module (string).
5090
 */
5091
static KOS_OBJ_ID get_module_path(KOS_CONTEXT ctx,
1✔
5092
                                  KOS_OBJ_ID  this_obj,
5093
                                  KOS_OBJ_ID  args_obj)
5094
{
5095
    KOS_OBJ_ID ret;
5096

5097
    assert( ! IS_BAD_PTR(this_obj));
1✔
5098

5099
    if (GET_OBJ_TYPE(this_obj) == OBJ_MODULE)
1✔
5100
        ret = OBJPTR(MODULE, this_obj)->path;
1✔
5101
    else {
5102
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_module));
×
5103
        ret = KOS_BADPTR;
×
5104
    }
5105

5106
    return ret;
1✔
5107
}
5108

5109
int kos_module_base_init(KOS_CONTEXT ctx, KOS_OBJ_ID module_obj)
7,430✔
5110
{
5111
    int       error = KOS_SUCCESS;
7,430✔
5112
    KOS_LOCAL module;
5113

5114
    const KOS_CONVERT apply_args[3] = {
7,430✔
5115
        KOS_DEFINE_OPTIONAL_ARG(str_this_obj, KOS_VOID       ),
7,430✔
5116
        KOS_DEFINE_OPTIONAL_ARG(str_args,     KOS_EMPTY_ARRAY),
7,430✔
5117
        KOS_DEFINE_TAIL_ARG()
5118
    };
5119

5120
    KOS_init_local_with(ctx, &module, module_obj);
7,430✔
5121

5122
    TRY_ADD_FUNCTION( ctx, module.o, "print",     print,     KOS_NULL);
7,430✔
5123
    TRY_ADD_FUNCTION( ctx, module.o, "stringify", stringify, KOS_NULL);
7,425✔
5124
    TRY_ADD_GENERATOR(ctx, module.o, "deep",      deep,      deep_args);
7,421✔
5125
    TRY_ADD_GENERATOR(ctx, module.o, "shallow",   shallow,   deep_args);
7,415✔
5126

5127
    TRY_ADD_GLOBAL(   ctx, module.o, "args",      ctx->inst->args);
7,412✔
5128

5129
    TRY_CREATE_CONSTRUCTOR(array,         module.o, KOS_NULL);
7,412✔
5130
    TRY_CREATE_CONSTRUCTOR(boolean,       module.o, KOS_NULL);
7,398✔
5131
    TRY_CREATE_CONSTRUCTOR(buffer,        module.o, KOS_NULL);
7,392✔
5132
    TRY_CREATE_CONSTRUCTOR(class,         module.o, KOS_NULL);
7,384✔
5133
    TRY_CREATE_CONSTRUCTOR(exception,     module.o, KOS_NULL);
7,376✔
5134
    TRY_CREATE_CONSTRUCTOR(float,         module.o, KOS_NULL);
7,368✔
5135
    TRY_CREATE_CONSTRUCTOR(function,      module.o, KOS_NULL);
7,360✔
5136
    TRY_CREATE_CONSTRUCTOR(generator,     module.o, KOS_NULL);
7,352✔
5137
    TRY_CREATE_CONSTRUCTOR(generator_end, module.o, KOS_NULL);
7,344✔
5138
    TRY_CREATE_CONSTRUCTOR(integer,       module.o, KOS_NULL);
7,330✔
5139
    TRY_CREATE_CONSTRUCTOR(number,        module.o, KOS_NULL);
7,324✔
5140
    TRY_CREATE_CONSTRUCTOR(object,        module.o, object_args);
7,316✔
5141
    TRY_CREATE_CONSTRUCTOR(string,        module.o, KOS_NULL);
7,305✔
5142
    TRY_CREATE_CONSTRUCTOR(thread,        module.o, KOS_NULL);
7,297✔
5143
    TRY_CREATE_CONSTRUCTOR(module,        module.o, KOS_NULL);
7,289✔
5144

5145
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(array),     "cas",          array_cas,           array_cas_args);
7,281✔
5146
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(array),     "insert_array", insert_array,        insert_array_args);
7,274✔
5147
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(array),     "fill",         fill,                fill_args);
7,270✔
5148
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(array),     "pop",          pop,                 pop_args);
7,264✔
5149
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(array),     "push",         push,                KOS_NULL);
7,260✔
5150
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(array),     "reserve",      reserve,             reserve_args);
7,259✔
5151
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(array),     "resize",       resize,              resize_array_args);
7,255✔
5152
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(array),     "slice",        slice,               slice_args);
7,251✔
5153
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(array),     "sort",         sort,                sort_args);
7,248✔
5154
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(array),     "size",         get_array_size,      KOS_NULL);
7,244✔
5155

5156
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(buffer),    "copy_buffer",  copy_buffer,         copy_buffer_args);
7,242✔
5157
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(buffer),    "fill",         fill,                fill_args);
7,236✔
5158
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(buffer),    "pack",         pack,                pack_args);
7,231✔
5159
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(buffer),    "reserve",      reserve,             reserve_args);
7,227✔
5160
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(buffer),    "resize",       resize,              resize_buffer_args);
7,224✔
5161
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(buffer),    "slice",        slice,               slice_args);
7,220✔
5162
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(buffer),    "unpack",       unpack,              unpack_args);
7,216✔
5163
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(buffer),    "size",         get_buffer_size,     KOS_NULL);
7,212✔
5164

5165
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(exception), "print",        print_exception,     KOS_NULL);
7,210✔
5166

5167
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(function),  "apply",        apply,               apply_args);
7,208✔
5168
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(function),  "async",        async,               apply_args);
7,203✔
5169
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(function),  "instructions", get_instructions,    KOS_NULL);
7,199✔
5170
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(function),  "line",         get_function_line,   KOS_NULL);
7,196✔
5171
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(function),  "module",       get_function_module, KOS_NULL);
7,194✔
5172
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(function),  "name",         get_function_name,   KOS_NULL);
7,192✔
5173
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(function),  "registers",    get_registers,       KOS_NULL);
7,189✔
5174
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(function),  "size",         get_code_size,       KOS_NULL);
7,187✔
5175

5176
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(generator), "state",        get_gen_state,       KOS_NULL);
7,185✔
5177

5178
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(module),    "get",          get_module_global,   module_global_args);
7,182✔
5179
    TRY_ADD_STATIC_FUNCTION( ctx, module.o, "module",         "load",         module_load,         module_load_args);
7,177✔
5180
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(module),    "exports",      get_module_exports,  KOS_NULL);
7,173✔
5181
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(module),    "name",         get_module_name,     KOS_NULL);
7,171✔
5182
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(module),    "path",         get_module_path,     KOS_NULL);
7,168✔
5183

5184
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "ends_with",    ends_with,           ends_with_args);
7,166✔
5185
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "find",         find,                find_args);
7,162✔
5186
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "code",         code,                code_args);
7,158✔
5187
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "lowercase",    lowercase,           KOS_NULL);
7,153✔
5188
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "repeats",      repeats,             repeats_args);
7,152✔
5189
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "rfind",        rfind,               rfind_args);
7,149✔
5190
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "rscan",        rscan,               rscan_args);
7,144✔
5191
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "reverse",      reverse,             KOS_NULL);
7,139✔
5192
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "scan",         scan,                scan_args);
7,138✔
5193
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "slice",        slice,               slice_args);
7,133✔
5194
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "starts_with",  starts_with,         ends_with_args);
7,130✔
5195
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(string),    "uppercase",    uppercase,           KOS_NULL);
7,127✔
5196
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(string),    "size",         get_string_size,     KOS_NULL);
7,126✔
5197
    TRY_ADD_MEMBER_PROPERTY( ctx, module.o, PROTO(string),    "ascii",        get_string_ascii,    KOS_NULL);
7,122✔
5198

5199
    TRY_ADD_MEMBER_FUNCTION( ctx, module.o, PROTO(thread),    "wait",         wait,                KOS_NULL);
7,120✔
5200

5201
cleanup:
7,118✔
5202
    KOS_destroy_top_local(ctx, &module);
7,430✔
5203

5204
    return error;
7,430✔
5205
}
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