-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvm.js
2769 lines (2302 loc) · 108 KB
/
vm.js
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
const LB_REAL = 200;
const LB_NIL = 201;
const LB_FALSE = 202;
const LB_TRUE = 203;
const LB_FIBER = 204;
const LB_INTEGER = 205;
const LB_STRING = 206;
const LB_SYMBOL = 207;
const LB_KEYWORD = 208;
const LB_ARRAY = 209;
const LB_TUPLE = 210;
const LB_TABLE = 211;
const LB_TABLE_PROTO = 212;
const LB_STRUCT = 213;
// const LB_BUFFER = 214;
const LB_FUNCTION = 215;
const LB_REGISTRY = 216;
// const LB_ABSTRACT = 217;
const LB_REFERENCE = 218;
const LB_FUNCENV_REF = 219;
const LB_FUNCDEF_REF = 220;
// const LB_UNSAFE_CFUNCTION = 221;
// const LB_UNSAFE_POINTER = 222;
// const LB_STRUCT_PROTO = 223;
const JOP_RETURN = 3;
const JOP_RETURN_NIL = 4;
const JOP_ADD_IMMEDIATE = 5;
const JOP_ADD = 6;
const JOP_MOVE_NEAR = 25;
const JOP_JUMP = 26;
const JOP_JUMP_IF_NOT = 28;
const JOP_LESS_THAN = 33;
const JOP_LESS_THAN_IMMEDIATE = 34;
const JOP_EQUALS = 35;
const JOP_EQUALS_IMMEDIATE = 36;
const JOP_LOAD_NIL = 38;
const JOP_LOAD_TRUE = 39;
const JOP_LOAD_FALSE = 40;
const JOP_LOAD_INTEGER = 41;
const JOP_LOAD_CONSTANT = 42;
const JOP_LOAD_UPVALUE = 43;
const JOP_LOAD_SELF = 44;
const JOP_CLOSURE = 46;
const JOP_PUSH = 47;
const JOP_PUSH_2 = 48;
const JOP_PUSH_3 = 49;
const JOP_PUSH_ARRAY = 50;
const JOP_CALL = 51;
const JOP_TAILCALL = 52;
const JOP_RESUME = 53;
const JOP_SIGNAL = 54;
const JOP_GET = 57;
const JOP_PUT = 58;
const JOP_GET_INDEX = 59;
const JOP_LENGTH = 61;
const JOP_MAKE_ARRAY = 62;
const JOP_MAKE_TABLE = 66;
const JOP_MAKE_TUPLE = 67;
const JANET_MAX_Q_CAPACITY = 0x7FFFFFF; // max size of any queue
const JANET_RESUME_VALUE_NULL = new Object();
/* Basic types for all Janet Values */
const JANET_NUMBER = 0;
const JANET_NIL = 1;
const JANET_BOOLEAN = 2;
const JANET_FIBER = 3;
const JANET_STRING = 4;
const JANET_SYMBOL = 5;
const JANET_KEYWORD = 6;
const JANET_ARRAY = 7;
const JANET_TUPLE = 8;
const JANET_TABLE = 9;
const JANET_STRUCT = 10;
const JANET_BUFFER = 11;
const JANET_FUNCTION = 12;
const JANET_CFUNCTION = 13;
// const JANET_ABSTRACT = 14;
// const JANET_POINTER = 15;
// #define JANET_TFLAG_NIL (1 << JANET_NIL)
// #define JANET_TFLAG_BOOLEAN (1 << JANET_BOOLEAN)
// #define JANET_TFLAG_FIBER (1 << JANET_FIBER)
// #define JANET_TFLAG_NUMBER (1 << JANET_NUMBER)
// #define JANET_TFLAG_STRING (1 << JANET_STRING)
// #define JANET_TFLAG_SYMBOL (1 << JANET_SYMBOL)
// #define JANET_TFLAG_KEYWORD (1 << JANET_KEYWORD)
// #define JANET_TFLAG_ARRAY (1 << JANET_ARRAY)
// #define JANET_TFLAG_TUPLE (1 << JANET_TUPLE)
// #define JANET_TFLAG_TABLE (1 << JANET_TABLE)
// #define JANET_TFLAG_STRUCT (1 << JANET_STRUCT)
// #define JANET_TFLAG_BUFFER (1 << JANET_BUFFER)
// #define JANET_TFLAG_FUNCTION (1 << JANET_FUNCTION)
// #define JANET_TFLAG_CFUNCTION (1 << JANET_CFUNCTION)
// #define JANET_TFLAG_ABSTRACT (1 << JANET_ABSTRACT)
// #define JANET_TFLAG_POINTER (1 << JANET_POINTER)
// #define JANET_TFLAG_BYTES (JANET_TFLAG_STRING | JANET_TFLAG_SYMBOL | JANET_TFLAG_BUFFER | JANET_TFLAG_KEYWORD)
// #define JANET_TFLAG_INDEXED (JANET_TFLAG_ARRAY | JANET_TFLAG_TUPLE)
// #define JANET_TFLAG_DICTIONARY (JANET_TFLAG_TABLE | JANET_TFLAG_STRUCT)
// #define JANET_TFLAG_LENGTHABLE (JANET_TFLAG_BYTES | JANET_TFLAG_INDEXED | JANET_TFLAG_DICTIONARY)
// #define JANET_TFLAG_CALLABLE (JANET_TFLAG_FUNCTION | JANET_TFLAG_CFUNCTION | \
// JANET_TFLAG_LENGTHABLE | JANET_TFLAG_ABSTRACT)
function janet_type(x) {
const typeof_x = typeof x;
if (typeof_x == 'number') {
return JANET_NUMBER;
} else if (x instanceof JanetArray) {
return JANET_ARRAY;
} else if (x instanceof JanetTuple) {
return JANET_TUPLE;
} else if (x instanceof JanetString) {
return JANET_STRING;
} else if (x instanceof JanetKeyword) {
return JANET_KEYWORD;
} else if (x instanceof JanetTable) {
return JANET_TABLE;
}
console.log("TODO janet_type", x);
throw new Error("TODO janet_type"+ x);
}
class JanetTable extends Map {
jt_proto = null;
get(k) {
return super.get(k) || (this.jt_proto ? this.jt_proto.get(k) : null) ;
}
set(k, v) {
if (v) {
return super.set(k,v);
} else {
super.delete(k);
return this;
}
}
};
class JanetTuple extends Array {
jt_closed = false;
jt_tuple_brackets = false; // bracket or not
set(k, v) {
if (!this.jt_closed) {
super.set(k, v);
} else {
throw new Error("can't set closed tuple");
}
}
push(v) {
if (!this.jt_closed) {
super.push(v);
} else {
throw new Error("can't push closed tuple");
}
}
}
function janet_tuple_start(len) {
// TODO use len for init length
return new JanetTuple();
}
function janet_tuple_end(jtuple) {
jtuple.jt_closed = true;
return jtuple;
}
class JanetStruct extends Map {
js_closed = false;
set(k, v) {
if (!this.js_closed) {
super.set(k, v);
} else {
throw new Error("can't set closed struct");
}
}
}
function janet_struct_start(length) {
// TODO actually init the struct size
return new JanetStruct();
}
function janet_struct_end(jstruct) {
jstruct.js_closed = true;
return jstruct;
}
class JanetArray extends Array {};
class JanetString extends String {};
const SYM_CACHE = new Map();
class JanetSymbol extends String {};
function janet_symbol(name) {
let obj = SYM_CACHE.get(name);
if (obj){
return obj;
} else {
obj = new JanetSymbol(name);
SYM_CACHE.set(name, obj);
return obj;
}
}
const KW_CACHE = new Map();
class JanetKeyword extends String {};
function janet_keyword(name) {
let obj = KW_CACHE.get(name);
if (obj){
return obj;
} else {
obj = new JanetKeyword(name);
KW_CACHE.set(name, obj);
return obj;
}
}
class JanetFuncDef {
name;
source;
closure_bitset;
defs;
environments;
constants;
bytecode;
sourcemap;
flags;
slotcount;
arity; /* not including varargs */
min_arity; /* including varargs */
max_arity; /* including varargs */
}
class JanetFuncEnv {
//length = null; // is length of as_values or of fiber data
offset = null; // TODO offset is always 0 as both as_values and fiber envs are slots array?
as_values = [];
}
class JanetFunction {
def = null;
envs = [];
}
function janet_function() {
return new JanetFunction();
}
var expectedi1 = new JanetTable();
expectedi1.set(janet_keyword("a1"), 42);
expectedi1.set("lol", true);
console.log("janet map: ", expectedi1);
function readint(bytes_in, unmarshal_state) {
// todo handle ints larger than 128
const data = bytes_in[unmarshal_state.at];
if (data < 128) {
unmarshal_state.at++;
return data;
} else if (data < 192) {
const b0 = bytes_in[unmarshal_state.at];
const b1 = bytes_in[unmarshal_state.at + 1];
unmarshal_state.at += 2
var i = ((b0 & 0x3F) << 8) + b1;
i |= (i >> 13) ? 0xFFFFC000 : 0;
// 191 + 255 => -1
return i;
} else if (data == LB_INTEGER) {
unmarshal_state.at++;
const b0 = bytes_in[unmarshal_state.at++];
const b1 = bytes_in[unmarshal_state.at++];
const b2 = bytes_in[unmarshal_state.at++];
const b3 = bytes_in[unmarshal_state.at++];
const i = b3 | (b2 << 8) | (b1 << 16) | (b0 << 24);
return i;
} else {
throw new Error("Expected integer, got byte "+ bytes_in[unmarshal_state.at] + " at index " + unmarshal_state.at);
return 0;
}
}
function readnat(bytes_in, unmarshal_state) {
const n = readint(bytes_in, unmarshal_state);
if (n < 0) {
throw new Error("readnat expected 0 =< got: "+ n);
}
return n;
}
function unmarshal_u32s(bytes_in, unmarshal_state, bytecode_length) {
const arr = new Uint32Array(bytecode_length);
for (var i = 0; i < bytecode_length; i++) {
const b0 = bytes_in[unmarshal_state.at++];
const b1 = bytes_in[unmarshal_state.at++];
const b2 = bytes_in[unmarshal_state.at++];
const b3 = bytes_in[unmarshal_state.at++];
const ui = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
arr[i] = ui;
}
return arr;
}
const JANET_FUNCDEF_FLAG_VARARG = 0x10000;
const JANET_FUNCDEF_FLAG_NEEDSENV = 0x20000;
const JANET_FUNCDEF_FLAG_HASNAME = 0x80000;
const JANET_FUNCDEF_FLAG_HASSOURCE = 0x100000;
const JANET_FUNCDEF_FLAG_HASDEFS = 0x200000;
const JANET_FUNCDEF_FLAG_HASENVS = 0x400000;
const JANET_FUNCDEF_FLAG_HASSOURCEMAP = 0x800000;
const JANET_FUNCDEF_FLAG_STRUCTARG = 0x1000000;
const JANET_FUNCDEF_FLAG_HASCLOBITSET = 0x2000000;
const JANET_FUNCDEF_FLAG_TAG = 0xFFFF;
const JANET_FIBER_FLAG_HASCHILD = (1 << 29);
const JANET_FIBER_FLAG_HASENV = (1 << 30);
const JANET_STACKFRAME_HASENV = 0x80000000;
function janet_verify() {
// TODO
console.log("janet_verify TODO");
const has_errors = false;
return has_errors;
}
function unmarshal_one_def(bytes_in, unmarshal_state) {
const first_byte = bytes_in[unmarshal_state.at];
if (first_byte == LB_FUNCDEF_REF) {
unmarshal_state.at++;
const idx = readint(bytes_in, unmarshal_state);
if (idx < 0 || idx >= unmarshal_state.lookup_defs.length) {
throw new Error("invalid funcdef reference "+ idx);
}
return unmarshal_state.lookup_defs[index];
} else {
const def = new JanetFuncDef();
unmarshal_state.lookup_defs.push(def);
def.flags = readint(bytes_in, unmarshal_state);
def.slotcount = readnat(bytes_in, unmarshal_state);
def.arity = readnat(bytes_in, unmarshal_state);
def.min_arity = readnat(bytes_in, unmarshal_state);
def.max_arity = readnat(bytes_in, unmarshal_state);
const constants_length = readnat(bytes_in, unmarshal_state);
const bytecode_length = readnat(bytes_in, unmarshal_state);
let environments_length;
if (def.flags & JANET_FUNCDEF_FLAG_HASENVS) {
environments_length = readnat(bytes_in, unmarshal_state);
} else {
environments_length = 0;
}
let defs_length;
if (def.flags & JANET_FUNCDEF_FLAG_HASDEFS) {
defs_length = readnat(bytes_in, unmarshal_state);
} else {
defs_length = 0;
}
/* check name and source (optional) */
if (def.flags & JANET_FUNCDEF_FLAG_HASNAME) {
const name = unmarshal_one(bytes_in, unmarshal_state);
if (!(name instanceof JanetString)) {
throw new Error("function name is not a string");
}
def.name = name;
}
if (def.flags & JANET_FUNCDEF_FLAG_HASSOURCE) {
const source = unmarshal_one(bytes_in, unmarshal_state);
if (!(source instanceof JanetString)) {
throw new Error("function source is not a string");
}
def.source = source;
}
/* unmarshal constants */
if (constants_length) {
def.constants = [];
for (var i = 0; i < constants_length; i++) {
const con = unmarshal_one(bytes_in, unmarshal_state);
def.constants.push(con);
}
}
def.bytecode = unmarshal_u32s(bytes_in, unmarshal_state, bytecode_length);
/* unmarshal environments */
if (def.flags & JANET_FUNCDEF_FLAG_HASENVS) {
def.environments = [];
for (var i = 0; i < environments_length; i++) {
const i = readint(bytes_in, unmarshal_state);
def.environments.push(i);
}
}
/* unmarshal sub funcdefs */
if (def.flags & JANET_FUNCDEF_FLAG_HASDEFS) {
def.defs = [];
for (var i = 0; i < defs_length; i++) {
const one_def = unmarshal_one_def(bytes_in, unmarshal_state);
def.defs.push(one_def);
}
}
/* unmarshal source maps if needed */
if (def.flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
var current = 0;
def.sourcemap = [];
for (var i = 0; i < bytecode_length; i++) {
current += readint(bytes_in, unmarshal_state);
const column = readint(bytes_in, unmarshal_state);
const sm = {line: current,
column: column};
def.sourcemap.push(sm);
}
}
/* unmarshal closure bitset if needed */
if (def.flags & JANET_FUNCDEF_FLAG_HASCLOBITSET) {
const n = (def.slotcount + 31) >> 5;
def.closure_bitset = unmarshal_u32s(bytes_in, unmarshal_state, n);
}
/* validate */
if (janet_verify(def)) {
throw new Error("funcdef has invalid bytecode");
}
return def;
}
}
function unmarshal_one_env(bytes_in, unmarshal_state) {
const first_byte = bytes_in[unmarshal_state.at];
if (first_byte == LB_FUNCENV_REF) {
unmarshal_state.at++;
const idx = readint(bytes_in, unmarshal_state);
if (idx < 0 || idx >= unmarshal_state.lookup_envs.length) {
throw new Error("invalid funcenv reference "+ idx);
}
return unmarshal_state.lookup_envs[index];
} else {
const env = new JanetFuncEnv();
unmarshal_state.lookup_envs.push(env);
const offset = readnat(bytes_in, unmarshal_state);
const length = readnat(bytes_in, unmarshal_state);
if (offset > 0) {
throw new Error("Todo unmarshal funcenv offset > 0 ", offset);
} else {
if (length == 0) {
janet_panic("invalid funcenv length");
}
env.offset = 0;
for (var i = 0; i < length; i++) {
const data = unmarshal_one(bytes_in, unmarshal_state);
env.as_values.push(data);
}
return env;
}
}
}
function unmarshal_one_fiber(bytes_in, unmarshal_state) {
const fiber = new JanetFiber();
unmarshal_state.lookup.push(fiber);
const fiber_flags = readnat(bytes_in, unmarshal_state);
const frame_offset = readnat(bytes_in, unmarshal_state)
const fiber_stackstart = readnat(bytes_in, unmarshal_state)
const fiber_stacktop = readnat(bytes_in, unmarshal_state)
const fiber_maxstack = readnat(bytes_in, unmarshal_state)
console.log("fiber_flags", fiber_flags);
console.log("stack", stack);
console.log("fiber_stackstart", fiber_stackstart);
console.log("fiber_stacktop", fiber_stacktop);
console.log("fiber_maxstack", fiber_maxstack);
var stack = frame_offset; // 'stack' is pointer in stack of fiber to walk the frames
while (stack > 0) { // frame offset in data means precense of a stackframe with a function
const frameflags = readint(bytes_in, unmarshal_state);
const prevframe = readnat(bytes_in, unmarshal_state);
const pcdiff = readnat(bytes_in, unmarshal_state);
const func = unmarshal_one(bytes_in, unmarshal_state); // JanetFunction
const def = func.def
/* get env */
let env = null;
if (frameflags & JANET_STACKFRAME_HASENV) {
console.log("STACKFRAME HAS ENV!!!");
frameflags &= ~JANET_STACKFRAME_HASENV;
env = unmarshal_one_env(bytes_in, unmarshal_state);
}
const expected_framesize = def.slotcount;
const stacktop = fiber_stackstart - 4; // 4 = JANET_FRAME_SIZE
if (expected_framesize != stacktop - frame_offset) {
// we don't have a stack in the fiber (instead there are slots in the frames), but we do check the numbers
janet_panic("fiber stackframe size mismatch");
}
if (pcdiff >= def.bytecode.length) {
janet_panic("fiber stackframe has invalid pc");
}
// skipping the part in janet proper checking if the next frame aligns with this one
janet_fiber_funcframe(fiber, func);
frame = fiber.frame;
console.log("prevframe: "+ prevframe);
/* get stack items */
for (var i = 0; i < def.slotcount; i++) {
const data = unmarshal_one(bytes_in, unmarshal_state);
frame.slots[i] = data;
}
frame.env = env;
frame.pc = pcdiff;
frame.flags = frameflags;
stack = prevframe; // will break loop
}
if (stack < 0) {
janet_panic("fiber has too many stackframes");
}
let fiber_env = null;
if (fiber_flags & JANET_FIBER_FLAG_HASENV) {
console.log("unmarshal fiber has env");
// TODO not checked if this works
fiber_flags &= ~JANET_FIBER_FLAAG_HASENV;
fiber_env = unmarshal_one(bytes_in, unmarshal_state);
if (!(env instanceof JanetTable)){
throw new Error("env should be table");
}
}
if (fiber_flags & JANET_FIBER_FLAG_HASCHILD) {
console.log("unmashal fiber has child");
// TODO not checked if this works
fiber_flags &= ~JANET_FIBER_FLAG_HASCHILD;
const fiberv = unmarshal_one(bytes_in, unmarshal_state);
if (!(fiberv instanceof JanetFiber)) { throw new Error("Fiber child is not a fiber"); }
fiber.child = fiberv;
}
const last_value = unmarshal_one(bytes_in, unmarshal_state);
fiber.flags = fiber_flags;
fiber.last_value = last_value;
fiber.env = fiber_env;
const state = janet_fiber_status(fiber);
if (status < 0 || status > JANET_STATUS_ALIVE) {
janet_panic("invalid fiber status");
}
console.log("unm fiber", fiber);
return fiber;
}
// does not handle reference
function unmarshal_one(bytes_in, unmarshal_state){
const lead = bytes_in[unmarshal_state.at];
if (lead == null) { throw new Error("Unmarshal missing first byte");}
if (lead < LB_REAL) {
const i = readint(bytes_in, unmarshal_state);
return i;
}
switch (lead) {
case LB_REAL: {
// prob 0.5 or 0.25
// 0.5 = 0 0 0 .. 0 224 63
// 0.25 = 0 0 ... 0 208 63
unmarshal_state.at++;
const b0 = bytes_in[unmarshal_state.at++];
const b1 = bytes_in[unmarshal_state.at++];
const b2 = bytes_in[unmarshal_state.at++];
const b3 = bytes_in[unmarshal_state.at++];
const b4 = bytes_in[unmarshal_state.at++];
const b5 = bytes_in[unmarshal_state.at++];
const b6 = bytes_in[unmarshal_state.at++];
const b7 = bytes_in[unmarshal_state.at++];
const dv = new DataView(new Uint8Array([b0,b1,b2,b3,b4,b5,b6,b7]).buffer);
const d = dv.getFloat64(0, true); // true == littleEndian
unmarshal_state.lookup.push(d);
return d;
}
case LB_NIL: {
unmarshal_state.at++;
return null;
}
case LB_FALSE: {
unmarshal_state.at++;
return false;
}
case LB_TRUE: {
unmarshal_state.at++;
return true;
}
case LB_FIBER: {
unmarshal_state.at++;
const fiber = unmarshal_one_fiber(bytes_in, unmarshal_state);
return fiber;
}
case LB_INTEGER: {
unmarshal_state.at++;
const b0 = bytes_in[unmarshal_state.at++];
const b1 = bytes_in[unmarshal_state.at++];
const b2 = bytes_in[unmarshal_state.at++];
const b3 = bytes_in[unmarshal_state.at++];
const i = b3 | (b2 << 8) | (b1 << 16) | (b0 << 24);
return i;
}
case LB_STRING: {
unmarshal_state.at++;
const len = readnat(bytes_in, unmarshal_state);
const s_str = String.fromCharCode.apply(String, bytes_in.slice(unmarshal_state.at, unmarshal_state.at + len));
const s = new JanetString(s_str);
unmarshal_state.at += len;
unmarshal_state.lookup.push(s);
return s;
}
case LB_SYMBOL: {
unmarshal_state.at++;
const len = readnat(bytes_in, unmarshal_state);
const symbol_str = String.fromCharCode.apply(String, bytes_in.slice(unmarshal_state.at, unmarshal_state.at + len));
const symbol = janet_symbol(symbol_str);
unmarshal_state.at += len;
unmarshal_state.lookup.push(symbol);
return symbol;
}
case LB_KEYWORD: {
unmarshal_state.at++;
const len = readnat(bytes_in, unmarshal_state);
const keyword_str = String.fromCharCode.apply(String, bytes_in.slice(unmarshal_state.at, unmarshal_state.at + len));
const kw = janet_keyword(keyword_str);
unmarshal_state.at += len;
unmarshal_state.lookup.push(kw);
return kw;
}
case LB_REGISTRY: {
unmarshal_state.at++;
const len = readnat(bytes_in, unmarshal_state);
var reg;
if (unmarshal_state.reg) {
const regkey = String.fromCharCode.apply(String, bytes_in.slice(unmarshal_state.at, unmarshal_state.at + len));
const reg_class = unmarshal_state.reg[regkey];
if (!reg_class) { throw new Error("Referenced registry not found: "+ regkey);}
if (reg_class instanceof JanetTable) { // for root-env table
reg = reg_class;
} else{
// for JanetLib_* to get new JanetLib_*();
reg = reg_class.create_new();
}
} else {
/* reg null */
}
unmarshal_state.at += len;
unmarshal_state.lookup.push(reg);
return reg;
}
case LB_ARRAY: {
unmarshal_state.at++;
const len = readnat(bytes_in, unmarshal_state);
const arr = new JanetArray();
unmarshal_state.lookup.push(arr);
for (var i = 0; i < len; i++) {
const data = unmarshal_one(bytes_in, unmarshal_state);
arr.push(data);
}
return arr;
}
case LB_TUPLE: {
// tuple is brackets or non-brackets
unmarshal_state.at++;
const len = readnat(bytes_in, unmarshal_state);
const jt = janet_tuple_start(len);
const bracket_flag = readint(bytes_in, unmarshal_state);
// if (bracket_flag > 0) {
// jt.bracket_flag = true;
// }
for (var i = 0; i < len; i++) {
const value = unmarshal_one(bytes_in, unmarshal_state);
jt.push(value);
}
const jt_closed = janet_tuple_end(jt)
unmarshal_state.lookup.push(jt_closed);
return jt_closed;
}
case LB_STRUCT: {
unmarshal_state.at++;
const len = readnat(bytes_in, unmarshal_state);
const jstruct = janet_struct_start(len);
for (var i = 0; i < len; i++) {
const key = unmarshal_one(bytes_in, unmarshal_state);
const value = unmarshal_one(bytes_in, unmarshal_state);
jstruct.set(key,value);
}
const jstruct_closed = janet_struct_end(jstruct);
unmarshal_state.lookup.push(jstruct_closed);
return jstruct_closed;
}
case LB_TABLE:
case LB_TABLE_PROTO: {
const jt = new JanetTable();
unmarshal_state.at++;
const len = readnat(bytes_in, unmarshal_state);
// TODO MARSH_EOS check is stream contains at least len more bytes!
unmarshal_state.lookup.push(jt);
if (lead == LB_TABLE_PROTO) {
const data = unmarshal_one(bytes_in, unmarshal_state);
jt.jt_proto = data;
}
for (var i = 0; i < len; i++) {
const key = unmarshal_one(bytes_in, unmarshal_state);
const value = unmarshal_one(bytes_in, unmarshal_state);
jt.set(key, value);
}
return jt;
}
case LB_REFERENCE: {
unmarshal_state.at++;
const idx = readnat(bytes_in, unmarshal_state);
if (idx >= unmarshal_state.lookup.length) {
throw new Error("unmarshall invalid reference: "+ idx);
}
return unmarshal_state.lookup[idx];
}
case LB_FUNCTION: {
unmarshal_state.at++;
const len = readnat(bytes_in, unmarshal_state);
if (len > 255) {
throw new Error("invalid function - too many environments " + len);
}
const func = janet_function();
func.def = null;
unmarshal_state.lookup.push(func);
const def = unmarshal_one_def(bytes_in, unmarshal_state);
func.def = def;
for (var i = 0; i < len; i++){
const env = unmarshal_one_env(bytes_in, unmarshal_state);
func.envs.push(env);
}
return func;
}
default: {
throw new Error("Unhandled unmarshal lead byte "+ lead + " at unmarshal_state.at " + unmarshal_state.at);
}
}
}
function unmarshal(bytes_in) {
const root_env = new JanetTable();
root_env.set("hello", "world");
const unmarshal_state = {at: 0,
reg: {"root-env": root_env,
"fiber/new": JanetLib_fiber_new, //new JanetLib_fiber_new(),
"print": JanetLib_print, //new JanetLib_print()
"ev/sleep": JanetLib_ev_sleep,
"ev/go": JanetLib_ev_go,
"ev/chan": JanetLib_ev_chan,
"ev/select": JanetLib_ev_select,
"ev/give": JanetLib_ev_give,
"ev/take": JanetLib_ev_take,
"ev/chan-close": JanetLib_ev_chan_close,
"array/push": JanetLib_array_push,
"string": JanetLib_string,
"keys": JanetLib_keys
}, // registry, root-env used, but not set
lookup: [],
lookup_defs: [],
lookup_envs: []
};
return unmarshal_one(bytes_in, unmarshal_state);
}
function ts_now() {
return Date.now();
}
class JanetFiber {
flags;
frame;
next_slots = [];
last_value;
sched_id = 0; // should increment everytime this fiber is scheduled in a task, to track whether the fiber has progressed when it is scheduled to run
supervisor_channel;
};
class JanetTimeout {
when;
fiber;
//is_error;
sched_id;
curr_fiber;
}
/* Fiber signals */
const JANET_SIGNAL_OK = 0;
const JANET_SIGNAL_ERROR = 1;
const JANET_SIGNAL_DEBUG = 2;
const JANET_SIGNAL_YIELD = 3;
const JANET_SIGNAL_USER0 = 4;
const JANET_SIGNAL_USER1 = 5;
const JANET_SIGNAL_USER2 = 6;
const JANET_SIGNAL_USER3 = 7;
const JANET_SIGNAL_USER4 = 8;
const JANET_SIGNAL_USER5 = 9;
const JANET_SIGNAL_USER6 = 10;
const JANET_SIGNAL_USER7 = 11;
const JANET_SIGNAL_USER8 = 12;
const JANET_SIGNAL_USER9 = 13;
const JANET_SIGNAL_EVENT = JANET_SIGNAL_USER9;
const JANET_SIGNAL_INTERRUPT = JANET_SIGNAL_USER8;
const JANET_SIGNAL_FETCH = 255;
const JANET_AWAIT_FETCH = new Object();
/* Fiber signal masks. */
const JANET_FIBER_MASK_ERROR = 2;
//const JANET_FIBER_MASK_DEBUG = 4
const JANET_FIBER_MASK_YIELD = 8;
// #define JANET_FIBER_MASK_USER0 (16 << 0)
// #define JANET_FIBER_MASK_USER1 (16 << 1)
// #define JANET_FIBER_MASK_USER2 (16 << 2)
// #define JANET_FIBER_MASK_USER3 (16 << 3)
// #define JANET_FIBER_MASK_USER4 (16 << 4)
// #define JANET_FIBER_MASK_USER5 (16 << 5)
// #define JANET_FIBER_MASK_USER6 (16 << 6)
// #define JANET_FIBER_MASK_USER7 (16 << 7)
// #define JANET_FIBER_MASK_USER8 (16 << 8)
// #define JANET_FIBER_MASK_USER9 (16 << 9)
// #define JANET_FIBER_MASK_USERN(N) (16 << (N))
// #define JANET_FIBER_MASK_USER 0x3FF0
const JANET_FIBER_STATUS_MASK = 0x3F0000;
// #define JANET_FIBER_RESUME_SIGNAL 0x400000
const JANET_FIBER_FLAG_CANCELED = 0x400000;
const JANET_FIBER_STATUS_OFFSET = 16;
// #define JANET_FIBER_BREAKPOINT 0x1000000
// #define JANET_FIBER_RESUME_NO_USEVAL 0x2000000
// #define JANET_FIBER_RESUME_NO_SKIP 0x4000000
// #define JANET_FIBER_DID_LONGJUMP 0x8000000
// #define JANET_FIBER_FLAG_MASK 0xF000000
// WARNING these are on fiber.gc->flags in janet proper, but on fiber.flags here, make sure they don't collide with any other fiber flags
//const JANET_FIBER_EV_FLAG_CANCELLED = 0x10000
const JANET_FIBER_EV_FLAG_SUSPENDED = 0x20000;
/* Fiber statuses - mostly corresponds to signals. */
const JANET_STATUS_DEAD = 0;
const JANET_STATUS_ERROR = 1;
const JANET_STATUS_DEBUG = 2;
const JANET_STATUS_PENDING = 3;
const JANET_STATUS_USER0 = 4;
const JANET_STATUS_USER1 = 5;
const JANET_STATUS_USER2 = 6;
const JANET_STATUS_USER3 = 7;
const JANET_STATUS_USER4 = 8;
const JANET_STATUS_USER5 = 9;
const JANET_STATUS_USER6 = 10;
const JANET_STATUS_USER7 = 11;
const JANET_STATUS_USER8 = 12;
const JANET_STATUS_USER9 = 13;
const JANET_STATUS_NEW = 14;
const JANET_STATUS_ALIVE = 15;
function janet_fiber_status(fiber) {
// TODO move status into proper field
return ((fiber.flags & JANET_FIBER_STATUS_MASK) >> JANET_FIBER_STATUS_OFFSET);
}
class JanetStackFrame {
func;
pc; // this is 0-based in bytecode of func, not a pointer to start/a place in of bytecode! (diff from Janet proper)
env;
prevframe;
flags;
slots = []; // different from janet proper
}
/* push a stack frame to a fiber */
function janet_fiber_funcframe(fiber, func) {
const newframe = new JanetStackFrame();
// no need for these checks as we size slots to fit it
//if (next_arity < func.def.min_arity) throw new Error("no space for min_arity on stack");
//if (next_arity > func.def.max_arity) throw new Error("next arity larger than max_arity");
newframe.prevframe = fiber.frame;
newframe.pc = 0;
newframe.func = func;
newframe.env = null;
newframe.flags = 0;
// TODO do we need to address fiber.next_slots here?
newframe.slots = new Array(func.def.slotcount);
fiber.frame = newframe;
if (func.def.flags & JANET_FUNCDEF_FLAG_VARARG) {
// collapse the varargs into next_slots[arity] = tuple of varargs
//throw new Error("TODO implement varargs for stack frame");
if (func.def.flags & JANET_FUNCDEF_FLAG_STRUCTARG) {
throw new Error("TODO implement varargs for stack frame for STRUCT ARGS");
}
const varargs_length = (fiber.next_slots.length - func.def.arity);
const varargs = fiber.next_slots.splice(func.def.arity, varargs_length);
//
const tuple_varargs = janet_tuple_start(varargs_length);
tuple_varargs.splice(0,0, ...varargs);
fiber.next_slots[func.def.arity] = janet_tuple_end(tuple_varargs);
//console.log("FIB next_slots", fiber.next_slots, func.def.arity, varargs, tuple_varargs);
//throw new Error("SET VARARGS");
}
return; // NOTE the frame is set on the fiber, it is not returned
}
// function janet_fiber(callee, argv) {
// /* fiber alloc */
// const fiber = new JanetFiber();
// /* fiber reset */
// // TODO
// /* janet_fiber_reset */
// /* fill frame with callee function */
// janet_fiber_funcframe(fiber, callee);
// const frame = fiber.frame;
// frame.slots.splice(0, argv.length, ...argv);
// return fiber;
// };
class JanetLibFunction {
lib_call(argv) {}
};
class JanetLib_fiber_new extends JanetLibFunction {
static create_new() { return new JanetLib_fiber_new() };
lib_call(...argv) {
const f = argv[0];
if (!f || !(f instanceof JanetFunction)) {
janet_panic("fiber/new needs a function as the first argument");
}
const opt_flags = argv[1];
if (opt_flags && !(opt_flags instanceof JanetKeyword)) {
janet_panic("fiber/new opt_flags needs to be a keyword");
}
const flags = opt_flags || janet_keyword("y");
//const flags = janet_keyword("e"); // TEMP DEV
console.log("MAking fiber wrapping function: ", f);
//const fiber = janet_fiber(f, []);
const fiber = new JanetFiber();