Coverage Report

Created: 2026-02-14 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/njs/src/njs_json.c
Line
Count
Source
1
2
/*
3
 * Copyright (C) Dmitry Volyntsev
4
 * Copyright (C) NGINX, Inc.
5
 */
6
7
8
#include <njs_main.h>
9
10
11
typedef struct {
12
    njs_vm_t                   *vm;
13
    njs_mp_t                   *pool;
14
    njs_uint_t                 depth;
15
    const u_char               *start;
16
    const u_char               *end;
17
} njs_json_parse_ctx_t;
18
19
20
typedef struct {
21
    njs_value_t                value;
22
23
    uint8_t                    written;       /* 1 bit */
24
    uint8_t                    array;         /* 1 bit */
25
    uint8_t                    fast_array;    /* 1 bit */
26
27
    int64_t                    index;
28
    int64_t                    length;
29
    njs_array_t                *keys;
30
    njs_value_t                *key;
31
    njs_value_t                prop;
32
} njs_json_state_t;
33
34
35
typedef struct {
36
    njs_value_t                retval;
37
38
    njs_vm_t                   *vm;
39
40
    njs_uint_t                 depth;
41
2.75k
#define NJS_JSON_MAX_DEPTH     32
42
    njs_json_state_t           states[NJS_JSON_MAX_DEPTH];
43
44
    njs_value_t                replacer;
45
    njs_str_t                  space;
46
    u_char                     space_buf[16];
47
    uint32_t                   keys_type;
48
} njs_json_stringify_t;
49
50
51
static const u_char *njs_json_parse_value(njs_json_parse_ctx_t *ctx,
52
    njs_value_t *value, const u_char *p);
53
static const u_char *njs_json_parse_object(njs_json_parse_ctx_t *ctx,
54
    njs_value_t *value, const u_char *p);
55
static const u_char *njs_json_parse_array(njs_json_parse_ctx_t *ctx,
56
    njs_value_t *value, const u_char *p);
57
static const u_char *njs_json_parse_string(njs_json_parse_ctx_t *ctx,
58
    njs_value_t *value, const u_char *p);
59
static const u_char *njs_json_parse_number(njs_json_parse_ctx_t *ctx,
60
    njs_value_t *value, const u_char *p);
61
njs_inline uint32_t njs_json_unicode(const u_char *p);
62
static const u_char *njs_json_skip_space(const u_char *start,
63
    const u_char *end);
64
65
static njs_int_t njs_json_internalize_property(njs_vm_t *vm,
66
    njs_function_t *reviver, njs_value_t *holder, uint32_t atom_id,
67
    njs_int_t depth, njs_value_t *retval);
68
static void njs_json_parse_exception(njs_json_parse_ctx_t *ctx,
69
    const char *msg, const u_char *pos);
70
71
static njs_int_t njs_json_stringify_iterator(njs_json_stringify_t *stringify,
72
    njs_value_t *value, njs_value_t *retval);
73
static njs_function_t *njs_object_to_json_function(njs_vm_t *vm,
74
    njs_value_t *value);
75
static njs_int_t njs_json_stringify_to_json(njs_json_stringify_t* stringify,
76
    njs_json_state_t *state, njs_value_t *key, njs_value_t *value);
77
static njs_int_t njs_json_stringify_replacer(njs_json_stringify_t* stringify,
78
    njs_json_state_t  *state, njs_value_t *key, njs_value_t *value);
79
static njs_int_t njs_json_stringify_array(njs_json_stringify_t *stringify);
80
81
static njs_int_t njs_json_append_value(njs_vm_t *vm, njs_chb_t *chain,
82
    njs_value_t *value);
83
static void njs_json_append_string(njs_vm_t *vm, njs_chb_t *chain,
84
    const njs_value_t *value, char quote);
85
static void njs_json_append_number(njs_chb_t *chain, const njs_value_t *value);
86
87
static njs_object_t *njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper,
88
    const njs_value_t *value);
89
90
91
static const njs_object_prop_init_t  njs_json_object_properties[];
92
93
94
static njs_int_t
95
njs_json_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
96
    njs_index_t unused, njs_value_t *retval)
97
2.75k
{
98
2.75k
    njs_int_t             ret;
99
2.75k
    njs_value_t           *text, value, lvalue, wrapper;
100
2.75k
    njs_object_t          *obj;
101
2.75k
    const u_char          *p, *end;
102
2.75k
    const njs_value_t     *reviver;
103
2.75k
    njs_string_prop_t     string;
104
2.75k
    njs_json_parse_ctx_t  ctx;
105
106
2.75k
    text = njs_lvalue_arg(&lvalue, args, nargs, 1);
107
108
2.75k
    if (njs_slow_path(!njs_is_string(text))) {
109
2.53k
        ret = njs_value_to_string(vm, text, text);
110
2.53k
        if (njs_slow_path(ret != NJS_OK)) {
111
0
            return ret;
112
0
        }
113
2.53k
    }
114
115
2.75k
    (void) njs_string_prop(vm, &string, text);
116
117
2.75k
    p = string.start;
118
2.75k
    end = p + string.size;
119
120
2.75k
    ctx.vm = vm;
121
2.75k
    ctx.pool = vm->mem_pool;
122
2.75k
    ctx.depth = NJS_JSON_MAX_DEPTH;
123
2.75k
    ctx.start = string.start;
124
2.75k
    ctx.end = end;
125
126
2.75k
    p = njs_json_skip_space(p, end);
127
2.75k
    if (njs_slow_path(p == end)) {
128
0
        njs_json_parse_exception(&ctx, "Unexpected end of input", p);
129
0
        return NJS_ERROR;
130
0
    }
131
132
2.75k
    p = njs_json_parse_value(&ctx, &value, p);
133
2.75k
    if (njs_slow_path(p == NULL)) {
134
1.29k
        return NJS_ERROR;
135
1.29k
    }
136
137
1.46k
    p = njs_json_skip_space(p, end);
138
1.46k
    if (njs_slow_path(p != end)) {
139
0
        njs_json_parse_exception(&ctx, "Unexpected token", p);
140
0
        return NJS_ERROR;
141
0
    }
142
143
1.46k
    reviver = njs_arg(args, nargs, 2);
144
145
1.46k
    if (njs_slow_path(njs_is_function(reviver))) {
146
220
        obj = njs_json_wrap_value(vm, &wrapper, &value);
147
220
        if (njs_slow_path(obj == NULL)) {
148
0
            return NJS_ERROR;
149
0
        }
150
151
220
        return njs_json_internalize_property(vm, njs_function(reviver),
152
220
                                             &wrapper, NJS_ATOM_STRING_empty, 0,
153
220
                                             retval);
154
220
    }
155
156
1.24k
    njs_value_assign(retval, &value);
157
158
1.24k
    return NJS_OK;
159
1.46k
}
160
161
162
njs_int_t
163
njs_vm_json_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
164
    njs_value_t *retval)
165
0
{
166
0
    njs_function_t  *parse;
167
168
0
    parse = njs_function(&njs_json_object_properties[1].desc.u.value);
169
170
0
    return njs_vm_invoke(vm, parse, args, nargs, retval);
171
0
}
172
173
174
static njs_int_t
175
njs_json_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
176
    njs_index_t unused, njs_value_t *retval)
177
0
{
178
0
    size_t                length;
179
0
    int64_t               i64;
180
0
    njs_int_t             i;
181
0
    njs_int_t             ret;
182
0
    njs_value_t           *replacer, *space;
183
0
    const u_char          *p;
184
0
    njs_string_prop_t     prop;
185
0
    njs_json_stringify_t  *stringify, json_stringify;
186
187
0
    stringify = &json_stringify;
188
189
0
    stringify->vm = vm;
190
0
    stringify->depth = 0;
191
0
    stringify->keys_type = NJS_ENUM_STRING;
192
193
0
    replacer = njs_arg(args, nargs, 2);
194
195
0
    if (njs_is_function(replacer) || njs_is_array(replacer)) {
196
0
        stringify->replacer = *replacer;
197
0
        if (njs_is_array(replacer)) {
198
0
            ret = njs_json_stringify_array(stringify);
199
0
            if (njs_slow_path(ret != NJS_OK)) {
200
0
                goto memory_error;
201
0
            }
202
0
        }
203
204
0
    } else {
205
0
        njs_set_undefined(&stringify->replacer);
206
0
    }
207
208
0
    space = njs_arg(args, nargs, 3);
209
210
0
    if (njs_is_object(space)) {
211
0
        if (njs_is_object_number(space)) {
212
0
            ret = njs_value_to_numeric(vm, space, space);
213
0
            if (njs_slow_path(ret != NJS_OK)) {
214
0
                return ret;
215
0
            }
216
217
0
        } else if (njs_is_object_string(space)) {
218
0
            ret = njs_value_to_string(vm, space, space);
219
0
            if (njs_slow_path(ret != NJS_OK)) {
220
0
                return ret;
221
0
            }
222
0
        }
223
0
    }
224
225
0
    switch (space->type) {
226
0
    case NJS_STRING:
227
0
        length = njs_string_prop(vm, &prop, space);
228
0
        p = njs_string_offset(&prop, njs_min(length, 10));
229
230
0
        stringify->space.start = prop.start;
231
0
        stringify->space.length = p - prop.start;
232
233
0
        break;
234
235
0
    case NJS_NUMBER:
236
0
        i64 = njs_min(njs_number_to_integer(njs_number(space)), 10);
237
238
0
        if (i64 > 0) {
239
0
            stringify->space.length = i64;
240
0
            stringify->space.start = stringify->space_buf;
241
242
0
            for (i = 0; i < i64; i++) {
243
0
                stringify->space.start[i] = ' ';
244
0
            }
245
246
0
            break;
247
0
        }
248
249
        /* Fall through. */
250
251
0
    default:
252
0
        stringify->space.length = 0;
253
254
0
        break;
255
0
     }
256
257
0
    return njs_json_stringify_iterator(stringify, njs_arg(args, nargs, 1),
258
0
                                       retval);
259
260
0
memory_error:
261
262
0
    njs_memory_error(vm);
263
264
0
    return NJS_ERROR;
265
0
}
266
267
268
njs_int_t
269
njs_vm_json_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
270
    njs_value_t *retval)
