Coverage Report

Created: 2025-10-27 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/njs/src/njs_object_prop.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
static njs_object_prop_t *njs_descriptor_prop(njs_vm_t *vm,
12
    const njs_value_t *desc, njs_object_prop_t *prop,
13
    uint32_t *unset_enumerable, uint32_t *unset_configuarble,
14
    uint32_t *enum_writable);
15
16
17
void
18
njs_object_prop_init(njs_object_prop_t *prop, njs_object_prop_type_t type,
19
    uint8_t flags)
20
23.4M
{
21
23.4M
    prop->next_elt = 0;
22
23.4M
    prop->atom_id = 0;
23
24
23.4M
    prop->type = type;
25
26
23.4M
    if (flags != NJS_OBJECT_PROP_UNSET) {
27
23.4M
        prop->enumerable = !!(flags & NJS_OBJECT_PROP_ENUMERABLE);
28
23.4M
        prop->configurable = !!(flags & NJS_OBJECT_PROP_CONFIGURABLE);
29
30
23.4M
        if (type == NJS_PROPERTY) {
31
23.4M
            prop->writable = !!(flags & NJS_OBJECT_PROP_WRITABLE);
32
33
23.4M
        } else {
34
2.43k
            prop->writable = 0;
35
2.43k
        }
36
37
23.4M
    } else {
38
6.51k
        prop->enumerable = 0;
39
6.51k
        prop->configurable = 0;
40
6.51k
        prop->writable = 0;
41
6.51k
    }
42
23.4M
}
43
44
45
njs_int_t
46
njs_object_property(njs_vm_t *vm, njs_object_t *object,
47
    njs_flathsh_query_t *fhq, njs_value_t *retval)
48
2.77M
{
49
2.77M
    njs_int_t          ret;
50
2.77M
    njs_value_t        value;
51
2.77M
    njs_object_prop_t  *prop;
52
53
6.32M
    do {
54
6.32M
        ret = njs_flathsh_unique_find(&object->hash, fhq);
55
56
6.32M
        if (njs_fast_path(ret == NJS_OK)) {
57
142k
            prop = fhq->value;
58
59
142k
            if (prop->type != NJS_WHITEOUT) {
60
142k
                goto found;
61
142k
            }
62
142k
        }
63
64
6.17M
        ret = njs_flathsh_unique_find(&object->shared_hash, fhq);
65
66
6.17M
        if (njs_fast_path(ret == NJS_OK)) {
67
2.59M
            goto found;
68
2.59M
        }
69
70
3.58M
        object = object->__proto__;
71
72
3.58M
    } while (object != NULL);
73
74
36.7k
    njs_set_undefined(retval);
75
76
36.7k
    return NJS_DECLINED;
77
78
2.73M
found:
79
80
2.73M
    prop = fhq->value;
81
82
2.73M
    if (njs_is_data_descriptor(prop)) {
83
2.73M
        njs_value_assign(retval, njs_prop_value(prop));
84
2.73M
        return NJS_OK;
85
2.73M
    }
86
87
0
    if (njs_prop_getter(prop) == NULL) {
88
0
        njs_set_undefined(retval);
89
0
        return NJS_OK;
90
0
    }
91
92
0
    njs_set_object(&value, object);
93
94
0
    return njs_function_apply(vm, njs_prop_getter(prop), &value, 1, retval);
95
0
}
96
97
98
njs_object_prop_t *
99
njs_object_property_add(njs_vm_t *vm, njs_value_t *object, unsigned atom_id,
100
    njs_bool_t replace)
101
511k
{
102
511k
    njs_int_t            ret;
103
511k
    njs_object_prop_t    *prop;
104
511k
    njs_flathsh_query_t  fhq;
105
106
511k
    fhq.key_hash = atom_id;
107
511k
    fhq.replace = replace;
108
511k
    fhq.pool = vm->mem_pool;
109
511k
    fhq.proto = &njs_object_hash_proto;
110
111
511k
    ret = njs_flathsh_unique_insert(njs_object_hash(object), &fhq);
112
511k
    if (njs_slow_path(ret != NJS_OK)) {
113
0
        njs_internal_error(vm, "flathsh insert failed");
114
0
        return NULL;
115
0
    }
116
117
511k
    prop = fhq.value;
118
119
511k
    prop->type = NJS_PROPERTY;
120
511k
    prop->enumerable = 1;
121
511k
    prop->configurable = 1;
122
511k
    prop->writable = 1;
123
511k
    prop->u.value = njs_value_invalid;
124
125
511k
    return prop;
126
511k
}
127
128
129
/*
130
 * ES5.1, 8.12.9: [[DefineOwnProperty]]
131
 */
132
njs_int_t
133
njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, unsigned atom_id,
134
    njs_value_t *value, unsigned flags)
