1 /*
2 * Copyright (c) 2021 Calvin Rose
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 * IN THE SOFTWARE.
21 */
22 
23 #ifndef JANET_AMALG
24 #include "features.h"
25 #include <janet.h>
26 #include "util.h"
27 #endif
28 
29 #include <setjmp.h>
30 
31 /* Conditionally compile this file */
32 #ifdef JANET_ASSEMBLER
33 
34 /* Definition for an instruction in the assembler */
35 typedef struct JanetInstructionDef JanetInstructionDef;
36 struct JanetInstructionDef {
37     const char *name;
38     enum JanetOpCode opcode;
39 };
40 
41 /* Hold all state needed during assembly */
42 typedef struct JanetAssembler JanetAssembler;
43 struct JanetAssembler {
44     JanetAssembler *parent;
45     JanetFuncDef *def;
46     jmp_buf on_error;
47     const uint8_t *errmessage;
48     int32_t errindex;
49 
50     int32_t environments_capacity;
51     int32_t defs_capacity;
52     int32_t bytecode_count; /* Used for calculating labels */
53 
54     Janet name;
55     JanetTable labels; /* keyword -> bytecode index */
56     JanetTable slots; /* symbol -> slot index */
57     JanetTable envs; /* symbol -> environment index */
58     JanetTable defs; /* symbol -> funcdefs index */
59 };
60 
61 /* Janet opcode descriptions in lexicographic order. This
62  * allows a binary search over the elements to find the
63  * correct opcode given a name. This works in reasonable
64  * time and is easier to setup statically than a hash table or
65  * prefix tree. */
66 static const JanetInstructionDef janet_ops[] = {
67     {"add", JOP_ADD},
68     {"addim", JOP_ADD_IMMEDIATE},
69     {"band", JOP_BAND},
70     {"bnot", JOP_BNOT},
71     {"bor", JOP_BOR},
72     {"bxor", JOP_BXOR},
73     {"call", JOP_CALL},
74     {"clo", JOP_CLOSURE},
75     {"cmp", JOP_COMPARE},
76     {"cncl", JOP_CANCEL},
77     {"div", JOP_DIVIDE},
78     {"divim", JOP_DIVIDE_IMMEDIATE},
79     {"eq", JOP_EQUALS},
80     {"eqim", JOP_EQUALS_IMMEDIATE},
81     {"err", JOP_ERROR},
82     {"get", JOP_GET},
83     {"geti", JOP_GET_INDEX},
84     {"gt", JOP_GREATER_THAN},
85     {"gte", JOP_GREATER_THAN_EQUAL},
86     {"gtim", JOP_GREATER_THAN_IMMEDIATE},
87     {"in", JOP_IN},
88     {"jmp", JOP_JUMP},
89     {"jmpif", JOP_JUMP_IF},
90     {"jmpni", JOP_JUMP_IF_NIL},
91     {"jmpnn", JOP_JUMP_IF_NOT_NIL},
92     {"jmpno", JOP_JUMP_IF_NOT},
93     {"ldc", JOP_LOAD_CONSTANT},
94     {"ldf", JOP_LOAD_FALSE},
95     {"ldi", JOP_LOAD_INTEGER},
96     {"ldn", JOP_LOAD_NIL},
97     {"lds", JOP_LOAD_SELF},
98     {"ldt", JOP_LOAD_TRUE},
99     {"ldu", JOP_LOAD_UPVALUE},
100     {"len", JOP_LENGTH},
101     {"lt", JOP_LESS_THAN},
102     {"lte", JOP_LESS_THAN_EQUAL},
103     {"ltim", JOP_LESS_THAN_IMMEDIATE},
104     {"mkarr", JOP_MAKE_ARRAY},
105     {"mkbtp", JOP_MAKE_BRACKET_TUPLE},
106     {"mkbuf", JOP_MAKE_BUFFER},
107     {"mkstr", JOP_MAKE_STRING},
108     {"mkstu", JOP_MAKE_STRUCT},
109     {"mktab", JOP_MAKE_TABLE},
110     {"mktup", JOP_MAKE_TUPLE},
111     {"mod", JOP_MODULO},
112     {"movf", JOP_MOVE_FAR},
113     {"movn", JOP_MOVE_NEAR},
114     {"mul", JOP_MULTIPLY},
115     {"mulim", JOP_MULTIPLY_IMMEDIATE},
116     {"neq", JOP_NOT_EQUALS},
117     {"neqim", JOP_NOT_EQUALS_IMMEDIATE},
118     {"next", JOP_NEXT},
119     {"noop", JOP_NOOP},
120     {"prop", JOP_PROPAGATE},
121     {"push", JOP_PUSH},
122     {"push2", JOP_PUSH_2},
123     {"push3", JOP_PUSH_3},
124     {"pusha", JOP_PUSH_ARRAY},
125     {"put", JOP_PUT},
126     {"puti", JOP_PUT_INDEX},
127     {"rem", JOP_REMAINDER},
128     {"res", JOP_RESUME},
129     {"ret", JOP_RETURN},
130     {"retn", JOP_RETURN_NIL},
131     {"setu", JOP_SET_UPVALUE},
132     {"sig", JOP_SIGNAL},
133     {"sl", JOP_SHIFT_LEFT},
134     {"slim", JOP_SHIFT_LEFT_IMMEDIATE},
135     {"sr", JOP_SHIFT_RIGHT},
136     {"srim", JOP_SHIFT_RIGHT_IMMEDIATE},
137     {"sru", JOP_SHIFT_RIGHT_UNSIGNED},
138     {"sruim", JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE},
139     {"sub", JOP_SUBTRACT},
140     {"tcall", JOP_TAILCALL},
141     {"tchck", JOP_TYPECHECK}
142 };
143 
144 /* Typename aliases for tchck instruction */
145 typedef struct TypeAlias {
146     const char *name;
147     int32_t mask;
148 } TypeAlias;
149 
150 static const TypeAlias type_aliases[] = {
151     {"abstract", JANET_TFLAG_ABSTRACT},
152     {"array", JANET_TFLAG_ARRAY},
153     {"boolean", JANET_TFLAG_BOOLEAN},
154     {"buffer", JANET_TFLAG_BUFFER},
155     {"callable", JANET_TFLAG_CALLABLE},
156     {"cfunction", JANET_TFLAG_CFUNCTION},
157     {"dictionary", JANET_TFLAG_DICTIONARY},
158     {"fiber", JANET_TFLAG_FIBER},
159     {"function", JANET_TFLAG_FUNCTION},
160     {"indexed", JANET_TFLAG_INDEXED},
161     {"keyword", JANET_TFLAG_KEYWORD},
162     {"nil", JANET_TFLAG_NIL},
163     {"number", JANET_TFLAG_NUMBER},
164     {"pointer", JANET_TFLAG_POINTER},
165     {"string", JANET_TFLAG_STRING},
166     {"struct", JANET_TFLAG_STRUCT},
167     {"symbol", JANET_TFLAG_SYMBOL},
168     {"table", JANET_TFLAG_TABLE},
169     {"tuple", JANET_TFLAG_TUPLE}
170 };
171 
172 /* Deinitialize an Assembler. Does not deinitialize the parents. */
janet_asm_deinit(JanetAssembler * a)173 static void janet_asm_deinit(JanetAssembler *a) {
174     janet_table_deinit(&a->slots);
175     janet_table_deinit(&a->labels);
176     janet_table_deinit(&a->envs);
177     janet_table_deinit(&a->defs);
178 }
179 
janet_asm_longjmp(JanetAssembler * a)180 static void janet_asm_longjmp(JanetAssembler *a) {
181 #if defined(JANET_BSD) || defined(JANET_APPLE)
182     _longjmp(a->on_error, 1);
183 #else
184     longjmp(a->on_error, 1);
185 #endif
186 }
187 
188 /* Throw some kind of assembly error */
janet_asm_error(JanetAssembler * a,const char * message)189 static void janet_asm_error(JanetAssembler *a, const char *message) {
190     a->errmessage = janet_formatc("%s, instruction %d", message, a->errindex);
191     janet_asm_longjmp(a);
192 }
193 #define janet_asm_assert(a, c, m) do { if (!(c)) janet_asm_error((a), (m)); } while (0)
194 
195 /* Throw some kind of assembly error */
janet_asm_errorv(JanetAssembler * a,const uint8_t * m)196 static void janet_asm_errorv(JanetAssembler *a, const uint8_t *m) {
197     a->errmessage = m;
198     janet_asm_longjmp(a);
199 }
200 
201 /* Add a closure environment to the assembler. Sub funcdefs may need
202  * to reference outer function environments, and may change the outer environment.
203  * Returns the index of the environment in the assembler's environments, or -1
204  * if not found. */
janet_asm_addenv(JanetAssembler * a,Janet envname)205 static int32_t janet_asm_addenv(JanetAssembler *a, Janet envname) {
206     Janet check;
207     JanetFuncDef *def = a->def;
208     int32_t envindex;
209     int32_t res;
210     if (janet_equals(a->name, envname)) {
211         return -1;
212     }
213     /* Check for memoized value */
214     check = janet_table_get(&a->envs, envname);
215     if (janet_checktype(check, JANET_NUMBER)) {
216         return (int32_t) janet_unwrap_number(check);
217     }
218     if (NULL == a->parent) return -2;
219     res = janet_asm_addenv(a->parent, envname);
220     if (res < -1) {
221         return res;
222     }
223     envindex = def->environments_length;
224     janet_table_put(&a->envs, envname, janet_wrap_number(envindex));
225     if (envindex >= a->environments_capacity) {
226         int32_t newcap = 2 * envindex;
227         def->environments = janet_realloc(def->environments, newcap * sizeof(int32_t));
228         if (NULL == def->environments) {
229             JANET_OUT_OF_MEMORY;
230         }
231         a->environments_capacity = newcap;
232     }
233     def->environments[envindex] = (int32_t) res;
234     def->environments_length = envindex + 1;
235     return envindex;
236 }
237 
238 /* Parse an argument to an assembly instruction, and return the result as an
239  * integer. This integer will need to be bounds checked. */
doarg_1(JanetAssembler * a,enum JanetOpArgType argtype,Janet x)240 static int32_t doarg_1(
241     JanetAssembler *a,
242     enum JanetOpArgType argtype,
243     Janet x) {
244     int32_t ret = -1;
245     JanetTable *c;
246     switch (argtype) {
247         default:
248             c = NULL;
249             break;
250         case JANET_OAT_SLOT:
251             c = &a->slots;
252             break;
253         case JANET_OAT_ENVIRONMENT:
254             c = &a->envs;
255             break;
256         case JANET_OAT_LABEL:
257             c = &a->labels;
258             break;
259         case JANET_OAT_FUNCDEF:
260             c = &a->defs;
261             break;
262     }
263     switch (janet_type(x)) {
264         default:
265             goto error;
266             break;
267         case JANET_NUMBER: {
268             double y = janet_unwrap_number(x);
269             if (janet_checkintrange(y)) {
270                 ret = (int32_t) y;
271             } else {
272                 goto error;
273             }
274             break;
275         }
276         case JANET_TUPLE: {
277             const Janet *t = janet_unwrap_tuple(x);
278             if (argtype == JANET_OAT_TYPE) {
279                 int32_t i = 0;
280                 ret = 0;
281                 for (i = 0; i < janet_tuple_length(t); i++) {
282                     ret |= doarg_1(a, JANET_OAT_SIMPLETYPE, t[i]);
283                 }
284             } else {
285                 goto error;
286             }
287             break;
288         }
289         case JANET_KEYWORD: {
290             if (NULL != c && argtype == JANET_OAT_LABEL) {
291                 Janet result = janet_table_get(c, x);
292                 if (janet_checktype(result, JANET_NUMBER)) {
293                     ret = janet_unwrap_integer(result) - a->bytecode_count;
294                 } else {
295                     goto error;
296                 }
297             } else if (argtype == JANET_OAT_TYPE || argtype == JANET_OAT_SIMPLETYPE) {
298                 const TypeAlias *alias = janet_strbinsearch(
299                                              &type_aliases,
300                                              sizeof(type_aliases) / sizeof(TypeAlias),
301                                              sizeof(TypeAlias),
302                                              janet_unwrap_keyword(x));
303                 if (alias) {
304                     ret = alias->mask;
305                 } else {
306                     janet_asm_errorv(a, janet_formatc("unknown type %v", x));
307                 }
308             } else {
309                 goto error;
310             }
311             break;
312         }
313         case JANET_SYMBOL: {
314             if (NULL != c) {
315                 Janet result = janet_table_get(c, x);
316                 if (janet_checktype(result, JANET_NUMBER)) {
317                     ret = (int32_t) janet_unwrap_number(result);
318                 } else {
319                     janet_asm_errorv(a, janet_formatc("unknown name %v", x));
320                 }
321             } else {
322                 goto error;
323             }
324             if (argtype == JANET_OAT_ENVIRONMENT && ret == -1) {
325                 /* Add a new env */
326                 ret = janet_asm_addenv(a, x);
327                 if (ret < -1) {
328                     janet_asm_errorv(a, janet_formatc("unknown environment %v", x));
329                 }
330             }
331             break;
332         }
333     }
334     if (argtype == JANET_OAT_SLOT && ret >= a->def->slotcount)
335         a->def->slotcount = (int32_t) ret + 1;
336     return ret;
337 
338 error:
339     janet_asm_errorv(a, janet_formatc("error parsing instruction argument %v", x));
340     return 0;
341 }
342 
343 /* Parse a single argument to an instruction. Trims it as well as
344  * try to convert arguments to bit patterns */
doarg(JanetAssembler * a,enum JanetOpArgType argtype,int nth,int nbytes,int hassign,Janet x)345 static uint32_t doarg(
346     JanetAssembler *a,
347     enum JanetOpArgType argtype,
348     int nth,
349     int nbytes,
350     int hassign,
351     Janet x) {
352     int32_t arg = doarg_1(a, argtype, x);
353     /* Calculate the min and max values that can be stored given
354      * nbytes, and whether or not the storage is signed */
355     int32_t max = (1 << ((nbytes << 3) - hassign)) - 1;
356     int32_t min = hassign ? -max - 1 : 0;
357     if (arg < min)
358         janet_asm_errorv(a, janet_formatc("instruction argument %v is too small, must be %d byte%s",
359                                           x, nbytes, nbytes > 1 ? "s" : ""));
360     if (arg > max)
361         janet_asm_errorv(a, janet_formatc("instruction argument %v is too large, must be %d byte%s",
362                                           x, nbytes, nbytes > 1 ? "s" : ""));
363     return ((uint32_t) arg) << (nth << 3);
364 }
365 
366 /* Provide parsing methods for the different kinds of arguments */
read_instruction(JanetAssembler * a,const JanetInstructionDef * idef,const Janet * argt)367 static uint32_t read_instruction(
368     JanetAssembler *a,
369     const JanetInstructionDef *idef,
370     const Janet *argt) {
371     uint32_t instr = idef->opcode;
372     enum JanetInstructionType type = janet_instructions[idef->opcode];
373     switch (type) {
374         case JINT_0: {
375             if (janet_tuple_length(argt) != 1)
376                 janet_asm_error(a, "expected 0 arguments: (op)");
377             break;
378         }
379         case JINT_S: {
380             if (janet_tuple_length(argt) != 2)
381                 janet_asm_error(a, "expected 1 argument: (op, slot)");
382             instr |= doarg(a, JANET_OAT_SLOT, 1, 2, 0, argt[1]);
383             break;
384         }
385         case JINT_L: {
386             if (janet_tuple_length(argt) != 2)
387                 janet_asm_error(a, "expected 1 argument: (op, label)");
388             instr |= doarg(a, JANET_OAT_LABEL, 1, 3, 1, argt[1]);
389             break;
390         }
391         case JINT_SS: {
392             if (janet_tuple_length(argt) != 3)
393                 janet_asm_error(a, "expected 2 arguments: (op, slot, slot)");
394             instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
395             instr |= doarg(a, JANET_OAT_SLOT, 2, 2, 0, argt[2]);
396             break;
397         }
398         case JINT_SL: {
399             if (janet_tuple_length(argt) != 3)
400                 janet_asm_error(a, "expected 2 arguments: (op, slot, label)");
401             instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
402             instr |= doarg(a, JANET_OAT_LABEL, 2, 2, 1, argt[2]);
403             break;
404         }
405         case JINT_ST: {
406             if (janet_tuple_length(argt) != 3)
407                 janet_asm_error(a, "expected 2 arguments: (op, slot, type)");
408             instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
409             instr |= doarg(a, JANET_OAT_TYPE, 2, 2, 0, argt[2]);
410             break;
411         }
412         case JINT_SI:
413         case JINT_SU: {
414             if (janet_tuple_length(argt) != 3)
415                 janet_asm_error(a, "expected 2 arguments: (op, slot, integer)");
416             instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
417             instr |= doarg(a, JANET_OAT_INTEGER, 2, 2, type == JINT_SI, argt[2]);
418             break;
419         }
420         case JINT_SD: {
421             if (janet_tuple_length(argt) != 3)
422                 janet_asm_error(a, "expected 2 arguments: (op, slot, funcdef)");
423             instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
424             instr |= doarg(a, JANET_OAT_FUNCDEF, 2, 2, 0, argt[2]);
425             break;
426         }
427         case JINT_SSS: {
428             if (janet_tuple_length(argt) != 4)
429                 janet_asm_error(a, "expected 3 arguments: (op, slot, slot, slot)");
430             instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
431             instr |= doarg(a, JANET_OAT_SLOT, 2, 1, 0, argt[2]);
432             instr |= doarg(a, JANET_OAT_SLOT, 3, 1, 0, argt[3]);
433             break;
434         }
435         case JINT_SSI:
436         case JINT_SSU: {
437             if (janet_tuple_length(argt) != 4)
438                 janet_asm_error(a, "expected 3 arguments: (op, slot, slot, integer)");
439             instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
440             instr |= doarg(a, JANET_OAT_SLOT, 2, 1, 0, argt[2]);
441             instr |= doarg(a, JANET_OAT_INTEGER, 3, 1, type == JINT_SSI, argt[3]);
442             break;
443         }
444         case JINT_SES: {
445             JanetAssembler *b = a;
446             uint32_t env;
447             if (janet_tuple_length(argt) != 4)
448                 janet_asm_error(a, "expected 3 arguments: (op, slot, environment, envslot)");
449             instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
450             env = doarg(a, JANET_OAT_ENVIRONMENT, 0, 1, 0, argt[2]);
451             instr |= env << 16;
452             for (env += 1; env > 0; env--) {
453                 b = b->parent;
454                 if (NULL == b)
455                     janet_asm_error(a, "invalid environment index");
456             }
457             instr |= doarg(b, JANET_OAT_SLOT, 3, 1, 0, argt[3]);
458             break;
459         }
460         case JINT_SC: {
461             if (janet_tuple_length(argt) != 3)
462                 janet_asm_error(a, "expected 2 arguments: (op, slot, constant)");
463             instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
464             instr |= doarg(a, JANET_OAT_CONSTANT, 2, 2, 0, argt[2]);
465             break;
466         }
467     }
468     return instr;
469 }
470 
471 /* Helper to get from a structure */
janet_get1(Janet ds,Janet key)472 static Janet janet_get1(Janet ds, Janet key) {
473     switch (janet_type(ds)) {
474         default:
475             return janet_wrap_nil();
476         case JANET_TABLE:
477             return janet_table_get(janet_unwrap_table(ds), key);
478         case JANET_STRUCT:
479             return janet_struct_get(janet_unwrap_struct(ds), key);
480     }
481 }
482 
483 /* Helper to assembly. Return the assembly result */
janet_asm1(JanetAssembler * parent,Janet source,int flags)484 static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int flags) {
485     JanetAssembleResult result;
486     JanetAssembler a;
487     Janet s = source;
488     JanetFuncDef *def;
489     int32_t count, i;
490     const Janet *arr;
491     Janet x;
492     (void) flags;
493 
494     /* Initialize funcdef */
495     def = janet_funcdef_alloc();
496 
497     /* Initialize Assembler */
498     a.def = def;
499     a.parent = parent;
500     a.errmessage = NULL;
501     a.errindex = 0;
502     a.environments_capacity = 0;
503     a.bytecode_count = 0;
504     a.defs_capacity = 0;
505     a.name = janet_wrap_nil();
506     janet_table_init(&a.labels, 0);
507     janet_table_init(&a.slots, 0);
508     janet_table_init(&a.envs, 0);
509     janet_table_init(&a.defs, 0);
510 
511     /* Set error jump */
512 #if defined(JANET_BSD) || defined(JANET_APPLE)
513     if (_setjmp(a.on_error)) {
514 #else
515     if (setjmp(a.on_error)) {
516 #endif
517         if (NULL != a.parent) {
518             janet_asm_deinit(&a);
519             janet_asm_longjmp(a.parent);
520         }
521         result.funcdef = NULL;
522         result.error = a.errmessage;
523         result.status = JANET_ASSEMBLE_ERROR;
524         janet_asm_deinit(&a);
525         return result;
526     }
527 
528     janet_asm_assert(&a,
529                      janet_checktype(s, JANET_STRUCT) ||
530                      janet_checktype(s, JANET_TABLE),
531                      "expected struct or table for assembly source");
532 
533     /* Check for function name */
534     a.name = janet_get1(s, janet_ckeywordv("name"));
535     if (!janet_checktype(a.name, JANET_NIL)) {
536         def->name = janet_to_string(a.name);
537     }
538 
539     /* Set function arity */
540     x = janet_get1(s, janet_ckeywordv("arity"));
541     def->arity = janet_checkint(x) ? janet_unwrap_integer(x) : 0;
542     janet_asm_assert(&a, def->arity >= 0, "arity must be non-negative");
543 
544     x = janet_get1(s, janet_ckeywordv("max-arity"));
545     def->max_arity = janet_checkint(x) ? janet_unwrap_integer(x) : def->arity;
546     janet_asm_assert(&a, def->max_arity >= def->arity, "max-arity must be greater than or equal to arity");
547 
548     x = janet_get1(s, janet_ckeywordv("min-arity"));
549     def->min_arity = janet_checkint(x) ? janet_unwrap_integer(x) : def->arity;
550     janet_asm_assert(&a, def->min_arity <= def->arity, "min-arity must be less than or equal to arity");
551 
552     /* Check vararg */
553     x = janet_get1(s, janet_ckeywordv("vararg"));
554     if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
555 
556     /* Check source */
557     x = janet_get1(s, janet_ckeywordv("source"));
558     if (janet_checktype(x, JANET_STRING)) def->source = janet_unwrap_string(x);
559 
560     /* Create slot aliases */
561     x = janet_get1(s, janet_ckeywordv("slots"));
562     if (janet_indexed_view(x, &arr, &count)) {
563         for (i = 0; i < count; i++) {
564             Janet v = arr[i];
565             if (janet_checktype(v, JANET_TUPLE)) {
566                 const Janet *t = janet_unwrap_tuple(v);
567                 int32_t j;
568                 for (j = 0; j < janet_tuple_length(t); j++) {
569                     if (!janet_checktype(t[j], JANET_SYMBOL))
570                         janet_asm_error(&a, "slot names must be symbols");
571                     janet_table_put(&a.slots, t[j], janet_wrap_integer(i));
572                 }
573             } else if (janet_checktype(v, JANET_SYMBOL)) {
574                 janet_table_put(&a.slots, v, janet_wrap_integer(i));
575             } else {
576                 janet_asm_error(&a, "slot names must be symbols or tuple of symbols");
577             }
578         }
579     }
580 
581     /* Parse constants */
582     x = janet_get1(s, janet_ckeywordv("constants"));
583     if (janet_indexed_view(x, &arr, &count)) {
584         def->constants_length = count;
585         def->constants = janet_malloc(sizeof(Janet) * (size_t) count);
586         if (NULL == def->constants) {
587             JANET_OUT_OF_MEMORY;
588         }
589         for (i = 0; i < count; i++) {
590             Janet ct = arr[i];
591             def->constants[i] = ct;
592         }
593     } else {
594         def->constants = NULL;
595         def->constants_length = 0;
596     }
597 
598     /* Parse sub funcdefs */
599     x = janet_get1(s, janet_ckeywordv("closures"));
600     if (janet_indexed_view(x, &arr, &count)) {
601         int32_t i;
602         for (i = 0; i < count; i++) {
603             JanetAssembleResult subres;
604             Janet subname;
605             int32_t newlen;
606             subres = janet_asm1(&a, arr[i], flags);
607             if (subres.status != JANET_ASSEMBLE_OK) {
608                 janet_asm_errorv(&a, subres.error);
609             }
610             subname = janet_get1(arr[i], janet_ckeywordv("name"));
611             if (!janet_checktype(subname, JANET_NIL)) {
612                 janet_table_put(&a.defs, subname, janet_wrap_integer(def->defs_length));
613             }
614             newlen = def->defs_length + 1;
615             if (a.defs_capacity < newlen) {
616                 int32_t newcap = newlen;
617                 def->defs = janet_realloc(def->defs, newcap * sizeof(JanetFuncDef *));
618                 if (NULL == def->defs) {
619                     JANET_OUT_OF_MEMORY;
620                 }
621                 a.defs_capacity = newcap;
622             }
623             def->defs[def->defs_length] = subres.funcdef;
624             def->defs_length = newlen;
625         }
626     }
627 
628     /* Parse bytecode and labels */
629     x = janet_get1(s, janet_ckeywordv("bytecode"));
630     if (janet_indexed_view(x, &arr, &count)) {
631         /* Do labels and find length */
632         int32_t blength = 0;
633         for (i = 0; i < count; ++i) {
634             Janet instr = arr[i];
635             if (janet_checktype(instr, JANET_KEYWORD)) {
636                 janet_table_put(&a.labels, instr, janet_wrap_integer(blength));
637             } else if (janet_checktype(instr, JANET_TUPLE)) {
638                 blength++;
639             } else {
640                 a.errindex = i;
641                 janet_asm_error(&a, "expected assembly instruction");
642             }
643         }
644         /* Allocate bytecode array */
645         def->bytecode_length = blength;
646         def->bytecode = janet_malloc(sizeof(uint32_t) * (size_t) blength);
647         if (NULL == def->bytecode) {
648             JANET_OUT_OF_MEMORY;
649         }
650         /* Do bytecode */
651         for (i = 0; i < count; ++i) {
652             Janet instr = arr[i];
653             if (janet_checktype(instr, JANET_KEYWORD)) {
654                 continue;
655             } else {
656                 uint32_t op;
657                 const JanetInstructionDef *idef;
658                 const Janet *t;
659                 a.errindex = i;
660                 janet_asm_assert(&a, janet_checktype(instr, JANET_TUPLE), "expected tuple");
661                 t = janet_unwrap_tuple(instr);
662                 if (janet_tuple_length(t) == 0) {
663                     op = 0;
664                 } else {
665                     janet_asm_assert(&a, janet_checktype(t[0], JANET_SYMBOL),
666                                      "expected symbol in assembly instruction");
667                     idef = janet_strbinsearch(
668                                &janet_ops,
669                                sizeof(janet_ops) / sizeof(JanetInstructionDef),
670                                sizeof(JanetInstructionDef),
671                                janet_unwrap_symbol(t[0]));
672                     if (NULL == idef)
673                         janet_asm_errorv(&a, janet_formatc("unknown instruction %v", t[0]));
674                     op = read_instruction(&a, idef, t);
675                 }
676                 def->bytecode[a.bytecode_count++] = op;
677             }
678         }
679     } else {
680         janet_asm_error(&a, "bytecode expected");
681     }
682     a.errindex = -1;
683 
684     /* Check for source mapping */
685     x = janet_get1(s, janet_ckeywordv("sourcemap"));
686     if (janet_indexed_view(x, &arr, &count)) {
687         janet_asm_assert(&a, count == def->bytecode_length, "sourcemap must have the same length as the bytecode");
688         def->sourcemap = janet_malloc(sizeof(JanetSourceMapping) * (size_t) count);
689         if (NULL == def->sourcemap) {
690             JANET_OUT_OF_MEMORY;
691         }
692         for (i = 0; i < count; i++) {
693             const Janet *tup;
694             Janet entry = arr[i];
695             JanetSourceMapping mapping;
696             if (!janet_checktype(entry, JANET_TUPLE)) {
697                 janet_asm_error(&a, "expected tuple");
698             }
699             tup = janet_unwrap_tuple(entry);
700             if (!janet_checkint(tup[0])) {
701                 janet_asm_error(&a, "expected integer");
702             }
703             if (!janet_checkint(tup[1])) {
704                 janet_asm_error(&a, "expected integer");
705             }
706             mapping.line = janet_unwrap_integer(tup[0]);
707             mapping.column = janet_unwrap_integer(tup[1]);
708             def->sourcemap[i] = mapping;
709         }
710     }
711 
712     /* Set environments */
713     def->environments =
714         janet_realloc(def->environments, def->environments_length * sizeof(int32_t));
715     if (NULL == def->environments) {
716         JANET_OUT_OF_MEMORY;
717     }
718 
719     /* Verify the func def */
720     if (janet_verify(def)) {
721         janet_asm_error(&a, "invalid assembly");
722     }
723 
724     /* Add final flags */
725     janet_def_addflags(def);
726 
727     /* Finish everything and return funcdef */
728     janet_asm_deinit(&a);
729     result.error = NULL;
730     result.funcdef = def;
731     result.status = JANET_ASSEMBLE_OK;
732     return result;
733 }
734 
735 /* Assemble a function */
736 JanetAssembleResult janet_asm(Janet source, int flags) {
737     return janet_asm1(NULL, source, flags);
738 }
739 
740 /* Disassembly */
741 
742 /* Find the definition of an instruction given the instruction word. Return
743  * NULL if not found. */
744 static const JanetInstructionDef *janet_asm_reverse_lookup(uint32_t instr) {
745     size_t i;
746     uint32_t opcode = instr & 0x7F;
747     for (i = 0; i < sizeof(janet_ops) / sizeof(JanetInstructionDef); i++) {
748         const JanetInstructionDef *def = janet_ops + i;
749         if (def->opcode == opcode)
750             return def;
751     }
752     return NULL;
753 }
754 
755 /* Create some constant sized tuples */
756 static const Janet *tup1(Janet x) {
757     Janet *tup = janet_tuple_begin(1);
758     tup[0] = x;
759     return janet_tuple_end(tup);
760 }
761 static const Janet *tup2(Janet x, Janet y) {
762     Janet *tup = janet_tuple_begin(2);
763     tup[0] = x;
764     tup[1] = y;
765     return janet_tuple_end(tup);
766 }
767 static const Janet *tup3(Janet x, Janet y, Janet z) {
768     Janet *tup = janet_tuple_begin(3);
769     tup[0] = x;
770     tup[1] = y;
771     tup[2] = z;
772     return janet_tuple_end(tup);
773 }
774 static const Janet *tup4(Janet w, Janet x, Janet y, Janet z) {
775     Janet *tup = janet_tuple_begin(4);
776     tup[0] = w;
777     tup[1] = x;
778     tup[2] = y;
779     tup[3] = z;
780     return janet_tuple_end(tup);
781 }
782 
783 /* Given an argument, convert it to the appropriate integer or symbol */
784 Janet janet_asm_decode_instruction(uint32_t instr) {
785     const JanetInstructionDef *def = janet_asm_reverse_lookup(instr);
786     Janet name;
787     if (NULL == def) {
788         return janet_wrap_integer((int32_t)instr);
789     }
790     name = janet_csymbolv(def->name);
791     const Janet *ret = NULL;
792 #define oparg(shift, mask) ((instr >> ((shift) << 3)) & (mask))
793     switch (janet_instructions[def->opcode]) {
794         case JINT_0:
795             ret = tup1(name);
796             break;
797         case JINT_S:
798             ret = tup2(name, janet_wrap_integer(oparg(1, 0xFFFFFF)));
799             break;
800         case JINT_L:
801             ret = tup2(name, janet_wrap_integer((int32_t)instr >> 8));
802             break;
803         case JINT_SS:
804         case JINT_ST:
805         case JINT_SC:
806         case JINT_SU:
807         case JINT_SD:
808             ret = tup3(name,
809                        janet_wrap_integer(oparg(1, 0xFF)),
810                        janet_wrap_integer(oparg(2, 0xFFFF)));
811             break;
812         case JINT_SI:
813         case JINT_SL:
814             ret =  tup3(name,
815                         janet_wrap_integer(oparg(1, 0xFF)),
816                         janet_wrap_integer((int32_t)instr >> 16));
817             break;
818         case JINT_SSS:
819         case JINT_SES:
820         case JINT_SSU:
821             ret = tup4(name,
822                        janet_wrap_integer(oparg(1, 0xFF)),
823                        janet_wrap_integer(oparg(2, 0xFF)),
824                        janet_wrap_integer(oparg(3, 0xFF)));
825             break;
826         case JINT_SSI:
827             ret = tup4(name,
828                        janet_wrap_integer(oparg(1, 0xFF)),
829                        janet_wrap_integer(oparg(2, 0xFF)),
830                        janet_wrap_integer((int32_t)instr >> 24));
831             break;
832     }
833 #undef oparg
834     if (ret) {
835         /* Check if break point set */
836         if (instr & 0x80) {
837             janet_tuple_flag(ret) |= JANET_TUPLE_FLAG_BRACKETCTOR;
838         }
839         return janet_wrap_tuple(ret);
840     }
841     return janet_wrap_nil();
842 }
843 
844 /*
845  * Disasm sections
846  */
847 
848 static Janet janet_disasm_arity(JanetFuncDef *def) {
849     return janet_wrap_integer(def->arity);
850 }
851 
852 static Janet janet_disasm_min_arity(JanetFuncDef *def) {
853     return janet_wrap_integer(def->min_arity);
854 }
855 
856 static Janet janet_disasm_max_arity(JanetFuncDef *def) {
857     return janet_wrap_integer(def->max_arity);
858 }
859 
860 static Janet janet_disasm_slotcount(JanetFuncDef *def) {
861     return janet_wrap_integer(def->slotcount);
862 }
863 
864 static Janet janet_disasm_bytecode(JanetFuncDef *def) {
865     JanetArray *bcode = janet_array(def->bytecode_length);
866     for (int32_t i = 0; i < def->bytecode_length; i++) {
867         bcode->data[i] = janet_asm_decode_instruction(def->bytecode[i]);
868     }
869     bcode->count = def->bytecode_length;
870     return janet_wrap_array(bcode);
871 }
872 
873 static Janet janet_disasm_source(JanetFuncDef *def) {
874     if (def->source != NULL) return janet_wrap_string(def->source);
875     return janet_wrap_nil();
876 }
877 
878 static Janet janet_disasm_name(JanetFuncDef *def) {
879     if (def->name != NULL) return janet_wrap_string(def->name);
880     return janet_wrap_nil();
881 }
882 
883 static Janet janet_disasm_vararg(JanetFuncDef *def) {
884     return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_VARARG);
885 }
886 
887 static Janet janet_disasm_constants(JanetFuncDef *def) {
888     JanetArray *constants = janet_array(def->constants_length);
889     for (int32_t i = 0; i < def->constants_length; i++) {
890         constants->data[i] = def->constants[i];
891     }
892     constants->count = def->constants_length;
893     return janet_wrap_array(constants);
894 }
895 
896 static Janet janet_disasm_sourcemap(JanetFuncDef *def) {
897     if (NULL == def->sourcemap) return janet_wrap_nil();
898     JanetArray *sourcemap = janet_array(def->bytecode_length);
899     for (int32_t i = 0; i < def->bytecode_length; i++) {
900         Janet *t = janet_tuple_begin(2);
901         JanetSourceMapping mapping = def->sourcemap[i];
902         t[0] = janet_wrap_integer(mapping.line);
903         t[1] = janet_wrap_integer(mapping.column);
904         sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t));
905     }
906     sourcemap->count = def->bytecode_length;
907     return janet_wrap_array(sourcemap);
908 }
909 
910 static Janet janet_disasm_environments(JanetFuncDef *def) {
911     JanetArray *envs = janet_array(def->environments_length);
912     for (int32_t i = 0; i < def->environments_length; i++) {
913         envs->data[i] = janet_wrap_integer(def->environments[i]);
914     }
915     envs->count = def->environments_length;
916     return janet_wrap_array(envs);
917 }
918 
919 static Janet janet_disasm_defs(JanetFuncDef *def) {
920     JanetArray *defs = janet_array(def->defs_length);
921     for (int32_t i = 0; i < def->defs_length; i++) {
922         defs->data[i] = janet_disasm(def->defs[i]);
923     }
924     defs->count = def->defs_length;
925     return janet_wrap_array(defs);
926 }
927 
928 Janet janet_disasm(JanetFuncDef *def) {
929     JanetTable *ret = janet_table(10);
930     janet_table_put(ret, janet_ckeywordv("arity"), janet_disasm_arity(def));
931     janet_table_put(ret, janet_ckeywordv("min-arity"), janet_disasm_min_arity(def));
932     janet_table_put(ret, janet_ckeywordv("max-arity"), janet_disasm_max_arity(def));
933     janet_table_put(ret, janet_ckeywordv("bytecode"), janet_disasm_bytecode(def));
934     janet_table_put(ret, janet_ckeywordv("source"), janet_disasm_source(def));
935     janet_table_put(ret, janet_ckeywordv("vararg"), janet_disasm_vararg(def));
936     janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def));
937     janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def));
938     janet_table_put(ret, janet_ckeywordv("constants"), janet_disasm_constants(def));
939     janet_table_put(ret, janet_ckeywordv("sourcemap"), janet_disasm_sourcemap(def));
940     janet_table_put(ret, janet_ckeywordv("environments"), janet_disasm_environments(def));
941     janet_table_put(ret, janet_ckeywordv("defs"), janet_disasm_defs(def));
942     return janet_wrap_struct(janet_table_to_struct(ret));
943 }
944 
945 JANET_CORE_FN(cfun_asm,
946               "(asm assembly)",
947               "Returns a new function that is the compiled result of the assembly.\n"
948               "The syntax for the assembly can be found on the Janet website, and should correspond\n"
949               "to the return value of disasm. Will throw an\n"
950               "error on invalid assembly.") {
951     janet_fixarity(argc, 1);
952     JanetAssembleResult res;
953     res = janet_asm(argv[0], 0);
954     if (res.status != JANET_ASSEMBLE_OK) {
955         janet_panics(res.error);
956     }
957     return janet_wrap_function(janet_thunk(res.funcdef));
958 }
959 
960 JANET_CORE_FN(cfun_disasm,
961               "(disasm func &opt field)",
962               "Returns assembly that could be used to compile the given function. "
963               "func must be a function, not a c function. Will throw on error on a badly "
964               "typed argument. If given a field name, will only return that part of the function assembly. "
965               "Possible fields are:\n\n"
966               "* :arity - number of required and optional arguments.\n"
967               "* :min-arity - minimum number of arguments function can be called with.\n"
968               "* :max-arity - maximum number of arguments function can be called with.\n"
969               "* :vararg - true if function can take a variable number of arguments.\n"
970               "* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n"
971               "* :source - name of source file that this function was compiled from.\n"
972               "* :name - name of function.\n"
973               "* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n"
974               "* :constants - an array of constants referenced by this function.\n"
975               "* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n"
976               "* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n"
977               "* :defs - other function definitions that this function may instantiate.\n") {
978     janet_arity(argc, 1, 2);
979     JanetFunction *f = janet_getfunction(argv, 0);
980     if (argc == 2) {
981         JanetKeyword kw = janet_getkeyword(argv, 1);
982         if (!janet_cstrcmp(kw, "arity")) return janet_disasm_arity(f->def);
983         if (!janet_cstrcmp(kw, "min-arity")) return janet_disasm_min_arity(f->def);
984         if (!janet_cstrcmp(kw, "max-arity")) return janet_disasm_max_arity(f->def);
985         if (!janet_cstrcmp(kw, "bytecode")) return janet_disasm_bytecode(f->def);
986         if (!janet_cstrcmp(kw, "source")) return janet_disasm_source(f->def);
987         if (!janet_cstrcmp(kw, "name")) return janet_disasm_name(f->def);
988         if (!janet_cstrcmp(kw, "vararg")) return janet_disasm_vararg(f->def);
989         if (!janet_cstrcmp(kw, "slotcount")) return janet_disasm_slotcount(f->def);
990         if (!janet_cstrcmp(kw, "constants")) return janet_disasm_constants(f->def);
991         if (!janet_cstrcmp(kw, "sourcemap")) return janet_disasm_sourcemap(f->def);
992         if (!janet_cstrcmp(kw, "environments")) return janet_disasm_environments(f->def);
993         if (!janet_cstrcmp(kw, "defs")) return janet_disasm_defs(f->def);
994         janet_panicf("unknown disasm key %v", argv[1]);
995     } else {
996         return janet_disasm(f->def);
997     }
998 }
999 
1000 /* Load the library */
1001 void janet_lib_asm(JanetTable *env) {
1002     JanetRegExt asm_cfuns[] = {
1003         JANET_CORE_REG("asm", cfun_asm),
1004         JANET_CORE_REG("disasm", cfun_disasm),
1005         JANET_REG_END
1006     };
1007     janet_core_cfuns_ext(env, NULL, asm_cfuns);
1008 }
1009 
1010 #endif
1011