Coverage Report

Created: 2025-12-16 08:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/njs/src/njs_object.c
Line
Count
Source
1
2
/*
3
 * Copyright (C) Igor Sysoev
4
 * Copyright (C) NGINX, Inc.
5
 */
6
7
8
#include <njs_main.h>
9
10
11
typedef enum {
12
    NJS_OBJECT_INTEGRITY_SEALED,
13
    NJS_OBJECT_INTEGRITY_FROZEN,
14
} njs_object_integrity_level_t;
15
16
17
static njs_object_prop_t *njs_object_exist_in_proto(const njs_object_t *begin,
18
    const njs_object_t *end, njs_flathsh_query_t *fhq);
19
static njs_int_t njs_object_enumerate_array(njs_vm_t *vm,
20
    const njs_array_t *array, njs_array_t *items, uint32_t flags);
21
static njs_int_t njs_object_enumerate_typed_array(njs_vm_t *vm,
22
    const njs_typed_array_t *array, njs_array_t *items, uint32_t flags);
23
static njs_int_t njs_object_enumerate_string(njs_vm_t *vm,
24
    const njs_value_t *value, njs_array_t *items, uint32_t flags);
25
static njs_int_t njs_object_enumerate_object(njs_vm_t *vm,
26
    const njs_object_t *object, njs_array_t *items, uint32_t flags);
27
static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm,
28
    const njs_object_t *object, const njs_object_t *parent, njs_array_t *items,
29
    uint32_t flags);
30
static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args,
31
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
32
static njs_int_t njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object,
33
    const njs_value_t *value);
34
35
36
njs_object_t *
37
njs_object_alloc(njs_vm_t *vm)
38
175k
{
39
175k
    njs_object_t  *object;
40
41
175k
    object = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_t));
42
43
175k
    if (njs_fast_path(object != NULL)) {
44
175k
        njs_flathsh_init(&object->hash);
45
175k
        njs_flathsh_init(&object->shared_hash);
46
175k
        object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT);
47
175k
        object->slots = NULL;
48
175k
        object->type = NJS_OBJECT;
49
175k
        object->shared = 0;
50
175k
        object->extensible = 1;
51
175k
        object->error_data = 0;
52
175k
        object->fast_array = 0;
53
54
175k
        return object;
55
175k
    }
56
57
0
    njs_memory_error(vm);
58
59
0
    return NULL;
60
175k
}
61
62
63
njs_object_t *
64
njs_object_value_copy(njs_vm_t *vm, njs_value_t *value)
65
141
{
66
141
    size_t        size;
67
141
    njs_object_t  *object, *proto;
68
69
141
    object = njs_object(value);
70
71
141
    if (!object->shared) {
72
0
        return object;
73
0
    }
74
75
141
    switch (object->type) {
76
139
    case NJS_OBJECT:
77
139
        size = sizeof(njs_object_t);
78
139
        proto = (object->__proto__ != NULL)
79
139
                    ? njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT)
80
139
                    : NULL;
81
139
        break;
82
0
    case NJS_ARRAY:
83
0
        size = sizeof(njs_array_t);
84
0
        njs_assert_msg(!object->fast_array,
85
0
                       "shared fast_array is not supported");
86
0
        proto = (object->__proto__ != NULL)
87
0
                    ? njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY)
88
0
                    : NULL;
89
0
        break;
90
2
    case NJS_OBJECT_VALUE:
91
2
        size = sizeof(njs_object_value_t);
92
2
        proto = (object->__proto__ != NULL)
93
2
                    ? njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT)
94
2
                    : NULL;
95
2
        break;
96
0
    default:
97
0
        njs_internal_error(vm, "unexpected object type to copy");
98
0
        return NULL;
99
141
    }
100
101
141
    object = njs_mp_alloc(vm->mem_pool, size);
102
103
141
    if (njs_fast_path(object != NULL)) {
104
141
        memcpy(object, njs_object(value), size);
105
141
        object->__proto__ = proto;
106
141
        object->shared = 0;
107
141
        value->data.u.object = object;
108
141
        return object;
109
141
    }
110
111
0
    njs_memory_error(vm);
112
113
0
    return NULL;
114
141
}
115
116
117
njs_object_value_t *
118
njs_object_value_alloc(njs_vm_t *vm, njs_uint_t prototype_index, size_t extra,
119
    const njs_value_t *value)
120
72.2k
{
121
72.2k
    njs_object_value_t  *ov;
122
123
72.2k
    ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t) + extra);
124
72.2k
    if (njs_slow_path(ov == NULL)) {
125
0
        njs_memory_error(vm);
126
0
        return NULL;
127
0
    }
128
129
72.2k
    njs_flathsh_init(&ov->object.hash);
130
131
72.2k
    if (prototype_index == NJS_OBJ_TYPE_STRING) {
132
40.7k
        ov->object.shared_hash = vm->shared->string_instance_hash;
133
134
40.7k
    } else {
135
31.5k
        njs_flathsh_init(&ov->object.shared_hash);
136
31.5k
    }
137
138
72.2k
    ov->object.type = NJS_OBJECT_VALUE;
139
72.2k
    ov->object.shared = 0;
140
72.2k
    ov->object.extensible = 1;
141
72.2k
    ov->object.error_data = 0;
142
72.2k
    ov->object.fast_array = 0;
143
144
72.2k
    ov->object.__proto__ = njs_vm_proto(vm, prototype_index);
145
72.2k
    ov->object.slots = NULL;
146
147
72.2k
    if (value != NULL) {
148
65.4k
        ov->value = *value;
149
65.4k
    }
150
151
72.2k
    return ov;
152
72.2k
}
153
154
155
njs_int_t
156
njs_object_hash_create(njs_vm_t *vm, njs_flathsh_t *hash,
157
    const njs_object_prop_init_t *prop, njs_uint_t n)
158
84.1k
{
159
84.1k
    njs_int_t            ret;
160
84.1k
    njs_object_prop_t    *obj_prop;
161
84.1k
    njs_flathsh_query_t  fhq;
162
163
84.1k
    fhq.replace = 0;
164
84.1k
    fhq.proto = &njs_object_hash_proto;
165
84.1k
    fhq.pool = vm->mem_pool;
166
167
729k
    while (n != 0) {
168
645k
        fhq.key_hash = prop->desc.atom_id;
169
645k
        fhq.value = (void *) prop;
170
171
645k
        ret = njs_flathsh_unique_insert(hash, &fhq);
172
645k
        if (njs_slow_path(ret != NJS_OK)) {
173
0
            njs_internal_error(vm, "flathsh insert failed");
174
0
            return NJS_ERROR;
175
0
        }
176
177
645k
        obj_prop = fhq.value;
178
179
645k
        obj_prop->type = prop->desc.type;
180
645k
        obj_prop->enumerable = prop->desc.enumerable;
181
645k
        obj_prop->configurable = prop->desc.configurable;
182
645k
        obj_prop->writable = prop->desc.writable;
183
645k
        obj_prop->u.value = prop->desc.u.value;
184
185
645k
        prop++;
186
645k
        n--;
187
645k
    }
188
189
84.1k
    return NJS_OK;
190
84.1k
}
191
192
193
const njs_flathsh_proto_t  njs_object_hash_proto
194
    njs_aligned(64) =
195
{
196
    NULL,
197
    njs_flathsh_proto_alloc,
198
    njs_flathsh_proto_free,
199
};
200
201
202
static njs_int_t
203
njs_object_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
204
    njs_index_t unused, njs_value_t *retval)
205
2.21k
{
206
2.21k
    njs_uint_t          type, index;
207
2.21k
    njs_value_t         *value;
208
2.21k
    njs_object_t        *object;
209
2.21k
    njs_object_value_t  *obj_val;
210
211
2.21k
    value = njs_arg(args, nargs, 1);
212
2.21k
    type = value->type;
213
214
2.21k
    if (njs_is_null_or_undefined(value)) {
215
49
        object = njs_object_alloc(vm);
216
49
        if (njs_slow_path(object == NULL)) {
217
0
            return NJS_ERROR;
218
0
        }
219
220
49
        njs_set_object(retval, object);
221
222
49
        return NJS_OK;
223
49
    }
224
225
2.16k
    if (njs_is_primitive(value)) {
226
2.16k
        index = njs_primitive_prototype_index(type);
227
2.16k
        obj_val = njs_object_value_alloc(vm, index, 0, value);
228
2.16k
        if (njs_slow_path(obj_val == NULL)) {
229
0
            return NJS_ERROR;
230
0
        }
231
232
2.16k
        njs_set_object_value(retval, obj_val);
233
234
2.16k
        return NJS_OK;
235
2.16k
    }
236
237
0
    if (njs_slow_path(!njs_is_object(value))) {
238
0
        njs_type_error(vm, "unexpected constructor argument:%s",
239
0
                       njs_type_string(type));
240
241
0
        return NJS_ERROR;
242
0
    }
243
244
0
    njs_value_assign(retval, value);
245
246
0
    return NJS_OK;
247
0
}
248
249
250
static njs_int_t
251
njs_object_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
252
    njs_index_t unused, njs_value_t *retval)
253
0
{
254
0
    njs_value_t   *value, *descs, arguments[3];
255
0
    njs_object_t  *object;
256
257
0
    value = njs_arg(args, nargs, 1);
258
259
0
    if (njs_is_object(value) || njs_is_null(value)) {
260
261
0
        object = njs_object_alloc(vm);
262
0
        if (njs_slow_path(object == NULL)) {
263
0
            return NJS_ERROR;
264
0
        }
265
266
0
        if (!njs_is_null(value)) {
267
0
            object->__proto__ = njs_object(value);
268
269
0
        } else {
270
0
            object->__proto__ = NULL;
271
0
        }
272
273
0
        descs = njs_arg(args, nargs, 2);
274
275
0
        if (njs_slow_path(!njs_is_undefined(descs))) {
276
0
            arguments[0] = args[0];
277
0
            njs_set_object(&arguments[1], object);
278
0
            arguments[2] = *descs;
279
280
0
            return njs_object_define_properties(vm, arguments, 3, unused,
281
0
                                                retval);
282
0
        }
283
284
0
        njs_set_object(retval, object);
285
286
0
        return NJS_OK;
287
0
    }
288
289
0
    njs_type_error(vm, "prototype may only be an object or null: %s",
290
0
                   njs_type_string(value->type));
291
292
0
    return NJS_ERROR;
293
0
}
294
295
296
static njs_int_t
297
njs_object_keys(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
298
    njs_index_t unused, njs_value_t *retval)
299
0
{
300
0
    njs_value_t  *value;
301
0
    njs_array_t  *keys;
302
303
0
    value = njs_arg(args, nargs, 1);
304
305
0
    if (njs_is_null_or_undefined(value)) {
306
0
        njs_type_error(vm, "cannot convert %s argument to object",
307
0
                       njs_type_string(value->type));
308
309
0
        return NJS_ERROR;
310
0
    }
311
312
0
    keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS | NJS_ENUM_STRING
313
0
                                   | NJS_ENUM_ENUMERABLE_ONLY);
314
0
    if (keys == NULL) {
315
0
        return NJS_ERROR;
316
0
    }
317
318
0
    njs_set_array(retval, keys);
319
320
0
    return NJS_OK;
321
0
}
322
323
324
static njs_int_t
325
njs_object_values(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
326
    njs_index_t unused, njs_value_t *retval)
327
0
{
328
0
    njs_array_t  *array;
329
0
    njs_value_t  *value;
330
331
0
    value = njs_arg(args, nargs, 1);
332
333
0
    if (njs_is_null_or_undefined(value)) {
334
0
        njs_type_error(vm, "cannot convert %s argument to object",
335
0
                       njs_type_string(value->type));
336
337
0
        return NJS_ERROR;
338
0
    }
339
340
0
    array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES | NJS_ENUM_STRING
341
0
                                    | NJS_ENUM_ENUMERABLE_ONLY);
342
0
    if (array == NULL) {
343
0
        return NJS_ERROR;
344
0
    }
345
346
0
    njs_set_array(retval, array);
347
348
0
    return NJS_OK;
349
0
}
350
351
352
static njs_int_t
353
njs_object_entries(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
354
    njs_index_t unused, njs_value_t *retval)
355
0
{
356
0
    njs_array_t  *array;
357
0
    njs_value_t  *value;
358
359
0
    value = njs_arg(args, nargs, 1);
360
361
0
    if (njs_is_null_or_undefined(value)) {
362
0
        njs_type_error(vm, "cannot convert %s argument to object",
363
0
                       njs_type_string(value->type));
364
365
0
        return NJS_ERROR;
366
0
    }
367
368
0
    array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH | NJS_ENUM_STRING
369
0
                                    | NJS_ENUM_ENUMERABLE_ONLY);
