1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  *
6  * njs public header.
7  */
8 
9 #ifndef _NJS_H_INCLUDED_
10 #define _NJS_H_INCLUDED_
11 
12 #include <njs_auto_config.h>
13 
14 #define NJS_VERSION                 "0.7.1"
15 
16 
17 #include <unistd.h>                 /* STDOUT_FILENO, STDERR_FILENO */
18 #include <njs_types.h>
19 #include <njs_clang.h>
20 #include <njs_str.h>
21 #include <njs_unicode.h>
22 #include <njs_utf8.h>
23 #include <njs_mp.h>
24 #include <njs_chb.h>
25 #include <njs_lvlhsh.h>
26 #include <njs_sprintf.h>
27 
28 
29 typedef uintptr_t                   njs_index_t;
30 typedef struct njs_vm_s             njs_vm_t;
31 typedef union  njs_value_s          njs_value_t;
32 typedef struct njs_function_s       njs_function_t;
33 typedef struct njs_vm_shared_s      njs_vm_shared_t;
34 typedef struct njs_object_prop_s    njs_object_prop_t;
35 typedef struct njs_external_s       njs_external_t;
36 
37 /*
38  * njs_opaque_value_t is the external storage type for native njs_value_t type.
39  * sizeof(njs_opaque_value_t) == sizeof(njs_value_t).
40  */
41 
42 typedef struct {
43     uint64_t                        filler[2];
44 } njs_opaque_value_t;
45 
46 
47 /* sizeof(njs_value_t) is 16 bytes. */
48 #define njs_argument(args, n)                                                 \
49     (njs_value_t *) ((u_char *) args + (n) * 16)
50 
51 
52 extern const njs_value_t            njs_value_undefined;
53 
54 #define njs_arg(args, nargs, n)                                               \
55     ((n < nargs) ? njs_argument(args, n)                                      \
56                  : (njs_value_t *) &njs_value_undefined)
57 
58 #define njs_value_assign(dst, src)                                            \
59     memcpy(dst, src, sizeof(njs_opaque_value_t))
60 
61 #define njs_value_arg(val) ((njs_value_t *) val)
62 
63 #define njs_lvalue_arg(lvalue, args, nargs, n)                                \
64     ((n < nargs) ? njs_argument(args, n)                                      \
65                  : (njs_value_assign(lvalue, &njs_value_undefined), lvalue))
66 
67 #define njs_vm_error(vm, fmt, ...)                                            \
68     njs_vm_value_error_set(vm, njs_vm_retval(vm), fmt, ##__VA_ARGS__)
69 
70 
71 /*
72  * njs_prop_handler_t operates as a property getter/setter or delete handler.
73  * - retval != NULL && setval == NULL - GET context.
74  * - retval != NULL && setval != NULL - SET context.
75  * - retval == NULL - DELETE context.
76  *
77  * njs_prop_handler_t is expected to return:
78  *   NJS_OK - handler executed successfully;
79  *   NJS_DECLINED - handler was applied to inappropriate object, retval
80  *   contains undefined value;
81  *   NJS_ERROR - some error, vm->retval contains appropriate exception.
82  */
83 typedef njs_int_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_object_prop_t *prop,
84     njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
85 typedef njs_int_t (*njs_exotic_keys_t)(njs_vm_t *vm, njs_value_t *value,
86     njs_value_t *retval);
87 typedef njs_int_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args,
88     njs_uint_t nargs, njs_index_t magic8);
89 
90 
91 typedef enum {
92     NJS_SYMBOL_INVALID,
93     NJS_SYMBOL_ASYNC_ITERATOR,
94     NJS_SYMBOL_HAS_INSTANCE,
95     NJS_SYMBOL_IS_CONCAT_SPREADABLE,
96     NJS_SYMBOL_ITERATOR,
97     NJS_SYMBOL_MATCH,
98     NJS_SYMBOL_MATCH_ALL,
99     NJS_SYMBOL_REPLACE,
100     NJS_SYMBOL_SEARCH,
101     NJS_SYMBOL_SPECIES,
102     NJS_SYMBOL_SPLIT,
103     NJS_SYMBOL_TO_PRIMITIVE,
104     NJS_SYMBOL_TO_STRING_TAG,
105     NJS_SYMBOL_UNSCOPABLES,
106     NJS_SYMBOL_KNOWN_MAX,
107 } njs_wellknown_symbol_t;
108 
109 
110 typedef enum {
111     NJS_EXTERN_PROPERTY = 0,
112     NJS_EXTERN_METHOD = 1,
113     NJS_EXTERN_OBJECT = 2,
114     NJS_EXTERN_SYMBOL = 4,
115 } njs_extern_flag_t;
116 
117 
118 struct njs_external_s {
119     njs_extern_flag_t               flags;
120 
121     union {
122         njs_str_t                   string;
123         uint32_t                    symbol;
124     } name;
125 
126     unsigned                        writable;
127     unsigned                        configurable;
128     unsigned                        enumerable;
129 
130     union {
131         struct {
132             const char              value[15]; /* NJS_STRING_SHORT + 1. */
133             njs_prop_handler_t      handler;
134             uint32_t                magic32;
135         } property;
136 
137         struct {
138             njs_function_native_t   native;
139             uint8_t                 magic8;
140             uint8_t                 ctor;
141         } method;
142 
143         struct {
144             njs_external_t          *properties;
145             njs_uint_t              nproperties;
146 
147             unsigned                writable;
148             unsigned                configurable;
149             unsigned                enumerable;
150             njs_prop_handler_t      prop_handler;
151             uint32_t                magic32;
152             njs_exotic_keys_t       keys;
153         } object;
154     } u;
155 };
156 
157 
158 /*
159  * NJS and event loops.
160  *
161  * njs_vm_ops_t callbacks are used to interact with the event loop environment.
162  *
163  * Functions get an external object as the first argument. The external
164  * object is provided as the third argument to njs_vm_clone().
165  *
166  * The callbacks are expected to return to the VM the unique id of an
167  * underlying event.  This id will be passed as the second argument to
168  * njs_event_destructor() at the moment the VM wants to destroy it.
169  *
170  * When an underlying events fires njs_vm_post_event() should be invoked with
171  * the value provided as vm_event.
172  *
173  * The events posted by njs_vm_post_event() are processed as soon as
174  * njs_vm_run() is invoked. njs_vm_run() returns NJS_AGAIN until pending events
175  * are present.
176  */
177 
178 typedef void *                      njs_vm_event_t;
179 typedef void *                      njs_host_event_t;
180 typedef void *                      njs_external_ptr_t;
181 
182 typedef njs_host_event_t (*njs_set_timer_t)(njs_external_ptr_t external,
183     uint64_t delay, njs_vm_event_t vm_event);
184 typedef void (*njs_event_destructor_t)(njs_external_ptr_t external,
185     njs_host_event_t event);
186 
187 
188 typedef struct {
189     njs_set_timer_t                 set_timer;
190     njs_event_destructor_t          clear_timer;
191 } njs_vm_ops_t;
192 
193 
194 typedef struct {
195     size_t                          size;
196     uintptr_t                       *values;
197 } njs_vm_meta_t;
198 
199 
200 typedef njs_int_t (*njs_addon_init_pt)(njs_vm_t *vm);
201 
202 typedef struct {
203     njs_str_t                       name;
204     njs_addon_init_pt               init;
205 } njs_module_t;
206 
207 
208 typedef struct {
209     njs_external_ptr_t              external;
210     njs_vm_shared_t                 *shared;
211     njs_vm_ops_t                    *ops;
212     njs_vm_meta_t                   *metas;
213     njs_module_t                    **addons;
214     njs_str_t                       file;
215 
216     char                            **argv;
217     njs_uint_t                      argc;
218 
219 #define NJS_VM_OPT_UNHANDLED_REJECTION_IGNORE   0
220 #define NJS_VM_OPT_UNHANDLED_REJECTION_THROW    1
221 
222 /*
223  * interactive  - enables "interactive" mode.
224  *  (REPL). Allows starting parent VM without cloning.
225  * disassemble   - enables disassemble.
226  * backtrace     - enables backtraces.
227  * quiet         - removes filenames from backtraces. To produce comparable
228     test262 diffs.
229  * sandbox       - "sandbox" mode. Disables file access.
230  * unsafe        - enables unsafe language features:
231  *   - Function constructors.
232  * module        - ES6 "module" mode. Script mode is default.
233  * ast           - print AST.
234  * unhandled_rejection IGNORE | THROW - tracks unhandled promise rejections:
235  *   - throwing inside a Promise without a catch block.
236  *   - throwing inside in a finally or catch block.
237  */
238     uint8_t                         interactive;     /* 1 bit */
239     uint8_t                         trailer;         /* 1 bit */
240     uint8_t                         init;            /* 1 bit */
241     uint8_t                         disassemble;     /* 1 bit */
242     uint8_t                         backtrace;       /* 1 bit */
243     uint8_t                         quiet;           /* 1 bit */
244     uint8_t                         sandbox;         /* 1 bit */
245     uint8_t                         unsafe;          /* 1 bit */
246     uint8_t                         module;          /* 1 bit */
247     uint8_t                         ast;             /* 1 bit */
248     uint8_t                         unhandled_rejection;
249 } njs_vm_opt_t;
250 
251 
252 NJS_EXPORT void njs_vm_opt_init(njs_vm_opt_t *options);
253 NJS_EXPORT njs_vm_t *njs_vm_create(njs_vm_opt_t *options);
254 NJS_EXPORT void njs_vm_destroy(njs_vm_t *vm);
255 
256 NJS_EXPORT njs_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end);
257 NJS_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external);
258 
259 NJS_EXPORT njs_vm_event_t njs_vm_add_event(njs_vm_t *vm,
260     njs_function_t *function, njs_uint_t once, njs_host_event_t host_ev,
261     njs_event_destructor_t destructor);
262 NJS_EXPORT void njs_vm_del_event(njs_vm_t *vm, njs_vm_event_t vm_event);
263 NJS_EXPORT njs_int_t njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event,
264     const njs_value_t *args, njs_uint_t nargs);
265 
266 /*
267  * Returns 1 if async events are present.
268  */
269 NJS_EXPORT njs_int_t njs_vm_waiting(njs_vm_t *vm);
270 
271 /*
272  * Returns 1 if posted events are ready to be executed.
273  */
274 NJS_EXPORT njs_int_t njs_vm_posted(njs_vm_t *vm);
275 
276 #define njs_vm_pending(vm)  (njs_vm_waiting(vm) || njs_vm_posted(vm))
277 
278 #define njs_vm_unhandled_rejection(vm)                                         \
279     ((vm)->options.unhandled_rejection == NJS_VM_OPT_UNHANDLED_REJECTION_THROW \
280     && (vm)->promise_reason != NULL && (vm)->promise_reason->length != 0)
281 
282 /*
283  * Runs the specified function with provided arguments.
284  *  NJS_OK successful run.
285  *  NJS_ERROR some exception or internal error happens.
286  *
287  *  njs_vm_retval(vm) can be used to get the retval or exception value.
288  */
289 NJS_EXPORT njs_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function,
290     const njs_value_t *args, njs_uint_t nargs);
291 NJS_EXPORT njs_int_t njs_vm_invoke(njs_vm_t *vm, njs_function_t *function,
292     const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval);
293 
294 /*
295  * Runs posted events.
296  *  NJS_OK successfully processed all posted events, no more events.
297  *  NJS_AGAIN successfully processed all events, some posted events are
298  *    still pending.
299  *  NJS_ERROR some exception or internal error happens.
300  *    njs_vm_retval(vm) can be used to get the retval or exception value.
301  */
302 NJS_EXPORT njs_int_t njs_vm_run(njs_vm_t *vm);
303 
304 /*
305  * Runs the global code.
306  *   NJS_OK successful run.
307  *   NJS_ERROR some exception or internal error happens.
308  *
309  *   njs_vm_retval(vm) can be used to get the retval or exception value.
310  */
311 NJS_EXPORT njs_int_t njs_vm_start(njs_vm_t *vm);
312 
313 NJS_EXPORT njs_int_t njs_vm_add_path(njs_vm_t *vm, const njs_str_t *path);
314 
315 #define NJS_PROTO_ID_ANY    (-1)
316 
317 NJS_EXPORT njs_int_t njs_vm_external_prototype(njs_vm_t *vm,
318     const njs_external_t *definition, njs_uint_t n);
319 NJS_EXPORT njs_int_t njs_vm_external_create(njs_vm_t *vm, njs_value_t *value,
320     njs_int_t proto_id, njs_external_ptr_t external, njs_bool_t shared);
321 NJS_EXPORT njs_external_ptr_t njs_vm_external(njs_vm_t *vm,
322     njs_int_t proto_id, const njs_value_t *value);
323 NJS_EXPORT uintptr_t njs_vm_meta(njs_vm_t *vm, njs_uint_t index);
324 
325 NJS_EXPORT njs_function_t *njs_vm_function_alloc(njs_vm_t *vm,
326     njs_function_native_t native);
327 
328 NJS_EXPORT void njs_disassembler(njs_vm_t *vm);
329 
330 NJS_EXPORT njs_int_t njs_vm_bind(njs_vm_t *vm, const njs_str_t *var_name,
331     const njs_value_t *value, njs_bool_t shared);
332 NJS_EXPORT njs_int_t njs_vm_value(njs_vm_t *vm, const njs_str_t *path,
333     njs_value_t *retval);
334 NJS_EXPORT njs_function_t *njs_vm_function(njs_vm_t *vm, const njs_str_t *name);
335 
336 NJS_EXPORT njs_value_t *njs_vm_retval(njs_vm_t *vm);
337 NJS_EXPORT void njs_vm_retval_set(njs_vm_t *vm, const njs_value_t *value);
338 NJS_EXPORT njs_mp_t *njs_vm_memory_pool(njs_vm_t *vm);
339 
340 /*  Gets string value, no copy. */
341 NJS_EXPORT void njs_value_string_get(njs_value_t *value, njs_str_t *dst);
342 /*
343  * Sets a byte string value.
344  *   start data is not copied and should not be freed.
345  */
346 NJS_EXPORT njs_int_t njs_vm_value_string_set(njs_vm_t *vm, njs_value_t *value,
347     const u_char *start, uint32_t size);
348 NJS_EXPORT u_char *njs_vm_value_string_alloc(njs_vm_t *vm, njs_value_t *value,
349     uint32_t size);
350 NJS_EXPORT njs_int_t njs_vm_value_string_copy(njs_vm_t *vm, njs_str_t *retval,
351     njs_value_t *value, uintptr_t *next);
352 
353 NJS_EXPORT njs_int_t njs_vm_value_array_buffer_set(njs_vm_t *vm,
354     njs_value_t *value, const u_char *start, uint32_t size);
355 
356 /*
357  * Sets a Buffer value.
358  *   start data is not copied and should not be freed.
359  */
360 NJS_EXPORT njs_int_t njs_vm_value_buffer_set(njs_vm_t *vm, njs_value_t *value,
361     const u_char *start, uint32_t size);
362 
363 /*
364  * Converts a value to bytes.
365  */
366 NJS_EXPORT njs_int_t njs_vm_value_to_bytes(njs_vm_t *vm, njs_str_t *dst,
367     njs_value_t *src);
368 
369 /*
370  * Converts a value to string.
371  */
372 NJS_EXPORT njs_int_t njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst,
373     njs_value_t *src);
374 
375 /*
376  * Calls njs_vm_value_to_string(), if exception was thrown adds backtrace.
377  */
378 NJS_EXPORT njs_int_t njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst,
379     njs_value_t *src);
380 NJS_EXPORT njs_int_t njs_vm_retval_string(njs_vm_t *vm, njs_str_t *dst);
381 
382 NJS_EXPORT njs_int_t njs_vm_value_dump(njs_vm_t *vm, njs_str_t *dst,
383     njs_value_t *value, njs_uint_t console, njs_uint_t indent);
384 NJS_EXPORT njs_int_t njs_vm_retval_dump(njs_vm_t *vm, njs_str_t *dst,
385     njs_uint_t indent);
386 
387 NJS_EXPORT void njs_vm_value_error_set(njs_vm_t *vm, njs_value_t *value,
388     const char *fmt, ...);
389 NJS_EXPORT void njs_vm_memory_error(njs_vm_t *vm);
390 
391 NJS_EXPORT void njs_value_undefined_set(njs_value_t *value);
392 NJS_EXPORT void njs_value_null_set(njs_value_t *value);
393 NJS_EXPORT void njs_value_invalid_set(njs_value_t *value);
394 NJS_EXPORT void njs_value_boolean_set(njs_value_t *value, int yn);
395 NJS_EXPORT void njs_value_number_set(njs_value_t *value, double num);
396 
397 NJS_EXPORT uint8_t njs_value_bool(const njs_value_t *value);
398 NJS_EXPORT double njs_value_number(const njs_value_t *value);
399 NJS_EXPORT njs_function_t *njs_value_function(const njs_value_t *value);
400 NJS_EXPORT njs_int_t njs_value_external_tag(const njs_value_t *value);
401 
402 NJS_EXPORT uint16_t njs_vm_prop_magic16(njs_object_prop_t *prop);
403 NJS_EXPORT uint32_t njs_vm_prop_magic32(njs_object_prop_t *prop);
404 NJS_EXPORT njs_int_t njs_vm_prop_name(njs_vm_t *vm, njs_object_prop_t *prop,
405     njs_str_t *dst);
406 
407 NJS_EXPORT njs_int_t njs_value_is_null(const njs_value_t *value);
408 NJS_EXPORT njs_int_t njs_value_is_undefined(const njs_value_t *value);
409 NJS_EXPORT njs_int_t njs_value_is_null_or_undefined(const njs_value_t *value);
410 NJS_EXPORT njs_int_t njs_value_is_valid(const njs_value_t *value);
411 NJS_EXPORT njs_int_t njs_value_is_boolean(const njs_value_t *value);
412 NJS_EXPORT njs_int_t njs_value_is_number(const njs_value_t *value);
413 NJS_EXPORT njs_int_t njs_value_is_valid_number(const njs_value_t *value);
414 NJS_EXPORT njs_int_t njs_value_is_string(const njs_value_t *value);
415 NJS_EXPORT njs_int_t njs_value_is_object(const njs_value_t *value);
416 NJS_EXPORT njs_int_t njs_value_is_array(const njs_value_t *value);
417 NJS_EXPORT njs_int_t njs_value_is_function(const njs_value_t *value);
418 NJS_EXPORT njs_int_t njs_value_is_buffer(const njs_value_t *value);
419 
420 NJS_EXPORT njs_int_t njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval,
421     ...);
422 NJS_EXPORT njs_value_t *njs_vm_object_keys(njs_vm_t *vm, njs_value_t *value,
423     njs_value_t *retval);
424 NJS_EXPORT njs_value_t *njs_vm_object_prop(njs_vm_t *vm,
425     njs_value_t *value, const njs_str_t *key, njs_opaque_value_t *retval);
426 
427 NJS_EXPORT njs_int_t njs_vm_array_alloc(njs_vm_t *vm, njs_value_t *retval,
428     uint32_t spare);
429 NJS_EXPORT njs_int_t njs_vm_array_length(njs_vm_t *vm, njs_value_t *value,
430     int64_t *length);
431 NJS_EXPORT njs_value_t *njs_vm_array_start(njs_vm_t *vm, njs_value_t *value);
432 NJS_EXPORT njs_value_t *njs_vm_array_prop(njs_vm_t *vm,
433     njs_value_t *value, int64_t index, njs_opaque_value_t *retval);
434 NJS_EXPORT njs_value_t *njs_vm_array_push(njs_vm_t *vm, njs_value_t *value);
435 
436 NJS_EXPORT njs_int_t njs_vm_json_parse(njs_vm_t *vm, njs_value_t *args,
437     njs_uint_t nargs);
438 NJS_EXPORT njs_int_t njs_vm_json_stringify(njs_vm_t *vm, njs_value_t *args,
439     njs_uint_t nargs);
440 
441 NJS_EXPORT njs_int_t njs_vm_promise_create(njs_vm_t *vm, njs_value_t *retval,
442     njs_value_t *callbacks);
443 
444 
445 #endif /* _NJS_H_INCLUDED_ */
446