-
Notifications
You must be signed in to change notification settings - Fork 177
/
Copy pathmjs_no_common.c
9981 lines (8741 loc) · 287 KB
/
mjs_no_common.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 2017 Cesanta Software Limited
* All rights reserved
*
* This software is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http://www.gnu.org/licenses/>.
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* Alternatively, you can license this software under a commercial
* license, as set out in <https://www.cesanta.com/license>.
*/
#include "mjs.h"
#ifdef MJS_MODULE_LINES
#line 1 "src/ffi/ffi.h"
#endif
#ifndef MJS_FFI_FFI_H_
#define MJS_FFI_FFI_H_
#include "common/platform.h"
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Maximum number of word-sized args to ffi-ed function. If at least one
* of the args is double, only 2 args are allowed.
*/
#define FFI_MAX_ARGS_CNT 6
typedef void(ffi_fn_t)(void);
typedef intptr_t ffi_word_t;
enum ffi_ctype {
FFI_CTYPE_WORD,
FFI_CTYPE_BOOL,
FFI_CTYPE_FLOAT,
FFI_CTYPE_DOUBLE,
};
struct ffi_arg {
enum ffi_ctype ctype;
union {
uint64_t i;
double d;
float f;
} v;
};
int ffi_call(ffi_fn_t *func, int nargs, struct ffi_arg *res,
struct ffi_arg *args);
void ffi_set_word(struct ffi_arg *arg, ffi_word_t v);
void ffi_set_bool(struct ffi_arg *arg, bool v);
void ffi_set_ptr(struct ffi_arg *arg, void *v);
void ffi_set_double(struct ffi_arg *arg, double v);
void ffi_set_float(struct ffi_arg *arg, float v);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_FFI_FFI_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_internal.h"
#endif
#ifndef MJS_INTERNAL_H_
#define MJS_INTERNAL_H_
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifndef FAST
#define FAST
#endif
#ifndef STATIC
#define STATIC
#endif
#ifndef ENDL
#define ENDL "\n"
#endif
#ifdef MJS_EXPOSE_PRIVATE
#define MJS_PRIVATE
#define MJS_EXTERN extern
#else
#define MJS_PRIVATE static
#define MJS_EXTERN static
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
#if !defined(WEAK)
#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32)
#define WEAK __attribute__((weak))
#else
#define WEAK
#endif
#endif
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
#include "common/cs_dbg.h"
#include "common/cs_file.h"
#include "common/mbuf.h"
#if defined(_WIN32) && _MSC_VER < 1700
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef __int64 int64_t;
typedef unsigned long uintptr_t;
#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ __FILE__ ":" STR(__LINE__)
// #define snprintf _snprintf
#define vsnprintf _vsnprintf
#define isnan(x) _isnan(x)
#define va_copy(x, y) (x) = (y)
#define CS_DEFINE_DIRENT
#include <windows.h>
#else
#if defined(__unix__) || defined(__APPLE__)
#include <dlfcn.h>
#endif
#endif
/*
* Number of bytes reserved for the jump offset initially. The most practical
* value is 1, but for testing it's useful to set it to 0 and to some large
* value as well (like, 4), to make sure that the code behaves correctly under
* all circumstances.
*/
#ifndef MJS_INIT_OFFSET_SIZE
#define MJS_INIT_OFFSET_SIZE 1
#endif
#endif /* MJS_INTERNAL_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_features.h"
#endif
#ifndef MJS_FEATURES_H_
#define MJS_FEATURES_H_
#if !defined(MJS_AGGRESSIVE_GC)
#define MJS_AGGRESSIVE_GC 0
#endif
#if !defined(MJS_MEMORY_STATS)
#define MJS_MEMORY_STATS 0
#endif
/*
* MJS_GENERATE_JSC: if enabled, and if mmapping is also enabled (CS_MMAP),
* then execution of any .js file will result in creation of a .jsc file with
* precompiled bcode, and this .jsc file will be mmapped, instead of keeping
* bcode in RAM.
*
* By default it's enabled (provided that CS_MMAP is defined)
*/
#if !defined(MJS_GENERATE_JSC)
#if defined(CS_MMAP)
#define MJS_GENERATE_JSC 1
#else
#define MJS_GENERATE_JSC 0
#endif
#endif
#endif /* MJS_FEATURES_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_core_public.h"
#endif
#ifndef MJS_CORE_PUBLIC_H_
#define MJS_CORE_PUBLIC_H_
#if !defined(_MSC_VER) || _MSC_VER >= 1700
#include <stdint.h>
#else
typedef unsigned __int64 uint64_t;
typedef int int32_t;
typedef unsigned char uint8_t;
#endif
#include <stdio.h>
#include <stddef.h>
/* Amalgamated: #include "mjs_features.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
#ifndef MJS_ENABLE_DEBUG
#define MJS_ENABLE_DEBUG 0
#endif
/*
* Double-precision floating-point number, IEEE 754
*
* 64 bit (8 bytes) in total
* 1 bit sign
* 11 bits exponent
* 52 bits mantissa
* 7 6 5 4 3 2 1 0
* seeeeeee|eeeemmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm
*
* If an exponent is all-1 and mantissa is all-0, then it is an INFINITY:
* 11111111|11110000|00000000|00000000|00000000|00000000|00000000|00000000
*
* If an exponent is all-1 and mantissa's MSB is 1, it is a quiet NaN:
* 11111111|11111000|00000000|00000000|00000000|00000000|00000000|00000000
*
* MJS NaN-packing:
* sign and exponent is 0xfff
* 4 bits specify type (tag), must be non-zero
* 48 bits specify value
*
* 11111111|1111tttt|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv
* NaN marker |type| 48-bit placeholder for values: pointers, strings
*
* On 64-bit platforms, pointers are really 48 bit only, so they can fit,
* provided they are sign extended
*/
typedef uint64_t mjs_val_t;
/* This if-0 is a dirty workaround to force etags to pick `struct mjs` */
#if 0
/* Opaque structure. MJS engine context. */
struct mjs {
/* ... */
};
#endif
struct mjs;
typedef enum mjs_err {
MJS_OK,
MJS_SYNTAX_ERROR,
MJS_REFERENCE_ERROR,
MJS_TYPE_ERROR,
MJS_OUT_OF_MEMORY,
MJS_INTERNAL_ERROR,
MJS_NOT_IMPLEMENTED_ERROR,
MJS_FILE_READ_ERROR,
MJS_BAD_ARGS_ERROR,
MJS_ERRS_CNT
} mjs_err_t;
struct mjs;
/* Create MJS instance */
struct mjs *mjs_create();
struct mjs_create_opts {
/* use non-default bytecode definition file, testing-only */
const struct bf_code *code;
};
/*
* Like `msj_create()`, but allows to customize initial MJS state, see `struct
* mjs_create_opts`.
*/
struct mjs *mjs_create_opt(struct mjs_create_opts opts);
/* Destroy MJS instance */
void mjs_destroy(struct mjs *mjs);
mjs_val_t mjs_get_global(struct mjs *mjs);
/*
* Tells the GC about an MJS value variable/field owned by C code.
*
* The user's C code should own mjs_val_t variables if the value's lifetime
* crosses any invocation of `mjs_exec()` and friends, including `mjs_call()`.
*
* The registration of the variable prevents the GC from mistakenly treat the
* object as garbage.
*
* User code should also explicitly disown the variables with `mjs_disown()`
* once it goes out of scope or the structure containing the mjs_val_t field is
* freed.
*
* Consider the following examples:
*
* Correct (owning is not necessary):
* ```c
* mjs_val_t res;
* mjs_exec(mjs, "....some script", &res);
* // ... use res somehow
*
* mjs_val_t res;
* mjs_exec(mjs, "....some script2", &res);
* // ... use new res somehow
* ```
*
* WRONG:
* ```c
* mjs_val_t res1;
* mjs_exec(mjs, "....some script", &res1);
*
* mjs_val_t res2;
* mjs_exec(mjs, "....some script2", &res2);
*
* // ... use res1 (WRONG!) and res2
* ```
*
* The code above is wrong, because after the second invocation of
* `mjs_exec()`, the value of `res1` is invalidated.
*
* Correct (res1 is owned)
* ```c
* mjs_val_t res1 = MJS_UNDEFINED;
* mjs_own(mjs, &res1);
* mjs_exec(mjs, "....some script", &res1);
*
* mjs_val_t res2 = MJS_UNDEFINED;
* mjs_exec(mjs, "....some script2", &res2);
*
* // ... use res1 and res2
* mjs_disown(mjs, &res1);
* ```
*
* NOTE that we explicly initialized `res1` to a valid value before owning it
* (in this case, the value is `MJS_UNDEFINED`). Owning an uninitialized
* variable is an undefined behaviour.
*
* Of course, it's not an error to own a variable even if it's not mandatory:
* e.g. in the last example we could own both `res1` and `res2`. Probably it
* would help us in the future, when we refactor the code so that `res2` has to
* be owned, and we could forget to do that.
*
* Also, if the user code has some C function called from MJS, and in this C
* function some MJS value (`mjs_val_t`) needs to be stored somewhere and to
* stay alive after the C function has returned, it also needs to be properly
* owned.
*/
void mjs_own(struct mjs *mjs, mjs_val_t *v);
/*
* Disowns the value previously owned by `mjs_own()`.
*
* Returns 1 if value is found, 0 otherwise.
*/
int mjs_disown(struct mjs *mjs, mjs_val_t *v);
mjs_err_t mjs_set_errorf(struct mjs *mjs, mjs_err_t err, const char *fmt, ...);
/*
* If there is no error message already set, then it's equal to
* `mjs_set_errorf()`.
*
* Otherwise, an old message gets prepended with the new one, followed by a
* colon. (the previously set error code is kept)
*/
mjs_err_t mjs_prepend_errorf(struct mjs *mjs, mjs_err_t err, const char *fmt,
...);
/*
* Print the last error details. If print_stack_trace is non-zero, also
* print stack trace. `msg` is the message which gets prepended to the actual
* error message, if it's NULL, then "MJS error" is used.
*/
void mjs_print_error(struct mjs *mjs, FILE *fp, const char *msg,
int print_stack_trace);
/*
* return a string representation of an error.
* the error string might be overwritten by calls to `mjs_set_errorf`.
*/
const char *mjs_strerror(struct mjs *mjs, enum mjs_err err);
/*
* Sets whether *.jsc files are generated when *.js file is executed. By
* default it's 0.
*
* If either `MJS_GENERATE_JSC` or `CS_MMAP` is off, then this function has no
* effect.
*/
void mjs_set_generate_jsc(struct mjs *mjs, int generate_jsc);
/*
* When invoked from a cfunction, returns number of arguments passed to the
* current JS function call.
*/
int mjs_nargs(struct mjs *mjs);
/*
* When invoked from a cfunction, returns n-th argument to the current JS
* function call.
*/
mjs_val_t mjs_arg(struct mjs *mjs, int n);
/*
* Sets return value for the current JS function call.
*/
void mjs_return(struct mjs *mjs, mjs_val_t v);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_CORE_PUBLIC_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_array_public.h"
#endif
/*
* === Arrays
*/
#ifndef MJS_ARRAY_PUBLIC_H_
#define MJS_ARRAY_PUBLIC_H_
/* Amalgamated: #include "mjs_core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/* Make an empty array object */
mjs_val_t mjs_mk_array(struct mjs *mjs);
/* Returns length on an array. If `arr` is not an array, 0 is returned. */
unsigned long mjs_array_length(struct mjs *mjs, mjs_val_t arr);
/* Insert value `v` in array `arr` at the end of the array. */
mjs_err_t mjs_array_push(struct mjs *mjs, mjs_val_t arr, mjs_val_t v);
/*
* Return array member at index `index`. If `index` is out of bounds, undefined
* is returned.
*/
mjs_val_t mjs_array_get(struct mjs *, mjs_val_t arr, unsigned long index);
/* Insert value `v` into `arr` at index `index`. */
mjs_err_t mjs_array_set(struct mjs *mjs, mjs_val_t arr, unsigned long index,
mjs_val_t v);
/* Returns true if the given value is an array */
int mjs_is_array(mjs_val_t v);
/* Delete value in array `arr` at index `index`, if it exists. */
void mjs_array_del(struct mjs *mjs, mjs_val_t arr, unsigned long index);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_ARRAY_PUBLIC_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_array.h"
#endif
#ifndef MJS_ARRAY_H_
#define MJS_ARRAY_H_
/* Amalgamated: #include "mjs_internal.h" */
/* Amalgamated: #include "mjs_array_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
MJS_PRIVATE mjs_val_t
mjs_array_get2(struct mjs *mjs, mjs_val_t arr, unsigned long index, int *has);
MJS_PRIVATE void mjs_array_splice(struct mjs *mjs);
MJS_PRIVATE void mjs_array_push_internal(struct mjs *mjs);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_ARRAY_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_ffi_public.h"
#endif
#ifndef MJS_FFI_PUBLIC_H_
#define MJS_FFI_PUBLIC_H_
/* Amalgamated: #include "mjs_core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
enum mjs_ffi_ctype {
MJS_FFI_CTYPE_NONE,
MJS_FFI_CTYPE_USERDATA,
MJS_FFI_CTYPE_CALLBACK,
MJS_FFI_CTYPE_INT,
MJS_FFI_CTYPE_BOOL,
MJS_FFI_CTYPE_DOUBLE,
MJS_FFI_CTYPE_FLOAT,
MJS_FFI_CTYPE_CHAR_PTR,
MJS_FFI_CTYPE_VOID_PTR,
MJS_FFI_CTYPE_STRUCT_MG_STR_PTR,
MJS_FFI_CTYPE_STRUCT_MG_STR,
MJS_FFI_CTYPE_INVALID,
};
typedef void *(mjs_ffi_resolver_t)(void *handle, const char *symbol);
void mjs_set_ffi_resolver(struct mjs *mjs, mjs_ffi_resolver_t *dlsym);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_FFI_PUBLIC_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_ffi.h"
#endif
#ifndef MJS_FFI_H_
#define MJS_FFI_H_
/* Amalgamated: #include "ffi/ffi.h" */
/* Amalgamated: #include "mjs_ffi_public.h" */
/* Amalgamated: #include "mjs_internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
mjs_ffi_resolver_t dlsym;
#define MJS_CB_ARGS_MAX_CNT 6
#define MJS_CB_SIGNATURE_MAX_SIZE (MJS_CB_ARGS_MAX_CNT + 1 /* return type */)
typedef uint8_t mjs_ffi_ctype_t;
enum ffi_sig_type {
FFI_SIG_FUNC,
FFI_SIG_CALLBACK,
};
/*
* Parsed FFI signature
*/
struct mjs_ffi_sig {
/*
* Callback signature, corresponds to the arg of type MJS_FFI_CTYPE_CALLBACK
* TODO(dfrank): probably we'll need to support multiple callback/userdata
* pairs
*
* NOTE(dfrank): instances of this structure are grouped into GC arenas and
* managed by GC, and for the GC mark to work, the first element should be
* a pointer (so that the two LSBs are not used).
*/
struct mjs_ffi_sig *cb_sig;
/*
* The first item is the return value type (for `void`, `MJS_FFI_CTYPE_NONE`
* is used); the rest are arguments. If some argument is
* `MJS_FFI_CTYPE_NONE`, it means that there are no more arguments.
*/
mjs_ffi_ctype_t val_types[MJS_CB_SIGNATURE_MAX_SIZE];
/*
* Function to call. If `is_callback` is not set, then it's the function
* obtained by dlsym; otherwise it's a pointer to the appropriate callback
* implementation.
*/
ffi_fn_t *fn;
/* Number of arguments in the signature */
int8_t args_cnt;
/*
* If set, then the signature represents the callback (as opposed to a normal
* function), and `fn` points to the suitable callback implementation.
*/
unsigned is_callback : 1;
unsigned is_valid : 1;
};
typedef struct mjs_ffi_sig mjs_ffi_sig_t;
/* Initialize new FFI signature */
MJS_PRIVATE void mjs_ffi_sig_init(mjs_ffi_sig_t *sig);
/* Copy existing FFI signature */
MJS_PRIVATE void mjs_ffi_sig_copy(mjs_ffi_sig_t *to, const mjs_ffi_sig_t *from);
/* Free FFI signature. NOTE: the pointer `sig` itself is not freed */
MJS_PRIVATE void mjs_ffi_sig_free(mjs_ffi_sig_t *sig);
/*
* Creates a new FFI signature from the GC arena, and return mjs_val_t which
* wraps it.
*/
MJS_PRIVATE mjs_val_t mjs_mk_ffi_sig(struct mjs *mjs);
/*
* Checks whether the given value is a FFI signature.
*/
MJS_PRIVATE int mjs_is_ffi_sig(mjs_val_t v);
/*
* Wraps FFI signature structure into mjs_val_t value.
*/
MJS_PRIVATE mjs_val_t mjs_ffi_sig_to_value(struct mjs_ffi_sig *psig);
/*
* Extracts a pointer to the FFI signature struct from the mjs_val_t value.
*/
MJS_PRIVATE struct mjs_ffi_sig *mjs_get_ffi_sig_struct(mjs_val_t v);
/*
* A wrapper for mjs_ffi_sig_free() suitable to use as a GC cell destructor.
*/
MJS_PRIVATE void mjs_ffi_sig_destructor(struct mjs *mjs, void *psig);
MJS_PRIVATE int mjs_ffi_sig_set_val_type(mjs_ffi_sig_t *sig, int idx,
mjs_ffi_ctype_t type);
MJS_PRIVATE int mjs_ffi_sig_validate(struct mjs *mjs, mjs_ffi_sig_t *sig,
enum ffi_sig_type sig_type);
MJS_PRIVATE int mjs_ffi_is_regular_word(mjs_ffi_ctype_t type);
MJS_PRIVATE int mjs_ffi_is_regular_word_or_void(mjs_ffi_ctype_t type);
struct mjs_ffi_cb_args {
struct mjs_ffi_cb_args *next;
struct mjs *mjs;
mjs_ffi_sig_t sig;
mjs_val_t func;
mjs_val_t userdata;
};
typedef struct mjs_ffi_cb_args ffi_cb_args_t;
/*
* cfunction:
* Parses the FFI signature string and returns a value wrapping mjs_ffi_sig_t.
*/
MJS_PRIVATE mjs_err_t mjs_ffi_call(struct mjs *mjs);
/*
* cfunction:
* Performs the FFI signature call.
*/
MJS_PRIVATE mjs_err_t mjs_ffi_call2(struct mjs *mjs);
MJS_PRIVATE void mjs_ffi_cb_free(struct mjs *);
MJS_PRIVATE void mjs_ffi_args_free_list(struct mjs *mjs);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_FFI_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_mm.h"
#endif
#ifndef MJS_MM_H_
#define MJS_MM_H_
/* Amalgamated: #include "mjs_internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
struct mjs;
typedef void (*gc_cell_destructor_t)(struct mjs *mjs, void *);
struct gc_block {
struct gc_block *next;
struct gc_cell *base;
size_t size;
};
struct gc_arena {
struct gc_block *blocks;
size_t size_increment;
struct gc_cell *free; /* head of free list */
size_t cell_size;
#if MJS_MEMORY_STATS
unsigned long allocations; /* cumulative counter of allocations */
unsigned long garbage; /* cumulative counter of garbage */
unsigned long alive; /* number of living cells */
#endif
gc_cell_destructor_t destructor;
};
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_MM_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_gc_public.h"
#endif
#ifndef MJS_GC_PUBLIC_H_
#define MJS_GC_PUBLIC_H_
/* Amalgamated: #include "mjs_core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Perform garbage collection.
* Pass true to full in order to reclaim unused heap back to the OS.
*/
void mjs_gc(struct mjs *mjs, int full);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_GC_PUBLIC_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_gc.h"
#endif
#ifndef MJS_GC_H_
#define MJS_GC_H_
/* Amalgamated: #include "mjs_core.h" */
/* Amalgamated: #include "mjs_mm.h" */
/* Amalgamated: #include "mjs_internal.h" */
/* Amalgamated: #include "mjs_gc_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* performs arithmetics on gc_cell pointers as if they were arena->cell_size
* bytes wide
*/
#define GC_CELL_OP(arena, cell, op, arg) \
((struct gc_cell *) (((char *) (cell)) op((arg) * (arena)->cell_size)))
struct gc_cell {
union {
struct gc_cell *link;
uintptr_t word;
} head;
};
MJS_PRIVATE int gc_strings_is_gc_needed(struct mjs *mjs);
/* perform gc if not inhibited */
MJS_PRIVATE int maybe_gc(struct mjs *mjs);
MJS_PRIVATE struct mjs_object *new_object(struct mjs *);
MJS_PRIVATE struct mjs_property *new_property(struct mjs *);
MJS_PRIVATE struct mjs_ffi_sig *new_ffi_sig(struct mjs *mjs);
MJS_PRIVATE void gc_mark(struct mjs *mjs, mjs_val_t *val);
MJS_PRIVATE void gc_arena_init(struct gc_arena *, size_t, size_t, size_t);
MJS_PRIVATE void gc_arena_destroy(struct mjs *, struct gc_arena *a);
MJS_PRIVATE void gc_sweep(struct mjs *, struct gc_arena *, size_t);
MJS_PRIVATE void *gc_alloc_cell(struct mjs *, struct gc_arena *);
MJS_PRIVATE uint64_t gc_string_mjs_val_to_offset(mjs_val_t v);
/* return 0 if v is an object/function with a bad pointer */
MJS_PRIVATE int gc_check_val(struct mjs *mjs, mjs_val_t v);
/* checks whether a pointer is within the ranges of an arena */
MJS_PRIVATE int gc_check_ptr(const struct gc_arena *a, const void *p);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_GC_H_ */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_core.h"
#endif
#ifndef MJS_CORE_H
#define MJS_CORE_H
/* Amalgamated: #include "mjs_ffi.h" */
/* Amalgamated: #include "mjs_gc.h" */
/* Amalgamated: #include "mjs_internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
#define JUMP_INSTRUCTION_SIZE 2
enum mjs_type {
/* Primitive types */
MJS_TYPE_UNDEFINED,
MJS_TYPE_NULL,
MJS_TYPE_BOOLEAN,
MJS_TYPE_NUMBER,
MJS_TYPE_STRING,
MJS_TYPE_FOREIGN,
/* Different classes of Object type */
MJS_TYPE_OBJECT_GENERIC,
MJS_TYPE_OBJECT_ARRAY,
MJS_TYPE_OBJECT_FUNCTION,
/*
* TODO(dfrank): if we support prototypes, need to add items for them here
*/
MJS_TYPES_CNT
};
enum mjs_call_stack_frame_item {
CALL_STACK_FRAME_ITEM_RETVAL_STACK_IDX, /* TOS */
CALL_STACK_FRAME_ITEM_LOOP_ADDR_IDX,
CALL_STACK_FRAME_ITEM_SCOPE_IDX,
CALL_STACK_FRAME_ITEM_RETURN_ADDR,
CALL_STACK_FRAME_ITEM_THIS,
CALL_STACK_FRAME_ITEMS_CNT
};
/*
* A tag is made of the sign bit and the 4 lower order bits of byte 6.
* So in total we have 32 possible tags.
*
* Tag (1,0) however cannot hold a zero payload otherwise it's interpreted as an
* INFINITY; for simplicity we're just not going to use that combination.
*/
#define MAKE_TAG(s, t) \
((uint64_t)(s) << 63 | (uint64_t) 0x7ff0 << 48 | (uint64_t)(t) << 48)
#define MJS_TAG_OBJECT MAKE_TAG(1, 1)
#define MJS_TAG_FOREIGN MAKE_TAG(1, 2)
#define MJS_TAG_UNDEFINED MAKE_TAG(1, 3)
#define MJS_TAG_BOOLEAN MAKE_TAG(1, 4)
#define MJS_TAG_NAN MAKE_TAG(1, 5)
#define MJS_TAG_STRING_I MAKE_TAG(1, 6) /* Inlined string len < 5 */
#define MJS_TAG_STRING_5 MAKE_TAG(1, 7) /* Inlined string len 5 */
#define MJS_TAG_STRING_O MAKE_TAG(1, 8) /* Owned string */
#define MJS_TAG_STRING_F MAKE_TAG(1, 9) /* Foreign string */
#define MJS_TAG_STRING_C MAKE_TAG(1, 10) /* String chunk */
#define MJS_TAG_STRING_D MAKE_TAG(1, 11) /* Dictionary string */
#define MJS_TAG_ARRAY MAKE_TAG(1, 12)
#define MJS_TAG_FUNCTION MAKE_TAG(1, 13)
#define MJS_TAG_FUNCTION_FFI MAKE_TAG(1, 14)
#define MJS_TAG_NULL MAKE_TAG(1, 15)
#define MJS_TAG_MASK MAKE_TAG(1, 15)
struct mjs_vals {
/* Current `this` value */
mjs_val_t this_obj;
mjs_val_t dataview_proto;
/*
* The object against which the last `OP_GET` was invoked. Needed for
* "method invocation pattern".
*/
mjs_val_t last_getprop_obj;
};
struct mjs_bcode_part {
/* Global index of the bcode part */
size_t start_idx;
/* Actual bcode data */
struct {
const char *p; /* Memory chunk pointer */
size_t len; /* Memory chunk length */
} data;
/*
* Result of evaluation (not parsing: if there is an error during parsing,
* the bcode is not even committed). It is used to determine whether we
* need to evaluate the file: if file was already evaluated, and the result
* was MJS_OK, then we won't evaluate it again. Otherwise, we will.
*/
mjs_err_t exec_res : 4;
/* If set, bcode data does not need to be freed */
unsigned in_rom : 1;
};
struct mjs {
struct mbuf bcode_gen;
struct mbuf bcode_parts;
size_t bcode_len;
struct mbuf stack;
struct mbuf call_stack;
struct mbuf arg_stack;
struct mbuf scopes; /* Scope objects */
struct mbuf loop_addresses; /* Addresses for breaks & continues */
struct mbuf owned_strings; /* Sequence of (varint len, char data[]) */
struct mbuf foreign_strings; /* Sequence of (varint len, char *data) */
struct mbuf owned_values;
struct mbuf json_visited_stack;
struct mjs_vals vals;
char *error_msg;
char *stack_trace;
enum mjs_err error;
mjs_ffi_resolver_t *dlsym; /* Symbol resolver function for FFI */
ffi_cb_args_t *ffi_cb_args; /* List of FFI args descriptors */
size_t cur_bcode_offset;
struct gc_arena object_arena;
struct gc_arena property_arena;
struct gc_arena ffi_sig_arena;
unsigned inhibit_gc : 1;
unsigned need_gc : 1;
unsigned generate_jsc : 1;
};
/*
* Bcode header: type of the items, and item numbers.
*/
typedef uint32_t mjs_header_item_t;
enum mjs_header_items {
MJS_HDR_ITEM_TOTAL_SIZE, /* Total size of the bcode (not counting the
OP_BCODE_HEADER byte) */
MJS_HDR_ITEM_BCODE_OFFSET, /* Offset to the start of the actual bcode (not
counting the OP_BCODE_HEADER byte) */
MJS_HDR_ITEM_MAP_OFFSET, /* Offset to the start of offset-to-line_no mapping
k*/
MJS_HDR_ITEMS_CNT
};
MJS_PRIVATE size_t mjs_get_func_addr(mjs_val_t v);
MJS_PRIVATE int mjs_getretvalpos(struct mjs *mjs);
MJS_PRIVATE enum mjs_type mjs_get_type(mjs_val_t v);
/*
* Prints stack trace starting from the given bcode offset; other offsets
* (if any) will be fetched from the call_stack.
*/
MJS_PRIVATE void mjs_gen_stack_trace(struct mjs *mjs, size_t offset);
MJS_PRIVATE mjs_val_t vtop(struct mbuf *m);
MJS_PRIVATE size_t mjs_stack_size(const struct mbuf *m);
MJS_PRIVATE mjs_val_t *vptr(struct mbuf *m, int idx);
MJS_PRIVATE void push_mjs_val(struct mbuf *m, mjs_val_t v);
MJS_PRIVATE mjs_val_t mjs_pop_val(struct mbuf *m);
MJS_PRIVATE mjs_val_t mjs_pop(struct mjs *mjs);
MJS_PRIVATE void mjs_push(struct mjs *mjs, mjs_val_t v);
MJS_PRIVATE void mjs_die(struct mjs *mjs);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* MJS_CORE_H */
#ifdef MJS_MODULE_LINES
#line 1 "src/mjs_conversion.h"
#endif
#ifndef MJS_CONVERSION_H_
#define MJS_CONVERSION_H_
/* Amalgamated: #include "mjs_internal.h" */
/* Amalgamated: #include "mjs_core.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Tries to convert `mjs_val_t` to a string, returns MJS_OK if successful.
* String is returned as a pair of pointers: `char **p, size_t *sizep`.
*
* Caller must also provide a non-null `need_free`, and if it is non-zero,
* then the string `*p` should be freed by the caller.
*