370
0
    if (array == NULL) {
371
0
        return NJS_ERROR;
372
0
    }
373
374
0
    njs_set_array(retval, array);
375
376
0
    return NJS_OK;
377
0
}
378
379
380
static njs_object_prop_t *
381
njs_object_exist_in_proto(const njs_object_t *object, const njs_object_t *end,
382
    njs_flathsh_query_t *fhq)
383
2.60M
{
384
2.60M
    njs_int_t          ret;
385
2.60M
    njs_object_prop_t  *prop;
386
387
3.48M
    while (object != end) {
388
991k
        ret = njs_flathsh_unique_find(&object->hash, fhq);
389
390
991k
        if (njs_fast_path(ret == NJS_OK)) {
391
5.30k
            prop = fhq->value;
392
393
5.30k
            if (prop->type == NJS_WHITEOUT) {
394
0
                goto next;
395
0
            }
396
397
5.30k
            return fhq->value;
398
5.30k
        }
399
400
986k
        ret = njs_flathsh_unique_find(&object->shared_hash, fhq);
401
402
986k
        if (njs_fast_path(ret == NJS_OK)) {
403
107k
            return fhq->value;
404
107k
        }
405
406
879k
next:
407
408
879k
        object = object->__proto__;
409
879k
    }
410
411
2.48M
    return NULL;
412
2.60M
}
413
414
415
njs_inline njs_int_t
416
njs_object_enumerate_value(njs_vm_t *vm, const njs_object_t *object,
417
    njs_array_t *items, uint32_t flags)
418
22.5k
{
419
22.5k
    njs_int_t           ret;
420
22.5k
    njs_object_value_t  *obj_val;
421
422
22.5k
    if (flags & NJS_ENUM_STRING) {
423
22.5k
        switch (object->type) {
424
34
        case NJS_ARRAY:
425
34
            ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items,
426
34
                                             flags);
427
34
            break;
428
429
5
        case NJS_TYPED_ARRAY:
430
5
            ret = njs_object_enumerate_typed_array(vm,
431
5
                                                  (njs_typed_array_t *) object,
432
5
                                                  items, flags);
433
5
            break;
434
435
22.4k
        case NJS_OBJECT_VALUE:
436
22.4k
            obj_val = (njs_object_value_t *) object;
437
438
22.4k
            if (njs_is_string(&obj_val->value)) {
439
22.4k
                ret = njs_object_enumerate_string(vm, &obj_val->value, items,
440
22.4k
                                                  flags);
441
22.4k
                break;
442
22.4k
            }
443
444
        /* Fall through. */
445
446
0
        default:
447
0
            goto object;
448
22.5k
        }
449
450
22.5k
        if (njs_slow_path(ret != NJS_OK)) {
451
0
            return NJS_ERROR;
452
0
        }
453
22.5k
    }
454
455
22.5k
object:
456
457
22.5k
    ret = njs_object_enumerate_object(vm, object, items, flags);
458
22.5k
    if (njs_slow_path(ret != NJS_OK)) {
459
0
        return NJS_ERROR;
460
0
    }
461
462
22.5k
    return NJS_OK;
463
22.5k
}
464
465
466
njs_inline njs_int_t
467
njs_object_own_enumerate_value(njs_vm_t *vm, const njs_object_t *object,
468
    const njs_object_t *parent, njs_array_t *items, uint32_t flags)
469
154k
{
470
154k
    njs_int_t           ret;
471
154k
    njs_object_value_t  *obj_val;
472
473
154k
    if (flags & NJS_ENUM_STRING) {
474
154k
        switch (object->type) {
475
97.9k
        case NJS_ARRAY:
476
97.9k
            ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items,
477
97.9k
                                             flags);
478
97.9k
            break;
479
480
0
        case NJS_TYPED_ARRAY:
481
0
            ret = njs_object_enumerate_typed_array(vm,
482
0
                                                   (njs_typed_array_t *) object,
483
0
                                                   items, flags);
484
0
            break;
485
486
27.9k
        case NJS_OBJECT_VALUE:
487
27.9k
            obj_val = (njs_object_value_t *) object;
488
489
27.9k
            if (njs_is_string(&obj_val->value)) {
490
22.4k
                ret = njs_object_enumerate_string(vm, &obj_val->value, items,
491
22.4k
                                                  flags);
492
22.4k
                break;
493
22.4k
            }
494
495
            /* Fall through. */
496
497
34.3k
        default:
498
34.3k
            goto object;
499
154k
        }
500
501
120k
        if (njs_slow_path(ret != NJS_OK)) {
502
0
            return NJS_ERROR;
503
0
        }
504
120k
    }
505
506
154k
object:
507
508
154k
    ret = njs_object_own_enumerate_object(vm, object, parent, items, flags);
509
154k
    if (njs_slow_path(ret != NJS_OK)) {
510
0
        return NJS_ERROR;
511
0
    }
512
513
154k
    return NJS_OK;
514
154k
}
515
516
517
njs_array_t *
518
njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object,
519
    uint32_t flags)
520
22.5k
{
521
22.5k
    njs_int_t    ret;
522
22.5k
    njs_array_t  *items;
523
524
22.5k
    items = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
525
22.5k
    if (njs_slow_path(items == NULL)) {
526
0
        return NULL;
527
0
    }
528
529
22.5k
    ret = njs_object_enumerate_value(vm, object, items, flags);
530
22.5k
    if (njs_slow_path(ret != NJS_OK)) {
531
0
        return NULL;
532
0
    }
533
534
22.5k
    return items;
535
22.5k
}
536
537
538
njs_array_t *
539
njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object,
540
    uint32_t flags)
541
109k
{
542
109k
    njs_int_t    ret;
543
109k
    njs_array_t  *items;
544
545
109k
    items = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
546
109k
    if (njs_slow_path(items == NULL)) {
547
0
        return NULL;
548
0
    }
549
550
109k
    ret = njs_object_own_enumerate_value(vm, object, object, items, flags);
551
109k
    if (njs_slow_path(ret != NJS_OK)) {
552
0
        return NULL;
553
0
    }
554
555
109k
    return items;
556
109k
}
557
558
559
njs_inline njs_bool_t
560
njs_is_enumerable(const njs_value_t *value, uint32_t flags)
561
3.25M
{
562
3.25M
    return (njs_is_string(value) && (flags & NJS_ENUM_STRING))
563
41.5k
           || (njs_is_symbol(value) && (flags & NJS_ENUM_SYMBOL));
564
3.25M
}
565
566
567
static njs_int_t
568
njs_object_enumerate_array(njs_vm_t *vm, const njs_array_t *array,
569
    njs_array_t *items, uint32_t flags)
570
97.9k
{
571
97.9k
    njs_int_t    ret;
572
97.9k
    njs_value_t  *p, *start, *end;
573
97.9k
    njs_array_t  *entry;
574
575
97.9k
    if (!array->object.fast_array || array->length == 0) {
576
2.66k
        return NJS_OK;
577
2.66k
    }
578
579
95.3k
    start = array->start;
580
581
95.3k
    p = start;
582
95.3k
    end = p + array->length;
583
584
95.3k
    switch (njs_object_enum_kind(flags)) {
585
95.3k
    case NJS_ENUM_KEYS:
586
1.54M
        while (p < end) {
587
1.45M
            if (njs_is_valid(p)) {
588
320k
                ret = njs_array_expand(vm, items, 0, 1);
589
320k
                if (njs_slow_path(ret != NJS_OK)) {
590
0
                    return NJS_ERROR;
591
0
                }
592
593
320k
                ret = njs_uint32_to_string(vm, &items->start[items->length++],
594
320k
                                           p - start);
595
320k
                if (njs_slow_path(ret != NJS_OK)) {
596
0
                    return NJS_ERROR;
597
0
                }
598
320k
            }
599
600
1.45M
            p++;
601
1.45M
        }
602
603
95.3k
        break;
604
605
95.3k
    case NJS_ENUM_VALUES:
606
0
        while (p < end) {
607
0
            if (njs_is_valid(p)) {
608
0
                ret = njs_array_add(vm, items, p);
609
0
                if (njs_slow_path(ret != NJS_OK)) {
610
0
                    return NJS_ERROR;
611
0
                }
612
0
            }
613
614
0
            p++;
615
0
        }
616
617
0
        break;
618
619
0
    case NJS_ENUM_BOTH:
620
0
        while (p < end) {
621
0
            if (njs_is_valid(p)) {
622
0
                entry = njs_array_alloc(vm, 0, 2, 0);
623
0
                if (njs_slow_path(entry == NULL)) {
624
0
                    return NJS_ERROR;
625
0
                }
626
627
0
                ret = njs_uint32_to_string(vm, &entry->start[0], p - start);
628
0
                if (njs_slow_path(ret != NJS_OK)) {
629
0
                    return NJS_ERROR;
630
0
                }
631
632
0
                entry->start[1] = *p;
633
634
0
                ret = njs_array_expand(vm, items, 0, 1);
635
0
                if (njs_slow_path(ret != NJS_OK)) {
636
0
                    return NJS_ERROR;
637
0
                }
638
639
0
                njs_set_array(&items->start[items->length++], entry);
640
0
            }
641
642
0
            p++;
643
0
        }
644
645
0
        break;
646
95.3k
    }
647
648
95.3k
    return NJS_OK;
649
95.3k
}
650
651
652
static njs_int_t
653
njs_object_enumerate_typed_array(njs_vm_t *vm, const njs_typed_array_t *array,
654
    njs_array_t *items, uint32_t flags)
655
5
{
656
5
    uint32_t     i, length;
657
5
    njs_int_t    ret;
658
5
    njs_value_t  *item;
659
5
    njs_array_t  *entry;
660
661
5
    length = njs_typed_array_length(array);
662
663
5
    ret = njs_array_expand(vm, items, 0, length);
664
5
    if (njs_slow_path(ret != NJS_OK)) {
665
0
        return NJS_ERROR;
666
0
    }
667
668
5
    item = &items->start[items->length];
669
670
5
    switch (njs_object_enum_kind(flags)) {
671
5
    case NJS_ENUM_KEYS:
672
15
        for (i = 0; i < length; i++) {
673
10
            ret = njs_uint32_to_string(vm, item++, i);
674
10
            if (njs_slow_path(ret != NJS_OK)) {
675
0
                return NJS_ERROR;
676
0
            }
677
10
        }
678
679
5
        break;
680
681
5
    case NJS_ENUM_VALUES:
682
0
        for (i = 0; i < length; i++) {
683
0
            njs_set_number(item++, njs_typed_array_prop(array, i));
684
0
        }
685
686
0
        break;
687
688
0
    case NJS_ENUM_BOTH:
689
0
        for (i = 0; i < length; i++) {
690
0
            entry = njs_array_alloc(vm, 0, 2, 0);
691
0
            if (njs_slow_path(entry == NULL)) {
692
0
                return NJS_ERROR;
693
0
            }
694
695
0
            ret = njs_uint32_to_string(vm, &entry->start[0], i);
696
0
            if (njs_slow_path(ret != NJS_OK)) {
697
0
                return NJS_ERROR;
698
0
            }
699
700
0
            njs_set_number(&entry->start[1], njs_typed_array_prop(array, i));
701
702
0
            njs_set_array(item++, entry);
703
0
        }
704
705
0
        break;
706
5
    }
707
708
5
    items->length += length;
709
710
5
    return NJS_OK;
711
5
}
712
713
714
static njs_int_t
715
njs_object_enumerate_string(njs_vm_t *vm, const njs_value_t *value,
716
    njs_array_t *items, uint32_t flags)