135
23.4M
{
136
23.4M
    uint32_t              length, index, set_enumerable, set_configurable,
137
23.4M
                          set_writable;
138
23.4M
    njs_int_t             ret;
139
23.4M
    njs_array_t           *array;
140
23.4M
    njs_value_t           key, retval;
141
23.4M
    njs_object_prop_t     _prop;
142
23.4M
    njs_object_prop_t     *prop = &_prop, *prev, *obj_prop;
143
23.4M
    njs_property_query_t  pq;
144
145
23.4M
again:
146
147
23.4M
    set_enumerable = 1;
148
23.4M
    set_configurable = 1;
149
23.4M
    set_writable = 1;
150
151
23.4M
    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1);
152
153
23.4M
    ret = (flags & NJS_OBJECT_PROP_CREATE)
154
23.4M
                ? NJS_DECLINED
155
23.4M
                : njs_property_query(vm, &pq, object, atom_id);
156
157
23.4M
    if (njs_slow_path(ret == NJS_ERROR)) {
158
0
        return ret;
159
0
    }
160
161
23.4M
    switch (njs_prop_type(flags)) {
162
6.51k
    case NJS_OBJECT_PROP_DESCRIPTOR:
163
6.51k
        prop = njs_descriptor_prop(vm, value, prop, &set_enumerable,
164
6.51k
                                   &set_configurable, &set_writable);
165
6.51k
        if (njs_slow_path(prop == NULL)) {
166
0
            return NJS_ERROR;
167
0
        }
168
169
6.51k
        break;
170
171
23.4M
    case NJS_OBJECT_PROP_VALUE:
172
23.4M
        if (((flags & NJS_OBJECT_PROP_VALUE_ECW) == NJS_OBJECT_PROP_VALUE_ECW)
173
23.4M
            && njs_is_fast_array(object)
174
11.7k
            && njs_atom_is_number(atom_id))
175
11.7k
        {
176
11.7k
            array = njs_array(object);
177
11.7k
            index = njs_atom_number(atom_id);
178
179
11.7k
            if (index < array->length) {
180
11.7k
                njs_value_assign(&array->start[index], value);
181
11.7k
                return NJS_OK;
182
11.7k
            }
183
11.7k
        }
184
185
23.4M
        njs_object_prop_init(prop, NJS_PROPERTY,
186
23.4M
                              flags & NJS_OBJECT_PROP_VALUE_ECW);
187
23.4M
        njs_value_assign(njs_prop_value(prop), value);
188
23.4M
        break;
189
190
2.43k
    case NJS_OBJECT_PROP_GETTER:
191
2.43k
    case NJS_OBJECT_PROP_SETTER:
192
2.43k
    default:
193
2.43k
        njs_assert(njs_is_function(value));
194
195
2.43k
        njs_object_prop_init(prop, NJS_ACCESSOR, NJS_OBJECT_PROP_VALUE_EC);
196
197
2.43k
        set_writable = 0;
198
199
2.43k
        if (njs_prop_type(flags) == NJS_OBJECT_PROP_GETTER) {
200
2.43k
            njs_prop_getter(prop) = njs_function(value);
201
2.43k
            njs_prop_setter(prop) = NJS_PROP_PTR_UNSET;
202
203
2.43k
        } else {
204
0
            njs_prop_getter(prop) = NJS_PROP_PTR_UNSET;
205
0
            njs_prop_setter(prop) = njs_function(value);
206
0
        }
207
208
2.43k
        break;
209
23.4M
    }
