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