717
44.9k
{
718
44.9k
    uint32_t           i, len, size;
719
44.9k
    njs_int_t          ret;
720
44.9k
    njs_value_t        *item, *string;
721
44.9k
    njs_array_t        *entry;
722
44.9k
    const u_char       *src, *end;
723
44.9k
    njs_string_prop_t  str_prop;
724
725
44.9k
    len = (uint32_t) njs_string_prop(vm, &str_prop, value);
726
727
44.9k
    ret = njs_array_expand(vm, items, 0, len);
728
44.9k
    if (njs_slow_path(ret != NJS_OK)) {
729
0
        return NJS_ERROR;
730
0
    }
731
732
44.9k
    item = &items->start[items->length];
733
734
44.9k
    switch (njs_object_enum_kind(flags)) {
735
44.9k
    case NJS_ENUM_KEYS:
736
1.30M
        for (i = 0; i < len; i++) {
737
1.26M
            ret = njs_uint32_to_string(vm, item++, i);
738
1.26M
            if (njs_slow_path(ret != NJS_OK)) {
739
0
                return NJS_ERROR;
740
0
            }
741
1.26M
        }
742
743
44.9k
        break;
744
745
44.9k
    case NJS_ENUM_VALUES:
746
0
        if (str_prop.size == (size_t) len) {
747
            /* ASCII string. */
748
749
0
            for (i = 0; i < len; i++) {
750
0
                ret = njs_string_new(vm, item, &str_prop.start[i], 1, 1);
751
0
                if (njs_slow_path(ret != NJS_OK)) {
752
0
                    return NJS_ERROR;
753
0
                }
754
755
0
                item++;
756
0
            }
757
758
0
        } else {
759
            /* UTF-8 string. */
760
761
0
            src = str_prop.start;
762
0
            end = src + str_prop.size;
763
764
0
            do {
765
0
                size = njs_utf8_next(src, end) - src;
766
767
0
                ret = njs_string_new(vm, item, src, size, 1);
768
0
                if (njs_slow_path(ret != NJS_OK)) {
769
0
                    return NJS_ERROR;
770
0
                }
771
772
0
                item++;
773
0
                src += size;
774
775
0
            } while (src != end);
776
0
        }
777
778
0
        break;
779
780
0
    case NJS_ENUM_BOTH:
781
0
        if (str_prop.size == (size_t) len) {
782
            /* ASCII string. */
783
784
0
            for (i = 0; i < len; i++) {
785
786
0
                entry = njs_array_alloc(vm, 0, 2, 0);
787
0
                if (njs_slow_path(entry == NULL)) {
788
0
                    return NJS_ERROR;
789
0
                }
790
791
0
                ret = njs_uint32_to_string(vm, &entry->start[0], i);
792
0
                if (njs_slow_path(ret != NJS_OK)) {
793
0
                    return NJS_ERROR;
794
0
                }
795
796
0
                string = &entry->start[1];
797
798
0
                ret = njs_string_new(vm, string, &str_prop.start[i], 1, 1);
799
0
                if (njs_slow_path(ret != NJS_OK)) {
800
0
                    return NJS_ERROR;
801
0
                }
802
803
0
                njs_set_array(item, entry);
804
805
0
                item++;
806
0
            }
807
808
0
        } else {
809
            /* UTF-8 string. */
810
811
0
            src = str_prop.start;
812
0
            end = src + str_prop.size;
813
0
            i = 0;
814
815
0
            do {
816
0
                entry = njs_array_alloc(vm, 0, 2, 0);
817
0
                if (njs_slow_path(entry == NULL)) {
818
0
                    return NJS_ERROR;
819
0
                }
820
821
0
                ret = njs_uint32_to_string(vm, &entry->start[0], i++);
822
0
                if (njs_slow_path(ret != NJS_OK)) {
823
0
                    return NJS_ERROR;
824
0
                }
825
826
0
                string = &entry->start[1];
827
828
0
                size = njs_utf8_next(src, end) - src;
829
830
0
                ret = njs_string_new(vm, string, src, size, 1);
831
0
                if (njs_slow_path(ret != NJS_OK)) {
832
0
                    return NJS_ERROR;
833
0
                }
834
835
0
                njs_set_array(item, entry);
836
837
0
                item++;
838
0
                src += size;
839
840
0
            } while (src != end);
841
0
        }
842
843
0
        break;
844
44.9k
    }
845
846
44.9k
    items->length += len;
847
848
44.9k
    return NJS_OK;
849
44.9k
}
850
851
852
static njs_int_t
853
njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object,
854
    njs_array_t *items, uint32_t flags)
855
22.5k
{
856
22.5k
    njs_int_t           ret;
857
22.5k
    const njs_object_t  *proto;
858
859
22.5k
    ret = njs_object_own_enumerate_object(vm, object, object, items, flags);
860
22.5k
    if (njs_slow_path(ret != NJS_OK)) {
861
0
        return NJS_ERROR;
862
0
    }
863
864
22.5k
    proto = object->__proto__;
865
866
67.6k
    while (proto != NULL) {
867
45.0k
        ret = njs_object_own_enumerate_value(vm, proto, object, items, flags);
868
45.0k
        if (njs_slow_path(ret != NJS_OK)) {
869
0
            return NJS_ERROR;
870
0
        }
871
872
45.0k
        proto = proto->__proto__;
873
45.0k
    }
874
875
22.5k
    return NJS_OK;
876
22.5k
}
877
878
879
#define njs_process_prop(vm, prop_name, flags, items, items_symbol)            \
880
152k
    if (!(flags & NJS_ENUM_SYMBOL && njs_is_symbol(prop_name))) {              \
881
152k
        /*                                                                     \
882
152k
         * prop from shared_hash is not symbol:                                \
883
152k
         * add to items before props from hash                                 \
884
152k
         */                                                                    \
885
152k
                                                                               \
886
152k
        ret = njs_array_add(vm, items, prop_name);                             \
887
152k
        if (njs_slow_path(ret != NJS_OK)) {                                    \
888
0
            return NJS_ERROR;                                                  \
889
0
        }                                                                      \
890
152k
                                                                               \
891
152k
    } else {                                                                   \
892
0
        /*                                                                     \
893
0
         * prop from shared_hash is symbol:                                    \
894
0
         * add to items_symbol                                                 \
895
0
         */                                                                    \
896
0
        ret = njs_array_add(vm, items_symbol, prop_name);                      \
897
0
        if (njs_slow_path(ret != NJS_OK)) {                                    \
898
0
            return NJS_ERROR;                                                  \
899
0
        }                                                                      \
900
0
    }
901
902
903
static njs_int_t
904
njs_get_own_ordered_keys(njs_vm_t *vm, const njs_object_t *object,
905
    const njs_object_t *parent, njs_array_t *items, uint32_t flags)
906
177k
{
907
177k
    double               num;
908
177k
    uint32_t             items_length;
909
177k
    njs_int_t            ret;
910
177k
    njs_array_t          *items_string, *items_symbol;
911
177k
    njs_value_t          prop_name;
912
177k
    njs_object_prop_t    *prop, *ext_prop;
913
177k
    njs_flathsh_elt_t    *elt;
914
177k
    njs_flathsh_each_t   lhe;
915
177k
    const njs_flathsh_t  *hash;
916
177k
    njs_flathsh_query_t  fhq;
917
918
177k
    items_length = items->length;
919
920
177k
    items_string = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
921
177k
    if (njs_slow_path(items_string == NULL)) {
922
0
        return NJS_ERROR;
923
0
    }
924
925
177k
    items_symbol = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
926
177k
    if (njs_slow_path(items_symbol == NULL)) {
927
0
        return NJS_ERROR;
928
0
    }
929
930
177k
    fhq.proto = &njs_object_hash_proto;
931
932
177k
    njs_flathsh_each_init(&lhe, &njs_object_hash_proto);
933
177k
    hash = &object->shared_hash;
934
935
177k
    if (flags & NJS_ENUM_NON_SHARED_ONLY) {
936
0
        goto local_hash;
937
0
    }
938
939
1.29M
    for ( ;; ) {
940
1.29M
        elt = njs_flathsh_each(hash, &lhe);
941
1.29M
        if (elt == NULL) {
942
177k
            break;
943
177k
        }
944
945
1.11M
        prop = (njs_object_prop_t *) elt;
946
947
1.11M
        ret = njs_atom_to_value(vm, &prop_name, elt->key_hash);
948
1.11M
        if (ret != NJS_OK) {
949
0
            return NJS_ERROR;
950
0
        }
951
952
1.11M
        if (!njs_is_enumerable(&prop_name, flags)) {
953
22.5k
            continue;
954
22.5k
        }
955
956
1.09M
        fhq.key_hash = elt->key_hash;
957
958
1.09M
        ext_prop = njs_object_exist_in_proto(parent, object, &fhq);
959
1.09M
        if (ext_prop != NULL) {
960
112k
            continue;
961
112k
        }
962
963
979k
        ret = njs_flathsh_unique_find(&object->hash, &fhq);
964
979k
        if (ret != NJS_OK) {
965
966
901k
            if (!(prop->enumerable || !(flags & NJS_ENUM_ENUMERABLE_ONLY))) {
967
805k
                continue;
968
805k
            }
969
970
            /* prop is:  !in_hash && in_shared_hash */
971
972
95.3k
            num = njs_string_to_index(&prop_name);
973
95.3k
            if (!njs_number_is_integer_index(num)) {
974
95.3k
                njs_process_prop(vm, &prop_name, flags, items_string,
975
95.3k
                                 items_symbol);
976
977
95.3k
            } else {
978
0
                ret = njs_array_add(vm, items, &prop_name);
979
0
                if (njs_slow_path(ret != NJS_OK)) {
980
0
                    return NJS_ERROR;
981
0
                }
982
0
            }
983
984
95.3k
        } else {
985
986
78.1k
            if (!(((njs_object_prop_t *) (fhq.value))->enumerable
987
31.8k
                  || !(flags & NJS_ENUM_ENUMERABLE_ONLY)))
988
29.2k
            {
989
29.2k
                continue;
990
29.2k
            }
991
992
            /* prop is:  in_hash && in_shared_hash */
993
994
48.9k
            num = njs_string_to_index(&prop_name);
995
48.9k
            if (!njs_number_is_integer_index(num)) {
996
997
48.9k
                njs_object_prop_t *hash_prop = fhq.value;
998
999
48.9k
                if (hash_prop->type != NJS_WHITEOUT) {
1000
48.9k
                    njs_process_prop(vm, &prop_name, flags, items_string,
1001
48.9k
                                     items_symbol);
1002
48.9k
                }
1003
48.9k
            }
1004
48.9k
        }
1005
979k
    }
1006
1007
177k
local_hash:
1008
1009
177k
    njs_flathsh_each_init(&lhe, &njs_object_hash_proto);
1010
177k
    hash = &object->hash;
1011
1012
2.31M
    for ( ;; ) {
1013
2.31M
        elt = njs_flathsh_each(hash, &lhe);
1014
2.31M
        if (elt == NULL) {
1015
177k
            break;
1016
177k
        }
1017
1018
2.14M
        prop = (njs_object_prop_t *) elt;
1019
1020
2.14M
        ret = njs_atom_to_value(vm, &prop_name, elt->key_hash);
1021
2.14M
        if (ret != NJS_OK) {
1022
0
            return NJS_ERROR;
1023
0
        }
1024
1025
2.14M
        if (!njs_is_enumerable(&prop_name, flags) ||
1026
2.14M
            !(prop->enumerable || !(flags & NJS_ENUM_ENUMERABLE_ONLY)) ||
1027
2.10M
            prop->type == NJS_WHITEOUT)
1028
631k
        {
1029
631k
            continue;
1030
631k
        }
1031
1032
1.50M
        fhq.key_hash = elt->key_hash;
1033
1034
1.50M
        ext_prop = njs_object_exist_in_proto(parent, object, &fhq);
1035
1.50M
        if (ext_prop != NULL) {
1036
0
            continue;
1037
0
        }
1038
1039
1.50M
        num = njs_string_to_index(&prop_name);
1040
1.50M
        if (njs_number_is_integer_index(num)) {
1041
1042
1.45M
            ret = njs_array_add(vm, items, &prop_name);
1043
1.45M
            if (njs_slow_path(ret != NJS_OK)) {
1044
0
                return NJS_ERROR;
1045
0
            }
1046
1047
1.45M
        } else {
1048
1049
57.3k
            ret = njs_flathsh_unique_find(&object->shared_hash, &fhq);
1050
57.3k
            if (ret != NJS_OK) {
1051
                /* prop is:  in_hash && !in_shared_hash */
1052
1053
                /* select names of not deleted props */
1054
8.49k
                njs_process_prop(vm, &prop_name, flags, items_string,
1055
8.49k
                                 items_symbol);
1056
1057
48.9k
            } else {
1058
                /* prop is:  in_hash && in_shared_hash */
1059
1060
48.9k
                if (prop->type == NJS_WHITEOUT) {
1061
0
                    njs_process_prop(vm, &prop_name, flags, items_string,
1062
0
                                     items_symbol);
1063
0
                }
1064
48.9k
            }
1065
57.3k
        }
1066
1.50M
    }
1067
1068
177k
    if (items->length >= 2) {
1069
149k
        njs_qsort(&items->start[items_length], items->length-items_length,
1070
149k
                  sizeof(njs_value_t), njs_array_indices_handler_nums, NULL);
1071
149k
    }
1072
1073
177k
    if (items_string->length != 0) {
1074
107k
        ret = njs_array_expand(vm, items, 0, items_string->length);
1075
107k
        if (njs_slow_path(ret != NJS_OK)) {
1076
0
            return NJS_ERROR;
1077
0
        }
1078
1079
107k
        memcpy(&items->start[items->length], &items_string->start[0],
1080
107k
               items_string->length * sizeof(njs_value_t));
1081
1082
107k
        items->length += items_string->length;
1083
107k
    }
1084
1085
177k
    if (items_symbol->length != 0) {
1086
0
        ret = njs_array_expand(vm, items, 0, items_symbol->length);
1087
0
        if (njs_slow_path(ret != NJS_OK)) {
1088
0
            return NJS_ERROR;
1089
0
        }
1090
1091
0
        memcpy(&items->start[items->length], &items_symbol->start[0],
1092
0
               items_symbol->length * sizeof(njs_value_t));
1093
1094
0
        items->length += items_symbol->length;
1095
0
    }
1096
1097
177k
    njs_array_destroy(vm, items_string);
1098
177k
    njs_array_destroy(vm, items_symbol);
1099
1100
177k
    return NJS_OK;
1101
177k
}
1102
1103
1104
static njs_int_t
1105
njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object,
1106
    const njs_object_t *parent, njs_array_t *items, uint32_t flags)
