Coverage Report

Created: 2025-07-12 06:23

/src/njs/src/njs_extern.c
Line
Count
Source (jump to first uncovered line)
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_int_t njs_external_prop_handler(njs_vm_t *vm,
12
    njs_object_prop_t *self, uint32_t atom_id, njs_value_t *value,
13
    njs_value_t *setval, njs_value_t *retval);
14
15
16
static njs_int_t
17
njs_external_add(njs_vm_t *vm, njs_arr_t *protos,
18
    const njs_external_t *external, njs_uint_t n)
19
133k
{
20
133k
    size_t                size;
21
133k
    njs_int_t             ret;
22
133k
    njs_value_t           prop_name;
23
133k
    const u_char          *start;
24
133k
    njs_flathsh_t         *hash;
25
133k
    njs_function_t        *function;
26
133k
    njs_object_prop_t     *prop;
27
133k
    njs_exotic_slots_t    *slot, *next;
28
133k
    njs_flathsh_query_t   lhq;
29
133k
    const njs_external_t  *end;
30
31
133k
    slot = njs_arr_add(protos);
32
133k
    njs_memzero(slot, sizeof(njs_exotic_slots_t));
33
34
133k
    hash = &slot->external_shared_hash;
35
133k
    njs_lvlhsh_init(hash);
36
37
133k
    if (n == 0) {
38
0
        return NJS_OK;
39
0
    }
40
41
133k
    lhq.replace = 0;
42
133k
    lhq.proto = &njs_object_hash_proto;
43
133k
    lhq.pool = vm->mem_pool;
44
45
133k
    end = external + n;
46
47
1.32M
    while (external < end) {
48
49
1.18M
        if ((external->flags & NJS_EXTERN_TYPE_MASK) == NJS_EXTERN_SELF) {
50
0
            slot->writable = external->u.object.writable;
51
0
            slot->configurable = external->u.object.configurable;
52
0
            slot->enumerable = external->u.object.enumerable;
53
0
            slot->prop_handler = external->u.object.prop_handler;
54
0
            slot->magic32 = external->u.object.magic32;
55
0
            slot->keys = external->u.object.keys;
56
57
0
            external++;
58
0
            continue;
59
0
        }
60
61
1.18M
        if (external->flags & NJS_EXTERN_SYMBOL) {
62
111k
            lhq.key_hash = external->name.symbol;
63
64
1.07M
        } else {
65
1.07M
            ret = njs_atom_string_create(vm, &prop_name,
66
1.07M
                                         external->name.string.start,
67
1.07M
                                         external->name.string.length);
68
1.07M
            if (njs_slow_path(ret != NJS_OK)) {
69
0
                return NJS_ERROR;
70
0
            }
71
72
1.07M
            lhq.key_hash = prop_name.atom_id;
73
1.07M
        }
74
75
1.18M
        ret = njs_flathsh_unique_insert(hash, &lhq);
76
1.18M
        if (njs_slow_path(ret != NJS_OK)) {
77
0
            njs_internal_error(vm, "lvlhsh insert failed");
78
0
            return NJS_ERROR;
79
0
        }
80
81
1.18M
        prop = lhq.value;
82
83
1.18M
        prop->type = NJS_PROPERTY;
84
1.18M
        prop->enumerable = external->enumerable;
85
1.18M
        prop->configurable = external->configurable;
86
1.18M
        prop->writable = external->writable;
87
1.18M
        prop->u.value = njs_value_invalid;
88
89
90
1.18M
        switch (external->flags & NJS_EXTERN_TYPE_MASK) {
91
802k
        case NJS_EXTERN_METHOD:
92
802k
            function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t));
93
802k
            if (njs_slow_path(function == NULL)) {
94
0
                goto memory_error;
95
0
            }
96
97
802k
            function->object.shared_hash = vm->shared->arrow_instance_hash;
98
802k
            function->object.type = NJS_FUNCTION;
99
802k
            function->object.shared = 1;
100
802k
            function->object.extensible = 1;
101
802k
            function->native = 1;
102
802k
            function->u.native = external->u.method.native;
103
802k
            function->magic8 = external->u.method.magic8;
104
802k
            function->ctor = external->u.method.ctor;
105
106
802k
            njs_set_function(njs_prop_value(prop), function);
107
108
802k
            break;
109
110
364k
        case NJS_EXTERN_PROPERTY:
111
364k
            if (external->u.property.handler != NULL) {
112
252k
                prop->type = NJS_PROPERTY_HANDLER;
113
252k
                prop->u.value.type = NJS_INVALID;
114
252k
                prop->u.value.data.truth = 1;
115
252k
                njs_prop_magic16(prop) = external->u.property.magic16;
116
252k
                njs_prop_magic32(prop) = external->u.property.magic32;
117
252k
                njs_prop_handler(prop) = external->u.property.handler;
118
119
252k
            } else {
120
111k
                start = (u_char *) external->u.property.value;
121
111k
                size = njs_strlen(start);
122
123
111k
                ret = njs_string_create(vm, &prop->u.value, start, size);
124
111k
                if (njs_slow_path(ret != NJS_OK)) {
125
0
                    return NJS_ERROR;
126
0
                }
127
111k
            }
128
129
364k
            break;
130
131
364k
        case NJS_EXTERN_OBJECT:
132
22.2k
        default:
133
22.2k
            next = njs_arr_item(protos, protos->items);
134
135
22.2k
            ret = njs_external_add(vm, protos, external->u.object.properties,
136
22.2k
                                   external->u.object.nproperties);
137
22.2k
            if (njs_slow_path(ret != NJS_OK)) {
138
0
                return ret;
139
0
            }
140
141
22.2k
            prop->type = NJS_PROPERTY_HANDLER;
142
22.2k
            prop->u.value.type = NJS_INVALID;
143
22.2k
            prop->u.value.data.truth = 1;
144
22.2k
            njs_prop_magic16(prop) = next - slot;
145
22.2k
            njs_prop_magic32(prop) = lhq.key_hash;
146
22.2k
            njs_prop_handler(prop) = njs_external_prop_handler;
147
148
22.2k
            if (external->u.object.prop_handler) {
149
0
                if (next->prop_handler) {
150
0
                    njs_internal_error(vm, "overwritten self prop_handler");
151
0
                    return NJS_ERROR;
152
0
                }
153
154
0
                next->writable = external->u.object.writable;
155
0
                next->configurable = external->u.object.configurable;
156
0
                next->enumerable = external->u.object.enumerable;
157
158
0
                next->prop_handler = external->u.object.prop_handler;
159
0
                next->magic32 = external->u.object.magic32;
160
0
            }
161
162
22.2k
            if (external->u.object.keys) {
163
0
                if (next->keys) {
164
0
                    njs_internal_error(vm, "overwritten self keys");
165
0
                    return NJS_ERROR;
166
0
                }
167
168
0
                next->keys = external->u.object.keys;
169
0
            }
170
171
22.2k
            break;
172
1.18M
        }
