2009-09-01 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / tests / metadata-verifier / gen-md-tests.c
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <glib.h>
6
7 #include <mono/metadata/image.h>
8 #include <mono/metadata/metadata.h>
9 #include <mono/metadata/assembly.h>
10 #include <mono/metadata/marshal.h>
11 #include <mono/metadata/class-internals.h>
12 #include <mono/metadata/metadata-internals.h>
13
14 #if 1
15 #define DEBUG_PARSER(stmt) do { stmt; } while (0)
16 #else
17 #define DEBUG_PARSER(stmt)
18 #endif
19
20 #if 0
21 #define DEBUG_SCANNER(stmt) do { stmt; } while (0)
22 #define SCANNER_DEBUG 1
23 #else
24 #define DEBUG_SCANNER(stmt)
25 #endif
26
27 /*
28 Grammar:
29
30 tokens:
31         comment ::= '#.*<eol>
32         identifier ::= ([a-z] | [A-Z]) ([a-z] | [A-Z] | [0-9] | [_-.])* 
33         hexa_digit = [0-9] | [a-f] | [A-F]
34         number ::= hexadecimal | decimal
35         hexadecimal ::= (+-)?('0' [xX])? hexa_digit+
36         decimal ::= 0 [0-9]+
37         eol ::= <eol>
38         punctuation ::= [{}]
39
40 program:
41         test_case*
42
43 test_case:
44         identifier '{' assembly_directive test_entry* '}'
45
46 assembly_directive:
47  'assembly' identifier  
48
49 test_entry:
50         validity patch (',' patch)*
51
52 validity:
53         'valid' | 'invalid' | 'badrt'
54
55 patch:
56         selector effect
57
58 selector:
59         'offset' expression
60
61 effect:
62         ('set-byte' | 'set-ushort' | 'set-uint' | 'set-bit' | 'or-byte' | 'or-ushort' | 'or-uint' | 'truncate' ) expression
63
64 expression:
65         atom ([+-] atom)*
66
67 atom:
68         number | variable | function_call
69
70 function_call:
71         fun_name '(' arg_list ')'
72
73 fun_name:
74         read.byte |
75         read.ushort |
76         read.uint |
77         translate.rva |
78         translate.rva.ind |
79         stream-header |
80         table-row |
81         blob.i
82
83 arg_list:
84         expression |
85         expression ',' arg_list
86
87 variable:
88         file-size |
89         pe-header |
90         pe-optional-header |
91         pe-signature |
92         section-table |
93         cli-header |
94         cli-metadata |
95         tables-header
96
97 TODO For the sake of a simple implementation, tokens are space delimited.
98 */
99
100 enum {
101         TOKEN_INVALID,
102         TOKEN_ID,
103         TOKEN_NUM,
104         TOKEN_PUNC,
105         TOKEN_EOF,
106 };
107
108 enum {
109         OK,
110         INVALID_TOKEN_TYPE,
111         INVALID_PUNC_TEXT,
112         INVALID_ID_TEXT,
113         INVALID_VALIDITY_TEST,
114         INVALID_NUMBER,
115         INVALID_FILE_NAME,
116         INVALID_SELECTOR,
117         INVALID_EFFECT,
118         INVALID_EXPRESSION,
119         INVALID_VARIABLE_NAME,
120         INVALID_FUNCTION_NAME,
121         INVALID_ARG_COUNT,
122         INVALID_RVA,
123         INVALID_BAD_FILE
124 };
125
126 enum {
127         TEST_TYPE_VALID,
128         TEST_TYPE_INVALID,
129         TEST_TYPE_BADRT
130 };
131
132 enum {
133         SELECTOR_ABS_OFFSET,
134 };
135
136 enum {
137         EFFECT_SET_BYTE,
138         EFFECT_SET_USHORT,
139         EFFECT_SET_UINT,
140         EFFECT_SET_TRUNC,
141         EFFECT_SET_BIT,
142         EFFECT_OR_BYTE,
143         EFFECT_OR_USHORT,
144         EFFECT_OR_UINT,
145 };
146
147 enum {
148         EXPRESSION_CONSTANT,
149         EXPRESSION_VARIABLE,
150         EXPRESSION_ADD,
151         EXPRESSION_SUB,
152         EXPRESSION_FUNC
153 };
154
155 typedef struct _expression expression_t;
156
157 typedef struct {
158         int type;
159         int start, end; /*stream range text is in [start, end[*/
160         int line;
161 } token_t;
162
163 typedef struct {
164         char *input;
165         int idx, size, line;
166         token_t current;
167 } scanner_t;
168
169
170 struct _expression {
171         int type;
172         union {
173                 gint32 constant;
174                 char *name;
175                 struct {
176                         expression_t *left;
177                         expression_t *right;
178                 } bin;
179                 struct {
180                         char *name;
181                         GSList *args;
182                 } func;
183         } data;
184 };
185
186
187 typedef struct {
188         int type;
189         expression_t *expression;
190 } patch_selector_t;
191
192
193 typedef struct {
194         int type;
195         expression_t *expression;
196 } patch_effect_t;
197
198 typedef struct {
199         patch_selector_t *selector;
200         patch_effect_t *effect;
201 } test_patch_t;
202
203 typedef struct {
204         char *name;
205         char *assembly;
206         int count;
207
208         char *assembly_data;
209         int assembly_size;
210         MonoImage *image;
211         int init;
212 } test_set_t;
213
214 typedef struct {
215         int validity;
216         GSList *patches; /*of test_patch_t*/
217         char *data;
218         int data_size;
219         test_set_t *test_set;
220 } test_entry_t;
221
222
223
224 /*******************************************************************************************************/
225 static guint32 expression_eval (expression_t *exp, test_entry_t *entry);
226 static expression_t* parse_expression (scanner_t *scanner);
227
228 static const char*
229 test_validity_name (int validity)
230 {
231         switch (validity) {
232         case TEST_TYPE_VALID:
233                 return "valid";
234         case TEST_TYPE_INVALID:
235                 return "invalid";
236         case TEST_TYPE_BADRT:
237                 return "badrt";
238         default:
239                 printf ("Invalid test type %d\n", validity);
240                 exit (INVALID_VALIDITY_TEST);
241         }
242 }
243
244 static char*
245 read_whole_file_and_close (const char *name, int *file_size)
246 {
247         FILE *file = fopen (name, "ro");
248         char *res;
249         int fsize;
250
251         if (!file) {
252                 printf ("Could not open file %s\n", name);
253                 exit (INVALID_FILE_NAME);
254         }
255
256         fseek (file, 0, SEEK_END);
257         fsize = ftell (file);
258         fseek (file, 0, SEEK_SET);
259
260         res = g_malloc (fsize + 1);
261
262         fread (res, fsize, 1, file);
263         fclose (file);
264         *file_size = fsize;
265         return res;
266 }
267
268 static void
269 init_test_set (test_set_t *test_set)
270 {
271         MonoImageOpenStatus status;
272         if (test_set->init)
273                 return;
274         test_set->assembly_data = read_whole_file_and_close (test_set->assembly, &test_set->assembly_size);
275         test_set->image = mono_image_open_from_data (test_set->assembly_data, test_set->assembly_size, FALSE, &status);
276         if (!test_set->image || status != MONO_IMAGE_OK) {
277                 printf ("Could not parse image %s\n", test_set->assembly);
278                 exit (INVALID_BAD_FILE);
279         }
280         
281         test_set->init = 1;
282 }
283
284 static char*
285 make_test_name (test_entry_t *entry, test_set_t *test_set)
286 {
287         return g_strdup_printf ("%s-%s-%d.exe", test_validity_name (entry->validity), test_set->name, test_set->count++);
288 }
289
290 #define READ_VAR(KIND, PTR) GUINT32_FROM_LE((guint32)*((KIND*)(PTR)))
291 #define SET_VAR(KIND, PTR, VAL) do { *((KIND*)(PTR)) = GUINT32_TO_LE ((KIND)VAL); }  while (0)
292
293 #define READ_BIT(PTR,OFF) ((((guint8*)(PTR))[(OFF / 8)] & (1 << ((OFF) % 8))) != 0)
294 #define SET_BIT(PTR,OFF) do { ((guint8*)(PTR))[(OFF / 8)] |= (1 << ((OFF) % 8)); } while (0)
295
296 static guint32 
297 get_pe_header (test_entry_t *entry)
298 {
299         return READ_VAR (guint32, entry->data + 0x3c) + 4;
300 }
301
302 static guint32
303 translate_rva (test_entry_t *entry, guint32 rva)
304 {
305         guint32 pe_header = get_pe_header (entry);
306         guint32 sectionCount = READ_VAR (guint16, entry->data + pe_header + 2);
307         guint32 idx = pe_header + 244;
308
309         while (sectionCount-- > 0) {
310                 guint32 size = READ_VAR (guint32, entry->data + idx + 8);
311                 guint32 base = READ_VAR (guint32, entry->data + idx + 12);
312                 guint32 offset = READ_VAR (guint32, entry->data + idx + 20);
313
314                 if (rva >= base && rva <= base + size)
315                         return (rva - base) + offset;
316                 idx += 40;
317         }
318         printf ("Could not translate RVA %x\n", rva);
319         exit (INVALID_RVA);
320 }
321
322 static guint32
323 get_cli_header (test_entry_t *entry)
324 {
325         guint32 offset = get_pe_header (entry) + 20; /*pe-optional-header*/
326         offset += 208; /*cli header entry offset in the pe-optional-header*/
327         return translate_rva (entry, READ_VAR (guint32, entry->data + offset));
328 }
329
330 static guint32
331 get_cli_metadata_root (test_entry_t *entry)
332 {
333         guint32 offset = get_cli_header (entry);
334         offset += 8; /*metadata rva offset*/
335         return translate_rva (entry, READ_VAR (guint32, entry->data + offset));
336 }
337
338 static guint32
339 pad4 (guint32 offset)
340 {
341         if (offset % 4)
342                 offset += 4 - (offset % 4);
343         return offset;
344 }
345
346 static guint32
347 get_metadata_stream_header (test_entry_t *entry, guint32 idx)
348 {
349         guint32 offset;
350
351         offset = get_cli_metadata_root (entry);
352         offset = pad4 (offset + 16 + READ_VAR (guint32, entry->data + offset + 12));
353
354         offset += 4;
355
356         while (idx--) {
357                 int i;
358
359                 offset += 8;
360                 for (i = 0; i < 32; ++i) {
361                         if (!READ_VAR (guint8, entry->data + offset++))
362                                 break;
363                 }
364                 offset = pad4 (offset);
365         }
366         return offset;  
367 }
368
369 static guint32
370 lookup_var (test_entry_t *entry, const char *name)
371 {
372         if (!strcmp ("file-size", name))
373                 return entry->data_size;
374         if (!strcmp ("pe-signature", name))
375                 return get_pe_header (entry) - 4;
376         if (!strcmp ("pe-header", name))
377                 return get_pe_header (entry); 
378         if (!strcmp ("pe-optional-header", name))
379                 return get_pe_header (entry) + 20; 
380         if (!strcmp ("section-table", name))
381                 return get_pe_header (entry) + 244; 
382         if (!strcmp ("cli-header", name))
383                 return get_cli_header (entry);
384         if (!strcmp ("cli-metadata", name)) 
385                 return get_cli_metadata_root (entry);
386         if (!strcmp ("tables-header", name)) {
387                 guint32 metadata_root = get_cli_metadata_root (entry);
388                 guint32 tilde_stream = get_metadata_stream_header (entry, 0);
389                 guint32 offset = READ_VAR (guint32, entry->data + tilde_stream);
390                 return metadata_root + offset;
391         }
392
393         printf ("Unknown variable in expression %s\n", name);
394         exit (INVALID_VARIABLE_NAME);
395 }
396
397 static guint32
398 call_func (test_entry_t *entry, const char *name, GSList *args)
399 {
400         if (!strcmp ("read.byte", name)) {
401                 guint32 offset;
402                 if (g_slist_length (args) != 1) {
403                         printf ("Invalid number of args to read.ushort %d\n", g_slist_length (args));
404                         exit (INVALID_ARG_COUNT);
405                 }
406                 offset = expression_eval (args->data, entry);
407                 return READ_VAR (guint8, entry->data + offset);
408         }
409         if (!strcmp ("read.ushort", name)) {
410                 guint32 offset;
411                 if (g_slist_length (args) != 1) {
412                         printf ("Invalid number of args to read.ushort %d\n", g_slist_length (args));
413                         exit (INVALID_ARG_COUNT);
414                 }
415                 offset = expression_eval (args->data, entry);
416                 return READ_VAR (guint16, entry->data + offset);
417         }
418         if (!strcmp ("read.uint", name)) {
419                 guint32 offset;
420                 if (g_slist_length (args) != 1) {
421                         printf ("Invalid number of args to read.uint %d\n", g_slist_length (args));
422                         exit (INVALID_ARG_COUNT);
423                 }
424                 offset = expression_eval (args->data, entry);
425                 return READ_VAR (guint32, entry->data + offset);
426         }
427         if (!strcmp ("translate.rva", name)) {
428                 guint32 rva;
429                 if (g_slist_length (args) != 1) {
430                         printf ("Invalid number of args to translate.rva %d\n", g_slist_length (args));
431                         exit (INVALID_ARG_COUNT);
432                 }
433                 rva = expression_eval (args->data, entry);
434                 return translate_rva (entry, rva);
435         }
436         if (!strcmp ("translate.rva.ind", name)) {
437                 guint32 rva;
438                 if (g_slist_length (args) != 1) {
439                         printf ("Invalid number of args to translate.rva.ind %d\n", g_slist_length (args));
440                         exit (INVALID_ARG_COUNT);
441                 }
442                 rva = expression_eval (args->data, entry);
443                 rva = READ_VAR (guint32, entry->data + rva);
444                 return translate_rva (entry, rva);
445         }
446         if (!strcmp ("stream-header", name)) {
447                 guint32 idx;
448                 if (g_slist_length (args) != 1) {
449                         printf ("Invalid number of args to stream-header %d\n", g_slist_length (args));
450                         exit (INVALID_ARG_COUNT);
451                 }
452                 idx = expression_eval (args->data, entry);
453                 return get_metadata_stream_header (entry, idx);
454         }
455         if (!strcmp ("table-row", name)) {
456                 const char *data;
457                 guint32 table, row;
458                 const MonoTableInfo *info;
459                 if (g_slist_length (args) != 2) {
460                         printf ("Invalid number of args to table-row %d\n", g_slist_length (args));
461                         exit (INVALID_ARG_COUNT);
462                 }
463                 table = expression_eval (args->data, entry);
464                 row = expression_eval (args->next->data, entry);
465                 info = mono_image_get_table_info (entry->test_set->image, table);
466                 data = info->base + row * info->row_size;
467                 return data - entry->test_set->assembly_data;
468         }
469         if (!strcmp ("blob.i", name)) {
470                 guint32 offset, base;
471                 MonoStreamHeader blob = entry->test_set->image->heap_blob;
472                 if (g_slist_length (args) != 1) {
473                         printf ("Invalid number of args to blob %d\n", g_slist_length (args));
474                         exit (INVALID_ARG_COUNT);
475                 }
476                 base = blob.data - entry->test_set->image->raw_data;
477                 offset = expression_eval (args->data, entry);
478                 offset = READ_VAR (guint16, entry->data + offset);
479                 return base + offset;
480         }
481
482         printf ("Unknown function %s\n", name);
483         exit (INVALID_FUNCTION_NAME);
484
485 }
486
487 static guint32
488 expression_eval (expression_t *exp, test_entry_t *entry)
489 {
490         switch (exp->type) {
491         case EXPRESSION_CONSTANT:
492                 return exp->data.constant;
493         case EXPRESSION_VARIABLE:
494                 return lookup_var (entry, exp->data.name);
495         case EXPRESSION_ADD:
496                 return expression_eval (exp->data.bin.left, entry) + expression_eval (exp->data.bin.right, entry);
497         case EXPRESSION_SUB:
498                 return expression_eval (exp->data.bin.left, entry) - expression_eval (exp->data.bin.right, entry);
499         case EXPRESSION_FUNC:
500                 return call_func (entry, exp->data.func.name, exp->data.func.args);
501         default:
502                 printf ("Invalid expression type %d\n", exp->type);
503                 exit (INVALID_EXPRESSION);
504         }       return 0;
505 }
506
507 static guint32
508 apply_selector (patch_selector_t *selector, test_entry_t *entry)
509 {
510         guint32 value = 0;
511         if (selector->expression)
512                 value = expression_eval (selector->expression, entry);
513         switch (selector->type) {
514         case SELECTOR_ABS_OFFSET:
515                 DEBUG_PARSER (printf("\tabsolute offset selector [%04x]\n", value));
516                 return value;
517         default:
518                 printf ("Invalid selector type %d\n", selector->type);
519                 exit (INVALID_SELECTOR);
520         }
521 }
522
523 static void
524 apply_effect (patch_effect_t *effect, test_entry_t *entry, guint32 offset)
525 {
526         gint32 value = 0;
527         char *ptr = entry->data + offset;
528         if (effect->expression)
529                 value = expression_eval (effect->expression, entry);
530
531         switch (effect->type) {
532         case EFFECT_SET_BYTE:
533                 DEBUG_PARSER (printf("\tset-byte effect old value [%x] new value [%x]\n", READ_VAR (guint8, ptr), value));
534                 SET_VAR (guint8, ptr, value);
535                 break;
536         case EFFECT_SET_USHORT:
537                 DEBUG_PARSER (printf("\tset-ushort effect old value [%x] new value [%x]\n", READ_VAR (guint16, ptr), value));
538                 SET_VAR (guint16, ptr, value);
539                 break;
540         case EFFECT_SET_UINT:
541                 DEBUG_PARSER (printf("\tset-uint effect old value [%x] new value [%x]\n", READ_VAR (guint32, ptr), value));
542                 SET_VAR (guint32, ptr, value);
543                 break;
544         case EFFECT_SET_TRUNC:
545                 DEBUG_PARSER (printf("\ttrunc effect [%d]\n", offset));
546                 entry->data_size = offset;
547                 break;
548         case EFFECT_SET_BIT:
549                 DEBUG_PARSER (printf("\tset-bit effect bit %d old value [%x]\n", value, READ_BIT (ptr, value)));
550                 SET_BIT (ptr, value);
551                 break;
552         case EFFECT_OR_BYTE:
553                 DEBUG_PARSER (printf("\tor-byte effect old value [%x] new value [%x]\n", READ_VAR (guint8, ptr), value));
554                 SET_VAR (guint8, ptr, READ_VAR (guint8, ptr) | value);
555                 break;
556         case EFFECT_OR_USHORT:
557                 DEBUG_PARSER (printf("\tor-ushort effect old value [%x] new value [%x]\n", READ_VAR (guint16, ptr), value));
558                 SET_VAR (guint16, ptr, READ_VAR (guint16, ptr) | value);
559                 break;
560         case EFFECT_OR_UINT:
561                 DEBUG_PARSER (printf("\tor-uint effect old value [%x] new value [%x]\n", READ_VAR (guint32, ptr), value));
562                 SET_VAR (guint32, ptr, READ_VAR (guint32, ptr) | value);
563                 break;
564         default:
565                 printf ("Invalid effect type %d\n", effect->type);
566                 exit (INVALID_EFFECT);
567         }
568 }
569
570 static void
571 apply_patch (test_entry_t *entry, test_patch_t *patch)
572 {
573         guint32 offset = apply_selector (patch->selector, entry);
574         apply_effect (patch->effect, entry, offset);
575 }
576
577 static void
578 process_test_entry (test_set_t *test_set, test_entry_t *entry)
579 {
580         GSList *tmp;
581         char *file_name;
582         FILE *f;
583
584         init_test_set (test_set);
585         entry->data = g_memdup (test_set->assembly_data, test_set->assembly_size);
586         entry->data_size = test_set->assembly_size;
587         entry->test_set = test_set;
588
589         DEBUG_PARSER (printf("(%d)%s\n", test_set->count, test_validity_name (entry->validity)));
590         for (tmp = entry->patches; tmp; tmp = tmp->next)
591                 apply_patch (entry, tmp->data);
592
593         file_name = make_test_name (entry, test_set);
594
595         f = fopen (file_name, "wo");
596         fwrite (entry->data, entry->data_size, 1, f);
597         fclose (f);
598
599         g_free (file_name);
600 }       
601
602 /*******************************************************************************************************/
603
604 static void
605 patch_free (test_patch_t *patch)
606 {
607         free (patch->selector);
608         free (patch->effect);
609         free (patch);
610 }
611
612 static void
613 test_set_free (test_set_t *set)
614 {
615         free (set->name);
616         free (set->assembly);
617         free (set->assembly_data);
618         if (set->image)
619                 mono_image_close (set->image);
620 }
621
622 static void
623 test_entry_free (test_entry_t *entry)
624 {
625         GSList *tmp;
626
627         free (entry->data);
628         for (tmp = entry->patches; tmp; tmp = tmp->next)
629                 patch_free (tmp->data);
630         g_slist_free (entry->patches);
631 }
632
633
634 /*******************************************************************************************************/
635 static const char*
636 token_type_name (int type)
637 {
638         switch (type) {
639         case TOKEN_INVALID:
640                 return "invalid";
641         case TOKEN_ID:
642                 return "identifier";
643         case TOKEN_NUM:
644                 return "number";
645         case TOKEN_PUNC:
646                 return "punctuation";
647         case TOKEN_EOF:
648                 return "end of file";
649         }
650         return "unknown token type";
651 }
652
653 #define CUR_CHAR (scanner->input [scanner->idx])
654
655 static int
656 is_eof (scanner_t *scanner)
657 {
658         return scanner->idx >= scanner->size;
659 }
660
661 static int
662 ispunct_char (int c)
663 {
664         return c == '{' || c == '}' || c == ',';
665 }
666
667 static void
668 skip_spaces (scanner_t *scanner)
669 {
670 start:
671         while (!is_eof (scanner) && isspace (CUR_CHAR)) {
672                 if (CUR_CHAR == '\n')
673                         ++scanner->line;
674                 ++scanner->idx;
675         }
676         if (CUR_CHAR == '#') {
677                 while (!is_eof (scanner) && CUR_CHAR != '\n') {
678                         ++scanner->idx;
679                 }
680                 goto start;
681         }
682 }
683
684 static char*
685 token_text_dup (scanner_t *scanner, token_t *token)
686 {
687         int len = token->end - token->start;
688         
689         char *str = g_memdup (scanner->input + token->start, len + 1);
690         str [len] = 0;
691         return str;
692 }
693
694 #if SCANNER_DEBUG
695 static void
696 dump_token (scanner_t *scanner, token_t *token)
697 {
698         char *str = token_text_dup (scanner, token);
699         
700         printf ("token '%s' of type '%s' at line %d\n", str, token_type_name (token->type), token->line);
701         free (str);
702 }
703
704 #endif
705
706 static gboolean
707 is_special_char (char c)
708 {
709         switch (c) {
710         case ';':
711         case ',':
712         case '{':
713         case '}':
714         case '(':
715         case ')':
716                 return TRUE;
717         }
718         return FALSE;
719 }
720
721 static void
722 next_token (scanner_t *scanner)
723 {
724         int start, end, type;
725         char c;
726         skip_spaces (scanner);
727         start = scanner->idx;
728         while (!is_eof (scanner) && !isspace (CUR_CHAR)) {
729                 if (scanner->idx == start) {
730                         if (is_special_char (CUR_CHAR)) {
731                                 ++scanner->idx;
732                                 break;
733                         }
734                 } else if (is_special_char (CUR_CHAR))
735                         break;
736                 ++scanner->idx;
737         }
738         end = scanner->idx;
739
740         c = scanner->input [start];
741         if (start >= scanner->size)
742                 type = TOKEN_EOF;
743         else if (isdigit (c) || c == '\'')
744                 type = TOKEN_NUM;
745         else if (ispunct_char (c))
746                 type = TOKEN_PUNC;
747         else
748                 type = TOKEN_ID;
749         scanner->current.start = start;
750         scanner->current.end = end;
751         scanner->current.type = type;
752         scanner->current.line = scanner->line;
753
754         DEBUG_SCANNER (dump_token (scanner, &scanner->current));
755 }
756
757 static scanner_t*
758 scanner_new (const char *file_name)
759 {
760         scanner_t *res;
761
762         res = g_new0 (scanner_t, 1);
763         res->input = read_whole_file_and_close (file_name, &res->size);
764
765         res->line = 1;
766         next_token (res);
767
768         return res;
769 }
770
771 static void
772 scanner_free (scanner_t *scanner)
773 {
774         free (scanner->input);
775         free (scanner);
776 }
777
778 static token_t*
779 scanner_get_current_token (scanner_t *scanner)
780 {
781         return &scanner->current;
782 }
783
784 static int
785 scanner_get_type (scanner_t *scanner)
786 {
787         return scanner_get_current_token (scanner)->type;
788 }
789
790 static int
791 scanner_get_line (scanner_t *scanner)
792 {
793         return scanner_get_current_token (scanner)->line;
794 }
795
796 static char*
797 scanner_text_dup (scanner_t *scanner)
798 {
799         return token_text_dup (scanner, scanner_get_current_token (scanner));
800 }
801
802 static int
803 scanner_text_parse_number (scanner_t *scanner, long *res)
804 {
805         char *text = scanner_text_dup (scanner);
806         char *end = NULL;
807         int ok;
808         if (text [0] == '\'') {
809                 ok = strlen (text) != 3 || text [2] != '\'';
810                 if (!ok)
811                         *res = text [1];
812         } else {
813                 *res = strtol (text, &end, 0);
814                 ok = *end;
815         }
816         free (text);
817
818         return ok;
819 }
820
821 static int
822 match_current_type (scanner_t *scanner, int type)
823 {
824         return scanner_get_type (scanner) == type;
825 }
826
827 static int
828 match_current_text (scanner_t *scanner, const char *text)
829 {
830         token_t *t = scanner_get_current_token (scanner);
831         return !strncmp (scanner->input + t->start, text, t->end - t->start);
832 }
833
834 static int
835 match_current_type_and_text (scanner_t *scanner, int type, const char *text)
836 {
837         return match_current_type (scanner, type)  && match_current_text (scanner, text);
838 }
839
840 /*******************************************************************************************************/
841 #define FAIL(MSG, REASON) do { \
842         printf ("%s at line %d for rule %s\n", MSG, scanner_get_line (scanner), __FUNCTION__);  \
843         exit (REASON);  \
844 } while (0);
845
846 #define EXPECT_TOKEN(TYPE) do { \
847         if (scanner_get_type (scanner) != TYPE) { \
848                 printf ("Expected %s but got %s '%s' at line %d for rule %s\n", token_type_name (TYPE), token_type_name (scanner_get_type (scanner)), scanner_text_dup (scanner), scanner_get_line (scanner), __FUNCTION__);    \
849                 exit (INVALID_TOKEN_TYPE);      \
850         }       \
851 } while (0)
852
853 #define CONSUME_SPECIFIC_PUNCT(TEXT) do { \
854         EXPECT_TOKEN (TOKEN_PUNC);      \
855         if (!match_current_text (scanner, TEXT)) { \
856                 char *__tmp = scanner_text_dup (scanner);       \
857                 printf ("Expected '%s' but got '%s' at line %d for rule %s\n", TEXT, __tmp, scanner_get_line (scanner), __FUNCTION__);  \
858                 free (__tmp); \
859                 exit (INVALID_PUNC_TEXT);       \
860         }       \
861         next_token (scanner); \
862 } while (0)
863
864 #define CONSUME_IDENTIFIER(DEST) do { \
865         EXPECT_TOKEN (TOKEN_ID);        \
866         DEST = scanner_text_dup (scanner);      \
867         next_token (scanner); \
868 } while (0)
869
870 #define CONSUME_SPECIFIC_IDENTIFIER(TEXT) do { \
871         EXPECT_TOKEN (TOKEN_ID);        \
872         if (!match_current_text (scanner, TEXT)) { \
873                 char *__tmp = scanner_text_dup (scanner);       \
874                 printf ("Expected '%s' but got '%s' at line %d for rule %s\n", TEXT, __tmp, scanner_get_line (scanner), __FUNCTION__);  \
875                 free (__tmp); \
876                 exit (INVALID_ID_TEXT); \
877         }       \
878         next_token (scanner); \
879 } while (0)
880
881 #define CONSUME_NUMBER(DEST) do { \
882         long __tmp_num; \
883         EXPECT_TOKEN (TOKEN_NUM);       \
884         if (scanner_text_parse_number (scanner, &__tmp_num)) {  \
885                 char *__tmp = scanner_text_dup (scanner);       \
886                 printf ("Expected a number but got '%s' at line %d for rule %s\n", __tmp, scanner_get_line (scanner), __FUNCTION__);    \
887                 free (__tmp); \
888                 exit (INVALID_NUMBER);  \
889         }       \
890         DEST = __tmp_num; \
891         next_token (scanner); \
892 } while (0)
893
894 #define LA_ID(TEXT) (scanner_get_type (scanner) == TOKEN_ID && match_current_text (scanner, TEXT))
895 #define LA_PUNCT(TEXT) (scanner_get_type (scanner) == TOKEN_PUNC && match_current_text (scanner, TEXT))
896
897 /*******************************************************************************************************/
898
899 static expression_t*
900 parse_atom (scanner_t *scanner)
901 {
902         expression_t *atom = g_new0 (expression_t, 1);
903         if (scanner_get_type (scanner) == TOKEN_NUM) {
904                 atom->type = EXPRESSION_CONSTANT;
905                 CONSUME_NUMBER (atom->data.constant);
906         } else {
907                 char *name;
908                 CONSUME_IDENTIFIER (name);
909                 if (LA_ID ("(")) {
910                         atom->data.func.name = name;
911                         atom->type = EXPRESSION_FUNC;
912                         CONSUME_SPECIFIC_IDENTIFIER ("(");
913
914                         while (!LA_ID (")") && !match_current_type (scanner, TOKEN_EOF))
915                                 atom->data.func.args = g_slist_append (atom->data.func.args, parse_expression (scanner));
916
917                         CONSUME_SPECIFIC_IDENTIFIER (")");
918                 } else {
919                         atom->data.name = name;
920                         atom->type = EXPRESSION_VARIABLE;
921                 }
922         }
923         return atom;
924 }
925
926
927 static expression_t*
928 parse_expression (scanner_t *scanner)
929 {
930         expression_t *exp = parse_atom (scanner);
931
932         while (LA_ID ("-") || LA_ID ("+")) {
933                 char *text;
934                 CONSUME_IDENTIFIER (text);
935                 expression_t *left = exp;
936                 exp = g_new0 (expression_t, 1);
937                 exp->type = !strcmp ("+", text) ? EXPRESSION_ADD: EXPRESSION_SUB;
938                 exp->data.bin.left = left;
939                 exp->data.bin.right = parse_atom (scanner);
940         }
941         return exp;
942 }
943
944
945 static patch_selector_t*
946 parse_selector (scanner_t *scanner)
947 {
948         patch_selector_t *selector;
949
950         CONSUME_SPECIFIC_IDENTIFIER ("offset");
951
952         selector = g_new0 (patch_selector_t, 1);
953         selector->type = SELECTOR_ABS_OFFSET;
954         selector->expression = parse_expression (scanner);
955         return selector;
956 }
957
958 static patch_effect_t*
959 parse_effect (scanner_t *scanner)
960 {
961         patch_effect_t *effect;
962         char *name;
963         int type;
964
965         CONSUME_IDENTIFIER(name);
966
967         if (!strcmp ("set-byte", name))
968                 type = EFFECT_SET_BYTE; 
969         else if (!strcmp ("set-ushort", name))
970                 type = EFFECT_SET_USHORT; 
971         else if (!strcmp ("set-uint", name))
972                 type = EFFECT_SET_UINT; 
973         else if (!strcmp ("set-bit", name))
974                 type = EFFECT_SET_BIT; 
975         else if (!strcmp ("truncate", name))
976                 type = EFFECT_SET_TRUNC;
977         else if (!strcmp ("or-byte", name))
978                 type = EFFECT_OR_BYTE;
979         else if (!strcmp ("or-ushort", name))
980                 type = EFFECT_OR_USHORT;
981         else if (!strcmp ("or-uint", name))
982                 type = EFFECT_OR_UINT;
983         else 
984                 FAIL(g_strdup_printf ("Invalid effect kind, expected one of: (set-byte set-ushort set-uint set-bit or-byte or-ushort or-uint truncate) but got %s",name), INVALID_ID_TEXT);
985
986         effect = g_new0 (patch_effect_t, 1);
987         effect->type = type;
988         if (type != EFFECT_SET_TRUNC)
989                 effect->expression = parse_expression (scanner);
990         return effect;
991 }
992
993 static test_patch_t*
994 parse_patch (scanner_t *scanner)
995 {
996         test_patch_t *patch;
997
998         patch = g_new0 (test_patch_t, 1);
999         patch->selector = parse_selector (scanner);
1000         patch->effect = parse_effect (scanner);
1001         return patch;
1002 }
1003
1004 static int
1005 parse_validity (scanner_t *scanner)
1006 {
1007         char *name = NULL;
1008         int validity;
1009         CONSUME_IDENTIFIER (name);
1010
1011         if (!strcmp (name, "valid"))
1012                 validity = TEST_TYPE_VALID;
1013         else if (!strcmp (name, "invalid"))
1014                 validity = TEST_TYPE_INVALID;
1015         else if (!strcmp (name, "badrt"))
1016                 validity = TEST_TYPE_BADRT;
1017         else {
1018                 printf ("Expected either 'valid', 'invalid' or 'badtr' but got '%s' at the begining of a test entry at line %d\n", name, scanner_get_line (scanner));
1019                 exit (INVALID_VALIDITY_TEST);
1020         }
1021
1022         free (name);
1023         return validity;
1024 }
1025
1026 static void
1027 parse_test_entry (scanner_t *scanner, test_set_t *test_set)
1028 {
1029         test_entry_t entry = { 0 };
1030         
1031         entry.validity = parse_validity (scanner);
1032
1033         do {
1034                 if (entry.patches)
1035                         CONSUME_SPECIFIC_PUNCT (",");
1036                 entry.patches = g_slist_append (entry.patches, parse_patch (scanner));
1037         } while (match_current_type_and_text (scanner, TOKEN_PUNC, ","));
1038
1039         process_test_entry (test_set, &entry);
1040
1041         test_entry_free (&entry);
1042 }
1043
1044 static void
1045 parse_test (scanner_t *scanner)
1046 {
1047         test_set_t set = { 0 };
1048
1049         CONSUME_IDENTIFIER (set.name);
1050         CONSUME_SPECIFIC_PUNCT ("{");
1051         CONSUME_SPECIFIC_IDENTIFIER ("assembly");
1052         CONSUME_IDENTIFIER (set.assembly);
1053
1054         DEBUG_PARSER (printf ("RULE %s using assembly %s\n", set.name, set.assembly));
1055
1056         while (!match_current_type (scanner, TOKEN_EOF) && !match_current_type_and_text (scanner, TOKEN_PUNC, "}"))
1057                 parse_test_entry (scanner, &set);
1058
1059         CONSUME_SPECIFIC_PUNCT ("}");
1060
1061         test_set_free (&set);
1062 }
1063
1064
1065 static void
1066 parse_program (scanner_t *scanner)
1067 {
1068         while (!match_current_type (scanner, TOKEN_EOF))
1069                 parse_test (scanner);
1070 }
1071
1072
1073 static void
1074 digest_file (const char *file)
1075 {
1076         scanner_t *scanner = scanner_new (file); 
1077         parse_program (scanner);
1078         scanner_free (scanner);
1079 }
1080
1081 int
1082 main (int argc, char **argv)
1083 {
1084         if (argc != 2) {
1085                 printf ("usage: gen-md.test file_to_process\n");
1086                 return 1;
1087         }
1088
1089         mono_init_version ("gen-md-test", "v2.0.50727");
1090         mono_marshal_init ();
1091
1092         digest_file (argv [1]);
1093         return 0;
1094 }
1095