271
0
{
272
0
    njs_function_t  *stringify;
273
274
0
    stringify = njs_function(&njs_json_object_properties[2].desc.u.value);
275
276
0
    return njs_vm_invoke(vm, stringify, args, nargs, retval);
277
0
}
278
279
280
static const u_char *
281
njs_json_parse_value(njs_json_parse_ctx_t *ctx, njs_value_t *value,
282
    const u_char *p)
283
5.55k
{
284
5.55k
    switch (*p) {
285
1.51k
    case '{':
286
1.51k
        return njs_json_parse_object(ctx, value, p);
287
288
769
    case '[':
289
769
        return njs_json_parse_array(ctx, value, p);
290
291
1.24k
    case '"':
292
1.24k
        return njs_json_parse_string(ctx, value, p);
293
294
0
    case 't':
295
0
        if (njs_fast_path(ctx->end - p >= 4 && memcmp(p, "true", 4) == 0)) {
296
0
            *value = njs_value_true;
297
298
0
            return p + 4;
299
0
        }
300
301
0
        goto error;
302
303
0
    case 'f':
304
0
        if (njs_fast_path(ctx->end - p >= 5 && memcmp(p, "false", 5) == 0)) {
305
0
            *value = njs_value_false;
306
307
0
            return p + 5;
308
0
        }
309
310
0
        goto error;
311
312
0
    case 'n':
313
0
        if (njs_fast_path(ctx->end - p >= 4 && memcmp(p, "null", 4) == 0)) {
314
0
            *value = njs_value_null;
315
316
0
            return p + 4;
317
0
        }
318
319
0
        goto error;
320
5.55k
    }
321
322
2.03k
    if (njs_fast_path(*p == '-' || (*p - '0') <= 9)) {
323
2.03k
        return njs_json_parse_number(ctx, value, p);
324
2.03k
    }
325
326
0
error:
327
328
0
    njs_json_parse_exception(ctx, "Unexpected token", p);
329
330
0
    return NULL;
331
2.03k
}
332
333
334
static const u_char *
335
njs_json_parse_object(njs_json_parse_ctx_t *ctx, njs_value_t *value,
336
    const u_char *p)
337
1.51k
{
338
1.51k
    njs_int_t            ret;
339
1.51k
    njs_object_t         *object;
340
1.51k
    njs_value_t          prop_name, prop_value;
341
1.51k
    njs_object_prop_t    *prop;
342
1.51k
    njs_flathsh_query_t  fhq;
343
344
1.51k
    if (njs_slow_path(--ctx->depth == 0)) {
345
0
        njs_json_parse_exception(ctx, "Nested too deep", p);
346
0
        return NULL;
347
0
    }
348
349
1.51k
    object = njs_object_alloc(ctx->vm);
350
1.51k
    if (njs_slow_path(object == NULL)) {
351
0
        goto memory_error;
352
0
    }
353
354
1.51k
    prop = NULL;
355
356
2.55k
    for ( ;; ) {
357
2.55k
        p = njs_json_skip_space(p + 1, ctx->end);
358
2.55k
        if (njs_slow_path(p == ctx->end)) {
359
525
            goto error_end;
360
525
        }
361
362
2.03k
        if (*p != '"') {
363
0
            if (njs_fast_path(*p == '}')) {
364
0
                if (njs_slow_path(prop != NULL)) {
365
0
                    njs_json_parse_exception(ctx, "Trailing comma", p - 1);
366
0
                    return NULL;
367
0
                }
368
369
0
                break;
370
0
            }
371
372
0
            goto error_token;
373
0
        }
374
375
2.03k
        p = njs_json_parse_string(ctx, &prop_name, p);
376
2.03k
        if (njs_slow_path(p == NULL)) {
377
            /* The exception is set by the called function. */
378
0
            return NULL;
379
0
        }
380
381
2.03k
        p = njs_json_skip_space(p, ctx->end);
382
2.03k
        if (njs_slow_path(p == ctx->end || *p != ':')) {
383
0
            goto error_token;
384
0
        }
385
386
2.03k
        p = njs_json_skip_space(p + 1, ctx->end);
387
2.03k
        if (njs_slow_path(p == ctx->end)) {
388
0
            goto error_end;
389
0
        }
390
391
2.03k
        p = njs_json_parse_value(ctx, &prop_value, p);
392
2.03k
        if (njs_slow_path(p == NULL)) {
393
            /* The exception is set by the called function. */
394
0
            return NULL;
395
0
        }
396
397
2.03k
        fhq.key_hash = prop_name.atom_id;
398
2.03k
        fhq.replace = 1;
399
2.03k
        fhq.pool = ctx->pool;
400
2.03k
        fhq.proto = &njs_object_hash_proto;
401
402
2.03k
        ret = njs_flathsh_unique_insert(&object->hash, &fhq);
403
2.03k
        if (njs_slow_path(ret != NJS_OK)) {
404
0
            njs_internal_error(ctx->vm, "flathsh insert/replace failed");
405
0
            return NULL;
406
0
        }
407
408
2.03k
        prop = fhq.value;
409
410
2.03k
        prop->type = NJS_PROPERTY;
411
2.03k
        prop->enumerable = 1;
412
2.03k
        prop->configurable = 1;
413
2.03k
        prop->writable = 1;
414
2.03k
        prop->u.value = prop_value;
415
416
2.03k
        p = njs_json_skip_space(p, ctx->end);
417
2.03k
        if (njs_slow_path(p == ctx->end)) {
418
0
            goto error_end;
419
0
        }
420
421
2.03k
        if (*p != ',') {
422
989
            if (njs_fast_path(*p == '}')) {
423
220
                break;
424
220
            }
425
426
769
            goto error_token;
427
989
        }
428
2.03k
    }
429
430
220
    njs_set_object(value, object);
431
432
220
    ctx->depth++;
433
434
220
    return p + 1;
435
436
769
error_token:
437
438
769
    njs_json_parse_exception(ctx, "Unexpected token", p);
439
440
769
    return NULL;
441
442
525
error_end:
443
444
525
    njs_json_parse_exception(ctx, "Unexpected end of input", p);
445
446
525
    return NULL;
447
448
0
memory_error:
449
450
0
    njs_memory_error(ctx->vm);
451
452
0
    return NULL;
453
1.51k
}
454
455
456
static const u_char *
457
njs_json_parse_array(njs_json_parse_ctx_t *ctx, njs_value_t *value,
458
    const u_char *p)
459
769
{
460
769
    njs_int_t    ret;
461
769
    njs_bool_t   empty;
462
769
    njs_array_t  *array;
463
769
    njs_value_t  element;
464
465
769
    if (njs_slow_path(--ctx->depth == 0)) {
466
0
        njs_json_parse_exception(ctx, "Nested too deep", p);
467
0
        return NULL;
468
0
    }
469
470
769
    array = njs_array_alloc(ctx->vm, 0, 0, NJS_ARRAY_SPARE);
471
769
    if (njs_slow_path(array == NULL)) {
472
0
        return NULL;
473
0
    }
474
475
769
    empty = 1;
476
477
769
    for ( ;; ) {
478
769
        p = njs_json_skip_space(p + 1, ctx->end);
479
769
        if (njs_slow_path(p == ctx->end)) {
480
0
            goto error_end;
481
0
        }
482
483
769
        if (*p == ']') {
484
0
            if (njs_slow_path(!empty)) {
485
0
                njs_json_parse_exception(ctx, "Trailing comma", p - 1);
486
0
                return NULL;
487
0
            }
488
489
0
            break;
490
0
        }
491
492
769
        p = njs_json_parse_value(ctx, &element, p);
493
769
        if (njs_slow_path(p == NULL)) {
494
0
            return NULL;
495
0
        }
496
497
769
        ret = njs_array_add(ctx->vm, array, &element);
498
769
        if (njs_slow_path(ret != NJS_OK)) {
499
0
            return NULL;
500
0
        }
501
502
769
        empty = 0;
503
504
769
        p = njs_json_skip_space(p, ctx->end);
505
769
        if (njs_slow_path(p == ctx->end)) {
506
0
            goto error_end;
507
0
        }
508
509
769
        if (*p != ',') {
510
769
            if (njs_fast_path(*p == ']')) {
511
769
                break;
512
769
            }
513
514
0
            goto error_token;
515
769
        }
516
769
    }
517
518
769
    njs_set_array(value, array);
519
520
769
    ctx->depth++;
521
522
769
    return p + 1;
523
524
0
error_token:
525
526
0
    njs_json_parse_exception(ctx, "Unexpected token", p);
527
528
0
    return NULL;
529
530
0
error_end:
531
532
0
    njs_json_parse_exception(ctx, "Unexpected end of input", p);
533
534
0
    return NULL;
535
769
}
536
537
538
static const u_char *
539
njs_json_parse_string(njs_json_parse_ctx_t *ctx, njs_value_t *value,
540
    const u_char *p)