173
174
1.18M
        external++;
175
1.18M
    }
176
177
133k
    return NJS_OK;
178
179
0
memory_error:
180
181
0
    njs_memory_error(vm);
182
183
0
    return NJS_ERROR;
184
133k
}
185
186
187
static njs_int_t
188
njs_external_prop_handler(njs_vm_t *vm, njs_object_prop_t *self,
189
    uint32_t atom_id, njs_value_t *value, njs_value_t *setval,
190
    njs_value_t *retval)
191
6
{
192
6
    njs_int_t            ret;
193
6
    njs_object_prop_t    *prop;
194
6
    njs_external_ptr_t   external;
195
6
    njs_object_value_t   *ov;
196
6
    njs_exotic_slots_t   *slots;
197
6
    njs_flathsh_query_t  lhq;
198
199
6
    if (njs_slow_path(retval == NULL)) {
200
0
        return NJS_DECLINED;
201
0
    }
202
203
6
    slots = NULL;
204
205
6
    if (njs_slow_path(setval != NULL)) {
206
0
        *retval = *setval;
207
208
6
    } else {
209
6
        ov = njs_object_value_alloc(vm, NJS_OBJ_TYPE_OBJECT, 0, NULL);
210
6
        if (njs_slow_path(ov == NULL)) {
211
0
            return NJS_ERROR;
212
0
        }
213
214
6
        slots = njs_object(value)->slots + njs_prop_magic16(self);
215
216
6
        ov->object.shared_hash = slots->external_shared_hash;
217
6
        ov->object.slots = slots;
218
219
6
        external = njs_vm_external(vm, NJS_PROTO_ID_ANY, value);
220
221
6
        njs_set_data(&ov->value, external, njs_value_external_tag(value));
222
6
        njs_set_object_value(retval, ov);
223
6
    }
224
225
6
    lhq.key_hash = atom_id;
226
6
    lhq.replace = 1;
227
6
    lhq.pool = vm->mem_pool;
228
6
    lhq.proto = &njs_object_hash_proto;
229
230
6
    ret = njs_flathsh_unique_insert(njs_object_hash(value), &lhq);
231
6
    if (njs_slow_path(ret != NJS_OK)) {
232
0
        njs_internal_error(vm, "lvlhsh insert/replace failed");
233
0
        return NJS_ERROR;
234
0
    }
235
236
6
    prop = lhq.value;
237
238
6
    prop->type = NJS_PROPERTY;
239
6
    prop->enumerable = self->enumerable;
240
6
    prop->configurable = self->configurable;
241
6
    prop->writable = self->writable;
242
243
6
    prop->u.value = *retval;
244
245
6
    return NJS_OK;
246
6
}
247
248
249
static njs_uint_t
250
njs_external_protos(const njs_external_t *external, njs_uint_t size)
251
133k
{
252
133k
    njs_uint_t  n;
253
254
133k
    n = 1;
255
256
1.32M
    while (size != 0) {
257
1.18M
        if ((external->flags & 3) == NJS_EXTERN_OBJECT) {
258
22.2k
            n += njs_external_protos(external->u.object.properties,
259
22.2k
                                     external->u.object.nproperties);
260
22.2k
        }
261
262
1.18M
        size--;
263
1.18M
        external++;
264
1.18M
    }
265
266
133k
    return n;
267
133k
}
268
269
270
njs_int_t
271
njs_vm_external_prototype(njs_vm_t *vm, const njs_external_t *definition,
272
    njs_uint_t n)
