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