210
211
23.4M
    if (njs_fast_path(ret == NJS_DECLINED)) {
212
213
23.4M
set_prop:
214
215
23.4M
        if (!njs_object(object)->extensible) {
216
0
            njs_atom_string_get(vm, atom_id, &pq.fhq.key);
217
0
            njs_type_error(vm, "Cannot add property \"%V\", "
218
0
                           "object is not extensible", &pq.fhq.key);
219
0
            return NJS_ERROR;
220
0
        }
221
222
23.4M
        if (njs_slow_path(njs_is_typed_array(object) &&
223
23.4M
           (flags & NJS_OBJECT_PROP_IS_STRING)))
224
0
        {
225
            /* Integer-Indexed Exotic Objects [[DefineOwnProperty]]. */
226
227
0
            ret = njs_atom_to_value(vm, &key, atom_id);
228
0
            if (njs_slow_path(ret != NJS_OK)) {
229
0
                return ret;
230
0
            }
231
232
0
            if (!isnan(njs_string_to_index(&key))) {
233
0
                njs_type_error(vm, "Invalid typed array index");
234
0
                return NJS_ERROR;
235
0
            }
236
0
        }
237
238
        /* 6.2.5.6 CompletePropertyDescriptor */
239
240
23.4M
        if (njs_is_accessor_descriptor(prop)) {
241
3.71k
            if (njs_prop_getter(prop) == NJS_PROP_PTR_UNSET) {
242
1.52k
                njs_prop_getter(prop) = NULL;
243
1.52k
            }
244
245
3.71k
            if (njs_prop_setter(prop) == NJS_PROP_PTR_UNSET) {
246
2.19k
                njs_prop_setter(prop) = NULL;
247
2.19k
            }
248
249
23.4M
        } else {
250
23.4M
            if (!set_writable) {
251
2.36k
                prop->writable = 0;
252
2.36k
            }
253
254
23.4M
            if (!njs_is_valid(njs_prop_value(prop))) {
255
2.36k
                njs_set_undefined(njs_prop_value(prop));
256
2.36k
            }
257
23.4M
        }
258
259
23.4M
        if (!set_enumerable) {
260
4.73k
            prop->enumerable = 0;
261
4.73k
        }
262
263
23.4M
        if (!set_configurable) {
264
4.73k
            prop->configurable = 0;
265
4.73k
        }
266
267
23.4M
        if (njs_slow_path(pq.fhq.value != NULL)) {
268
0
            prev = pq.fhq.value;
269
270
0
            if (njs_slow_path(prev->type == NJS_WHITEOUT)) {
271
                /* Previously deleted property.  */
272
0
                prop->atom_id = prev->atom_id;
273
0
                prop->next_elt = prev->next_elt;
274
0
                *prev = *prop;
275
0
            }
276
277
23.4M
        } else {
278
279
23.4M
            pq.fhq.key_hash = atom_id;
280
23.4M
            pq.fhq.proto = &njs_object_hash_proto;
281
23.4M
            pq.fhq.replace = 0;
282
23.4M
            pq.fhq.pool = vm->mem_pool;
283
284
23.4M
            ret = njs_flathsh_unique_insert(njs_object_hash(object), &pq.fhq);
285
23.4M
            if (njs_slow_path(ret != NJS_OK)) {
286
0
                njs_internal_error(vm, "flathsh insert failed");
287
0
                return NJS_ERROR;
288
0
            }
289
290
23.4M
            obj_prop = pq.fhq.value;
291
23.4M
            obj_prop->enumerable = prop->enumerable;
292
23.4M
            obj_prop->configurable = prop->configurable;
293
23.4M
            obj_prop->writable = prop->writable;
294
23.4M
            obj_prop->type = prop->type;
295
23.4M
            obj_prop->u.value = prop->u.value;
296
23.4M
        }
297
298
23.4M
        return NJS_OK;
299
23.4M
    }
300
301
    /* Updating existing prop. */
302
303
2.86k
    prev = pq.fhq.value;
304
305
2.86k
    switch (prev->type) {
306
284
    case NJS_PROPERTY:
307
2.60k
    case NJS_ACCESSOR:
308
2.60k
    case NJS_PROPERTY_HANDLER:
309
2.60k
        break;
310
311
0
    case NJS_PROPERTY_REF:
312
260
    case NJS_PROPERTY_PLACE_REF:
313
260
        if (prev->type == NJS_PROPERTY_REF
314
0
            && !njs_is_accessor_descriptor(prop)
315
0
            && (!set_configurable || prop->configurable)
316
0
            && (!set_enumerable || prop->enumerable)
317
0
            && (!set_writable || prop->writable))
318
0
        {
319
0
            if (njs_is_valid(njs_prop_value(prop))) {
320
0
                njs_value_assign(njs_prop_ref(prev), njs_prop_value(prop));
321
0
            }
322
323
0
            return NJS_OK;
324
0
        }
325
326
260
        array = njs_array(object);
327
260
        length = array->length;
328
329
260
        ret = njs_array_convert_to_slow_array(vm, array);
330
260
        if (njs_slow_path(ret != NJS_OK)) {
331
0
            return ret;
332
0
        }
333
334
260
        ret = njs_array_length_redefine(vm, object, length, 1);
335
260
        if (njs_slow_path(ret != NJS_OK)) {
336
0
            return ret;
337
0
        }
338
339
260
        flags &= ~NJS_OBJECT_PROP_CREATE;
340
341
260
        goto again;
342
343
0
    case NJS_PROPERTY_TYPED_ARRAY_REF:
344
0
        if (njs_is_accessor_descriptor(prop)) {
345
0
            goto exception;
346
0
        }
347
348
0
        if ((set_configurable && prop->configurable)
349
0
            || (set_enumerable && !prop->enumerable)
350
0
            || (set_writable && !prop->writable))
351
0
        {
352
0
            goto exception;
353
0
        }
354
355
0
        if (njs_is_valid(njs_prop_value(prop))) {
356
0
            return njs_typed_array_set_value(vm,
357
0
                                         njs_typed_array(njs_prop_value(prev)),
358
0
                                         njs_prop_magic32(prev),
359
0
                                         njs_prop_value(prop));
360
0
        }
361
362
0
        return NJS_OK;
363
364
0
    default:
365
0
        njs_internal_error(vm, "unexpected property type \"%s\" "
366
0
                           "while defining property",
367
0
                           njs_prop_type_string(prev->type));
368
369
0
        return NJS_ERROR;
370
2.86k
    }
371
372
    /* 9.1.6.3 ValidateAndApplyPropertyDescriptor */
373
374
2.60k
    if (!prev->configurable) {
375
376
1.52k
        if (prop->configurable) {
377
0
            goto exception;
378
0
        }
379
380
1.52k
        if (set_enumerable && prev->enumerable != prop->enumerable) {
381
0
            goto exception;
382
0
        }
383
1.52k
    }