541
3.27k
{
542
3.27k
    u_char        ch, *s, *dst;
543
3.27k
    size_t        size, surplus;
544
3.27k
    uint32_t      utf, utf_low;
545
3.27k
    njs_int_t     ret;
546
3.27k
    const u_char  *start, *last;
547
548
3.27k
    enum {
549
3.27k
        sw_usual = 0,
550
3.27k
        sw_escape,
551
3.27k
        sw_encoded1,
552
3.27k
        sw_encoded2,
553
3.27k
        sw_encoded3,
554
3.27k
        sw_encoded4,
555
3.27k
    } state;
556
557
3.27k
    start = p + 1;
558
559
3.27k
    dst = NULL;
560
3.27k
    state = 0;
561
3.27k
    surplus = 0;
562
563
151k
    for (p = start; p < ctx->end; p++) {
564
151k
        ch = *p;
565
566
151k
        switch (state) {
567
568
118k
        case sw_usual:
569
570
118k
            if (ch == '"') {
571
3.27k
                break;
572
3.27k
            }
573
574
115k
            if (ch == '\\') {
575
12.4k
                state = sw_escape;
576
12.4k
                continue;
577
12.4k
            }
578
579
103k
            if (njs_fast_path(ch >= ' ')) {
580
103k
                continue;
581
103k
            }
582
583
0
            njs_json_parse_exception(ctx, "Forbidden source char", p);
584
585
0
            return NULL;
586
587
12.4k
        case sw_escape:
588
589
12.4k
            switch (ch) {
590
1.24k
            case '"':
591
6.20k
            case '\\':
592
6.84k
            case '/':
593
6.84k
            case 'n':
594
6.84k
            case 'r':
595
6.84k
            case 't':
596
7.44k
            case 'b':
597
7.44k
            case 'f':
598
7.44k
                surplus++;
599
7.44k
                state = sw_usual;
600
7.44k
                continue;
601
602
4.96k
            case 'u':
603
                /*
604
                 * Basic unicode 6 bytes "\uXXXX" in JSON
605
                 * and up to 3 bytes in UTF-8.
606
                 *
607
                 * Surrogate pair: 12 bytes "\uXXXX\uXXXX" in JSON
608
                 * and 3 or 4 bytes in UTF-8.
609
                 */
610
4.96k
                surplus += 3;
611
4.96k
                state = sw_encoded1;
612
4.96k
                continue;
613
12.4k
            }
614
615
0
            njs_json_parse_exception(ctx, "Unknown escape char", p);
616
617
0
            return NULL;
618
619
4.96k
        case sw_encoded1:
620
9.92k
        case sw_encoded2:
621
14.8k
        case sw_encoded3:
622
19.8k
        case sw_encoded4:
623
624
19.8k
            if (njs_fast_path((ch >= '0' && ch <= '9')
625
19.8k
                              || (ch >= 'A' && ch <= 'F')
626
19.8k
                              || (ch >= 'a' && ch <= 'f')))
627
19.8k
            {
628
19.8k
                state = (state == sw_encoded4) ? sw_usual : state + 1;
629
19.8k
                continue;
630
19.8k
            }
631
632
0
            njs_json_parse_exception(ctx, "Invalid Unicode escape sequence", p);
633
634
0
            return NULL;
635
151k
        }
636
637
3.27k
        break;
638
151k
    }
639
640
3.27k
    if (njs_slow_path(p == ctx->end)) {
641
0
        njs_json_parse_exception(ctx, "Unexpected end of input", p);
642
0
        return NULL;
643
0
    }
644
645
    /* Points to the ending quote mark. */
646
3.27k
    last = p;
647
648
3.27k
    size = last - start - surplus;
649
650
3.27k
    if (surplus != 0) {
651
1.24k
        p = start;
652
653
1.24k
        dst = njs_mp_alloc(ctx->pool, size);
654
1.24k
        if (njs_slow_path(dst == NULL)) {
655
0
            njs_memory_error(ctx->vm);;
656
0
            return NULL;
657
0
        }
658
659
1.24k
        s = dst;
660
661
69.6k
        do {
662
69.6k
            ch = *p++;
663
664
69.6k
            if (ch != '\\') {
665
58.4k
                *s++ = ch;
666
58.4k
                continue;
667
58.4k
            }
668
669
11.1k
            ch = *p++;
670
671
11.1k
            switch (ch) {
672
1.24k
            case '"':
673
6.20k
            case '\\':
674
6.84k
            case '/':
675
6.84k
                *s++ = ch;
676
6.84k
                continue;
677
678
0
            case 'n':
679
0
                *s++ = '\n';
680
0
                continue;
681
682
0
            case 'r':
683
0
                *s++ = '\r';
684
0
                continue;
685
686
0
            case 't':
687
0
                *s++ = '\t';
688
0
                continue;
689
690
598
            case 'b':
691
598
                *s++ = '\b';
692
598
                continue;
693
694
0
            case 'f':
695
0
                *s++ = '\f';
696
0
                continue;
697
11.1k
            }
698
699
            /* "\uXXXX": Unicode escape sequence. */
700
701
3.72k
            utf = njs_json_unicode(p);
702
3.72k
            p += 4;
703
704
3.72k
            if (njs_surrogate_any(utf)) {
705
706
3.72k
                if (utf > 0xdbff || p[0] != '\\' || p[1] != 'u') {
707
2.48k
                    s = njs_utf8_encode(s, NJS_UNICODE_REPLACEMENT);
708
2.48k
                    continue;
709
2.48k
                }
710
711
1.24k
                p += 2;
712
713
1.24k
                utf_low = njs_json_unicode(p);
714
1.24k
                p += 4;
715
716
1.24k
                if (njs_fast_path(njs_surrogate_trailing(utf_low))) {
717
0
                    utf = njs_surrogate_pair(utf, utf_low);
718
719
1.24k
                } else if (njs_surrogate_leading(utf_low)) {
720
598
                    utf = NJS_UNICODE_REPLACEMENT;
721
598
                    s = njs_utf8_encode(s, NJS_UNICODE_REPLACEMENT);
722
723
643
                } else {
724
643
                    utf = utf_low;
725
643
                    s = njs_utf8_encode(s, NJS_UNICODE_REPLACEMENT);
726
643
                }
727
1.24k
            }
728
729
1.24k
            s = njs_utf8_encode(s, utf);
730
731
69.6k
        } while (p != last);
732
733
1.24k
        size = s - dst;
734
1.24k
        start = dst;
735
1.24k
    }
736
737
3.27k
    ret = njs_atom_string_create(ctx->vm, value, (u_char *) start, size);
738
3.27k
    if (njs_slow_path(ret != NJS_OK)) {
739
0
        return NULL;
740
0
    }
741
742
3.27k
    if (dst != NULL) {
743
1.24k
        njs_mp_free(ctx->pool, dst);
744
1.24k
    }
745
746
3.27k
    return last + 1;
747
3.27k
}
748
749
750
static const u_char *
751
njs_json_parse_number(njs_json_parse_ctx_t *ctx, njs_value_t *value,
752
    const u_char *p)
753
2.03k
{
754
2.03k
    double        num;
755
2.03k
    njs_int_t     sign;
756
2.03k
    const u_char  *start;
757
758
2.03k
    sign = 1;
759
760
2.03k
    if (*p == '-') {
761
220
        if (p + 1 == ctx->end) {
762
0
            goto error;
763
0
        }
764
765
220
        p++;
766
220
        sign = -1;
767
220
    }
768
769
2.03k
    start = p;
770
2.03k
    num = njs_number_dec_parse(&p, ctx->end, 0);
771
2.03k
    if (p != start) {
772
2.03k
        njs_set_number(value, sign * num);
773
2.03k
        return p;
774
2.03k
    }
775
776
0
error:
777
778
0
    njs_json_parse_exception(ctx, "Unexpected number", p);
779
780
0
    return NULL;
781
2.03k
}
782
783
784
njs_inline uint32_t
785
njs_json_unicode(const u_char *p)
786
4.96k
{
787
4.96k
    u_char      c;
788
4.96k
    uint32_t    utf;
789
4.96k
    njs_uint_t  i;
790
791
4.96k
    utf = 0;
792
793
24.8k
    for (i = 0; i < 4; i++) {
794
19.8k
        utf <<= 4;
795
19.8k
        c = p[i] | 0x20;
796
19.8k
        c -= '0';
797
19.8k
        if (c > 9) {
798
18.0k
            c += '0' - 'a' + 10;
799
18.0k
        }
800
801
19.8k
        utf |= c;
802
19.8k
    }
803
804
4.96k
    return utf;
805
4.96k
}
806
807
808
static const u_char *
809
njs_json_skip_space(const u_char *start, const u_char *end)
810
14.4k
{
811
14.4k
    const u_char  *p;
812
813
18.0k
    for (p = start; njs_fast_path(p != end); p++) {
814
815
18.0k
        switch (*p) {
816
1.53k
        case ' ':
817
1.53k
        case '\t':
818
2.83k
        case '\r':
819
5.61k
        case '\n':
820
5.61k
            continue;
821
18.0k
        }
822
823
12.4k
        break;
824
18.0k
    }
825
826
14.4k
    return p;
827
14.4k
}
828
829
830
static njs_int_t
831
njs_json_internalize_property(njs_vm_t *vm, njs_function_t *reviver,
832
    njs_value_t *holder, uint32_t atom_id, njs_int_t depth,
833
    njs_value_t *retval)