273
111k
{
274
111k
    njs_arr_t   *protos, **pr;
275
111k
    njs_int_t   ret;
276
111k
    njs_uint_t  size;
277
278
111k
    size = njs_external_protos(definition, n) + 1;
279
280
111k
    protos = njs_arr_create(vm->mem_pool, size, sizeof(njs_exotic_slots_t));
281
111k
    if (njs_slow_path(protos == NULL)) {
282
0
        njs_memory_error(vm);
283
0
        return -1;
284
0
    }
285
286
111k
    ret = njs_external_add(vm, protos, definition, n);
287
111k
    if (njs_slow_path(ret != NJS_OK)) {
288
0
        njs_internal_error(vm, "njs_vm_external_add() failed");
289
0
        return -1;
290
0
    }
291
292
111k
    if (vm->protos == NULL) {
293
7.42k
        vm->protos = njs_arr_create(vm->mem_pool, 4, sizeof(njs_arr_t *));
294
7.42k
        if (njs_slow_path(vm->protos == NULL)) {
295
0
            return -1;
296
0
        }
297
7.42k
    }
298
299
111k
    pr = njs_arr_add(vm->protos);
300
111k
    if (njs_slow_path(pr == NULL)) {
301
0
        return -1;
302
0
    }
303
304
111k
    *pr = protos;
305
306
111k
    return vm->protos->items - 1;
307
111k
}
308
309
310
static njs_int_t
311
njs_vm_external_constructor_handler(njs_vm_t *vm, njs_object_prop_t *prop,
312
    uint32_t atom_id, njs_value_t *value, njs_value_t *setval,
313
    njs_value_t *retval)
314
0
{
315
0
    njs_set_function(retval, &njs_vm_ctor(vm, njs_prop_magic32(prop)));
316
317
0
    return NJS_OK;
318
0
}
319
320
321
njs_int_t
322
njs_vm_external_constructor(njs_vm_t *vm, const njs_str_t *name,
323
    const njs_function_native_t native, const njs_external_t *ctor_props,
324
    njs_uint_t ctor_nprops, const njs_external_t *proto_props,
325
    njs_uint_t proto_nprops)
326
0
{
327
0
    njs_int_t               ret, index, proto_id;
328
0
    njs_arr_t               **pprotos;
329
0
    njs_function_t          *constructor;
330
0
    njs_exotic_slots_t      *slots;
331
0
    njs_object_prototype_t  *prototype;
332
333
0
    index = njs_vm_ctor_push(vm);
334
0
    if (njs_slow_path(index < 0)) {
335
0
        njs_internal_error(vm, "njs_vm_ctor_push() failed");
336
0
        return -1;
337
0
    }
338
339
0
    proto_id = njs_vm_external_prototype(vm, proto_props, proto_nprops);
340
0
    if (njs_slow_path(proto_id < 0)) {
341
0
        njs_internal_error(vm, "njs_vm_external_prototype(proto_props) failed");
342
0
        return -1;
343
0
    }
344
345
0
    prototype = njs_shared_prototype(vm->shared, index);
346
0
    njs_memzero(prototype, sizeof(njs_object_prototype_t));
347
0
    prototype->object.type = NJS_OBJECT;
348
0
    prototype->object.extensible = 1;
349
350
0
    pprotos = njs_arr_item(vm->protos, proto_id);
351
0
    slots = (*pprotos)->start;
352
0
    prototype->object.shared_hash = slots->external_shared_hash;
353
354
0
    proto_id = njs_vm_external_prototype(vm, ctor_props, ctor_nprops);
355
0
    if (njs_slow_path(proto_id < 0)) {
356
0
        njs_internal_error(vm, "njs_vm_external_prototype(ctor_props) failed");
357
0
        return -1;
358
0
    }
359
360
0
    constructor = njs_shared_ctor(vm->shared, index);
361
0
    njs_memzero(constructor, sizeof(njs_function_t));
362
0
    constructor->object.type = NJS_FUNCTION;
363
0
    constructor->u.native = native;
364
0
    constructor->magic8 = index;
365
0
    constructor->native = 1;
366
0
    constructor->ctor = 1;
367
368
0
    pprotos = njs_arr_item(vm->protos, proto_id);
369
0
    slots = (*pprotos)->start;
370
0
    constructor->object.shared_hash = slots->external_shared_hash;
371
372
0
    ret = njs_vm_bind_handler(vm, name, njs_vm_external_constructor_handler, 0,
373
0
                              index, 1);
374
0
    if (njs_slow_path(ret != NJS_OK)) {
375
0
        return NJS_ERROR;
376
0
    }
377
378
0
    return index;
379
0
}
380
381
382
njs_int_t
383
njs_vm_external_create(njs_vm_t *vm, njs_value_t *value, njs_int_t proto_id,
384
    njs_external_ptr_t external, njs_bool_t shared)