1107
177k
{
1108
177k
    uint32_t     i;
1109
177k
    njs_int_t    ret;
1110
177k
    njs_array_t  *items_sorted, *entry;
1111
177k
    njs_value_t  value, retval;
1112
1113
177k
    switch (njs_object_enum_kind(flags)) {
1114
177k
    case NJS_ENUM_KEYS:
1115
177k
        ret = njs_get_own_ordered_keys(vm, object, parent, items, flags);
1116
177k
        if (ret != NJS_OK) {
1117
0
            return NJS_ERROR;
1118
0
        }
1119
1120
177k
        break;
1121
1122
177k
    case NJS_ENUM_VALUES:
1123
0
    case NJS_ENUM_BOTH:
1124
0
        items_sorted = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
1125
0
        if (njs_slow_path(items_sorted == NULL)) {
1126
0
            return NJS_ERROR;
1127
0
        }
1128
1129
0
        ret = njs_get_own_ordered_keys(vm, object, parent, items_sorted, flags);
1130
0
        if (ret != NJS_OK) {
1131
0
            return NJS_ERROR;
1132
0
        }
1133
1134
0
        njs_set_object(&value, (njs_object_t *) object);
1135
1136
0
        for (i = 0; i< items_sorted->length; i++) {
1137
0
            ret = njs_value_property_val(vm, &value, &items_sorted->start[i],
1138
0
                                         &retval);
1139
0
            if (njs_slow_path(ret != NJS_OK)) {
1140
0
                njs_array_destroy(vm, items_sorted);
1141
0
                return NJS_ERROR;
1142
0
            }
1143
1144
0
            if (njs_object_enum_kind(flags) != NJS_ENUM_VALUES) {
1145
0
                entry = njs_array_alloc(vm, 0, 2, 0);
1146
0
                if (njs_slow_path(entry == NULL)) {
1147
0
                    return NJS_ERROR;
1148
0
                }
1149
1150
0
                njs_string_copy(&entry->start[0], &items_sorted->start[i]);
1151
0
                njs_value_assign(&entry->start[1], &retval);
1152
1153
0
                njs_set_array(&retval, entry);
1154
0
            }
1155
1156
0
            ret = njs_array_add(vm, items, &retval);
1157
0
            if (njs_slow_path(ret != NJS_OK)) {
1158
0
                return NJS_ERROR;
1159
0
            }
1160
0
        }
1161
1162
0
        njs_array_destroy(vm, items_sorted);
1163
1164
0
        break;
1165
1166
177k
    }
1167
1168
177k
    return NJS_OK;
1169
177k
}
1170
1171
1172
njs_inline njs_int_t
1173
njs_traverse_visit(njs_arr_t *list, const njs_value_t *value)
1174
0
{
1175
0
    njs_object_t  **p;
1176
1177
0
    if (njs_is_object(value)) {
1178
0
        p = njs_arr_add(list);
1179
0
        if (njs_slow_path(p == NULL)) {
1180
0
            return NJS_ERROR;
1181
0
        }
1182
1183
0
        *p = njs_object(value);
1184
0
    }
1185
1186
0
    return NJS_OK;
1187
0
}
1188
1189
1190
njs_inline njs_int_t
1191
njs_traverse_visited(njs_arr_t *list, const njs_value_t *value)
1192
0
{
1193
0
    njs_uint_t    items, n;
1194
0
    njs_object_t  **start, *obj;
1195
1196
0
    if (!njs_is_object(value)) {
1197
        /* External. */
1198
0
        return 0;
1199
0
    }
1200
1201
0
    start = list->start;
1202
0
    items = list->items;
1203
0
    obj = njs_object(value);
1204
1205
0
    for (n = 0; n < items; n++) {
1206
0
        if (start[n] == obj) {
1207
0
            return 1;
1208
0
        }
1209
0
    }
1210
1211
0
    return 0;
1212
0
}
1213
1214
1215
static njs_int_t
1216
njs_object_copy_shared_hash(njs_vm_t *vm, njs_object_t *object)
1217
0
{
1218
0
    njs_int_t            ret;
1219
0
    njs_value_t          prop_name;
1220
0
    njs_flathsh_t        new_hash, *shared_hash;
1221
0
    njs_object_prop_t    *prop, *obj_prop;
1222
0
    njs_flathsh_elt_t    *elt;
1223
0
    njs_flathsh_each_t   fhe;
1224
0
    njs_flathsh_query_t  fhq;
1225
1226
0
    fhq.replace = 0;
1227
0
    fhq.proto = &njs_object_hash_proto;
1228
0
    fhq.pool = vm->mem_pool;
1229
1230
0
    njs_flathsh_init(&new_hash);
1231
0
    shared_hash = &object->shared_hash;
1232
1233
0
    njs_flathsh_each_init(&fhe, &njs_object_hash_proto);
1234
1235
0
    for ( ;; ) {
1236
0
        elt = njs_flathsh_each(shared_hash, &fhe);
1237
0
        if (elt == NULL) {
1238
0
            break;
1239
0
        }
1240
1241
0
        prop = (njs_object_prop_t *) elt;
1242
1243
0
        ret = njs_atom_to_value(vm, &prop_name, elt->key_hash);
1244
0
        if (ret != NJS_OK) {
1245
0
            return NJS_ERROR;
1246
0
        }
1247
1248
0
        if (njs_is_symbol(&prop_name)) {
1249
0
            fhq.key_hash = njs_symbol_key(&prop_name);
1250
0
            fhq.key.start = NULL;
1251
1252
0
        } else {
1253
0
            njs_string_get(vm, &prop_name, &fhq.key);
1254
0
            fhq.key_hash = elt->key_hash;
1255
0
        }
1256
1257
0
        ret = njs_flathsh_unique_insert(&new_hash, &fhq);
1258
0
        if (njs_slow_path(ret != NJS_OK)) {
1259
0
            njs_internal_error(vm, "flathsh insert failed");
1260
0
            return NJS_ERROR;
1261
0
        }
1262
1263
0
        obj_prop = fhq.value;
1264
1265
0
        obj_prop->type = prop->type;
1266
0
        obj_prop->enumerable = prop->enumerable;
1267
0
        obj_prop->configurable = prop->configurable;
1268
0
        obj_prop->writable = prop->writable;
1269
0
        obj_prop->u.value = prop->u.value;
1270
0
    }
1271
1272
0
    object->shared_hash = new_hash;
1273
1274
0
    return NJS_OK;
1275
0
}
1276
1277
1278
njs_int_t
1279
njs_object_make_shared(njs_vm_t *vm, njs_object_t *object)
1280
0
{
1281
0
    njs_int_t             ret;
1282
0
    njs_arr_t             visited;
1283
0
    njs_object_t          **start;
1284
0
    njs_value_t           value, *key;
1285
0
    njs_traverse_t        *s;
1286
0
    njs_object_prop_t     *prop, *obj_prop;
1287
0
    njs_property_query_t  pq;
1288
0
    njs_traverse_t        state[NJS_TRAVERSE_MAX_DEPTH];
1289
1290
0
    s = &state[0];
1291
0
    s->parent = NULL;
1292
0
    s->index = 0;
1293
0
    njs_set_object(&s->value, object);
1294
1295
0
    s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS
1296
0
                                      | NJS_ENUM_STRING
1297
0
                                      | NJS_ENUM_NON_SHARED_ONLY);
1298
0
    if (njs_slow_path(s->keys == NULL)) {
1299
0
        return NJS_ERROR;
1300
0
    }
1301
1302
0
    if (s->keys->length != 0
1303
0
        && !njs_flathsh_is_empty(&object->shared_hash))
1304
0
    {
1305
        /*
1306
         * object->shared_hash can be shared with other objects
1307
         * and we do not want to modify other objects.
1308
         */
1309
1310
0
        ret = njs_object_copy_shared_hash(vm, object);
1311
0
        if (njs_slow_path(ret != NJS_OK)) {
1312
0
            return NJS_ERROR;
1313
0
        }
1314
0
    }
1315
1316
0
    start = njs_arr_init(vm->mem_pool, &visited, NULL, 8, sizeof(void *));
1317
0
    if (njs_slow_path(start == NULL)) {
1318
0
        return NJS_ERROR;
1319
0
    }
1320
1321
0
    (void) njs_traverse_visit(&visited, &s->value);
1322
1323
0
    pq.fhq.replace = 0;
1324
0
    pq.fhq.pool = vm->mem_pool;
1325
1326
0
    for ( ;; ) {
1327
1328
0
        if (s->index >= s->keys->length) {
1329
0
            njs_flathsh_init(&njs_object(&s->value)->hash);
1330
0
            njs_object(&s->value)->shared = 1;
1331
0
            njs_array_destroy(vm, s->keys);
1332
0
            s->keys = NULL;
1333
1334
0
            if (s == &state[0]) {
1335
0
                goto done;
1336
0
            }
1337
1338
0
            s--;
1339
0
            continue;
1340
0
        }
1341
1342
1343
0
        njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
1344
0
        key = &s->keys->start[s->index++];
1345
1346
0
        ret = njs_property_query_val(vm, &pq, &s->value, key);
1347
0
        if (njs_slow_path(ret != NJS_OK)) {
1348
0
            if (ret == NJS_DECLINED) {
1349
0
                continue;
1350
0
            }
1351
1352
0
            return NJS_ERROR;
1353
0
        }
1354
1355
1356
0
        prop = pq.fhq.value;
1357
1358
0
        ret = njs_flathsh_unique_insert(&njs_object(&s->value)->shared_hash,
1359
0
                                        &pq.fhq);
1360
0
        if (njs_slow_path(ret != NJS_OK)) {
1361
0
            njs_internal_error(vm, "flathsh insert failed");
1362
0
            return NJS_ERROR;
1363
0
        }
1364
1365
0
        obj_prop = pq.fhq.value;
1366
1367
0
        obj_prop->type = prop->type;
1368
0
        obj_prop->enumerable = prop->enumerable;
1369
0
        obj_prop->configurable = prop->configurable;
1370
0
        obj_prop->writable = prop->writable;
1371
0
        obj_prop->u.value = prop->u.value;
1372
1373
0
        njs_value_assign(&value, njs_prop_value(prop));
1374
1375
0
        if (njs_is_object(&value)
1376
0
            && !njs_object(&value)->shared
1377
0
            && !njs_traverse_visited(&visited, &value))
1378
0
        {
1379
0
            ret = njs_traverse_visit(&visited, &value);
1380
0
            if (njs_slow_path(ret != NJS_OK)) {
1381
0
                return NJS_ERROR;
1382
0
            }
1383
1384
0
            if (s == &state[NJS_TRAVERSE_MAX_DEPTH - 1]) {
1385
0
                njs_type_error(vm, "njs_object_traverse() recursion limit:%d",
1386
0
                               NJS_TRAVERSE_MAX_DEPTH);
1387
0
                return NJS_ERROR;
1388
0
            }
1389
1390
0
            s++;
1391
0
            s->prop = NULL;
1392
0
            s->parent = &s[-1];
1393
0
            s->index = 0;
1394
0
            njs_value_assign(&s->value, &value);
1395
0
            s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS
1396
0
                                           | NJS_ENUM_STRING
1397
0
                                           | NJS_ENUM_NON_SHARED_ONLY);
1398
0
            if (njs_slow_path(s->keys == NULL)) {
1399
0
                return NJS_ERROR;
1400
0
            }
1401
1402
0
            if (s->keys->length != 0
1403
0
                && !njs_flathsh_is_empty(&njs_object(&s->value)->shared_hash))
1404
0
            {
1405
0
                ret = njs_object_copy_shared_hash(vm, njs_object(&s->value));
1406
0
                if (njs_slow_path(ret != NJS_OK)) {
1407
0
                    return NJS_ERROR;
1408
0
                }
1409
0
            }
1410
0
        }
1411
0
    }
1412
1413
0
done:
1414
1415
0
    njs_arr_destroy(&visited);
1416
1417
0
    return NJS_OK;