834
864
{
835
864
    int64_t       k, length;
836
864
    njs_int_t     ret;
837
864
    njs_value_t   val, new_elem;
838
864
    njs_value_t   arguments[3];
839
864
    njs_array_t   *keys;
840
841
864
    if (njs_slow_path(depth++ >= NJS_JSON_MAX_DEPTH)) {
842
0
        njs_type_error(vm, "Nested too deep or a cyclic structure");
843
0
        return NJS_ERROR;
844
0
    }
845
846
864
    ret = njs_value_property(vm, holder, atom_id, &val);
847
864
    if (njs_slow_path(ret == NJS_ERROR)) {
848
0
        return NJS_ERROR;
849
0
    }
850
851
864
    keys = NULL;
852
853
864
    if (njs_is_object(&val)) {
854
220
        if (!njs_is_array(&val)) {
855
220
            keys = njs_array_keys(vm, &val, 0);
856
220
            if (njs_slow_path(keys == NULL)) {
857
0
                return NJS_ERROR;
858
0
            }
859
860
864
            for (k = 0; k < keys->length; k++) {
861
644
                ret = njs_json_internalize_property(vm, reviver, &val,
862
644
                                                    keys->start[k].atom_id,
863
644
                                                    depth, &new_elem);
864
865
644
                if (njs_slow_path(ret != NJS_OK)) {
866
0
                    goto done;
867
0
                }
868
869
644
                if (njs_is_undefined(&new_elem)) {
870
644
                    ret = njs_value_property_delete(vm, &val,
871
644
                                                    keys->start[k].atom_id,
872
644
                                                    NULL, 0);
873
874
644
                } else {
875
0
                    ret = njs_value_property_set(vm, &val,
876
0
                                                 keys->start[k].atom_id,
877
0
                                                 &new_elem);
878
0
                }
879
880
644
                if (njs_slow_path(ret == NJS_ERROR)) {
881
0
                    goto done;
882
0
                }
883
644
            }
884
885
220
        } else {
886
887
0
            ret = njs_object_length(vm, &val, &length);
888
0
            if (njs_slow_path(ret == NJS_ERROR)) {
889
0
                return NJS_ERROR;
890
0
            }
891
892
0
            for (k = 0; k < length; k++) {
893
0
                ret = njs_json_internalize_property(vm, reviver, &val,
894
0
                                                    njs_number_atom(k),
895
0
                                                    depth, &new_elem);
896
897
0
                if (njs_slow_path(ret != NJS_OK)) {
898
0
                    return NJS_ERROR;
899
0
                }
900
901
0
                if (njs_is_undefined(&new_elem)) {
902
0
                    ret = njs_value_property_delete(vm, &val,
903
0
                                                    njs_number_atom(k), NULL,
904
0
                                                    0);
905
906
0
                } else {
907
0
                    ret = njs_value_property_set(vm, &val, njs_number_atom(k),
908
0
                                                 &new_elem);
909
0
                }
910
911
0
                if (njs_slow_path(ret == NJS_ERROR)) {
912
0
                    return NJS_ERROR;
913
0
                }
914
0
            }
915
0
        }
916
220
    }
917
918
864
    njs_value_assign(&arguments[0], holder);
919
864
    njs_atom_to_value(vm, &arguments[1], atom_id);
920
864
    njs_value_assign(&arguments[2], &val);
921
922
864
    ret = njs_function_apply(vm, reviver, arguments, 3, retval);
923
924
864
done:
925
926
864
    if (keys != NULL) {
927
220
        njs_array_destroy(vm, keys);
928
220
    }
929
930
864
    return ret;
931
864
}
932
933
934
static void
935
njs_json_parse_exception(njs_json_parse_ctx_t *ctx, const char *msg,
936
    const u_char *pos)
937
1.29k
{
938
1.29k
    ssize_t  length;
939
940
1.29k
    length = njs_utf8_length(ctx->start, pos - ctx->start);
941
1.29k
    if (njs_slow_path(length < 0)) {
942
0
        length = 0;
943
0
    }
944
945
1.29k
    njs_syntax_error(ctx->vm, "%s at position %z", msg, length);
946
1.29k
}
947
948
949
static njs_json_state_t *
950
njs_json_push_stringify_state(njs_json_stringify_t *stringify,
951
    njs_value_t *value)
952
7.46k
{
953
7.46k
    njs_int_t         ret;
954
7.46k
    njs_json_state_t  *state;
955
956
7.46k
    if (njs_slow_path(stringify->depth >= NJS_JSON_MAX_DEPTH)) {
957
0
        njs_type_error(stringify->vm, "Nested too deep or a cyclic structure");
958
0
        return NULL;
959
0
    }
960
961
7.46k
    state = &stringify->states[stringify->depth++];
962
7.46k
    state->value = *value;
963
7.46k
    state->array = njs_is_array(value);
964
7.46k
    state->fast_array = njs_is_fast_array(value);
965
7.46k
    state->index = 0;
966
7.46k
    state->written = 0;
967
7.46k
    state->keys = NULL;
968
7.46k
    state->key = NULL;
969
970
7.46k
    if (state->fast_array) {
971
7.26k
        state->length = njs_array_len(value);
972
7.26k
    }
973
974
7.46k
    if (njs_is_array(&stringify->replacer)) {
975
0
        state->keys = njs_array(&stringify->replacer);
976
977
7.46k
    } else if (state->array) {
978
7.26k
        state->keys = njs_array_keys(stringify->vm, value, 1);
979
7.26k
        if (njs_slow_path(state->keys == NULL)) {
980
0
            return NULL;
981
0
        }
982
983
7.26k
        ret = njs_object_length(stringify->vm, &state->value, &state->length);
984
7.26k
        if (njs_slow_path(ret == NJS_ERROR)) {
985
0
            return NULL;
986
0
        }
987
988
7.26k
    } else {
989
202
        state->keys = njs_value_own_enumerate(stringify->vm, value,
990
202
                                              NJS_ENUM_KEYS
991
202
                                              | stringify->keys_type
992
202
                                              | NJS_ENUM_ENUMERABLE_ONLY);
993
994
202
        if (njs_slow_path(state->keys == NULL)) {
995
0
            return NULL;
996
0
        }
997
202
    }
998
999
7.46k
    return state;
1000
7.46k
}
1001
1002
1003
njs_inline njs_json_state_t *
1004
njs_json_pop_stringify_state(njs_json_stringify_t *stringify)
1005
7.46k
{
1006
7.46k
    njs_json_state_t  *state;
1007
1008
7.46k
    state = &stringify->states[stringify->depth - 1];
1009
7.46k
    if (!njs_is_array(&stringify->replacer) && state->keys != NULL) {
1010
7.46k
        njs_array_destroy(stringify->vm, state->keys);
1011
7.46k
        state->keys = NULL;
1012
7.46k
    }
1013
1014
7.46k
    if (stringify->depth > 1) {
1015
0
        stringify->depth--;
1016
0
        state = &stringify->states[stringify->depth - 1];
1017
0
        state->written = 1;
1018
0
        return state;
1019
0
    }
1020
1021
7.46k
    return NULL;
1022
7.46k
}
1023
1024
1025
njs_inline njs_bool_t
1026
njs_json_is_object(const njs_value_t *value)
1027
0
{
1028
0
    if (!njs_is_object(value)) {
1029
0
        return 0;
1030
0
    }
1031
1032
0
    if (njs_is_function(value)) {
1033
0
        return 0;
1034
0
    }
1035
1036
0
    if (njs_is_object_value(value)) {
1037
0
        switch (njs_object_value(value)->type) {
1038
0
        case NJS_BOOLEAN:
1039
0
        case NJS_NUMBER:
1040
0
        case NJS_STRING:
1041
0
            return 0;
1042
1043
0
        default:
1044
0
            break;
1045
0
        }
1046
0
    }
1047
1048
0
    return 1;
1049
0
}
1050
1051
1052
njs_inline void
1053
njs_json_stringify_indent(njs_json_stringify_t *stringify, njs_chb_t *chain,
1054
    njs_int_t times)
1055
27.0k
{
1056
27.0k
    njs_int_t  i;
1057
1058
27.0k
    if (stringify->space.length != 0) {
1059
0
        times += stringify->depth;
1060
0
        njs_chb_append(chain,"\n", 1);
1061
0
        for (i = 0; i < (times - 1); i++) {
1062
0
            njs_chb_append_str(chain, &stringify->space);
1063
0
        }
1064
0
    }
1065
27.0k
}
1066
1067
1068
njs_inline njs_bool_t
1069
njs_json_stringify_done(njs_json_state_t *state, njs_bool_t array)
1070
31.5k
{
1071
31.5k
    return array ? state->index >= state->length
1072
31.5k
                 : state->index >= state->keys->length;
1073
31.5k
}
1074
1075
1076
static njs_int_t
1077
njs_json_stringify_iterator(njs_json_stringify_t *stringify,
1078
    njs_value_t *object, njs_value_t *retval)