384
385
2.60k
    if (!(set_writable || njs_is_data_descriptor(prop))
386
2.60k
        && !njs_is_accessor_descriptor(prop))
387
0
    {
388
0
        goto done;
389
0
    }
390
391
2.60k
    if (njs_is_data_descriptor(prev)
392
2.60k
        != (set_writable || njs_is_data_descriptor(prop)))
393
284
    {
394
284
        if (!prev->configurable) {
395
0
            goto exception;
396
0
        }
397
398
        /*
399
         * 6.b-c Preserve the existing values of the converted property's
400
         * [[Configurable]] and [[Enumerable]] attributes and set the rest of
401
         * the property's attributes to their default values.
402
         */
403
404
284
        if (pq.temp) {
405
0
            pq.fhq.value = NULL;
406
0
            prop->configurable = prev->configurable;
407
0
            prop->enumerable = prev->enumerable;
408
0
            goto set_prop;
409
0
        }
410
411
284
        if (njs_is_data_descriptor(prev)) {
412
284
            set_writable = 0;
413
284
            njs_prop_getter(prev) = NULL;
414
284
            njs_prop_setter(prev) = NULL;
415
416
284
        } else {
417
0
            prev->writable = 0;
418
419
0
            njs_set_undefined(njs_prop_value(prev));
420
0
        }
421
422
284
        prev->type = prop->type;
423
424
2.32k
    } else if (njs_is_data_descriptor(prev)
425
0
               && (set_writable || njs_is_data_descriptor(prop)))
426
0
    {
427
0
        if (!prev->configurable && !prev->writable) {
428
0
            if (prop->writable) {
429
0
                goto exception;
430
0
            }
431
432
0
            if (njs_is_valid(njs_prop_value(prop))
433
0
                && prev->type != NJS_PROPERTY_HANDLER
434
0
                && !njs_values_same(vm, njs_prop_value(prop),
435
0
                                    njs_prop_value(prev)))
436
0
            {
437
0
                goto exception;
438
0
            }
439
0
        }
440
441
2.32k
    } else {
442
2.32k
        if (!prev->configurable) {
443
1.52k
            if (njs_prop_getter(prop) != NJS_PROP_PTR_UNSET
444
1.26k
                && njs_prop_getter(prop) != njs_prop_getter(prev))
445
0
            {
446
0
                goto exception;
447
0
            }
448
449
1.52k
            if (njs_prop_setter(prop) != NJS_PROP_PTR_UNSET
450
260
                && njs_prop_setter(prop) != njs_prop_setter(prev))
451
260
            {
452
260
                goto exception;
453
260
            }
454
1.52k
        }
455
2.32k
    }
456
457
2.34k
done:
458
459
2.34k
    if (njs_slow_path(njs_is_fast_array(object)
460
0
                      && pq.fhq.key_hash == NJS_ATOM_STRING_length)
461
0
                      && (set_writable && !prop->writable))
462
0
    {
463
0
        array = njs_array(object);
464
0
        length = array->length;
465
466
0
        ret = njs_array_convert_to_slow_array(vm, array);
467
0
        if (njs_slow_path(ret != NJS_OK)) {
468
0
            return ret;
469
0
        }
470
471
0
        ret = njs_array_length_redefine(vm, object, length, 1);
472
0
        if (njs_slow_path(ret != NJS_OK)) {
473
0
            return ret;
474
0
        }
475
476
0
        goto again;
477
0
    }
478
479
2.34k
    if (njs_is_accessor_descriptor(prop)) {
480
2.34k
        prev->type = prop->type;
481
482
2.34k
        if (njs_prop_getter(prop) != NJS_PROP_PTR_UNSET) {
483
2.34k
            njs_prop_getter(prev) = njs_prop_getter(prop);
484
2.34k
        }
485
486
2.34k
        if (njs_prop_setter(prop) != NJS_PROP_PTR_UNSET) {
487
0
            njs_prop_setter(prev) = njs_prop_setter(prop);
488
0
        }
489
490
2.34k
    } else if (njs_is_valid(njs_prop_value(prop))) {
491
492
0
        if (prev->type == NJS_PROPERTY_HANDLER) {
493
0
            if (prev->writable) {
494
0
                ret = njs_prop_handler(prev)(vm, prev, atom_id, object,
495
0
                                             njs_prop_value(prop), &retval);
496
0
                if (njs_slow_path(ret == NJS_ERROR)) {
497
0
                    return ret;
498
0
                }
499
500
0
                if (ret == NJS_DECLINED) {
501
0
                    pq.fhq.value = NULL;
502
0
                    goto set_prop;
503
0
                }
504
505
0
            } else {
506
507
0
                prev->type = prop->type;
508
0
                njs_value_assign(njs_prop_value(prev), njs_prop_value(prop));
509
0
            }
510
511
0
        } else {
512
513
0
            if (njs_slow_path(njs_is_array(object)
514
0
                              && pq.fhq.key_hash == NJS_ATOM_STRING_length))
515
0
            {
516
0
                if (!prev->configurable
517
0
                    && !prev->writable
518
0
                    && !njs_values_strict_equal(vm, njs_prop_value(prev),
519
0
                                                njs_prop_value(prop)))
520
0
                {
521
0
                    njs_type_error(vm, "Cannot redefine property: \"length\"");
522
0
                    return NJS_ERROR;
523
0
                }
524
525
0
                if (set_writable) {
526
0
                    prev->writable = prop->writable;
527
0
                }
528
529
0
                return njs_array_length_set(vm, object, prev,
530
0
                                            njs_prop_value(prop));
531
0
            }
532
533
0
            njs_value_assign(njs_prop_value(prev), njs_prop_value(prop));
534
0
        }
535
0
    }