385
52.0k
{
386
52.0k
    njs_arr_t           **pprotos;
387
52.0k
    njs_object_value_t  *ov;
388
52.0k
    njs_exotic_slots_t  *slots;
389
390
52.0k
    if (vm->protos == NULL || (njs_int_t) vm->protos->items <= proto_id) {
391
0
        return NJS_ERROR;
392
0
    }
393
394
52.0k
    ov = njs_object_value_alloc(vm, NJS_OBJ_TYPE_OBJECT, 0, NULL);
395
52.0k
    if (njs_slow_path(ov == NULL)) {
396
0
        return NJS_ERROR;
397
0
    }
398
399
52.0k
    pprotos = njs_arr_item(vm->protos, proto_id);
400
52.0k
    slots = (*pprotos)->start;
401
402
52.0k
    ov->object.shared_hash = slots->external_shared_hash;
403
52.0k
    ov->object.shared = shared;
404
52.0k
    ov->object.slots = slots;
405
406
52.0k
    njs_set_object_value(value, ov);
407
52.0k
    njs_set_data(&ov->value, external, njs_make_tag(proto_id));
408
409
52.0k
    return NJS_OK;
410
52.0k
}
411
412
413
njs_external_ptr_t
414
njs_vm_external(njs_vm_t *vm, njs_int_t proto_id, const njs_value_t *value)
415
6
{
416
6
    njs_external_ptr_t  external;
417
418
6
    if (njs_fast_path(njs_is_object_data(value, njs_make_tag(proto_id)))) {
419
6
        external = njs_object_data(value);
420
6
        if (external == NULL) {
421
6
            external = vm->external;
422
6
        }
423
424
6
        return external;
425
6
    }
426
427
0
    return NULL;
428
6
}
429
430
431
njs_int_t
432
njs_value_external_tag(const njs_value_t *value)
433
6
{
434
6
    if (njs_is_object_data(value, njs_make_tag(NJS_PROTO_ID_ANY))) {
435
6
        return njs_object_value(value)->data.magic32;
436
6
    }
437
438
0
    return -1;
439
6
}
440
441
442
njs_int_t
443
njs_external_property(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
444
    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
445
0
{
446
0
    char        *p;
447
0
    njs_int_t   i;
448
0
    njs_uint_t  ui;
449
450
0
    p = njs_vm_external(vm, NJS_PROTO_ID_ANY, value);
451
0
    if (p == NULL) {
452
0
        njs_value_undefined_set(retval);
453
0
        return NJS_DECLINED;
454
0
    }
455
456
0
    switch (njs_vm_prop_magic16(prop)) {
457
0
    case NJS_EXTERN_TYPE_INT:
458
0
        i = *(njs_int_t *) (p + njs_vm_prop_magic32(prop));
459
0
        njs_value_number_set(retval, i);
460
0
        break;
461
462
0
    case NJS_EXTERN_TYPE_UINT:
463
0
        ui = *(njs_uint_t *) (p + njs_vm_prop_magic32(prop));
464
0
        njs_value_number_set(retval, ui);
465
0
        break;
466
467
0
    case NJS_EXTERN_TYPE_VALUE:
468
0
    default:
469
0
        njs_value_assign(retval,
470
0
                         (njs_value_t *) (p + njs_vm_prop_magic32(prop)));
471
472
0
    }
473
474
0
    return NJS_OK;
475
0
}