1079
0
{
1080
0
    int64_t           size;
1081
0
    njs_int_t         ret;
1082
0
    njs_chb_t         chain;
1083
0
    njs_value_t       *key, *value, index, wrapper;
1084
0
    njs_object_t      *obj;
1085
0
    njs_json_state_t  *state;
1086
1087
0
    obj = njs_json_wrap_value(stringify->vm, &wrapper, object);
1088
0
    if (njs_slow_path(obj == NULL)) {
1089
0
        goto memory_error;
1090
0
    }
1091
1092
0
    state = njs_json_push_stringify_state(stringify, &wrapper);
1093
0
    if (njs_slow_path(state == NULL)) {
1094
0
        goto memory_error;
1095
0
    }
1096
1097
0
    NJS_CHB_MP_INIT(&chain, njs_vm_memory_pool(stringify->vm));
1098
1099
0
    for ( ;; ) {
1100
0
        if (state->index == 0) {
1101
0
            njs_chb_append(&chain, state->array ? "[" : "{", 1);
1102
0
            njs_json_stringify_indent(stringify, &chain, 0);
1103
0
        }
1104
1105
0
        if (njs_json_stringify_done(state, state->array)) {
1106
0
            njs_json_stringify_indent(stringify, &chain, -1);
1107
0
            njs_chb_append(&chain, state->array ? "]" : "}", 1);
1108
1109
0
            state = njs_json_pop_stringify_state(stringify);
1110
0
            if (state == NULL) {
1111
0
                goto done;
1112
0
            }
1113
1114
0
            continue;
1115
0
        }
1116
1117
0
        value = &stringify->retval;
1118
1119
0
        if (state->array) {
1120
0
            njs_set_number(&index, state->index);
1121
0
            key = &index;
1122
1123
0
        } else {
1124
0
            key = &state->keys->start[state->index];
1125
0
        }
1126
1127
0
        ret = njs_value_property_val(stringify->vm, &state->value, key, value);
1128
0
        if (njs_slow_path(ret == NJS_ERROR)) {
1129
0
            return ret;
1130
0
        }
1131
1132
0
        if (state->array && ret == NJS_DECLINED) {
1133
0
            njs_set_null(value);
1134
0
        }
1135
1136
0
        ret = njs_json_stringify_to_json(stringify, state, key, value);
1137
0
        if (njs_slow_path(ret != NJS_OK)) {
1138
0
            return ret;
1139
0
        }
1140
1141
0
        ret = njs_json_stringify_replacer(stringify, state, key, value);
1142
0
        if (njs_slow_path(ret != NJS_OK)) {
1143
0
            return ret;
1144
0
        }
1145
1146
0
        state->index++;
1147
1148
0
        if (!state->array
1149
0
            && (njs_is_undefined(value)
1150
0
                || njs_is_symbol(value)
1151
0
                || njs_is_function(value)
1152
0
                || !njs_is_valid(value)))
1153
0
        {
1154
0
            continue;
1155
0
        }
1156
1157
0
        if (state->written) {
1158
0
            njs_chb_append_literal(&chain,",");
1159
0
            njs_json_stringify_indent(stringify, &chain, 0);
1160
0
        }
1161
1162
0
        state->written = 1;
1163
1164
0
        if (!state->array) {
1165
0
            njs_json_append_string(stringify->vm, &chain, key, '\"');
1166
0
            njs_chb_append_literal(&chain,":");
1167
0
            if (stringify->space.length != 0) {
1168
0
                njs_chb_append_literal(&chain," ");
1169
0
            }
1170
0
        }
1171
1172
0
        if (njs_json_is_object(value)) {
1173
0
            state = njs_json_push_stringify_state(stringify, value);
1174
0
            if (njs_slow_path(state == NULL)) {
1175
0
                return NJS_ERROR;
1176
0
            }
1177
1178
0
            continue;
1179
0
        }
1180
1181
0
        ret = njs_json_append_value(stringify->vm, &chain, value);
1182
0
        if (njs_slow_path(ret != NJS_OK)) {
1183
0
            return ret;
1184
0
        }
1185
0
    }
1186
1187
0
done:
1188
1189
    /*
1190
     * The value to stringify is wrapped as '{"": value}'.
1191
     * Stripping the wrapper's data.
1192
     */
1193
1194
0
    njs_chb_drain(&chain, njs_length("{\"\":"));
1195
0
    njs_chb_drop(&chain, njs_length("}"));
1196
1197
0
    if (stringify->space.length != 0) {
1198
0
        njs_chb_drain(&chain, njs_length("\n "));
1199
0
        njs_chb_drop(&chain, njs_length("\n"));
1200
0
    }
1201
1202
0
    size = njs_chb_size(&chain);
1203
0
    if (njs_slow_path(size < 0)) {
1204
0
        njs_chb_destroy(&chain);
1205
0
        goto memory_error;
1206
0
    }
1207
1208
0
    if (size == 0) {
1209
0
        njs_set_undefined(retval);
1210
0
        goto release;
1211
0
    }
1212
1213
0
    ret = njs_string_create_chb(stringify->vm, retval, &chain);
1214
0
    if (njs_slow_path(ret != NJS_OK)) {
1215
0
        njs_chb_destroy(&chain);
1216
0
        goto memory_error;
1217
0
    }
1218
1219
0
release:
1220
1221
0
    njs_chb_destroy(&chain);
1222
1223
0
    return NJS_OK;
1224
1225
0
memory_error:
1226
1227
0
    njs_memory_error(stringify->vm);
1228
1229
0
    return NJS_ERROR;
1230
0
}
1231
1232
1233
static njs_function_t *
1234
njs_object_to_json_function(njs_vm_t *vm, njs_value_t *value)
1235
0
{
1236
0
    njs_int_t            ret;
1237
0
    njs_value_t          retval;
1238
0
    njs_flathsh_query_t  fhq;
1239
1240
0
    if (njs_is_object(value)) {
1241
0
        fhq.proto = &njs_object_hash_proto;
1242
0
        fhq.key_hash = NJS_ATOM_STRING_toJSON;
1243
1244
0
        ret = njs_object_property(vm, njs_object(value), &fhq, &retval);
1245
1246
0
        if (njs_slow_path(ret == NJS_ERROR)) {
1247
0
            return NULL;
1248
0
        }
1249
1250
0
        return njs_is_function(&retval) ? njs_function(&retval) : NULL;
1251
0
    }
1252
1253
0
    return NULL;
1254
0
}
1255
1256
1257
static njs_int_t
1258
njs_json_stringify_to_json(njs_json_stringify_t* stringify,
1259
    njs_json_state_t *state, njs_value_t *key, njs_value_t *value)
1260
0
{
1261
0
    njs_int_t       ret;
1262
0
    njs_value_t     arguments[2];
1263
0
    njs_function_t  *to_json;
1264
1265
0
    if (!njs_is_object(value)) {
1266
0
        return NJS_OK;
1267
0
    }
1268
1269
0
    to_json = njs_object_to_json_function(stringify->vm, value);
1270
0
    if (to_json == NULL) {
1271
0
        return NJS_OK;
1272
0
    }
1273
1274
0
    arguments[0] = *value;
1275
1276
0
    if (!state->array) {
1277
0
        arguments[1] = *key;
1278
1279
0
    } else {
1280
0
        ret = njs_uint32_to_string(stringify->vm, &arguments[1], state->index);
1281
0
        if (njs_slow_path(ret != NJS_OK)) {
1282
0
            return NJS_ERROR;
1283
0
        }
1284
0
    }
1285
1286
0
    return njs_function_apply(stringify->vm, to_json, arguments, 2,
1287
0
                              &stringify->retval);
1288
0
}
1289
1290
1291
static njs_int_t
1292
njs_json_stringify_replacer(njs_json_stringify_t* stringify,
1293
    njs_json_state_t *state, njs_value_t *key, njs_value_t *value)
1294
0
{
1295
0
    njs_int_t    ret;
1296
0
    njs_value_t  arguments[3];
1297
1298
0
    if (!njs_is_function(&stringify->replacer)) {
1299
0
        return NJS_OK;
1300
0
    }
1301
1302
0
    arguments[0] = state->value;
1303
0
    arguments[2] = *value;
1304
1305
0
    if (!state->array) {
1306
0
        arguments[1] = *key;
1307
1308
0
    } else {
1309
0
        ret = njs_uint32_to_string(stringify->vm, &arguments[1], state->index);
1310
0
        if (njs_slow_path(ret != NJS_OK)) {
1311
0
            return NJS_ERROR;
1312
0
        }
1313
0
    }
1314
1315
0
    return njs_function_apply(stringify->vm, njs_function(&stringify->replacer),
1316
0
                              arguments, 3, &stringify->retval);
1317
0
}
1318
1319
1320
static njs_int_t
1321
njs_json_stringify_array(njs_json_stringify_t *stringify)
1322
0
{
1323
0
    njs_int_t    ret;
1324
0
    int64_t      i, k, length;
1325
0
    njs_value_t  *value, *item;
1326
0
    njs_array_t  *properties;
1327
1328
0
    ret = njs_object_length(stringify->vm, &stringify->replacer, &length);
1329
0
    if (njs_slow_path(ret != NJS_OK)) {
1330
0
        return ret;
1331
0
    }
1332
1333
0
    properties = njs_array_alloc(stringify->vm, 1, 0, NJS_ARRAY_SPARE);
1334
0
    if (njs_slow_path(properties == NULL)) {
1335
0
        return NJS_ERROR;
1336
0
    }
1337
1338
0
    item = njs_array_push(stringify->vm, properties);
1339
0
    njs_set_empty_string(stringify->vm, item);
1340
1341
0
    for (i = 0; i < length; i++) {
1342
0
        ret = njs_value_property_i64(stringify->vm, &stringify->replacer, i,
1343
0
                                     &stringify->retval);
1344
0
        if (njs_slow_path(ret == NJS_ERROR)) {
1345
0
            return ret;
1346
0
        }
1347
1348
0
        value = &stringify->retval;
1349
1350
0
        switch (value->type) {
1351
0
        case NJS_STRING:
1352
0
            break;
1353
1354
0
        case NJS_NUMBER:
1355
0
            ret = njs_number_to_string(stringify->vm, value, value);
1356
0
            if (njs_slow_path(ret != NJS_OK)) {
1357
0
                return NJS_ERROR;
1358
0
            }
1359
1360
0
            break;
1361
1362
0
        case NJS_OBJECT_VALUE:
1363
0
            switch (njs_object_value(value)->type) {
1364
0
            case NJS_NUMBER:
1365
0
            case NJS_STRING:
1366
0
                ret = njs_value_to_string(stringify->vm, value, value);
1367
0
                if (njs_slow_path(ret != NJS_OK)) {
1368
0
                    return NJS_ERROR;
1369
0
                }
1370
1371
0
                break;
1372
1373
0
            default:
1374
0
                continue;
1375
0
            }
1376
1377
0
            break;
1378
1379
0
        default:
1380
0
            continue;
1381
0
        }
1382
1383
0
        for (k = 0; k < properties->length; k++) {
1384
0
            if (njs_values_strict_equal(stringify->vm, value,
1385
0
                                        &properties->start[k]) == 1)
1386
0
            {
1387
0
                break;
1388
0
            }
1389
0
        }
1390
1391
0
        if (k == properties->length) {
1392
0
            item = njs_array_push(stringify->vm, properties);
1393
0
            if (njs_slow_path(item == NULL)) {
1394
0
                return NJS_ERROR;
1395
0
            }
1396
1397
0
            njs_value_assign(item, value);
1398
0
        }
1399
0
    }
1400
1401
0
    njs_set_array(&stringify->replacer, properties);
1402
1403
0
    return NJS_OK;
1404
0
}
1405
1406
1407
static njs_int_t
1408
njs_json_append_value(njs_vm_t *vm, njs_chb_t *chain, njs_value_t *value)
1409
0
{
1410
0
    njs_int_t  ret;
1411
1412
0
    if (njs_is_object_value(value)) {
1413
0
        switch (njs_object_value(value)->type) {
1414
0
        case NJS_NUMBER:
1415
0
            ret = njs_value_to_numeric(vm, value, value);
1416
0
            if (njs_slow_path(ret != NJS_OK)) {
1417
0
                return ret;
1418
0
            }
1419
1420
0
            break;
1421
1422
0
        case NJS_BOOLEAN:
1423
0
            njs_value_assign(value, njs_object_value(value));
1424
0
            break;
1425
1426
0
        case NJS_STRING:
1427
0
            ret = njs_value_to_string(vm, value, value);
1428
0
             if (njs_slow_path(ret != NJS_OK)) {
1429
0
                 return ret;
1430
0
             }
1431
1432
0
            break;
1433
1434
0
        default:
1435
0
            break;
1436
0
        }
1437
0
    }
1438
1439
0
    switch (value->type) {
1440
0
    case NJS_STRING:
1441
0
        njs_json_append_string(vm, chain, value, '\"');
1442
0
        break;
1443
1444
0
    case NJS_NUMBER:
1445
0
        njs_json_append_number(chain, value);
1446
0
        break;
1447
1448
0
    case NJS_BOOLEAN:
1449
0
        if (njs_is_true(value)) {
1450
0
            njs_chb_append_literal(chain, "true");
1451
1452
0
        } else {
1453
0
            njs_chb_append_literal(chain, "false");
1454
0
        }
1455
1456
0
        break;
1457
1458
0
    case NJS_UNDEFINED:
1459
0
    case NJS_NULL:
1460
0
    case NJS_SYMBOL:
1461
0
    case NJS_INVALID:
1462
0
    case NJS_FUNCTION:
1463
0
    default:
1464
0
        njs_chb_append_literal(chain, "null");
1465
0
    }
1466
1467
0
    return NJS_OK;
1468
0
}
1469
1470
1471
static void
1472
njs_json_append_string(njs_vm_t *vm, njs_chb_t *chain, const njs_value_t *value,
1473
    char quote)
