1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #ifndef _NJS_OBJECT_H_INCLUDED_
8 #define _NJS_OBJECT_H_INCLUDED_
9 
10 
11 typedef enum {
12     NJS_OBJECT_PROP_DESCRIPTOR,
13     NJS_OBJECT_PROP_GETTER,
14     NJS_OBJECT_PROP_SETTER,
15 } njs_object_prop_define_t;
16 
17 
18 struct njs_object_init_s {
19     const njs_object_prop_t     *properties;
20     njs_uint_t                  items;
21 };
22 
23 
24 typedef struct njs_traverse_s  njs_traverse_t;
25 
26 struct njs_traverse_s {
27     struct njs_traverse_s      *parent;
28     njs_object_prop_t          *prop;
29 
30     njs_value_t                value;
31     njs_array_t                *keys;
32     int64_t                    index;
33 
34 #define NJS_TRAVERSE_MAX_DEPTH 32
35 };
36 
37 
38 typedef njs_int_t (*njs_object_traverse_cb_t)(njs_vm_t *vm,
39     njs_traverse_t *traverse, void *ctx);
40 
41 
42 njs_object_t *njs_object_alloc(njs_vm_t *vm);
43 njs_object_t *njs_object_value_copy(njs_vm_t *vm, njs_value_t *value);
44 njs_object_value_t *njs_object_value_alloc(njs_vm_t *vm, njs_uint_t index,
45     size_t extra,const njs_value_t *value);
46 njs_array_t *njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object,
47     njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
48 njs_array_t *njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object,
49     njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
50 njs_int_t njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx,
51     njs_object_traverse_cb_t cb);
52 njs_int_t njs_object_hash_create(njs_vm_t *vm, njs_lvlhsh_t *hash,
53     const njs_object_prop_t *prop, njs_uint_t n);
54 njs_int_t njs_primitive_prototype_get_proto(njs_vm_t *vm,
55     njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
56     njs_value_t *retval);
57 njs_int_t njs_object_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
58     njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
59 njs_value_t *njs_property_prototype_create(njs_vm_t *vm, njs_lvlhsh_t *hash,
60     njs_object_t *prototype);
61 njs_int_t njs_object_prototype_proto(njs_vm_t *vm, njs_object_prop_t *prop,
62     njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
63 njs_int_t njs_object_prototype_create_constructor(njs_vm_t *vm,
64     njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
65     njs_value_t *retval);
66 njs_value_t *njs_property_constructor_set(njs_vm_t *vm, njs_lvlhsh_t *hash,
67     njs_value_t *constructor);
68 njs_int_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
69     njs_uint_t nargs, njs_index_t unused);
70 njs_int_t njs_object_length(njs_vm_t *vm, njs_value_t *value, int64_t *dst);
71 
72 njs_int_t njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq);
73 njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name,
74     const njs_value_t *value, uint8_t attributes);
75 njs_int_t njs_object_property(njs_vm_t *vm, const njs_value_t *value,
76     njs_lvlhsh_query_t *lhq, njs_value_t *retval);
77 njs_object_prop_t *njs_object_property_add(njs_vm_t *vm, njs_value_t *object,
78     njs_value_t *key, njs_bool_t replace);
79 njs_int_t njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
80     njs_value_t *name, njs_value_t *value, njs_object_prop_define_t type);
81 njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
82     njs_value_t *value, njs_value_t *setval);
83 const char *njs_prop_type_string(njs_object_prop_type_t type);
84 njs_int_t njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init,
85     const njs_object_prop_t *base, njs_value_t *value, njs_value_t *retval);
86 
87 
88 njs_inline njs_bool_t
njs_is_data_descriptor(njs_object_prop_t * prop)89 njs_is_data_descriptor(njs_object_prop_t *prop)
90 {
91     return prop->writable != NJS_ATTRIBUTE_UNSET || njs_is_valid(&prop->value);
92 }
93 
94 
95 njs_inline njs_bool_t
njs_is_accessor_descriptor(njs_object_prop_t * prop)96 njs_is_accessor_descriptor(njs_object_prop_t *prop)
97 {
98     return njs_is_function_or_undefined(&prop->getter)
99            || njs_is_function_or_undefined(&prop->setter);
100 }
101 
102 
103 njs_inline njs_bool_t
njs_is_generic_descriptor(njs_object_prop_t * prop)104 njs_is_generic_descriptor(njs_object_prop_t *prop)
105 {
106     return !njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop);
107 }
108 
109 
110 njs_inline void
njs_object_property_key_set(njs_lvlhsh_query_t * lhq,const njs_value_t * key,uint32_t hash)111 njs_object_property_key_set(njs_lvlhsh_query_t *lhq, const njs_value_t *key,
112     uint32_t hash)
113 {
114     if (njs_is_symbol(key)) {
115 
116         lhq->key.length = 0;
117         lhq->key.start = NULL;
118         lhq->key_hash = njs_symbol_key(key);
119 
120     } else {
121 
122         /* string. */
123 
124         njs_string_get(key, &lhq->key);
125 
126         if (hash == 0) {
127             lhq->key_hash = njs_djb_hash(lhq->key.start, lhq->key.length);
128 
129         } else {
130             lhq->key_hash = hash;
131         }
132     }
133 }
134 
135 
136 njs_inline void
njs_object_property_init(njs_lvlhsh_query_t * lhq,const njs_value_t * key,uint32_t hash)137 njs_object_property_init(njs_lvlhsh_query_t *lhq, const njs_value_t *key,
138     uint32_t hash)
139 {
140     lhq->proto = &njs_object_hash_proto;
141 
142     njs_object_property_key_set(lhq, key, hash);
143 }
144 
145 
146 njs_inline njs_int_t
njs_primitive_value_to_key(njs_vm_t * vm,njs_value_t * dst,const njs_value_t * src)147 njs_primitive_value_to_key(njs_vm_t *vm, njs_value_t *dst,
148     const njs_value_t *src)
149 {
150     const njs_value_t  *value;
151 
152     switch (src->type) {
153 
154     case NJS_NULL:
155         value = &njs_string_null;
156         break;
157 
158     case NJS_UNDEFINED:
159         value = &njs_string_undefined;
160         break;
161 
162     case NJS_BOOLEAN:
163         value = njs_is_true(src) ? &njs_string_true : &njs_string_false;
164         break;
165 
166     case NJS_NUMBER:
167         return njs_number_to_string(vm, dst, src);
168 
169     case NJS_SYMBOL:
170     case NJS_STRING:
171         /* GC: njs_retain(src); */
172         value = src;
173         break;
174 
175     default:
176         return NJS_ERROR;
177     }
178 
179     *dst = *value;
180 
181     return NJS_OK;
182 }
183 
184 
185 njs_inline njs_int_t
njs_value_to_key(njs_vm_t * vm,njs_value_t * dst,njs_value_t * value)186 njs_value_to_key(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value)
187 {
188     njs_int_t    ret;
189     njs_value_t  primitive;
190 
191     if (njs_slow_path(!njs_is_primitive(value))) {
192         if (njs_slow_path(njs_is_object_symbol(value))) {
193             /* should fail */
194             value = njs_object_value(value);
195 
196         } else {
197             ret = njs_value_to_primitive(vm, &primitive, value, 1);
198             if (njs_slow_path(ret != NJS_OK)) {
199                 return ret;
200             }
201 
202             value = &primitive;
203         }
204     }
205 
206     return njs_primitive_value_to_key(vm, dst, value);
207 }
208 
209 
210 njs_inline njs_int_t
njs_key_string_get(njs_vm_t * vm,njs_value_t * key,njs_str_t * str)211 njs_key_string_get(njs_vm_t *vm, njs_value_t *key, njs_str_t *str)
212 {
213     njs_int_t  ret;
214 
215     if (njs_slow_path(njs_is_symbol(key))) {
216         ret = njs_symbol_descriptive_string(vm, key, key);
217         if (njs_slow_path(ret != NJS_OK)) {
218             return ret;
219         }
220     }
221 
222     njs_string_get(key, str);
223 
224     return NJS_OK;
225 }
226 
227 
228 njs_inline njs_int_t
njs_object_length_set(njs_vm_t * vm,njs_value_t * value,int64_t length)229 njs_object_length_set(njs_vm_t *vm, njs_value_t *value, int64_t length)
230 {
231     njs_value_t  index;
232 
233     static const njs_value_t  string_length = njs_string("length");
234 
235     njs_value_number_set(&index, length);
236 
237     return njs_value_property_set(vm, value, njs_value_arg(&string_length),
238                                   &index);
239 }
240 
241 
242 njs_inline njs_int_t
njs_object_string_tag(njs_vm_t * vm,njs_value_t * value,njs_value_t * tag)243 njs_object_string_tag(njs_vm_t *vm, njs_value_t *value, njs_value_t *tag)
244 {
245     njs_int_t  ret;
246 
247     static const njs_value_t  to_string_tag =
248                                 njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG);
249 
250     ret = njs_value_property(vm, value, njs_value_arg(&to_string_tag), tag);
251     if (njs_slow_path(ret != NJS_OK)) {
252         return ret;
253     }
254 
255     if (!njs_is_string(tag)) {
256         return NJS_DECLINED;
257     }
258 
259     return NJS_OK;
260 }
261 
262 
263 njs_inline njs_object_t *
_njs_object_proto_lookup(njs_object_t * proto,njs_value_type_t type)264 _njs_object_proto_lookup(njs_object_t *proto, njs_value_type_t type)
265 {
266     do {
267         if (njs_fast_path(proto->type == type)) {
268             break;
269         }
270 
271         proto = proto->__proto__;
272     } while (proto != NULL);
273 
274     return proto;
275 }
276 
277 
278 #define njs_object_proto_lookup(proto, vtype, ctype)                         \
279     (ctype *) _njs_object_proto_lookup(proto, vtype)
280 
281 
282 extern const njs_object_type_init_t  njs_obj_type_init;
283 
284 
285 #endif /* _NJS_OBJECT_H_INCLUDED_ */
286