1418
0
}
1419
1420
1421
njs_int_t
1422
njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx,
1423
    njs_object_traverse_cb_t cb)
1424
0
{
1425
0
    njs_int_t             ret;
1426
0
    njs_arr_t             visited;
1427
0
    njs_object_t          **start;
1428
0
    njs_value_t           value, *key;
1429
0
    njs_traverse_t        *s;
1430
0
    njs_object_prop_t     *prop;
1431
0
    njs_property_query_t  pq;
1432
0
    njs_traverse_t        state[NJS_TRAVERSE_MAX_DEPTH];
1433
1434
0
    s = &state[0];
1435
0
    s->prop = NULL;
1436
0
    s->parent = NULL;
1437
0
    s->index = 0;
1438
0
    njs_set_object(&s->value, object);
1439
0
    s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS
1440
0
                                      | NJS_ENUM_STRING | NJS_ENUM_SYMBOL);
1441
0
    if (njs_slow_path(s->keys == NULL)) {
1442
0
        return NJS_ERROR;
1443
0
    }
1444
1445
0
    start = njs_arr_init(vm->mem_pool, &visited, NULL, 8, sizeof(void *));
1446
0
    if (njs_slow_path(start == NULL)) {
1447
0
        return NJS_ERROR;
1448
0
    }
1449
1450
0
    (void) njs_traverse_visit(&visited, &s->value);
1451
1452
0
    for ( ;; ) {
1453
1454
0
        if (s->index >= s->keys->length) {
1455
0
            njs_array_destroy(vm, s->keys);
1456
0
            s->keys = NULL;
1457
1458
0
            if (s == &state[0]) {
1459
0
                goto done;
1460
0
            }
1461
1462
0
            s--;
1463
0
            continue;
1464
0
        }
1465
1466
0
        njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
1467
0
        key = &s->keys->start[s->index++];
1468
1469
0
        ret = njs_property_query_val(vm, &pq, &s->value, key);
1470
0
        if (njs_slow_path(ret != NJS_OK)) {
1471
0
            if (ret == NJS_DECLINED) {
1472
0
                continue;
1473
0
            }
1474
1475
0
            return NJS_ERROR;
1476
0
        }
1477
1478
0
        prop = pq.fhq.value;
1479
0
        s->prop = prop;
1480
0
        s->atom_id = pq.fhq.key_hash;
1481
1482
0
        ret = cb(vm, s, ctx);
1483
0
        if (njs_slow_path(ret != NJS_OK)) {
1484
0
            return ret;
1485
0
        }
1486
1487
0
        if (njs_is_accessor_descriptor(prop)) {
1488
0
            continue;
1489
0
        }
1490
1491
0
        njs_value_assign(&value, njs_prop_value(prop));
1492
1493
0
        if (prop->type == NJS_PROPERTY_HANDLER) {
1494
0
            ret = njs_prop_handler(prop)(vm, prop, pq.fhq.key_hash, &s->value,
1495
0
                                         NULL, &value);
1496
0
            if (njs_slow_path(ret == NJS_ERROR)) {
1497
0
                return ret;
1498
1499
0
            }
1500
0
        }
1501
1502
0
        if (njs_is_object(&value)
1503
0
            && !njs_traverse_visited(&visited, &value))
1504
0
        {
1505
0
            ret = njs_traverse_visit(&visited, &value);
1506
0
            if (njs_slow_path(ret != NJS_OK)) {
1507
0
                return NJS_ERROR;
1508
0
            }
1509
1510
0
            if (s == &state[NJS_TRAVERSE_MAX_DEPTH - 1]) {
1511
0
                njs_type_error(vm, "njs_object_traverse() recursion limit:%d",
1512
0
                               NJS_TRAVERSE_MAX_DEPTH);
1513
0
                return NJS_ERROR;
1514
0
            }
1515
1516
0
            s++;
1517
0
            s->prop = NULL;
1518
0
            s->parent = &s[-1];
1519
0
            s->index = 0;
1520
0
            njs_value_assign(&s->value, &value);
1521
0
            s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS
1522
0
                                           | NJS_ENUM_STRING | NJS_ENUM_SYMBOL);
1523
0
            if (njs_slow_path(s->keys == NULL)) {
1524
0
                return NJS_ERROR;
1525
0
            }
1526
0
        }
1527
0
    }
1528
1529
0
done:
1530
1531
0
    njs_arr_destroy(&visited);
1532
1533
0
    return NJS_OK;
1534
0
}
1535
1536
1537
static njs_int_t
1538
njs_object_define_property(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1539
    njs_index_t unused, njs_value_t *retval)
1540
14.0k
{
1541
14.0k
    njs_int_t    ret;
1542
14.0k
    njs_value_t  *value, *name, *desc, lvalue;
1543
1544
14.0k
    if (!njs_is_object(njs_arg(args, nargs, 1))) {
1545
0
        njs_type_error(vm, "Object.defineProperty is called on non-object");
1546
0
        return NJS_ERROR;
1547
0
    }
1548
1549
14.0k
    desc = njs_arg(args, nargs, 3);
1550
1551
14.0k
    if (!njs_is_object(desc)) {
1552
0
        njs_type_error(vm, "descriptor is not an object");
1553
0
        return NJS_ERROR;
1554
0
    }
1555
1556
14.0k
    value = njs_argument(args, 1);
1557
14.0k
    name = njs_lvalue_arg(&lvalue, args, nargs, 2);
1558
1559
14.0k
    ret = njs_value_to_key(vm, name, name);
1560
14.0k
    if (njs_slow_path(ret != NJS_OK)) {
1561
0
        return NJS_ERROR;
1562
0
    }
1563
1564
14.0k
    ret = njs_object_prop_define_val(vm, value, name, desc,
1565
14.0k
                                     NJS_OBJECT_PROP_DESCRIPTOR);
1566
14.0k
    if (njs_slow_path(ret != NJS_OK)) {
1567
0
        return NJS_ERROR;
1568
0
    }
1569
1570
14.0k
    njs_value_assign(retval, value);
1571
1572
14.0k
    return NJS_OK;
1573
14.0k
}
1574
1575
1576
static njs_int_t
1577
njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1578
    njs_index_t unused, njs_value_t *retval)
1579
0
{
1580
0
    uint32_t              i, length;
1581
0
    njs_int_t             ret;
1582
0
    njs_array_t           *keys;
1583
0
    njs_value_t           desc, *value, *descs;
1584
0
    njs_object_prop_t     *prop;
1585
0
    njs_property_query_t  pq;
1586
1587
0
    if (!njs_is_object(njs_arg(args, nargs, 1))) {
1588
0
        njs_type_error(vm, "Object.defineProperties is called on non-object");
1589
0
        return NJS_ERROR;
1590
0
    }
1591
1592
0
    descs = njs_arg(args, nargs, 2);
1593
0
    ret = njs_value_to_object(vm, descs);
1594
0
    if (njs_slow_path(ret != NJS_OK)) {
1595
0
        return ret;
1596
0
    }
1597
1598
0
    keys = njs_value_own_enumerate(vm, descs, NJS_ENUM_KEYS | NJS_ENUM_STRING
1599
0
                                   | NJS_ENUM_SYMBOL);
1600
0
    if (njs_slow_path(keys == NULL)) {
1601
0
        return NJS_ERROR;
1602
0
    }
1603
1604
0
    length = keys->length;
1605
0
    value = njs_argument(args, 1);
1606
0
    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
1607
1608
0
    for (i = 0; i < length; i++) {
1609
0
        ret = njs_property_query_val(vm, &pq, descs, &keys->start[i]);
1610
0
        if (njs_slow_path(ret == NJS_ERROR)) {
1611
0
            goto done;
1612
0
        }
1613
1614
0
        prop = pq.fhq.value;
1615
1616
0
        if (ret == NJS_DECLINED || !prop->enumerable) {
1617
0
            continue;
1618
0
        }
1619
1620
0
        ret = njs_value_property(vm, descs, keys->start[i].atom_id, &desc);
1621
0
        if (njs_slow_path(ret == NJS_ERROR)) {
1622
0
            goto done;
1623
0
        }
1624
1625
0
        ret = njs_object_prop_define(vm, value, keys->start[i].atom_id, &desc,
1626
0
                                     NJS_OBJECT_PROP_DESCRIPTOR);
1627
0
        if (njs_slow_path(ret != NJS_OK)) {
1628
0
            goto done;
1629
0
        }
1630
0
    }
1631
1632
0
    ret = NJS_OK;
1633
0
    njs_value_assign(retval, value);
1634
1635
0
done:
1636
1637
0
    njs_array_destroy(vm, keys);
1638
1639
0
    return ret;
1640
0
}
1641
1642
1643
static njs_int_t
1644
njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args,
1645
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
1646
0
{
1647
0
    njs_value_t  lvalue, *value, *property;
1648
1649
0
    value = njs_arg(args, nargs, 1);
1650
1651
0
    if (njs_is_null_or_undefined(value)) {
1652
0
        njs_type_error(vm, "cannot convert %s argument to object",
1653
0
                       njs_type_string(value->type));
1654
0
        return NJS_ERROR;
1655
0
    }
1656
1657
0
    property = njs_lvalue_arg(&lvalue, args, nargs, 2);
1658
1659
0
    return njs_object_prop_descriptor(vm, retval, value, property);
1660
0
}
1661
1662
1663
static njs_int_t
1664
njs_object_get_own_property_descriptors(njs_vm_t *vm, njs_value_t *args,
1665
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
1666
0
{
1667
0
    uint32_t             i, length;
1668
0
    njs_int_t            ret;
1669
0
    njs_array_t          *names;
1670
0
    njs_value_t          descriptor, *value, *key;
1671
0
    njs_object_t         *descriptors;
1672
0
    njs_object_prop_t    *prop;
1673
0
    njs_flathsh_query_t  fhq;
1674
1675
0
    value = njs_arg(args, nargs, 1);
1676
1677
0
    if (njs_is_null_or_undefined(value)) {
1678
0
        njs_type_error(vm, "cannot convert %s argument to object",
1679
0
                       njs_type_string(value->type));
1680
1681
0
        return NJS_ERROR;
1682
0
    }
1683
1684
0
    names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS | NJS_ENUM_STRING
1685
0
                                    | NJS_ENUM_SYMBOL);
1686
0
    if (njs_slow_path(names == NULL)) {
1687
0
        return NJS_ERROR;
1688
0
    }
1689
1690
0
    length = names->length;
1691
1692
0
    descriptors = njs_object_alloc(vm);
1693
0
    if (njs_slow_path(descriptors == NULL)) {
1694
0
        ret = NJS_ERROR;
1695
0
        goto done;
1696
0
    }
1697
1698
0
    fhq.replace = 0;
1699
0
    fhq.pool = vm->mem_pool;
1700
0
    fhq.proto = &njs_object_hash_proto;
1701
1702
0
    for (i = 0; i < length; i++) {
1703
0
        key = &names->start[i];
1704
0
        ret = njs_object_prop_descriptor(vm, &descriptor, value, key);
1705
0
        if (njs_slow_path(ret != NJS_OK)) {
1706
0
            ret = NJS_ERROR;
1707
0
            goto done;
1708
0
        }
1709
1710
0
        fhq.key_hash = key->atom_id;
1711
1712
0
        ret = njs_flathsh_unique_insert(&descriptors->hash, &fhq);
1713
0
        if (njs_slow_path(ret != NJS_OK)) {
1714
0
            njs_internal_error(vm, "flathsh insert failed");
1715
0
            goto done;
1716
0
        }
1717
1718
0
        prop = fhq.value;
1719
1720
0
        prop->type = NJS_PROPERTY;
1721
0
        prop->enumerable = 1;
1722
0
        prop->configurable = 1;
1723
0
        prop->writable = 1;
1724
1725
0
        prop->u.value = descriptor;
1726
0
    }
1727
1728
0
    ret = NJS_OK;
1729
0
    njs_set_object(retval, descriptors);
1730
1731
0
done:
1732
1733
0
    njs_array_destroy(vm, names);
1734
1735
0
    return ret;
1736
0
}
1737
1738
1739
static njs_int_t
1740
njs_object_get_own_property(njs_vm_t *vm, njs_value_t *args,
1741
    njs_uint_t nargs, njs_index_t type, njs_value_t *retval)
1742
0
{
1743
0
    njs_array_t  *names;
1744
0
    njs_value_t  *value;
1745
1746
0
    value = njs_arg(args, nargs, 1);
1747
1748
0
    if (njs_is_null_or_undefined(value)) {
1749
0
        njs_type_error(vm, "cannot convert %s argument to object",
1750
0
                       njs_type_string(value->type));
1751
1752
0
        return NJS_ERROR;
1753
0
    }
1754
1755
0
    names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS | type);