1474
13.4k
{
1475
13.4k
    size_t             size;
1476
13.4k
    u_char             c, *dst, *dst_end;
1477
13.4k
    njs_bool_t         utf8;
1478
13.4k
    const u_char       *p, *end;
1479
13.4k
    njs_string_prop_t  string;
1480
1481
13.4k
    static char  hex2char[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
1482
13.4k
                                  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
1483
1484
13.4k
    (void) njs_string_prop(vm, &string, value);
1485
1486
13.4k
    p = string.start;
1487
13.4k
    end = p + string.size;
1488
13.4k
    utf8 = (string.length != 0 && string.length != string.size);
1489
1490
13.4k
    size = njs_max(string.size + 2, 7);
1491
13.4k
    dst = njs_chb_reserve(chain, size);
1492
13.4k
    if (njs_slow_path(dst == NULL)) {
1493
0
        return;
1494
0
    }
1495
1496
13.4k
    dst_end = dst + size;
1497
1498
13.4k
    *dst++ = quote;
1499
13.4k
    njs_chb_written(chain, 1);
1500
1501
273k
    while (p < end) {
1502
259k
        if (njs_slow_path(dst_end <= dst + njs_length("\\uXXXX"))) {
1503
40.8k
            size = njs_max(end - p + 1, 6);
1504
40.8k
            dst = njs_chb_reserve(chain, size);
1505
40.8k
            if (njs_slow_path(dst == NULL)) {
1506
0
                return;
1507
0
            }
1508
1509
40.8k
            dst_end = dst + size;
1510
40.8k
        }
1511
1512
259k
        if (njs_slow_path(*p < ' '
1513
259k
                          || *p == '\\'
1514
259k
                          || (*p == '\"' && quote == '\"')))
1515
68.1k
        {
1516
68.1k
            c = (u_char) *p++;
1517
68.1k
            *dst++ = '\\';
1518
68.1k
            njs_chb_written(chain, 2);
1519
1520
68.1k
            switch (c) {
1521
1.67k
            case '\\':
1522
1.67k
                *dst++ = '\\';
1523
1.67k
                break;
1524
0
            case '"':
1525
0
                *dst++ = '\"';
1526
0
                break;
1527
403
            case '\r':
1528
403
                *dst++ = 'r';
1529
403
                break;
1530
6.91k
            case '\n':
1531
6.91k
                *dst++ = 'n';
1532
6.91k
                break;
1533
1.44k
            case '\t':
1534
1.44k
                *dst++ = 't';
1535
1.44k
                break;
1536
4
            case '\b':
1537
4
                *dst++ = 'b';
1538
4
                break;
1539
494
            case '\f':
1540
494
                *dst++ = 'f';
1541
494
                break;
1542
57.2k
            default:
1543
57.2k
                *dst++ = 'u';
1544
57.2k
                *dst++ = '0';
1545
57.2k
                *dst++ = '0';
1546
57.2k
                *dst++ = hex2char[(c & 0xf0) >> 4];
1547
57.2k
                *dst++ = hex2char[c & 0x0f];
1548
57.2k
                njs_chb_written(chain, 4);
1549
68.1k
            }
1550
1551
68.1k
            continue;
1552
68.1k
        }
1553
1554
191k
        if (utf8) {
1555
            /* UTF-8 string. */
1556
178k
            dst = njs_utf8_copy(dst, &p, end);
1557
1558
178k
        } else {
1559
            /* ASCII string. */
1560
12.9k
            *dst++ = *p++;
1561
12.9k
        }
1562
1563
191k
        njs_chb_written(chain, dst - chain->last->pos);
1564
191k
    }
1565
1566
13.4k
    njs_chb_append(chain, &quote, 1);
1567
13.4k
}
1568
1569
1570
static void
1571
njs_json_append_number(njs_chb_t *chain, const njs_value_t *value)
1572
0
{
1573
0
    u_char  *p;
1574
0
    size_t  size;
1575
0
    double  num;
1576
1577
0
    num = njs_number(value);
1578
1579
0
    if (isnan(num) || isinf(num)) {
1580
0
        njs_chb_append_literal(chain, "null");
1581
1582
0
    } else {
1583
0
        p = njs_chb_reserve(chain, 64);
1584
0
        if (njs_slow_path(p == NULL)) {
1585
0
            return;
1586
0
        }
1587
1588
0
        size = njs_dtoa(num, (char *) p);
1589
1590
0
        njs_chb_written(chain, size);
1591
0
    }
1592
0
}
1593
1594
1595
/*
1596
 * Wraps a value as '{"": <value>}'.
1597
 */
1598
static njs_object_t *
1599
njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper,
1600
    const njs_value_t *value)
1601
220
{
1602
220
    njs_int_t            ret;
1603
220
    njs_object_prop_t    *prop;
1604
220
    njs_flathsh_query_t  fhq;
1605
1606
220
    wrapper->data.u.object = njs_object_alloc(vm);
1607
220
    if (njs_slow_path(njs_object(wrapper) == NULL)) {
1608
0
        return NULL;
1609
0
    }
1610
1611
220
    wrapper->type = NJS_OBJECT;
1612
220
    wrapper->data.truth = 1;
1613
1614
220
    fhq.key_hash = NJS_ATOM_STRING_empty;
1615
220
    fhq.replace = 0;
1616
220
    fhq.pool = vm->mem_pool;
1617
220
    fhq.proto = &njs_object_hash_proto;
1618
1619
220
    ret = njs_flathsh_unique_insert(njs_object_hash(wrapper), &fhq);
1620
220
    if (njs_slow_path(ret != NJS_OK)) {
1621
0
        return NULL;
1622
0
    }
1623
1624
220
    prop = fhq.value;
1625
1626
220
    prop->type = NJS_PROPERTY;
1627
220
    prop->enumerable = 1;
1628
220
    prop->configurable = 1;
1629
220
    prop->writable = 1;
1630
220
    prop->u.value = *value;
1631
1632
220
    return wrapper->data.u.object;
1633
220
}
1634
1635
1636
static const njs_object_prop_init_t  njs_json_object_properties[] =
1637
{
1638
    NJS_DECLARE_PROP_VALUE(SYMBOL_toStringTag, njs_ascii_strval("JSON"),
1639
                           NJS_OBJECT_PROP_VALUE_C),
1640
1641
    NJS_DECLARE_PROP_NATIVE(STRING_parse, njs_json_parse, 2, 0),
1642
1643
    NJS_DECLARE_PROP_NATIVE(STRING_stringify, njs_json_stringify, 3, 0),
1644
};
1645
1646
1647
const njs_object_init_t  njs_json_object_init = {
1648
    njs_json_object_properties,
1649
    njs_nitems(njs_json_object_properties),
1650
};
1651
1652
1653
static njs_int_t
1654
njs_dump_terminal(njs_json_stringify_t *stringify, njs_chb_t *chain,
1655
    njs_value_t *value, njs_uint_t console)