536
537
    /*
538
     * 9. For each field of Desc that is present, set the corresponding
539
     * attribute of the property named P of object O to the value of the field.
540
     */
541
542
2.34k
    if (set_writable) {
543
0
        prev->writable = prop->writable;
544
0
    }
545
546
2.34k
    if (set_enumerable) {
547
1.07k
        prev->enumerable = prop->enumerable;
548
1.07k
    }
549
550
2.34k
    if (set_configurable) {
551
1.07k
        prev->configurable = prop->configurable;
552
1.07k
    }
553
554
2.34k
    return NJS_OK;
555
556
260
exception:
557
558
260
    njs_atom_string_get(vm, atom_id, &pq.fhq.key);
559
260
    njs_type_error(vm, "Cannot redefine property: \"%V\"", &pq.fhq.key);
560
561
260
    return NJS_ERROR;
562
2.34k
}
563
564
565
njs_int_t
566
njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq,
567
    njs_object_t *proto)
568
261k
{
569
261k
    njs_int_t          ret;
570
261k
    njs_value_t        *value, prop_name;
571
261k
    njs_object_t       *object;
572
261k
    njs_function_t     *function;
573
261k
    njs_object_prop_t  *prop, *shared;
574
575
261k
    shared = pq->fhq.value;
576
577
261k
    pq->fhq.replace = 0;
578
261k
    pq->fhq.pool = vm->mem_pool;
579
580
261k
    ret = njs_flathsh_unique_insert(&proto->hash, &pq->fhq);
581
261k
    if (njs_slow_path(ret != NJS_OK)) {
582
0
        njs_internal_error(vm, "flathsh insert failed");
583
0
        return NJS_ERROR;
584
0
    }
585
586
261k
    prop = pq->fhq.value;
587
261k
    prop->enumerable = shared->enumerable;
588
261k
    prop->configurable = shared->configurable;
589
261k
    prop->writable = shared->writable;
590
261k
    prop->type = shared->type;
591
261k
    prop->u.value = shared->u.value;
592
593
261k
    if (njs_is_accessor_descriptor(prop)) {
594
1.13k
        if (njs_prop_getter(prop) != NULL) {
595
1.13k
            function = njs_function_copy(vm, njs_prop_getter(prop));
596
1.13k
            if (njs_slow_path(function == NULL)) {
597
0
                return NJS_ERROR;
598
0
            }
599
600
1.13k
            njs_prop_getter(prop) = function;
601
602
1.13k
            if (njs_prop_setter(prop) != NULL
603
0
                && function->native && njs_prop_setter(prop)->native
604
0
                && function->u.native == njs_prop_setter(prop)->u.native)
605
0
            {
606
0
                njs_prop_setter(prop) = njs_prop_getter(prop);
607
0
                return NJS_OK;
608
0
            }
609
1.13k
        }
610
611
1.13k
        if (njs_prop_setter(prop) != NULL) {
612
0
            function = njs_function_copy(vm, njs_prop_setter(prop));
613
0
            if (njs_slow_path(function == NULL)) {
614
0
                return NJS_ERROR;
615
0
            }
616
617
0
            njs_prop_setter(prop) = function;
618
0
        }
619
620
1.13k
        return NJS_OK;
621
1.13k
    }
622
623
259k
    value = njs_prop_value(prop);
624
625
259k
    switch (value->type) {
626
0
    case NJS_OBJECT:
627
0
    case NJS_ARRAY:
628
7
    case NJS_OBJECT_VALUE:
629
7
        object = njs_object_value_copy(vm, value);
630
7
        if (njs_slow_path(object == NULL)) {
631
0
            return NJS_ERROR;
632
0
        }
633
634
7
        value->data.u.object = object;
635
7
        return NJS_OK;
636
637
3.63k
    case NJS_FUNCTION:
638
3.63k
        function = njs_function_value_copy(vm, value);
639
3.63k
        if (njs_slow_path(function == NULL)) {
640
0
            return NJS_ERROR;
641
0
        }
642
643
3.63k
        ret = njs_atom_to_value(vm, &prop_name, pq->fhq.key_hash);
644
3.63k
        if (ret != NJS_OK) {
645
0
            return NJS_ERROR;
646
0
        }
647
648
3.63k
        return njs_function_name_set(vm, function, &prop_name, NULL);
649
650
256k
    default:
651
256k
        break;
652
259k
    }
653
654
256k
    return NJS_OK;
655
259k
}
656
657
658
static njs_object_prop_t *
659
njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *desc,
660
    njs_object_prop_t *prop, uint32_t *set_enumerable,
