]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.c
print builtin now prints all the passed strings, not just the first
[xonotic/gmqcc.git] / exec.c
1 #include <errno.h>
2 #include <string.h>
3 #include <stdarg.h>
4
5 #include "gmqcc.h"
6
7 MEM_VEC_FUNCTIONS(qc_program,   prog_section_statement, code)
8 MEM_VEC_FUNCTIONS(qc_program,   prog_section_def,       defs)
9 MEM_VEC_FUNCTIONS(qc_program,   prog_section_def,       fields)
10 MEM_VEC_FUNCTIONS(qc_program,   prog_section_function,  functions)
11 MEM_VEC_FUNCTIONS(qc_program,   char,                   strings)
12 MEM_VEC_FUN_APPEND(qc_program,  char,                   strings)
13 MEM_VEC_FUN_RESIZE(qc_program,  char,                   strings)
14 MEM_VEC_FUNCTIONS(qc_program,   qcint,                  globals)
15 MEM_VEC_FUNCTIONS(qc_program,   qcint,                  entitydata)
16 MEM_VEC_FUNCTIONS(qc_program,   bool,                   entitypool)
17
18 MEM_VEC_FUNCTIONS(qc_program,   qcint,         localstack)
19 MEM_VEC_FUN_APPEND(qc_program,  qcint,         localstack)
20 MEM_VEC_FUN_RESIZE(qc_program,  qcint,         localstack)
21 MEM_VEC_FUNCTIONS(qc_program,   qc_exec_stack, stack)
22
23 MEM_VEC_FUNCTIONS(qc_program,   size_t, profile)
24 MEM_VEC_FUN_RESIZE(qc_program,  size_t, profile)
25
26 MEM_VEC_FUNCTIONS(qc_program,   prog_builtin, builtins)
27
28 static void loaderror(const char *fmt, ...)
29 {
30     int     err = errno;
31     va_list ap;
32     va_start(ap, fmt);
33     vprintf(fmt, ap);
34     va_end(ap);
35     printf(": %s\n", strerror(err));
36 }
37
38 static void printvmerr(const char *fmt, ...)
39 {
40     va_list ap;
41     va_start(ap, fmt);
42     vprintf(fmt, ap);
43     va_end(ap);
44     putchar('\n');
45 }
46
47 qc_program* prog_load(const char *filename)
48 {
49     qc_program *prog;
50     prog_header header;
51     size_t      i;
52     FILE *file;
53
54     file = fopen(filename, "rb");
55     if (!file)
56         return NULL;
57
58     if (fread(&header, sizeof(header), 1, file) != 1) {
59         loaderror("failed to read header from '%s'", filename);
60         fclose(file);
61         return NULL;
62     }
63
64     if (header.version != 6) {
65         loaderror("header says this is a version %i progs, we need version 6\n", header.version);
66         fclose(file);
67         return NULL;
68     }
69
70     prog = (qc_program*)mem_a(sizeof(qc_program));
71     if (!prog) {
72         fclose(file);
73         printf("failed to allocate program data\n");
74         return NULL;
75     }
76     memset(prog, 0, sizeof(*prog));
77
78     prog->entityfields = header.entfield;
79
80     prog->filename = util_strdup(filename);
81     if (!prog->filename) {
82         loaderror("failed to store program name");
83         goto error;
84     }
85
86 #define read_data(hdrvar, progvar, type)                                         \
87     if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {                      \
88         loaderror("seek failed");                                                \
89         goto error;                                                              \
90     }                                                                            \
91     prog->progvar##_alloc = header.hdrvar.length;                                \
92     prog->progvar##_count = header.hdrvar.length;                                \
93     prog->progvar = (type*)mem_a(header.hdrvar.length * sizeof(*prog->progvar)); \
94     if (!prog->progvar)                                                          \
95         goto error;                                                              \
96     if (fread(prog->progvar, sizeof(*prog->progvar), header.hdrvar.length, file) \
97         != header.hdrvar.length) {                                               \
98         loaderror("read failed");                                                \
99         goto error;                                                              \
100     }
101 #define read_data1(x, y) read_data(x, x, y)
102
103     read_data (statements, code, prog_section_statement);
104     read_data1(defs,             prog_section_def);
105     read_data1(fields,           prog_section_def);
106     read_data1(functions,        prog_section_function);
107     read_data1(strings,          char);
108     read_data1(globals,          qcint);
109
110     fclose(file);
111
112     /* profile counters */
113     if (!qc_program_profile_resize(prog, prog->code_count))
114         goto error;
115
116     /* Add tempstring area */
117     prog->tempstring_start = prog->strings_count;
118     prog->tempstring_at    = prog->strings_count;
119     if (!qc_program_strings_resize(prog, prog->strings_count + 16*1024))
120         goto error;
121
122     /* spawn the world entity */
123     if (!qc_program_entitypool_add(prog, true)) {
124         loaderror("failed to allocate world entity\n");
125         goto error;
126     }
127     for (i = 0; i < prog->entityfields; ++i) {
128         if (!qc_program_entitydata_add(prog, 0)) {
129             loaderror("failed to allocate world data\n");
130             goto error;
131         }
132     }
133     prog->entities = 1;
134
135     return prog;
136
137 error:
138     if (prog->filename)   mem_d(prog->filename);
139     if (prog->code)       mem_d(prog->code);
140     if (prog->defs)       mem_d(prog->defs);
141     if (prog->fields)     mem_d(prog->fields);
142     if (prog->functions)  mem_d(prog->functions);
143     if (prog->strings)    mem_d(prog->strings);
144     if (prog->globals)    mem_d(prog->globals);
145     if (prog->entitydata) mem_d(prog->entitydata);
146     if (prog->entitypool) mem_d(prog->entitypool);
147     mem_d(prog);
148     return NULL;
149 }
150
151 void prog_delete(qc_program *prog)
152 {
153     if (prog->filename) mem_d(prog->filename);
154     MEM_VECTOR_CLEAR(prog, code);
155     MEM_VECTOR_CLEAR(prog, defs);
156     MEM_VECTOR_CLEAR(prog, fields);
157     MEM_VECTOR_CLEAR(prog, functions);
158     MEM_VECTOR_CLEAR(prog, strings);
159     MEM_VECTOR_CLEAR(prog, globals);
160     MEM_VECTOR_CLEAR(prog, entitydata);
161     MEM_VECTOR_CLEAR(prog, entitypool);
162     MEM_VECTOR_CLEAR(prog, localstack);
163     MEM_VECTOR_CLEAR(prog, stack);
164     MEM_VECTOR_CLEAR(prog, profile);
165
166     if (prog->builtins_alloc) {
167         MEM_VECTOR_CLEAR(prog, builtins);
168     }
169     /* otherwise the builtins were statically allocated */
170     mem_d(prog);
171 }
172
173 /***********************************************************************
174  * VM code
175  */
176
177 char* prog_getstring(qc_program *prog, qcint str)
178 {
179     if (str < 0 || str >= prog->strings_count)
180         return "<<<invalid string>>>";
181     return prog->strings + str;
182 }
183
184 prog_section_def* prog_entfield(qc_program *prog, qcint off)
185 {
186     size_t i;
187     for (i = 0; i < prog->fields_count; ++i) {
188         if (prog->fields[i].offset == off)
189             return (prog->fields + i);
190     }
191     return NULL;
192 }
193
194 prog_section_def* prog_getdef(qc_program *prog, qcint off)
195 {
196     size_t i;
197     for (i = 0; i < prog->defs_count; ++i) {
198         if (prog->defs[i].offset == off)
199             return (prog->defs + i);
200     }
201     return NULL;
202 }
203
204 qcany* prog_getedict(qc_program *prog, qcint e)
205 {
206     if (e >= prog->entitypool_count) {
207         prog->vmerror++;
208         printf("Accessing out of bounds edict %i\n", (int)e);
209         e = 0;
210     }
211     return (qcany*)(prog->entitydata + (prog->entityfields * e));
212 }
213
214 qcint prog_spawn_entity(qc_program *prog)
215 {
216     size_t i;
217     qcint  e;
218     for (e = 0; e < (qcint)prog->entitypool_count; ++e) {
219         if (!prog->entitypool[e]) {
220             char *data = (char*)(prog->entitydata + (prog->entityfields * e));
221             memset(data, 0, prog->entityfields * sizeof(qcint));
222             return e;
223         }
224     }
225     if (!qc_program_entitypool_add(prog, true)) {
226         prog->vmerror++;
227         printf("Failed to allocate entity\n");
228         return 0;
229     }
230     prog->entities++;
231     for (i = 0; i < prog->entityfields; ++i) {
232         if (!qc_program_entitydata_add(prog, 0)) {
233             printf("Failed to allocate entity\n");
234             return 0;
235         }
236     }
237     return e;
238 }
239
240 void prog_free_entity(qc_program *prog, qcint e)
241 {
242     if (!e) {
243         prog->vmerror++;
244         printf("Trying to free world entity\n");
245         return;
246     }
247     if (e >= prog->entitypool_count) {
248         prog->vmerror++;
249         printf("Trying to free out of bounds entity\n");
250         return;
251     }
252     if (!prog->entitypool[e]) {
253         prog->vmerror++;
254         printf("Double free on entity\n");
255         return;
256     }
257     prog->entitypool[e] = false;
258 }
259
260 qcint prog_tempstring(qc_program *prog, const char *_str)
261 {
262     /* we don't access it, but the macro-generated functions don't use
263      * const
264      */
265     char *str = (char*)_str;
266
267     size_t len = strlen(str);
268     size_t at = prog->tempstring_at;
269
270     /* when we reach the end we start over */
271     if (at + len >= prog->strings_count)
272         at = prog->tempstring_start;
273
274     /* when it doesn't fit, reallocate */
275     if (at + len >= prog->strings_count)
276     {
277         prog->strings_count = at;
278         if (!qc_program_strings_append(prog, str, len+1)) {
279             prog->vmerror = VMERR_TEMPSTRING_ALLOC;
280             return 0;
281         }
282         return at;
283     }
284
285     /* when it fits, just copy */
286     memcpy(prog->strings + at, str, len+1);
287     prog->tempstring_at += len+1;
288     return at;
289 }
290
291 static int print_escaped_string(const char *str)
292 {
293     int len = 2;
294     putchar('"');
295     while (*str) {
296         switch (*str) {
297             case '\a': len += 2; putchar('\\'); putchar('a'); break;
298             case '\b': len += 2; putchar('\\'); putchar('b'); break;
299             case '\r': len += 2; putchar('\\'); putchar('r'); break;
300             case '\n': len += 2; putchar('\\'); putchar('n'); break;
301             case '\t': len += 2; putchar('\\'); putchar('t'); break;
302             case '\f': len += 2; putchar('\\'); putchar('f'); break;
303             case '\v': len += 2; putchar('\\'); putchar('v'); break;
304             case '\\': len += 2; putchar('\\'); putchar('\\'); break;
305             case '"':  len += 2; putchar('\\'); putchar('"'); break;
306             default:
307                 ++len;
308                 putchar(*str);
309                 break;
310         }
311         ++str;
312     }
313     putchar('"');
314     return len;
315 }
316
317 static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
318 {
319     static char spaces[16+1] = "            ";
320     prog_section_def *def;
321     qcany    *value;
322     int       len;
323
324     if (!glob)
325         return;
326
327     def = prog_getdef(prog, glob);
328     value = (qcany*)(&prog->globals[glob]);
329
330     if (def) {
331         len = printf("[%s] ", prog_getstring(prog, def->name));
332         vtype = def->type;
333     }
334     else
335         len = printf("[@%u] ", glob);
336
337     switch (vtype) {
338         case TYPE_VOID:
339         case TYPE_ENTITY:
340         case TYPE_FIELD:
341         case TYPE_FUNCTION:
342         case TYPE_POINTER:
343             len += printf("%i,", value->_int);
344             break;
345         case TYPE_VECTOR:
346             len += printf("'%g %g %g',", value->vector[0],
347                                          value->vector[1],
348                                          value->vector[2]);
349             break;
350         case TYPE_STRING:
351             len += print_escaped_string(prog_getstring(prog, value->string));
352             /* len += printf("\"%s\",", prog_getstring(prog, value->string)); */
353             break;
354         case TYPE_FLOAT:
355         default:
356             len += printf("%g,", value->_float);
357             break;
358     }
359     if (len < 16) {
360         spaces[16-len] = 0;
361         printf(spaces);
362         spaces[16-len] = ' ';
363     }
364 }
365
366 static void prog_print_statement(qc_program *prog, prog_section_statement *st)
367 {
368     if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
369         printf("<illegal instruction %d>\n", st->opcode);
370         return;
371     }
372     printf(" <> %-12s", asm_instr[st->opcode].m);
373     if (st->opcode >= INSTR_IF &&
374         st->opcode <= INSTR_IFNOT)
375     {
376         trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
377         printf("%d\n", st->o2.s1);
378     }
379     else if (st->opcode >= INSTR_CALL0 &&
380              st->opcode <= INSTR_CALL8)
381     {
382         printf("\n");
383     }
384     else if (st->opcode == INSTR_GOTO)
385     {
386         printf("%i\n", st->o1.s1);
387     }
388     else
389     {
390         int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
391         switch (st->opcode)
392         {
393             case INSTR_MUL_FV:
394                 t[1] = t[2] = TYPE_VECTOR;
395                 break;
396             case INSTR_MUL_VF:
397                 t[0] = t[2] = TYPE_VECTOR;
398                 break;
399             case INSTR_MUL_V:
400                 t[0] = t[1] = TYPE_VECTOR;
401                 break;
402             case INSTR_ADD_V:
403             case INSTR_SUB_V:
404             case INSTR_EQ_V:
405             case INSTR_NE_V:
406                 t[0] = t[1] = t[2] = TYPE_VECTOR;
407                 break;
408             case INSTR_EQ_S:
409             case INSTR_NE_S:
410                 t[0] = t[1] = TYPE_STRING;
411                 break;
412             case INSTR_STORE_V:
413                 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
414                 break;
415             case INSTR_STORE_S:
416                 t[0] = t[1] = TYPE_STRING; t[2] = -1;
417                 break;
418         }
419         if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
420         if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
421         if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
422         printf("\n");
423     }
424 }
425
426 static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
427 {
428     qc_exec_stack st;
429     size_t p, parampos;
430
431     /* back up locals */
432     st.localsp  = prog->localstack_count;
433     st.stmt     = prog->statement;
434     st.function = func;
435
436 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
437     if (prog->stack_count)
438     {
439         prog_section_function *cur;
440         cur = prog->stack[prog->stack_count-1].function;
441         if (cur)
442         {
443             qcint *globals = prog->globals + cur->firstlocal;
444             if (!qc_program_localstack_append(prog, globals, cur->locals))
445             {
446                 printf("out of memory\n");
447                 exit(1);
448             }
449         }
450     }
451 #else
452     {
453         qcint *globals = prog->globals + func->firstlocal;
454         if (!qc_program_localstack_append(prog, globals, func->locals))
455         {
456             printf("out of memory\n");
457             exit(1);
458         }
459     }
460 #endif
461
462     /* copy parameters */
463     parampos = func->firstlocal;
464     for (p = 0; p < func->nargs; ++p)
465     {
466         size_t s;
467         for (s = 0; s < func->argsize[p]; ++s) {
468             prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s];
469             ++parampos;
470         }
471     }
472
473     if (!qc_program_stack_add(prog, st)) {
474         printf("out of memory\n");
475         exit(1);
476     }
477
478     return func->entry;
479 }
480
481 static qcint prog_leavefunction(qc_program *prog)
482 {
483     prog_section_function *prev = NULL;
484     size_t oldsp;
485
486     qc_exec_stack st = prog->stack[prog->stack_count-1];
487
488 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
489     if (prog->stack_count > 1) {
490         prev  = prog->stack[prog->stack_count-2].function;
491         oldsp = prog->stack[prog->stack_count-2].localsp;
492     }
493 #else
494     prev  = prog->stack[prog->stack_count-1].function;
495     oldsp = prog->stack[prog->stack_count-1].localsp;
496 #endif
497     if (prev) {
498         qcint *globals = prog->globals + prev->firstlocal;
499         memcpy(globals, prog->localstack + oldsp, prev->locals);
500         if (!qc_program_localstack_resize(prog, oldsp)) {
501             printf("out of memory\n");
502             exit(1);
503         }
504     }
505
506     if (!qc_program_stack_remove(prog, prog->stack_count-1)) {
507         printf("out of memory\n");
508         exit(1);
509     }
510
511     return st.stmt;
512 }
513
514 bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps)
515 {
516     long jumpcount = 0;
517     prog_section_statement *st;
518
519     prog->vmerror = 0;
520
521     st = prog->code + prog_enterfunction(prog, func);
522     --st;
523     switch (flags)
524     {
525         default:
526         case 0:
527         {
528 #define QCVM_PROFILE 0
529 #define QCVM_TRACE   0
530 #           include "execloop.h"
531             break;
532         }
533         case (VMXF_TRACE):
534         {
535 #define QCVM_PROFILE 0
536 #define QCVM_TRACE   1
537 #           include "execloop.h"
538             break;
539         }
540         case (VMXF_PROFILE):
541         {
542 #define QCVM_PROFILE 1
543 #define QCVM_TRACE   0
544 #           include "execloop.h"
545             break;
546         }
547         case (VMXF_TRACE|VMXF_PROFILE):
548         {
549 #define QCVM_PROFILE 1
550 #define QCVM_TRACE   1
551 #           include "execloop.h"
552             break;
553         }
554     };
555
556 cleanup:
557     prog->localstack_count = 0;
558     prog->stack_count = 0;
559     if (prog->vmerror)
560         return false;
561     return true;
562 }
563
564 /***********************************************************************
565  * main for when building the standalone executor
566  */
567
568 #if defined(QCVM_EXECUTOR)
569 bool        opts_debug    = false;
570 bool        opts_memchk   = false;
571
572 #define CheckArgs(num) do {                                                    \
573     if (prog->argc != (num)) {                                                 \
574         prog->vmerror++;                                                       \
575         printf("ERROR: invalid number of arguments for %s: %i, expected %i\n", \
576         __FUNCTION__, prog->argc, (num));                                      \
577         return -1;                                                             \
578     }                                                                          \
579 } while (0)
580
581 #define GetGlobal(idx) ((qcany*)(prog->globals + (idx)))
582 #define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
583 #define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
584
585 static int qc_print(qc_program *prog)
586 {
587     size_t i;
588     for (i = 0; i < prog->argc; ++i) {
589         qcany *str = (qcany*)(prog->globals + OFS_PARM0 + 3*i);
590         printf("%s", prog_getstring(prog, str->string));
591     }
592     return 0;
593 }
594
595 static int qc_ftos(qc_program *prog)
596 {
597     char buffer[512];
598     qcany *num;
599     qcany str;
600     CheckArgs(1);
601     num = GetArg(0);
602     snprintf(buffer, sizeof(buffer), "%g", num->_float);
603     str.string = prog_tempstring(prog, buffer);
604     Return(str);
605     return 0;
606 }
607
608 static int qc_spawn(qc_program *prog)
609 {
610     qcany ent;
611     CheckArgs(0);
612     ent.edict = prog_spawn_entity(prog);
613     Return(ent);
614     return (ent.edict ? 0 : -1);
615 }
616
617 static int qc_kill(qc_program *prog)
618 {
619     qcany *ent;
620     CheckArgs(1);
621     ent = GetArg(0);
622     prog_free_entity(prog, ent->edict);
623     return 0;
624 }
625
626 static prog_builtin qc_builtins[] = {
627     NULL,
628     &qc_print,
629     &qc_ftos,
630     &qc_spawn,
631     &qc_kill
632 };
633 static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
634
635 int main(int argc, char **argv)
636 {
637     size_t      i;
638     qcint       fnmain = -1;
639     qc_program *prog;
640
641     if (argc != 2) {
642         printf("usage: %s file\n", argv[0]);
643         exit(1);
644     }
645
646     prog = prog_load(argv[1]);
647     if (!prog) {
648         printf("failed to load program '%s'\n", argv[1]);
649         exit(1);
650     }
651
652     prog->builtins       = qc_builtins;
653     prog->builtins_count = qc_builtins_count;
654     prog->builtins_alloc = 0;
655
656     for (i = 1; i < prog->functions_count; ++i) {
657         const char *name = prog_getstring(prog, prog->functions[i].name);
658         printf("Found function: %s\n", name);
659         if (!strcmp(name, "main"))
660             fnmain = (qcint)i;
661     }
662     printf("Entity field space: %i\n", (int)prog->entityfields);
663     if (fnmain > 0)
664     {
665         prog_exec(prog, &prog->functions[fnmain], VMXF_TRACE, VM_JUMPS_DEFAULT);
666     }
667     else
668         printf("No main function found\n");
669
670     prog_delete(prog);
671     return 0;
672 }
673 #endif