1656
17.0k
{
1657
17.0k
    njs_str_t          str;
1658
17.0k
    njs_int_t          ret;
1659
17.0k
    njs_value_t        str_val, tag;
1660
17.0k
    njs_typed_array_t  *array;
1661
17.0k
    njs_string_prop_t  string;
1662
1663
17.0k
    njs_int_t   (*to_string)(njs_vm_t *, njs_value_t *, const njs_value_t *);
1664
1665
17.0k
    switch (value->type) {
1666
0
    case NJS_NULL:
1667
0
        njs_chb_append_literal(chain, "null");
1668
0
        break;
1669
1670
0
    case NJS_UNDEFINED:
1671
0
        njs_chb_append_literal(chain, "undefined");
1672
0
        break;
1673
1674
0
    case NJS_BOOLEAN:
1675
0
        if (njs_is_true(value)) {
1676
0
            njs_chb_append_literal(chain, "true");
1677
1678
0
        } else {
1679
0
            njs_chb_append_literal(chain, "false");
1680
0
        }
1681
1682
0
        break;
1683
1684
13.4k
    case NJS_STRING:
1685
13.4k
        njs_string_get(stringify->vm, value, &str);
1686
1687
13.4k
        if (!console || stringify->depth != 0) {
1688
13.4k
            njs_json_append_string(stringify->vm, chain, value, '\'');
1689
13.4k
            return NJS_OK;
1690
13.4k
        }
1691
1692
0
        njs_chb_append_str(chain, &str);
1693
1694
0
        break;
1695
1696
0
    case NJS_SYMBOL:
1697
0
        ret = njs_symbol_descriptive_string(stringify->vm, &str_val, value);
1698
0
        if (njs_slow_path(ret != NJS_OK)) {
1699
0
            return NJS_ERROR;
1700
0
        }
1701
1702
0
        njs_string_get(stringify->vm, &str_val, &str);
1703
0
        njs_chb_append_str(chain, &str);
1704
1705
0
        break;
1706
1707
0
    case NJS_INVALID:
1708
        /* Fall through. */
1709
0
        break;
1710
1711
0
    case NJS_OBJECT_VALUE:
1712
0
        value = njs_object_value(value);
1713
1714
0
        switch (value->type) {
1715
0
        case NJS_BOOLEAN:
1716
0
            if (njs_is_true(value)) {
1717
0
                njs_chb_append_literal(chain, "[Boolean: true]");
1718
1719
0
            } else {
1720
0
                njs_chb_append_literal(chain, "[Boolean: false]");
1721
0
            }
1722
1723
0
            break;
1724
1725
0
        case NJS_NUMBER:
1726
0
            if (njs_slow_path(njs_number(value) == 0.0
1727
0
                              && signbit(njs_number(value))))
1728
0
            {
1729
1730
0
                njs_chb_append_literal(chain, "[Number: -0]");
1731
0
                break;
1732
0
            }
1733
1734
0
            ret = njs_number_to_string(stringify->vm, &str_val, value);
1735
0
            if (njs_slow_path(ret != NJS_OK)) {
1736
0
                return NJS_ERROR;
1737
0
            }
1738
1739
0
            njs_string_get(stringify->vm, &str_val, &str);
1740
0
            njs_chb_sprintf(chain, 16 + str.length, "[Number: %V]", &str);
1741
0
            break;
1742
1743
0
        case NJS_SYMBOL:
1744
0
            ret = njs_symbol_descriptive_string(stringify->vm, &str_val, value);
1745
0
            if (njs_slow_path(ret != NJS_OK)) {
1746
0
                return NJS_ERROR;
1747
0
            }
1748
1749
0
            njs_string_get(stringify->vm, &str_val, &str);
1750
0
            njs_chb_sprintf(chain, 16 + str.length, "[Symbol: %V]", &str);
1751
1752
0
            break;
1753
1754
0
        case NJS_STRING:
1755
0
        default:
1756
0
            njs_chb_append_literal(chain, "[String: ");
1757
0
            njs_json_append_string(stringify->vm, chain, value, '\'');
1758
0
            njs_chb_append_literal(chain, "]");
1759
0
            break;
1760
0
        }
1761
1762
0
        break;
1763
1764
752
    case NJS_FUNCTION:
1765
752
        if (njs_function(value)->native) {
1766
0
            str = njs_str_value("native");
1767
1768
752
        } else {
1769
752
            str = njs_str_value("");
1770
752
        }
1771
1772
752
        ret = njs_value_property(stringify->vm, value, NJS_ATOM_STRING_name,
1773
752
                                 &tag);
1774
752
        if (njs_slow_path(ret == NJS_ERROR)) {
1775
0
            return ret;
1776
0
        }
1777
1778
752
        if (njs_is_string(&tag)) {
1779
275
            njs_string_get(stringify->vm, &tag, &str);
1780
275
        }
1781
1782
752
        if (str.length != 0) {
1783
275
            njs_chb_sprintf(chain, 32 + str.length, "[Function: %V]", &str);
1784
1785
477
        } else {
1786
477
            njs_chb_append_literal(chain, "[Function]");
1787
477
        }
1788
1789
752
        break;
1790
1791
0
    case NJS_TYPED_ARRAY:
1792
0
        array = njs_typed_array(value);
1793
0
        ret = njs_object_string_tag(stringify->vm, value, &tag);
1794
0
        if (njs_slow_path(ret == NJS_ERROR)) {
1795
0
            return ret;
1796
0
        }
1797
1798
0
        if (ret == NJS_OK) {
1799
0
            (void) njs_string_prop(stringify->vm, &string, &tag);
1800
0
            njs_chb_append(chain, string.start, string.size);
1801
0
            njs_chb_append_literal(chain, " ");
1802
0
        }
1803
1804
0
        njs_chb_append_literal(chain, "[");
1805
1806
0
        njs_typed_array_to_chain(stringify->vm, chain, array, NULL);
1807
1808
0
        njs_chb_append_literal(chain, "]");
1809
1810
0
        break;
1811
1812
20
    case NJS_NUMBER:
1813
20
        if (njs_slow_path(njs_number(value) == 0.0
1814
20
                          && signbit(njs_number(value))))
1815
0
        {
1816
1817
0
            njs_chb_append_literal(chain, "-0");
1818
0
            break;
1819
0
        }
1820
1821
        /* Fall through. */
1822
1823
20
    case NJS_OBJECT:
1824
20
    case NJS_REGEXP:
1825
2.85k
    case NJS_DATE:
1826
1827
2.85k
        switch (value->type) {
1828
20
        case NJS_NUMBER:
1829
20
            to_string = njs_number_to_string;
1830
20
            break;
1831
1832
0
        case NJS_REGEXP:
1833
0
            to_string = njs_regexp_to_string;
1834
0
            break;
1835
1836
2.83k
        case NJS_DATE:
1837
2.83k
            to_string = njs_date_to_string;
1838
2.83k
            break;
1839
1840
0
        default:
1841
0
            to_string = njs_error_to_string;
1842
2.85k
        }
1843
1844
2.85k
        ret = to_string(stringify->vm, &str_val, value);
1845
2.85k
        if (njs_slow_path(ret != NJS_OK)) {
1846
0
            return NJS_ERROR;
1847
0
        }
1848
1849
2.85k
        njs_string_get(stringify->vm, &str_val, &str);
1850
2.85k
        njs_chb_append_str(chain, &str);
1851
1852
2.85k
        break;
1853
1854
0
    default:
1855
1856
0
        njs_chb_sprintf(chain, 64, "[Unknown value type:%uD]", value->type);
1857
17.0k
    }
1858
1859
3.60k
    return NJS_OK;
1860
17.0k
}
1861
1862
1863
njs_inline njs_bool_t
1864
njs_dump_is_recursive(const njs_value_t *value)
1865
24.4k
{
1866
24.4k
    return (value->type == NJS_OBJECT && !njs_object(value)->error_data)
1867
24.2k
           || (value->type == NJS_ARRAY)
1868
17.0k
           || (value->type >= NJS_OBJECT_SPECIAL_MAX
1869
0
               && !njs_is_object_primitive(value));
1870
24.4k
}
1871
1872
1873
njs_inline njs_int_t
1874
njs_dump_visited(njs_vm_t *vm, njs_json_stringify_t *stringify,
1875
    const njs_value_t *value)
1876
0
{
1877
0
    njs_int_t  depth;
1878
1879
0
    depth = stringify->depth - 1;
1880
1881
0
    for (; depth >= 0; depth--) {
1882
0
        if (njs_values_same(vm, &stringify->states[depth].value, value)) {
1883
0
            return 1;
1884
0
        }
1885
0
    }
1886
1887
0
    return 0;
1888
0
}
1889
1890
1891
njs_inline void
1892
njs_dump_empty(njs_json_stringify_t *stringify, njs_json_state_t *state,
1893
    njs_chb_t *chain, njs_bool_t sep_position)