1756
0
    if (names == NULL) {
1757
0
        return NJS_ERROR;
1758
0
    }
1759
1760
0
    njs_set_array(retval, names);
1761
1762
0
    return NJS_OK;
1763
0
}
1764
1765
1766
njs_int_t
1767
njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1768
    njs_index_t unused, njs_value_t *retval)
1769
0
{
1770
0
    uint32_t     index;
1771
0
    njs_value_t  *value;
1772
1773
0
    value = njs_arg(args, nargs, 1);
1774
1775
0
    if (njs_is_object(value)) {
1776
0
        njs_object_prototype_proto(vm, NULL, 0, value, NULL, retval);
1777
0
        return NJS_OK;
1778
0
    }
1779
1780
0
    if (!njs_is_null_or_undefined(value)) {
1781
0
        index = njs_primitive_prototype_index(value->type);
1782
1783
0
        if (njs_is_symbol(value)) {
1784
0
            njs_set_object(retval, njs_vm_proto(vm, index));
1785
1786
0
        } else {
1787
0
            njs_set_object_value(retval,
1788
0
                                 &vm->prototypes[index].object_value);
1789
0
        }
1790
1791
0
        return NJS_OK;
1792
0
    }
1793
1794
0
    njs_type_error(vm, "cannot convert %s argument to object",
1795
0
                   njs_type_string(value->type));
1796
1797
0
    return NJS_ERROR;
1798
0
}
1799
1800
1801
static njs_int_t
1802
njs_object_set_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1803
    njs_index_t unused, njs_value_t *retval)
1804
0
{
1805
0
    njs_int_t    ret;
1806
0
    njs_value_t  *value, *proto;
1807
1808
0
    value = njs_arg(args, nargs, 1);
1809
0
    if (njs_slow_path(njs_is_null_or_undefined(value))) {
1810
0
        njs_type_error(vm, "cannot convert %s argument to object",
1811
0
                       njs_type_string(value->type));
1812
0
        return NJS_ERROR;
1813
0
    }
1814
1815
0
    proto = njs_arg(args, nargs, 2);
1816
0
    if (njs_slow_path(!njs_is_object(proto) && !njs_is_null(proto))) {
1817
0
        njs_type_error(vm, "prototype may only be an object or null: %s",
1818
0
                       njs_type_string(proto->type));
1819
0
        return NJS_ERROR;
1820
0
    }
1821
1822
0
    if (njs_slow_path(!njs_is_object(value))) {
1823
0
        njs_value_assign(retval, value);
1824
0
        return NJS_OK;
1825
0
    }
1826
1827
0
    ret = njs_object_set_prototype(vm, njs_object(value), proto);
1828
0
    if (njs_fast_path(ret == NJS_OK)) {
1829
0
        njs_value_assign(retval, value);
1830
0
        return NJS_OK;
1831
0
    }
1832
1833
0
    if (ret == NJS_DECLINED) {
1834
0
        njs_type_error(vm, "Cannot set property \"prototype\", "
1835
0
                       "object is not extensible");
1836
1837
0
    } else {
1838
0
        njs_type_error(vm, "Cyclic __proto__ value");
1839
0
    }
1840
1841
0
    return NJS_ERROR;
1842
0
}
1843
1844
1845
/* 7.3.15 SetIntegrityLevel */
1846
1847
static njs_int_t
1848
njs_object_set_integrity_level(njs_vm_t *vm, njs_value_t *args,
1849
    njs_uint_t nargs, njs_index_t level, njs_value_t *retval)
1850
4.96k
{
1851
4.96k
    uint32_t                length;
1852
4.96k
    njs_int_t               ret;
1853
4.96k
    njs_array_t             *array;
1854
4.96k
    njs_value_t             *value;
1855
4.96k
    njs_object_t            *object;
1856
4.96k
    njs_flathsh_t           *hash;
1857
4.96k
    njs_object_prop_t       *prop;
1858
4.96k
    njs_flathsh_elt_t   *elt;
1859
4.96k
    njs_flathsh_each_t  lhe;
1860
1861
4.96k
    value = njs_arg(args, nargs, 1);
1862
1863
4.96k
    if (njs_slow_path(!njs_is_object(value))) {
1864
0
        njs_value_assign(retval, value);
1865
0
        return NJS_OK;
1866
0
    }
1867
1868
4.96k
    if (njs_slow_path(level == NJS_OBJECT_INTEGRITY_FROZEN
1869
4.96k
                      && njs_is_typed_array(value)
1870
4.96k
                      && njs_typed_array_length(njs_typed_array(value)) != 0))
1871
0
    {
1872
0
        njs_type_error(vm, "Cannot freeze array buffer views with elements");
1873
0
        return NJS_ERROR;
1874
0
    }
1875
1876
4.96k
    if (njs_is_fast_array(value)) {
1877
3.72k
        array = njs_array(value);
1878
3.72k
        length = array->length;
1879
1880
3.72k
        ret = njs_array_convert_to_slow_array(vm, array);
1881
3.72k
        if (ret != NJS_OK) {
1882
0
            return ret;
1883
0
        }
1884
1885
3.72k
        ret = njs_array_length_redefine(vm, value, length, 1);
1886
3.72k
        if (njs_slow_path(ret != NJS_OK)) {
1887
0
            return ret;
1888
0
        }
1889
3.72k
    }
1890
1891
4.96k
    object = njs_object(value);
1892
4.96k
    object->extensible = 0;
1893
1894
4.96k
    njs_flathsh_each_init(&lhe, &njs_object_hash_proto);
1895
1896
4.96k
    hash = &object->hash;
1897
1898
14.9k
    for ( ;; ) {
1899
14.9k
        elt = njs_flathsh_each(hash, &lhe);
1900
14.9k
        if (elt == NULL) {
1901
4.96k
            break;
1902
4.96k
        }
1903
1904
9.93k
        prop = (njs_object_prop_t *) elt;
1905
1906
9.93k
        if (level == NJS_OBJECT_INTEGRITY_FROZEN
1907
0
            && !njs_is_accessor_descriptor(prop))
1908
0
        {
1909
0
            prop->writable = 0;
1910
0
        }
1911
1912
9.93k
        prop->configurable = 0;
1913
9.93k
    }
1914
1915
4.96k
    njs_value_assign(retval, value);
1916
1917
4.96k
    return NJS_OK;
1918
4.96k
}
1919
1920
1921
static njs_int_t
1922
njs_object_test_integrity_level(njs_vm_t *vm, njs_value_t *args,
1923
    njs_uint_t nargs, njs_index_t level, njs_value_t *retval)
1924
0
{
1925
0
    njs_value_t             *value;
1926
0
    njs_object_t            *object;
1927
0
    njs_flathsh_t           *hash;
1928
0
    njs_object_prop_t       *prop;
1929
0
    njs_flathsh_elt_t   *elt;
1930
0
    njs_flathsh_each_t  lhe;
1931
1932
0
    value = njs_arg(args, nargs, 1);
1933
1934
0
    if (njs_slow_path(!njs_is_object(value))) {
1935
0
        njs_set_boolean(retval, 1);
1936
0
        return NJS_OK;
1937
0
    }
1938
1939
0
    njs_set_boolean(retval, 0);
1940
1941
0
    object = njs_object(value);
1942
1943
0
    if (object->extensible) {
1944
0
        goto done;
1945
0
    }
1946
1947
0
    if (njs_slow_path(level == NJS_OBJECT_INTEGRITY_FROZEN)
1948
0
                      && njs_is_typed_array(value)
1949
0
                      && njs_typed_array_length(njs_typed_array(value)) != 0)
1950
0
    {
1951
0
        goto done;
1952
0
    }
1953
1954
0
    njs_flathsh_each_init(&lhe, &njs_object_hash_proto);
1955
1956
0
    hash = &object->hash;
1957
1958
0
    for ( ;; ) {
1959
0
        elt = njs_flathsh_each(hash, &lhe);
1960
0
        if (elt == NULL) {
1961
0
            break;
1962
0
        }
1963
1964
0
        prop = (njs_object_prop_t *) elt;
1965
1966
0
        if (prop->configurable) {
1967
0
            goto done;
1968
0
        }
1969
1970
0
        if (level == NJS_OBJECT_INTEGRITY_FROZEN
1971
0
            && njs_is_data_descriptor(prop) && prop->writable)
1972
0
        {
1973
0
            goto done;
1974
0
        }
1975
0
    }
1976
1977
0
    njs_set_boolean(retval, 1);
1978
1979
0
done:
1980
1981
0
    return NJS_OK;
1982
0
}
1983
1984
1985
static njs_int_t
1986
njs_object_prevent_extensions(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1987
    njs_index_t unused, njs_value_t *retval)
1988
0
{
1989
0
    njs_value_t  *value;
1990
1991
0
    value = njs_arg(args, nargs, 1);
1992
1993
0
    if (!njs_is_object(value)) {
1994
0
        njs_value_assign(retval, value);
1995
0
        return NJS_OK;
1996
0
    }
1997
1998
0
    njs_object(&args[1])->extensible = 0;
1999
2000
0
    njs_value_assign(retval, value);
2001
2002
0
    return NJS_OK;
2003
0
}
2004
2005
2006
static njs_int_t
2007
njs_object_is_extensible(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2008
    njs_index_t unused, njs_value_t *retval)
2009
0
{
2010
0
    njs_value_t  *value;
2011
2012
0
    value = njs_arg(args, nargs, 1);
2013
2014
0
    if (!njs_is_object(value)) {
2015
0
        njs_set_boolean(retval, 0);
2016
0
        return NJS_OK;
2017
0
    }
2018
2019
0
    njs_set_boolean(retval, njs_object(value)->extensible);
2020
2021
0
    return NJS_OK;
2022
0
}
2023
2024
2025
static njs_int_t
2026
njs_object_assign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2027
    njs_index_t unused, njs_value_t *retval)
2028
0
{
2029
0
    uint32_t              i, j, length;
2030
0
    njs_int_t             ret;
2031
0
    njs_array_t           *names;
2032
0
    njs_value_t           *key, *source, *value, setval;
2033
0
    njs_object_prop_t     *prop;
2034
0
    njs_property_query_t  pq;
2035
2036
0
    value = njs_arg(args, nargs, 1);
2037
2038
0
    ret = njs_value_to_object(vm, value);
2039
0
    if (njs_slow_path(ret != NJS_OK)) {
2040
0
        return ret;
2041
0
    }
2042
2043
0
    names = NULL;
2044
2045
0
    for (i = 2; i < nargs; i++) {
2046
0
        source = &args[i];
2047
2048
0
        names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS
2049
0
                                        | NJS_ENUM_STRING | NJS_ENUM_SYMBOL);
2050
0
        if (njs_slow_path(names == NULL)) {
2051
0
            return NJS_ERROR;
2052
0
        }
2053
2054
0
        length = names->length;
2055
2056
0
        for (j = 0; j < length; j++) {
2057
0
            key = &names->start[j];
2058
2059
0
            njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
2060
2061
0
            ret = njs_property_query_val(vm, &pq, source, key);
2062
0
            if (njs_slow_path(ret != NJS_OK)) {
2063
0
                goto exception;
2064
0
            }
2065
2066
0
            prop = pq.fhq.value;
2067
0
            if (!prop->enumerable) {
2068
0
                continue;
2069
0
            }
2070
2071
0
            ret = njs_value_property(vm, source, key->atom_id, &setval);
2072
0
            if (njs_slow_path(ret != NJS_OK)) {
2073
0
                goto exception;
2074
0
            }
2075
2076
0
            ret = njs_value_property_set(vm, value, key->atom_id, &setval);
2077
0
            if (njs_slow_path(ret != NJS_OK)) {
2078
0
                goto exception;
2079
0
            }
2080
0
        }
2081
2082
0
        njs_array_destroy(vm, names);
2083
0
    }
2084
2085
0
    njs_value_assign(retval, value);
2086
2087
0
    return NJS_OK;
2088
2089
0
exception:
2090
2091
0
    njs_array_destroy(vm, names);
2092
2093
0
    return NJS_ERROR;
2094
0
}
2095
2096
2097
static njs_int_t
2098
njs_object_is(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2099
    njs_index_t unused, njs_value_t *retval)
2100
124k
{
2101
124k
    njs_set_boolean(retval, njs_values_same(vm, njs_arg(args, nargs, 1),
2102
124k
                                            njs_arg(args, nargs, 2)));
2103
2104
124k
    return NJS_OK;
2105
124k
}
2106
2107
2108
/*
2109
 * The __proto__ property of booleans, numbers and strings primitives,
2110
 * of objects created by Boolean(), Number(), and String() constructors,
2111
 * and of Boolean.prototype, Number.prototype, and String.prototype objects.
2112
 */