661
    uint32_t *set_configurable, uint32_t *set_writable)
662
6.51k
{
663
6.51k
    njs_int_t            ret;
664
6.51k
    njs_bool_t           data, accessor;
665
6.51k
    njs_value_t          value;
666
6.51k
    njs_object_t         *desc_object;
667
6.51k
    njs_function_t       *getter, *setter;
668
6.51k
    njs_flathsh_query_t  fhq;
669
670
6.51k
    if (!njs_is_object(desc)) {
671
0
        njs_type_error(vm, "property descriptor must be an object");
672
0
        return NULL;
673
0
    }
674
675
6.51k
    njs_object_prop_init(prop, NJS_PROPERTY, NJS_OBJECT_PROP_UNSET);
676
6.51k
    *njs_prop_value(prop) = njs_value_invalid;
677
678
6.51k
    *set_enumerable = 0;
679
6.51k
    *set_configurable = 0;
680
6.51k
    *set_writable = 0;
681
682
6.51k
    data = 0;
683
6.51k
    accessor = 0;
684
6.51k
    getter = NJS_PROP_PTR_UNSET;
685
6.51k
    setter = NJS_PROP_PTR_UNSET;
686
6.51k
    desc_object = njs_object(desc);
687
688
6.51k
    fhq.proto = &njs_object_hash_proto;
689
6.51k
    fhq.key_hash = NJS_ATOM_STRING_get;
690
691
6.51k
    ret = njs_object_property(vm, desc_object, &fhq, &value);
692
6.51k
    if (njs_slow_path(ret == NJS_ERROR)) {
693
0
        return NULL;
694
0
    }
695
696
6.51k
    if (ret == NJS_OK) {
697
2.10k
        if (njs_is_defined(&value) && !njs_is_function(&value)) {
698
0
            njs_type_error(vm, "Getter must be a function");
699
0
            return NULL;
700
0
        }
701
702
2.10k
        accessor = 1;
703
2.10k
        getter = njs_is_function(&value) ? njs_function(&value) : NULL;
704
2.10k
    }
705
706
6.51k
    fhq.key_hash = NJS_ATOM_STRING_set;
707
708
6.51k
    ret = njs_object_property(vm, desc_object, &fhq, &value);
709
6.51k
    if (njs_slow_path(ret == NJS_ERROR)) {
710
0
        return NULL;
711
0
    }
712
713
6.51k
    if (ret == NJS_OK) {
714
2.04k
        if (njs_is_defined(&value) && !njs_is_function(&value)) {
715
0
            njs_type_error(vm, "Setter must be a function");
716
0
            return NULL;
717
0
        }
718
719
2.04k
        accessor = 1;
720
2.04k
        setter = njs_is_function(&value) ? njs_function(&value) : NULL;
721
2.04k
    }
722
723
6.51k
    fhq.key_hash = NJS_ATOM_STRING_value;
724
725
6.51k
    ret = njs_object_property(vm, desc_object, &fhq, &value);
726
6.51k
    if (njs_slow_path(ret == NJS_ERROR)) {
727
0
        return NULL;
728
0
    }
729
730
6.51k
    if (ret == NJS_OK) {
731
0
        data = 1;
732
0
        njs_value_assign(njs_prop_value(prop), &value);
733
0
    }
734
735
6.51k
    fhq.key_hash = NJS_ATOM_STRING_writable;
736
737
6.51k
    ret = njs_object_property(vm, desc_object, &fhq, &value);
738
6.51k
    if (njs_slow_path(ret == NJS_ERROR)) {
739
0
        return NULL;
740
0
    }
741
742
6.51k
    if (ret == NJS_OK) {
743
0
        data = 1;
744
0
        prop->writable = njs_is_true(&value);
745
0
        *set_writable = 1;
746
0
    }
747
748
6.51k
    if (accessor && data) {
749
0
        njs_type_error(vm, "Cannot both specify accessors "
750
0
                           "and a value or writable attribute");
751
0
        return NULL;
752
0
    }
753
754
6.51k
    fhq.key_hash = NJS_ATOM_STRING_enumerable;
755
756
6.51k
    ret = njs_object_property(vm, desc_object, &fhq, &value);
757
6.51k
    if (njs_slow_path(ret == NJS_ERROR)) {
758
0
        return NULL;
759
0
    }
760
761
6.51k
    if (ret == NJS_OK) {
762
0
        prop->enumerable = njs_is_true(&value);
763
0
        *set_enumerable = 1;
764
0
    }
765
766
6.51k
    fhq.key_hash = NJS_ATOM_STRING_configurable;
767
768
6.51k
    ret = njs_object_property(vm, desc_object, &fhq, &value);
769
6.51k
    if (njs_slow_path(ret == NJS_ERROR)) {
770
0
        return NULL;
771
0
    }
772
773
6.51k
    if (ret == NJS_OK) {
774
0
        prop->configurable = njs_is_true(&value);
775
0
        *set_configurable = 1;
776
0
    }
777
778
6.51k
    if (accessor) {
779
4.15k
        prop->type = NJS_ACCESSOR;
780
4.15k
        njs_prop_getter(prop) = getter;
781
4.15k
        njs_prop_setter(prop) = setter;
782
4.15k
    }
783
784
6.51k
    return prop;
785
6.51k
}
786
787
788
njs_int_t
789
njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
790
    njs_value_t *value, njs_value_t *key)