1894
24.2k
{
1895
24.2k
    double   key, prev;
1896
24.2k
    int64_t  diff;
1897
1898
24.2k
    if (!state->array) {
1899
742
        return;
1900
742
    }
1901
1902
23.5k
    if (sep_position) {
1903
16.2k
        key = njs_key_to_index(state->key);
1904
16.2k
        prev = (state->index > 1) ? njs_key_to_index(&state->key[-1]) : -1;
1905
1906
16.2k
    } else {
1907
7.26k
        key = state->length;
1908
7.26k
        if (state->key == NULL) {
1909
0
            prev = -1;
1910
1911
7.26k
        } else {
1912
7.26k
            prev = (state->index > 0) ? njs_key_to_index(state->key) : -1;
1913
7.26k
        }
1914
7.26k
    }
1915
1916
23.5k
    if (isnan(prev)) {
1917
0
        return;
1918
0
    }
1919
1920
23.5k
    if (isnan(key)) {
1921
0
        key = state->length;
1922
0
    }
1923
1924
23.5k
    diff = key - prev;
1925
1926
23.5k
    if (diff > 1) {
1927
2.83k
        if (sep_position == 0 && state->keys->length) {
1928
0
            if (prev != -1) {
1929
0
                njs_chb_append_literal(chain, ",");
1930
0
                njs_json_stringify_indent(stringify, chain, 1);
1931
0
            }
1932
0
        }
1933
1934
2.83k
        if (diff - 1 == 1) {
1935
0
            njs_chb_sprintf(chain, 64, "<empty>");
1936
1937
2.83k
        } else {
1938
2.83k
            njs_chb_sprintf(chain, 64, "<%L empty items>", diff - 1);
1939
2.83k
        }
1940
1941
2.83k
        state->written = 1;
1942
1943
2.83k
        if (sep_position == 1 && state->keys->length) {
1944
2.83k
            njs_chb_append_literal(chain, ",");
1945
2.83k
            njs_json_stringify_indent(stringify, chain, 1);
1946
2.83k
        }
1947
2.83k
    }
1948
23.5k
}
1949
1950
1951
njs_int_t
1952
njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, njs_value_t *value,
1953
    njs_uint_t console, njs_uint_t indent)
1954
7.72k
{
1955
7.72k
    njs_int_t             ret;
1956
7.72k
    njs_chb_t             chain;
1957
7.72k
    njs_str_t             str;
1958
7.72k
    njs_value_t           *key, *val, s, tag, exception;
1959
7.72k
    njs_json_state_t      *state;
1960
7.72k
    njs_string_prop_t     string;
1961
7.72k
    njs_object_prop_t     *prop;
1962
7.72k
    njs_property_query_t  pq;
1963
7.72k
    njs_json_stringify_t  *stringify, dump_stringify;
1964
1965
7.72k
    stringify = &dump_stringify;
1966
1967
7.72k
    stringify->vm = vm;
1968
7.72k
    stringify->depth = 0;
1969
1970
7.72k
    if (njs_slow_path(vm->top_frame == NULL)) {
1971
        /* An exception was thrown during compilation. */
1972
0
        njs_vm_runtime_init(vm);
1973
0
    }
1974
1975
7.72k
    if (njs_is_valid(&vm->exception)) {
1976
0
        exception = njs_vm_exception(vm);
1977
0
        value = &exception;
1978
0
    }
1979
1980
7.72k
    NJS_CHB_MP_INIT(&chain, njs_vm_memory_pool(vm));
1981
1982
7.72k
    if (!njs_dump_is_recursive(value)) {
1983
255
        ret = njs_dump_terminal(stringify, &chain, value, console);
1984
255
        if (njs_slow_path(ret != NJS_OK)) {
1985
0
            goto memory_error;
1986
0
        }
1987
1988
255
        goto done;
1989
255
    }
1990
1991
7.46k
    njs_set_undefined(&stringify->replacer);
1992
7.46k
    stringify->keys_type = NJS_ENUM_STRING | NJS_ENUM_SYMBOL;
1993
7.46k
    indent = njs_min(indent, 10);
1994
7.46k
    stringify->space.length = indent;
1995
7.46k
    stringify->space.start = stringify->space_buf;
1996
1997
7.46k
    njs_memset(stringify->space.start, ' ', indent);
1998
1999
7.46k
    state = njs_json_push_stringify_state(stringify, value);
2000
7.46k
    if (njs_slow_path(state == NULL)) {
2001
0
        goto memory_error;
2002
0
    }
2003
2004
31.5k
    for ( ;; ) {
2005
31.5k
        if (state->index == 0) {
2006
7.46k
            ret = njs_object_string_tag(vm, &state->value, &tag);
2007
7.46k
            if (njs_slow_path(ret == NJS_ERROR)) {
2008
0
                return ret;
2009
0
            }
2010
2011
7.46k
            if (ret == NJS_OK) {
2012
0
                (void) njs_string_prop(vm, &string, &tag);
2013
0
                njs_chb_append(&chain, string.start, string.size);
2014
0
                njs_chb_append_literal(&chain, " ");
2015
0
            }
2016
2017
7.46k
            njs_chb_append(&chain, state->array ? "[" : "{", 1);
2018
7.46k
            njs_json_stringify_indent(stringify, &chain, 1);
2019
7.46k
        }
2020
2021
31.5k
        if (njs_json_stringify_done(state, 0)) {
2022
7.46k
            njs_dump_empty(stringify, state, &chain, 0);
2023
2024
7.46k
            njs_json_stringify_indent(stringify, &chain, 0);
2025
7.46k
            njs_chb_append(&chain, state->array ? "]" : "}", 1);
2026
2027
7.46k
            state = njs_json_pop_stringify_state(stringify);
2028
7.46k
            if (state == NULL) {
2029
7.46k
                goto done;
2030
7.46k
            }
2031
2032
0
            continue;
2033
7.46k
        }
2034
2035
24.0k
        njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
2036
2037
24.0k
        key = &state->keys->start[state->index++];
2038
2039
24.0k
        if (state->array && key->atom_id == NJS_ATOM_STRING_length) {
2040
7.26k
            continue;
2041
7.26k
        }
2042
2043
16.7k
        state->key = key;
2044
2045
16.7k
        ret = njs_property_query_val(vm, &pq, &state->value, key);
2046
16.7k
        if (njs_slow_path(ret != NJS_OK)) {
2047
0
            if (ret == NJS_DECLINED) {
2048
0
                continue;
2049
0
            }
2050
2051
0
            goto exception;
2052
0
        }
2053
2054
16.7k
        prop = pq.fhq.value;
2055
2056
16.7k
        if (prop->type == NJS_WHITEOUT || !prop->enumerable) {
2057
0
            if (!state->array) {
2058
0
                continue;
2059
0
            }
2060
0
        }
2061
2062
16.7k
        if (state->written) {
2063
9.30k
            njs_chb_append_literal(&chain, ",");
2064
9.30k
            njs_json_stringify_indent(stringify, &chain, 1);
2065
9.30k
        }
2066
2067
16.7k
        state->written = 1;
2068
2069
16.7k
        njs_dump_empty(stringify, state, &chain, 1);
2070
2071
16.7k
        if (!state->array || isnan(njs_key_to_index(key))) {
2072
540
            njs_atom_string_get(vm, key->atom_id, &pq.fhq.key);
2073
540
            njs_chb_append(&chain, pq.fhq.key.start, pq.fhq.key.length);
2074
540
            njs_chb_append_literal(&chain, ":");
2075
540
            if (stringify->space.length != 0) {
2076
0
                njs_chb_append_literal(&chain, " ");
2077
0
            }
2078
540
        }
2079
2080
16.7k
        val = njs_prop_value(prop);
2081
2082
16.7k
        if (prop->type == NJS_PROPERTY_HANDLER) {
2083
0
            pq.scratch = *prop;
2084
0
            prop = &pq.scratch;
2085
0
            ret = njs_prop_handler(prop)(vm, prop, pq.fhq.key_hash,
2086
0
                                         &state->value, NULL,
2087
0
                                         njs_prop_value(prop));
2088
2089
0
            if (njs_slow_path(ret == NJS_ERROR)) {
2090
0
                return ret;
2091
0
            }
2092
2093
0
            val = njs_prop_value(prop);
2094
0
        }
2095
2096
16.7k
        if (njs_is_accessor_descriptor(prop)) {
2097
23
            if (njs_prop_getter(prop) != NULL) {
2098
20
                if (njs_prop_setter(prop) != NULL) {
2099
0
                    njs_atom_to_value(vm, &s, NJS_ATOM_STRING__Getter_Setter_);
2100
2101
20
                } else {
2102
20
                    njs_atom_to_value(vm, &s, NJS_ATOM_STRING__Getter_);
2103
20
                }
2104
2105
20
            } else {
2106
3
                njs_atom_to_value(vm, &s, NJS_ATOM_STRING__Setter_);
2107
3
            }
2108
2109
23
            val = &s;
2110
23
        }
2111
2112
16.7k
        if (njs_dump_is_recursive(val)) {
2113
0
            if (njs_slow_path(njs_dump_visited(vm, stringify, val))) {
2114
0
                njs_chb_append_literal(&chain, "[Circular]");
2115
0
                continue;
2116
0
            }
2117
2118
0
            state = njs_json_push_stringify_state(stringify, val);
2119
0
            if (njs_slow_path(state == NULL)) {
2120
0
                goto exception;
2121
0
            }
2122
2123
0
            continue;
2124
0
        }
2125
2126
16.7k
        ret = njs_dump_terminal(stringify, &chain, val, console);
2127
16.7k
        if (njs_slow_path(ret != NJS_OK)) {
2128
0
            if (ret == NJS_DECLINED) {
2129
0
                goto exception;
2130
0
            }
2131
2132
0
            goto memory_error;
2133
0
        }
2134
16.7k
    }
2135
2136
7.72k
done:
2137
2138
7.72k
    ret = njs_chb_join(&chain, &str);
2139
7.72k
    if (njs_slow_path(ret != NJS_OK)) {
2140
0
        goto memory_error;
2141
0
    }
2142
2143
7.72k
    njs_chb_destroy(&chain);
2144
2145
7.72k
    *retval = str;
2146
2147
7.72k
    return NJS_OK;
2148
2149
0
memory_error:
2150
2151
0
    njs_memory_error(vm);
2152
2153
0
exception:
2154
2155
0
    njs_vm_value_string(vm, retval, &vm->exception);
2156
2157
0
    return NJS_OK;
2158
0
}