2113
2114
njs_int_t
2115
njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_object_prop_t *prop,
2116
    uint32_t unused, njs_value_t *value, njs_value_t *setval,
2117
    njs_value_t *retval)
2118
115k
{
2119
115k
    njs_uint_t    index;
2120
115k
    njs_object_t  *proto;
2121
2122
    /*
2123
     * The __proto__ getters reside in object prototypes of primitive types
2124
     * and have to return different results for primitive type and for objects.
2125
     */
2126
115k
    if (njs_is_object(value)) {
2127
0
        proto = njs_object(value)->__proto__;
2128
2129
115k
    } else {
2130
115k
        index = njs_primitive_prototype_index(value->type);
2131
115k
        proto = njs_vm_proto(vm, index);
2132
115k
    }
2133
2134
115k
    if (proto != NULL) {
2135
115k
        njs_set_type_object(retval, proto, proto->type);
2136
2137
115k
    } else {
2138
0
        njs_set_undefined(retval);
2139
0
    }
2140
2141
115k
    return NJS_OK;
2142
115k
}
2143
2144
2145
/*
2146
 * The "prototype" property of Object(), Array() and other functions is
2147
 * created on demand in the functions' private hash by the "prototype"
2148
 * getter.  The properties are set to appropriate prototype.
2149
 */
2150
2151
njs_int_t
2152
njs_object_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
2153
    uint32_t unused, njs_value_t *value, njs_value_t *setval,
2154
    njs_value_t *retval)
2155
303
{
2156
303
    int64_t            index;
2157
303
    njs_function_t     *function;
2158
303
    const njs_value_t  *proto;
2159
2160
303
    proto = NULL;
2161
303
    function = njs_function(value);
2162
303
    index = function - vm->constructors;
2163
2164
303
    if (index >= 0 && (size_t) index < vm->constructors_size) {
2165
303
        proto = njs_property_prototype_create(vm, &function->object.hash,
2166
303
                                              &vm->prototypes[index].object);
2167
303
    }
2168
2169
303
    if (proto == NULL) {
2170
0
        proto = &njs_value_undefined;
2171
0
    }
2172
2173
303
    *retval = *proto;
2174
2175
303
    return NJS_OK;
2176
303
}
2177
2178
2179
njs_value_t *
2180
njs_property_prototype_create(njs_vm_t *vm, njs_flathsh_t *hash,
2181
    njs_object_t *prototype)
2182
303
{
2183
303
    njs_int_t            ret;
2184
303
    njs_object_prop_t    *prop;
2185
303
    njs_flathsh_query_t  fhq;
2186
2187
303
    fhq.key_hash = NJS_ATOM_STRING_prototype;
2188
2189
303
    fhq.replace = 1;
2190
303
    fhq.pool = vm->mem_pool;
2191
303
    fhq.proto = &njs_object_hash_proto;
2192
2193
303
    ret = njs_flathsh_unique_insert(hash, &fhq);
2194
2195
303
    if (njs_slow_path(ret != NJS_OK)) {
2196
0
        njs_internal_error(vm, "flathsh insert failed");
2197
0
        return NULL;
2198
0
    }
2199
2200
303
    prop = fhq.value;
2201
2202
303
    prop->type = NJS_PROPERTY;
2203
303
    prop->enumerable = 0;
2204
303
    prop->configurable = 0;
2205
303
    prop->writable = 0;
2206
2207
303
    njs_set_type_object(njs_prop_value(prop), prototype, prototype->type);
2208
2209
303
    return njs_prop_value(prop);
2210
303
}
2211
2212
2213
static const njs_object_prop_init_t  njs_object_constructor_properties[] =
2214
{
2215
    NJS_DECLARE_PROP_LENGTH(1),
2216
2217
    NJS_DECLARE_PROP_NAME("Object"),
2218
2219
    NJS_DECLARE_PROP_HANDLER(STRING_prototype, njs_object_prototype_create,
2220
                             0, 0),
2221
2222
    NJS_DECLARE_PROP_NATIVE(STRING_create, njs_object_create, 2, 0),
2223
2224
    NJS_DECLARE_PROP_NATIVE(STRING_keys, njs_object_keys, 1, 0),
2225
2226
    NJS_DECLARE_PROP_NATIVE(STRING_values, njs_object_values, 1, 0),
2227
2228
    NJS_DECLARE_PROP_NATIVE(STRING_entries, njs_object_entries, 1, 0),
2229
2230
    NJS_DECLARE_PROP_NATIVE(STRING_defineProperty,
2231
                            njs_object_define_property, 3, 0),
2232
2233
    NJS_DECLARE_PROP_NATIVE(STRING_defineProperties,
2234
                            njs_object_define_properties, 2, 0),
2235
2236
    NJS_DECLARE_PROP_NATIVE(STRING_getOwnPropertyDescriptor,
2237
                            njs_object_get_own_property_descriptor, 2, 0),
2238
2239
    NJS_DECLARE_PROP_NATIVE(STRING_getOwnPropertyDescriptors,
2240
                            njs_object_get_own_property_descriptors, 1, 0),
2241
2242
    NJS_DECLARE_PROP_NATIVE(STRING_getOwnPropertyNames,
2243
                            njs_object_get_own_property, 1, NJS_ENUM_STRING),
2244
2245
    NJS_DECLARE_PROP_NATIVE(STRING_getOwnPropertySymbols,
2246
                            njs_object_get_own_property, 1, NJS_ENUM_SYMBOL),
2247
2248
    NJS_DECLARE_PROP_NATIVE(STRING_getPrototypeOf,
2249
                            njs_object_get_prototype_of, 1, 0),
2250
2251
    NJS_DECLARE_PROP_NATIVE(STRING_setPrototypeOf,
2252
                            njs_object_set_prototype_of, 2, 0),
2253
2254
    NJS_DECLARE_PROP_NATIVE(STRING_freeze, njs_object_set_integrity_level,
2255
                            1, NJS_OBJECT_INTEGRITY_FROZEN),
2256
2257
    NJS_DECLARE_PROP_NATIVE(STRING_isFrozen,
2258
                            njs_object_test_integrity_level, 1,
2259
                            NJS_OBJECT_INTEGRITY_FROZEN),
2260
2261
    NJS_DECLARE_PROP_NATIVE(STRING_seal, njs_object_set_integrity_level, 1,
2262
                            NJS_OBJECT_INTEGRITY_SEALED),
2263
2264
    NJS_DECLARE_PROP_NATIVE(STRING_isSealed,
2265
                            njs_object_test_integrity_level, 1,
2266
                            NJS_OBJECT_INTEGRITY_SEALED),
2267
2268
    NJS_DECLARE_PROP_NATIVE(STRING_preventExtensions,
2269
                            njs_object_prevent_extensions, 1, 0),
2270
2271
    NJS_DECLARE_PROP_NATIVE(STRING_isExtensible, njs_object_is_extensible,
2272
                            1, 0),
2273
2274
    NJS_DECLARE_PROP_NATIVE(STRING_assign, njs_object_assign, 2, 0),
2275
2276
    NJS_DECLARE_PROP_NATIVE(STRING_is, njs_object_is, 2, 0),
2277
};
2278
2279
2280
const njs_object_init_t  njs_object_constructor_init = {
2281
    njs_object_constructor_properties,
2282
    njs_nitems(njs_object_constructor_properties),
2283
};
2284
2285
2286
static njs_int_t
2287
njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object,
2288
    const njs_value_t *value)
2289
0
{
2290
0
    const njs_object_t *proto;
2291
2292
0
    proto = njs_object(value);
2293
2294
0
    if (njs_slow_path(object->__proto__ == proto)) {
2295
0
        return NJS_OK;
2296
0
    }
2297
2298
0
    if (!object->extensible) {
2299
0
        return NJS_DECLINED;
2300
0
    }
2301
2302
0
    if (njs_slow_path(proto == NULL)) {
2303
0
        object->__proto__ = NULL;
2304
0
        return NJS_OK;
2305
0
    }
2306
2307
0
    do {
2308
0
        if (proto == object) {
2309
0
            return NJS_ERROR;
2310
0
        }
2311
2312
0
        proto = proto->__proto__;
2313
2314
0
    } while (proto != NULL);
2315
2316
0
    object->__proto__ = njs_object(value);
2317
2318
0
    return NJS_OK;
2319
0
}
2320
2321
2322
njs_int_t
2323
njs_object_prototype_proto(njs_vm_t *vm, njs_object_prop_t *prop,
2324
    uint32_t unused, njs_value_t *value, njs_value_t *setval,
2325
    njs_value_t *retval)
2326
52.6k
{
2327
52.6k
    njs_int_t     ret;
2328
52.6k
    njs_object_t  *proto, *object;
2329
2330
52.6k
    if (!njs_is_object(value)) {
2331
0
        *retval = *value;
2332
0
        return NJS_OK;
2333
0
    }
2334
2335
52.6k
    object = njs_object(value);
2336
2337
52.6k
    if (setval != NULL) {
2338
32.8k
        if (njs_is_object(setval) || njs_is_null(setval)) {
2339
0
            ret = njs_object_set_prototype(vm, object, setval);
2340
0
            if (njs_slow_path(ret == NJS_ERROR)) {
2341
0
                njs_type_error(vm, "Cyclic __proto__ value");
2342
0
                return NJS_ERROR;
2343
0
            }
2344
0
        }
2345
2346
32.8k
        njs_set_undefined(retval);
2347
2348
32.8k
        return NJS_OK;
2349
32.8k
    }
2350
2351
19.7k
    proto = object->__proto__;
2352
2353
19.7k
    if (njs_fast_path(proto != NULL)) {
2354
15.1k
        njs_set_type_object(retval, proto, proto->type);
2355
2356
15.1k
    } else {
2357
4.62k
        *retval = njs_value_null;
2358
4.62k
    }
2359
2360
19.7k
    return NJS_OK;
2361
52.6k
}
2362
2363
2364
/*
2365
 * The "constructor" property of Object(), Array() and other functions
2366
 * prototypes is created on demand in the prototypes' private hash by the
2367
 * "constructor" getter.  The properties are set to appropriate function.
2368
 */
2369
2370
njs_int_t
2371
njs_object_prototype_create_constructor(njs_vm_t *vm, njs_object_prop_t *prop,
2372
    uint32_t unused, njs_value_t *value, njs_value_t *setval,
2373
    njs_value_t *retval)
2374
915
{
2375
915
    int64_t                 index;
2376
915
    njs_value_t             *cons, constructor;
2377
915
    njs_object_t            *object;
2378
915
    njs_object_prototype_t  *prototype;
2379
2380
915
    if (setval != NULL) {
2381
757
        if (!njs_is_object(value)) {
2382
0
            njs_type_error(vm, "Cannot create property \"constructor\" on %s",
2383
0
                           njs_type_string(value->type));
2384
0
            return NJS_ERROR;
2385
0
        }
2386
2387
757
        cons = njs_property_constructor_set(vm, njs_object_hash(value), setval);
2388
757
        if (njs_slow_path(cons == NULL)) {
2389
0
            return NJS_ERROR;
2390
0
        }
2391
2392
757
        *retval = *cons;
2393
757
        return NJS_OK;
2394
757
    }
2395
2396
158
    if (njs_is_object(value)) {
2397
158
        object = njs_object(value);
2398
2399
317
        do {
2400
317
            prototype = (njs_object_prototype_t *) object;
2401
317
            index = prototype - vm->prototypes;
2402
2403
317
            if (index >= 0 && (size_t) index < vm->constructors_size) {
2404
158
                goto found;
2405
158
            }
2406
2407
159
            object = object->__proto__;
2408
2409
159
        } while (object != NULL);
2410
2411
0
        njs_internal_error(vm, "prototype not found");
2412
2413
0
        return NJS_ERROR;
2414
2415
158
    } else {
2416
0
        index = njs_primitive_prototype_index(value->type);
2417
0
        prototype = &vm->prototypes[index];
2418
0
    }
2419
2420
158
found:
2421
2422
158
    if (njs_flathsh_is_empty(&vm->constructors[index].object.shared_hash)) {
2423
0
        index = NJS_OBJ_TYPE_OBJECT;
2424
0
    }
2425
2426
158
    njs_set_function(&constructor, &njs_vm_ctor(vm, index));
2427
158
    setval = &constructor;
2428
2429
158
    cons = njs_property_constructor_set(vm, &prototype->object.hash, setval);
2430
158
    if (njs_slow_path(cons == NULL)) {
2431
0
        return NJS_ERROR;
2432
0
    }
2433
2434
158
    *retval = *cons;
2435
158
    return NJS_OK;
2436
158
}
2437
2438
2439
njs_value_t *
2440
njs_property_constructor_set(njs_vm_t *vm, njs_flathsh_t *hash,
2441
    njs_value_t *constructor)