791
0
{
792
0
    njs_int_t             ret;
793
0
    njs_object_t          *desc;
794
0
    njs_object_prop_t     *pr, *prop;
795
0
    const njs_value_t     *setval;
796
0
    njs_flathsh_query_t   fhq;
797
0
    njs_property_query_t  pq;
798
799
0
    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
800
801
0
    if (njs_slow_path(!njs_is_key(key))) {
802
0
        ret = njs_value_to_key(vm, key, key);
803
0
        if (njs_slow_path(ret != NJS_OK)) {
804
0
            return ret;
805
0
        }
806
0
    }
807
808
0
    ret = njs_property_query_val(vm, &pq, value, key);
809
810
0
    switch (ret) {
811
0
    case NJS_OK:
812
0
        prop = pq.fhq.value;
813
814
0
        switch (prop->type) {
815
0
        case NJS_PROPERTY:
816
0
        case NJS_ACCESSOR:
817
0
            break;
818
819
0
        case NJS_PROPERTY_HANDLER:
820
0
            pq.scratch = *prop;
821
0
            prop = &pq.scratch;
822
0
            ret = njs_prop_handler(prop)(vm, prop, key->atom_id, value, NULL,
823
0
                                         njs_prop_value(prop));
824
0
            if (njs_slow_path(ret == NJS_ERROR)) {
825
0
                return ret;
826
0
            }
827
828
0
            break;
829
830
0
        default:
831
0
            njs_type_error(vm, "unexpected property type: %s",
832
0
                           njs_prop_type_string(prop->type));
833
0
            return NJS_ERROR;
834
0
        }
835
836
0
        break;
837
838
0
    case NJS_DECLINED:
839
0
        njs_set_undefined(dest);
840
0
        return NJS_OK;
841
842
0
    case NJS_ERROR:
843
0
    default:
844
0
        return NJS_ERROR;
845
0
    }
846
847
0
    desc = njs_object_alloc(vm);
848
0
    if (njs_slow_path(desc == NULL)) {
849
0
        return NJS_ERROR;
850
0
    }
851
852
0
    fhq.proto = &njs_object_hash_proto;
853
0
    fhq.replace = 0;
854
0
    fhq.pool = vm->mem_pool;
855
856
0
    if (njs_is_data_descriptor(prop)) {
857
0
        fhq.key_hash = NJS_ATOM_STRING_value;
858
859
0
        ret = njs_flathsh_unique_insert(&desc->hash, &fhq);
860
0
        if (njs_slow_path(ret != NJS_OK)) {
861
0
            njs_internal_error(vm, "flathsh insert failed");
862
0
            return NJS_ERROR;
863
0
        }
864
865
0
        pr = fhq.value;
866
867
0
        pr->type = NJS_PROPERTY;
868
0
        pr->enumerable = 1;
869
0
        pr->configurable = 1;
870
0
        pr->writable = 1;
871
0
        pr->u.value = *(njs_prop_value(prop));
872
873
0
        fhq.key_hash = NJS_ATOM_STRING_writable;
874
875
0
        setval = (prop->writable == 1) ? &njs_value_true : &njs_value_false;
876
877
0
        ret = njs_flathsh_unique_insert(&desc->hash, &fhq);
878
0
        if (njs_slow_path(ret != NJS_OK)) {
879
0
            njs_internal_error(vm, "flathsh insert failed");
880
0
            return NJS_ERROR;
881
0
        }
882
883
0
        pr = fhq.value;
884
885
0
        pr->type = NJS_PROPERTY;
886
0
        pr->enumerable = 1;
887
0
        pr->configurable = 1;
888
0
        pr->writable = 1;
889
0
        pr->u.value = *setval;
890
891
0
    } else {
892
893
0
        fhq.key_hash = NJS_ATOM_STRING_get;
894
895
0
        ret = njs_flathsh_unique_insert(&desc->hash, &fhq);
896
0
        if (njs_slow_path(ret != NJS_OK)) {
897
0
            njs_internal_error(vm, "flathsh insert failed");
898
0
            return NJS_ERROR;
899
0
        }
900
901
0
        pr = fhq.value;
902
903
0
        pr->type = NJS_PROPERTY;
904
0
        pr->enumerable = 1;
905
0
        pr->configurable = 1;
906
0
        pr->writable = 1;
907
0
        pr->u.value = njs_value_undefined;
908
909
0
        if (njs_prop_getter(prop) != NULL) {
910
0
            njs_set_function(njs_prop_value(pr), njs_prop_getter(prop));
911
0
        }
912
913
0
        fhq.key_hash = NJS_ATOM_STRING_set;
914
915
0
        ret = njs_flathsh_unique_insert(&desc->hash, &fhq);
916
0
        if (njs_slow_path(ret != NJS_OK)) {
917
0
            njs_internal_error(vm, "flathsh insert failed");
918
0
            return NJS_ERROR;
919
0
        }
920
921
0
        pr = fhq.value;
922
923
0
        pr->type = NJS_PROPERTY;
924
0
        pr->enumerable = 1;
925
0
        pr->configurable = 1;
926
0
        pr->writable = 1;
927
0
        pr->u.value = njs_value_undefined;
928
929
0
        if (njs_prop_setter(prop) != NULL) {
930
0
            njs_set_function(njs_prop_value(pr), njs_prop_setter(prop));
931
0
        }
932
0
    }
933
934
0
    fhq.key_hash = NJS_ATOM_STRING_enumerable;
935
936
0
    setval = (prop->enumerable == 1) ? &njs_value_true : &njs_value_false;
937
938
0
    ret = njs_flathsh_unique_insert(&desc->hash, &fhq);
939
0
    if (njs_slow_path(ret != NJS_OK)) {
940
0
        njs_internal_error(vm, "flathsh insert failed");
941
0
        return NJS_ERROR;
942
0
    }
943
944
0
    pr = fhq.value;
945
946
0
    pr->type = NJS_PROPERTY;
947
0
    pr->enumerable = 1;
948
0
    pr->configurable = 1;
949
0
    pr->writable = 1;
950
0
    pr->u.value = *setval;
951
952
0
    fhq.key_hash = NJS_ATOM_STRING_configurable;
953
954
0
    setval = (prop->configurable == 1) ? &njs_value_true : &njs_value_false;
955
956
0
    ret = njs_flathsh_unique_insert(&desc->hash, &fhq);
957
0
    if (njs_slow_path(ret != NJS_OK)) {
958
0
        njs_internal_error(vm, "flathsh insert failed");
959
0
        return NJS_ERROR;
960
0
    }
961
962
0
    pr = fhq.value;
963
964
0
    pr->type = NJS_PROPERTY;
965
0
    pr->enumerable = 1;
966
0
    pr->configurable = 1;
967
0
    pr->writable = 1;
968
0
    pr->u.value = *setval;
969
970
0
    njs_set_object(dest, desc);
971
972
0
    return NJS_OK;
973
0
}
974
975
976
const char *
977
njs_prop_type_string(njs_object_prop_type_t type)
978
0
{
979
0
    switch (type) {
980
0
    case NJS_PROPERTY_REF:
981
0
    case NJS_PROPERTY_PLACE_REF:
982
0
        return "property_ref";
983
984
0
    case NJS_PROPERTY_HANDLER:
985
0
        return "property handler";
986
987
0
    case NJS_WHITEOUT:
988
0
        return "whiteout";
989
990
0
    case NJS_PROPERTY:
991
0
        return "property";
992
993
0
    case NJS_FREE_FLATHSH_ELEMENT:
994
0
        return "free hash element";
995
996
0
    default:
997
0
        return "unknown";
998
0
    }
999
0
}
1000
1001
1002
njs_int_t
1003
njs_object_props_init(njs_vm_t *vm, const njs_object_init_t* init,
1004
    njs_object_prop_t *base, uint32_t atom_id, njs_value_t *value,
1005
    njs_value_t *retval)
1006
0
{
1007
0
    njs_int_t            ret;
1008
0
    njs_object_t         *object;
1009
0
    njs_object_prop_t    *prop;
1010
0
    njs_flathsh_query_t  fhq;
1011
1012
0
    object = njs_object_alloc(vm);
1013
0
    if (object == NULL) {
1014
0
        return NJS_ERROR;
1015
0
    }
1016
1017
0
    ret = njs_object_hash_create(vm, &object->hash, init->properties,
1018
0
                                 init->items);
1019
0
    if (njs_slow_path(ret != NJS_OK)) {
1020
0
        return NJS_ERROR;
1021
0
    }
1022
1023
0
    fhq.key_hash = atom_id;
1024
0
    fhq.replace = 1;
1025
0
    fhq.pool = vm->mem_pool;
1026
0
    fhq.proto = &njs_object_hash_proto;
1027
1028
0
    ret = njs_flathsh_unique_insert(njs_object_hash(value), &fhq);
1029
0
    if (njs_slow_path(ret != NJS_OK)) {
1030
0
        njs_internal_error(vm, "flathsh insert failed");
1031
0
        return NJS_ERROR;
1032
0
    }
1033
1034
0
    prop = fhq.value;
1035
1036
0
    prop->enumerable = base->enumerable;
1037
0
    prop->configurable = base->configurable;
1038
0
    prop->writable = base->writable;
1039
0
    prop->type = NJS_PROPERTY;
1040
0
    njs_set_object(njs_prop_value(prop), object);
1041
1042
0
    njs_value_assign(retval, njs_prop_value(prop));
1043
1044
0
    return NJS_OK;
1045
0
}