2442
915
{
2443
915
    njs_int_t            ret;
2444
915
    njs_object_prop_t    *prop;
2445
915
    njs_flathsh_query_t  fhq;
2446
2447
915
    fhq.key_hash = NJS_ATOM_STRING_constructor;
2448
2449
915
    fhq.replace = 1;
2450
915
    fhq.pool = vm->mem_pool;
2451
915
    fhq.proto = &njs_object_hash_proto;
2452
2453
915
    ret = njs_flathsh_unique_insert(hash, &fhq);
2454
915
    if (njs_slow_path(ret != NJS_OK)) {
2455
0
        njs_internal_error(vm, "flathsh insert/replace failed");
2456
0
        return NULL;
2457
0
    }
2458
2459
915
    prop = fhq.value;
2460
2461
915
    prop->type = NJS_PROPERTY;
2462
915
    prop->enumerable = 0;
2463
915
    prop->configurable = 1;
2464
915
    prop->writable = 1;
2465
915
    prop->u.value = *constructor;
2466
2467
915
    return njs_prop_value(prop);
2468
2469
915
}
2470
2471
2472
static njs_int_t
2473
njs_object_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2474
    njs_index_t unused, njs_value_t *retval)
2475
409k
{
2476
409k
    njs_value_t  *value;
2477
2478
409k
    value = njs_argument(args, 0);
2479
2480
409k
    if (!njs_is_object(value)) {
2481
0
        if (njs_value_to_object(vm, value) != NJS_OK) {
2482
0
            return NJS_ERROR;
2483
0
        }
2484
0
    }
2485
2486
409k
    njs_value_assign(retval, value);
2487
2488
409k
    return NJS_OK;
2489
409k
}
2490
2491
2492
njs_int_t
2493
njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
2494
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
2495
260k
{
2496
260k
    return njs_object_to_string(vm, &args[0], retval);
2497
260k
}
2498
2499
2500
njs_int_t
2501
njs_object_to_string(njs_vm_t *vm, njs_value_t *this, njs_value_t *retval)
2502
260k
{
2503
260k
    u_char             *p;
2504
260k
    uint32_t           name;
2505
260k
    njs_int_t          ret;
2506
260k
    njs_value_t        tag;
2507
260k
    njs_string_prop_t  string;
2508
2509
260k
    if (njs_is_null_or_undefined(this)) {
2510
0
        njs_atom_to_value(vm, retval,
2511
0
                          njs_is_null(this) ? NJS_ATOM_STRING__object_Null_
2512
0
                                           : NJS_ATOM_STRING__object_Undefined_);
2513
0
        return NJS_OK;
2514
0
    }
2515
2516
260k
    ret = njs_value_to_object(vm, this);
2517
260k
    if (njs_slow_path(ret != NJS_OK)) {
2518
0
        return ret;
2519
0
    }
2520
2521
260k
    name = NJS_ATOM_STRING__object_Object_;
2522
2523
260k
    if (njs_is_array(this)) {
2524
262
        name = NJS_ATOM_STRING__object_Array_;
2525
2526
260k
    } else if (njs_is_object(this)
2527
260k
        && njs_flathsh_eq(&njs_object(this)->shared_hash,
2528
260k
                         &vm->shared->arguments_object_instance_hash))
2529
14.0k
    {
2530
14.0k
        name = NJS_ATOM_STRING__object_Arguments_;
2531
2532
246k
    } else if (njs_is_function(this)) {
2533
183k
        name = NJS_ATOM_STRING__object_Function_;
2534
2535
183k
    } else if (njs_is_error(this)) {
2536
0
        name = NJS_ATOM_STRING__object_Error_;
2537
2538
62.4k
    } else if (njs_is_object_value(this)) {
2539
2540
0
        switch (njs_object_value(this)->type) {
2541
0
        case NJS_BOOLEAN:
2542
0
            name = NJS_ATOM_STRING__object_Boolean_;
2543
0
            break;
2544
2545
0
        case NJS_NUMBER:
2546
0
            name = NJS_ATOM_STRING__object_Number_;
2547
0
            break;
2548
2549
0
        case NJS_STRING:
2550
0
            name = NJS_ATOM_STRING__object_String_;
2551
0
            break;
2552
2553
0
        default:
2554
0
            break;
2555
0
        }
2556
2557
62.4k
    } else if (njs_is_date(this)) {
2558
0
        name = NJS_ATOM_STRING__object_Date_;
2559
2560
62.4k
    } else if (njs_is_regexp(this)) {
2561
0
        name = NJS_ATOM_STRING__object_RegExp_;
2562
0
    }
2563
2564
260k
    ret = njs_object_string_tag(vm, this, &tag);
2565
260k
    if (njs_slow_path(ret == NJS_ERROR)) {
2566
0
        return ret;
2567
0
    }
2568
2569
260k
    if (ret == NJS_DECLINED) {
2570
157k
        njs_atom_to_value(vm, retval, name);
2571
2572
157k
        return NJS_OK;
2573
157k
    }
2574
2575
103k
    (void) njs_string_prop(vm, &string, &tag);
2576
2577
103k
    p = njs_string_alloc(vm, retval, string.size + njs_length("[object ]"),
2578
103k
                         string.length + njs_length("[object ]"));
2579
103k
    if (njs_slow_path(p == NULL)) {
2580
0
        return NJS_ERROR;
2581
0
    }
2582
2583
103k
    p = njs_cpymem(p, "[object ", 8);
2584
103k
    p = njs_cpymem(p, string.start, string.size);
2585
103k
    *p = ']';
2586
2587
103k
    return NJS_OK;
2588
103k
}
2589
2590
2591
static njs_int_t
2592
njs_object_prototype_has_own_property(njs_vm_t *vm, njs_value_t *args,
2593
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
2594
0
{
2595
0
    njs_int_t             ret;
2596
0
    njs_value_t           *value, *property, lvalue;
2597
0
    njs_property_query_t  pq;
2598
2599
0
    value = njs_argument(args, 0);
2600
2601
0
    if (njs_is_null_or_undefined(value)) {
2602
0
        njs_type_error(vm, "cannot convert %s argument to object",
2603
0
                       njs_type_string(value->type));
2604
0
        return NJS_ERROR;
2605
0
    }
2606
2607
0
    property = njs_lvalue_arg(&lvalue, args, nargs, 1);
2608
2609
0
    if (njs_slow_path(!njs_is_key(property))) {
2610
0
        ret = njs_value_to_key(vm, property, property);
2611
0
        if (njs_slow_path(ret != NJS_OK)) {
2612
0
            return NJS_ERROR;
2613
0
        }
2614
0
    }
2615
2616
0
    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
2617
2618
0
    ret = njs_property_query_val(vm, &pq, value, property);
2619
2620
0
    switch (ret) {
2621
0
    case NJS_OK:
2622
0
        njs_set_boolean(retval, 1);
2623
0
        return NJS_OK;
2624
2625
0
    case NJS_DECLINED:
2626
0
        njs_set_boolean(retval, 0);
2627
0
        return NJS_OK;
2628
2629
0
    case NJS_ERROR:
2630
0
    default:
2631
0
        return NJS_ERROR;
2632
0
    }
2633
0
}
2634
2635
2636
static njs_int_t
2637
njs_object_prototype_prop_is_enumerable(njs_vm_t *vm, njs_value_t *args,
2638
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
2639
0
{
2640
0
    njs_int_t             ret;
2641
0
    njs_value_t           *value, *property, lvalue;
2642
0
    njs_object_prop_t     *prop;
2643
0
    njs_property_query_t  pq;
2644
2645
0
    value = njs_argument(args, 0);
2646
2647
0
    if (njs_is_null_or_undefined(value)) {
2648
0
        njs_type_error(vm, "cannot convert %s argument to object",
2649
0
                       njs_type_string(value->type));
2650
0
        return NJS_ERROR;
2651
0
    }
2652
2653
0
    property = njs_lvalue_arg(&lvalue, args, nargs, 1);
2654
2655
0
    if (njs_slow_path(!njs_is_key(property))) {
2656
0
        ret = njs_value_to_key(vm, property, property);
2657
0
        if (njs_slow_path(ret != NJS_OK)) {
2658
0
            return NJS_ERROR;
2659
0
        }
2660
0
    }
2661
2662
0
    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
2663
2664
0
    ret = njs_property_query_val(vm, &pq, value, property);
2665
2666
0
    switch (ret) {
2667
0
    case NJS_OK:
2668
0
        prop = pq.fhq.value;
2669
0
        njs_set_boolean(retval, prop->enumerable);
2670
0
        break;
2671
2672
0
    case NJS_DECLINED:
2673
0
        njs_set_boolean(retval, 0);
2674
0
        break;
2675
2676
0
    case NJS_ERROR:
2677
0
    default:
2678
0
        return NJS_ERROR;
2679
0
    }
2680
2681
0
    return NJS_OK;
2682
0
}
2683
2684
2685
static njs_int_t
2686
njs_object_prototype_is_prototype_of(njs_vm_t *vm, njs_value_t *args,
2687
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
2688
0
{
2689
0
    njs_value_t   *prototype, *value;
2690
0
    njs_object_t  *object, *proto;
2691
2692
0
    if (njs_slow_path(njs_is_null_or_undefined(njs_argument(args, 0)))) {
2693
0
        njs_type_error(vm, "cannot convert undefined to object");
2694
0
        return NJS_ERROR;
2695
0
    }
2696
2697
0
    prototype = &args[0];
2698
0
    value = njs_arg(args, nargs, 1);
2699
2700
0
    if (njs_is_object(prototype) && njs_is_object(value)) {
2701
0
        proto = njs_object(prototype);
2702
0
        object = njs_object(value);
2703
2704
0
        do {
2705
0
            object = object->__proto__;
2706
2707
0
            if (object == proto) {
2708
0
                njs_set_boolean(retval, 1);
2709
0
                return NJS_OK;
2710
0
            }
2711
2712
0
        } while (object != NULL);
2713
0
    }
2714
2715
0
    njs_set_boolean(retval, 0);
2716
2717
0
    return NJS_OK;
2718
0
}
2719
2720
2721
static const njs_object_prop_init_t  njs_object_prototype_properties[] =
2722
{
2723
    NJS_DECLARE_PROP_HANDLER(STRING___proto__, njs_object_prototype_proto,
2724
                             0, NJS_OBJECT_PROP_VALUE_CW),
2725
2726
    NJS_DECLARE_PROP_HANDLER(STRING_constructor,
2727
                             njs_object_prototype_create_constructor, 0,
2728
                             NJS_OBJECT_PROP_VALUE_CW),
2729
2730
    NJS_DECLARE_PROP_NATIVE(STRING_valueOf, njs_object_prototype_value_of,
2731
                            0, 0),
2732
2733
    NJS_DECLARE_PROP_NATIVE(STRING_toString, njs_object_prototype_to_string,
2734
                            0, 0),
2735
2736
    NJS_DECLARE_PROP_NATIVE(STRING_hasOwnProperty,
2737
                            njs_object_prototype_has_own_property, 1, 0),
2738
2739
    NJS_DECLARE_PROP_NATIVE(STRING_propertyIsEnumerable,
2740
                            njs_object_prototype_prop_is_enumerable, 1, 0),
2741
2742
    NJS_DECLARE_PROP_NATIVE(STRING_isPrototypeOf,
2743
                            njs_object_prototype_is_prototype_of, 1, 0),
2744
};
2745
2746
2747
const njs_object_init_t  njs_object_prototype_init = {
2748
    njs_object_prototype_properties,
2749
    njs_nitems(njs_object_prototype_properties),
2750
};
2751
2752
2753
njs_int_t
2754
njs_object_length(njs_vm_t *vm, njs_value_t *value, int64_t *length)
2755
1.18M
{
2756
1.18M
    njs_int_t    ret;
2757
1.18M
    njs_value_t  value_length;
2758
2759
1.18M
    if (njs_is_fast_array(value)) {
2760
490k
        *length = njs_array(value)->length;
2761
490k
        return NJS_OK;
2762
490k
    }
2763
2764
696k
    ret = njs_value_property(vm, value, NJS_ATOM_STRING_length, &value_length);
2765
696k
    if (njs_slow_path(ret == NJS_ERROR)) {
2766
0
        return ret;
2767
0
    }
2768
2769
696k
    return njs_value_to_length(vm, &value_length, length);
2770
696k
}
2771
2772
2773
const njs_object_type_init_t  njs_obj_type_init = {
2774
    .constructor = njs_native_ctor(njs_object_constructor, 1, 0),
2775
    .constructor_props = &njs_object_constructor_init,
2776
    .prototype_props = &njs_object_prototype_init,
2777
    .prototype_value = { .object = { .type = NJS_OBJECT } },
2778
};