]> git.buserror.net Git - polintos/scott/priv.git/blob - idlcomp/input.cc
Add copyright to idlcomp files that were previously just "Written by:".
[polintos/scott/priv.git] / idlcomp / input.cc
1 // Code to load symbols from a legacy filesystem
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal with
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 // of the Software, and to permit persons to whom the Software is furnished to do
10 // so, subject to the following condition:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
21 // SOFTWARE.
22
23 #include <cstdio>
24 #include <vector>
25 #include <memory>
26 #include <cstring>
27
28 #include <errno.h>
29 #include <dirent.h>
30
31 #include <idlc.h>
32 #include <parser.h>
33
34 // To deal with namespaces on a case-insensitive filesystem, names
35 // are annotated with a hexadecimal prefix indicating the case of each
36 // letter.  Each bit in the number represents a letter, with 0 being
37 // lowercase (or a non-letter) and 1 being uppercase.  The
38 // least-significant bit is the first letter.  This is done on both
39 // case sensitive and case insensitive filesystems so that the output
40 // can be freely copied between them (it also simplifies the code).
41 // As such, hex digits should be in upper case so that case-sensitive
42 // lookups can be used.  This prefix is separated from the actual name
43 // by an underscore.
44 //
45 // When the time comes along that UTF-8 is supported, this applies to
46 // UTF-8 characters, not ASCII characters.
47 //
48 // This mangling can also be used (with a leading underscore or
49 // whatever) in case-insensitive languages, though ideally there
50 // should be aliases with the unannotated name if there is no
51 // conflict.
52 //
53 // I don't want to go the CORBA route of disallowing case conflicts,
54 // as I find using capitals for types and lowercase for instances to
55 // be a useful convention (and less ugly than appending _t, except
56 // when case sensitivity is missing), and would rather not be limited
57 // at such a fundamental level by broken languages.
58
59 static char tohex(int digit)
60 {
61         if (digit < 10)
62                 return digit + '0';
63         
64         return digit + 'A' - 10;
65 }
66
67 const String *NameSpace::mangle(const String *name)
68 {
69         String *mangled_string = new String;
70         
71         // The conversion is done manually so that I don't have to deal
72         // with bignum arithmetic.
73         
74         std::vector<bool> hex;
75         std::vector<bool>::size_type name_len = name->length();
76         
77         // Pad out the first digit with leading zeroes, so that
78         // we don't have to deal with that later.
79         
80         if (name_len & 3)
81                 for (int i = name_len & 3; i < 4; ++i)
82                         hex.push_back(0);
83
84         for (string::const_iterator i = name->begin(); i != name->end(); ++i)
85                 hex.push_back(isupper(*i));
86
87         assert((hex.size() & 3) == 0);
88
89         for (std::vector<bool>::size_type i = 0; i < hex.size(); i += 4) {
90                 int digit = hex[i]     * 8 +
91                             hex[i + 1] * 4 +
92                             hex[i + 2] * 2 +
93                             hex[i + 3];
94
95                 *mangled_string += tohex(digit);
96         }
97         
98         *mangled_string += '_';
99         *mangled_string += *name;
100         return mangled_string;
101 }
102
103 struct IOError
104 {
105         const char *msg;
106
107         IOError(FILE *f)
108         {
109                 if (ferror(f))
110                         msg = strerror(ferror(f));
111                 else
112                         msg = "Short File";
113         }
114 };
115
116 struct ImportContext {
117         const String *name;
118         const char *filename;
119         File f;
120         NameSpace *parent;
121         bool swap;
122         bool is_dir;
123         bool is_typedef;
124         CompiledDefHeader hdr;
125 };
126
127 static void import_and_validate_array(ImportContext &ctx,
128                                       CompiledArray &out,
129                                       CompiledArray &in)
130 {
131         out.bounds[0] = swap64(in.bounds[0], ctx.swap);
132         out.bounds[1] = swap64(in.bounds[1], ctx.swap);
133
134         if (out.bounds[0] < 0) {
135                 fprintf(stderr, "\"%s\" is not a valid object "
136                                 "(bad array lower bound %" PRId64 ").\n",
137                         ctx.filename, out.bounds[0]);
138                 throw UserError();
139         }
140         
141         if (out.bounds[1] < -1) {
142                 fprintf(stderr, "\"%s\" is not a valid object "
143                                 "(bad array upper bound %" PRId64 ").\n",
144                         ctx.filename, out.bounds[0]);
145                 throw UserError();
146         }
147         
148         if (out.bounds[1] > 0 &&
149             out.bounds[1] < out.bounds[0]) {
150                 fprintf(stderr, "\"%s\" is not a valid object "
151                                 "(array upper bound %" PRId64 " less than lower bound %" PRId64 ").\n",
152                         ctx.filename, out.bounds[1], out.bounds[0]);
153                 throw UserError();
154         }
155 }
156
157 static void import_and_validate_basictype(ImportContext &ctx,
158                                           CompiledBasicType &out, 
159                                           CompiledBasicType &in)
160 {
161         out.bits = swap32(in.bits, ctx.swap);
162         out.flags.raw = swap32(in.flags.raw, ctx.swap);
163
164         int num = 0;
165         if (out.flags.field.Unsigned)
166                 num++;
167         if (out.flags.field.Float)
168                 num++;
169         if (out.flags.field.Bool)
170                 num++;
171         if (num > 1) {
172                 fprintf(stderr, "\"%s\" is not a valid object "
173                                 "(bad flags 0x%08x).\n",
174                         ctx.filename, out.flags.raw);
175                 throw UserError();
176         }
177
178         if (out.flags.field.Bool) {
179                 if (out.bits != 0) {
180                         fprintf(stderr, "\"%s\" is not a valid object "
181                                         "(bad bits %d for bool type).\n",
182                                 ctx.filename, out.bits);
183                         throw UserError();
184                 }
185         } else if (out.flags.field.Float) {
186                 if (out.bits != 32 && out.bits != 64) {
187                         fprintf(stderr, "\"%s\" is not a valid object "
188                                         "(bad bits %d for floating point type).\n",
189                                 ctx.filename, out.bits);
190                         throw UserError();
191                 }
192         } else if (!dynamic_cast<BitField *>(ctx.parent) &&
193                    !dynamic_cast<Enum *>(ctx.parent)) {
194                 if (out.bits != 8 && out.bits != 16 &&
195                     out.bits != 32 && out.bits != 64) {
196                         fprintf(stderr, "\"%s\" is not a valid object "
197                                         "(bad bits %d for integer type).\n",
198                                 ctx.filename, out.bits);
199                         throw UserError();
200                 }
201                 
202                 if (out.bits == 8 && !out.flags.field.Unsigned) {
203                         fprintf(stderr, "\"%s\" is not a valid object "
204                                         "(octet type must be unsigned).\n",
205                                 ctx.filename);
206                         throw UserError();
207                 }
208         }
209         
210         // Bitfield entries can take anywhere from 1 to 64 bits, but
211         // will be verified by the parent after all entries have loaded,
212         // so it can verify that the sum does not exceed the size of
213         // the bitfield.
214
215         import_and_validate_array(ctx, out.array, in.array);
216 }
217
218 BasicType *BasicType::import(ImportContext &ctx)
219 {
220         if (ctx.is_dir) {
221                 fprintf(stderr, "\"%s\" is not a valid object "
222                                 "(a BasicType must be a file).\n",
223                         ctx.filename);
224                 throw UserError();
225         }
226
227         CompiledBasicType def;
228         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
229                 throw IOError(ctx.f);
230
231         BasicType *bt = new BasicType(ctx.name);
232         ctx.parent->add_import(bt, ctx.filename);
233         
234         import_and_validate_basictype(ctx, bt->def, def);
235
236         bt->complete = true;
237         return bt;
238 }
239
240 static String *read_string_raw(ImportContext &ctx, int32_t length)
241 {
242         if (length < 0 || length > 4096) {
243                 fprintf(stderr, "\"%s\" is not a valid object "
244                                 "(unreasonable string length %d).\n",
245                         ctx.filename, length);
246                 throw UserError();
247         }
248
249         // Is there a way to reserve space in a C++ string, and pass
250         // the buffer into fread?
251         
252         char *buf = new char[length + 1];
253         
254         if (fread(buf, length + 1, 1, ctx.f) != 1)
255                 throw IOError(ctx.f);
256         
257         String *s = new String(buf);
258         delete[] buf;
259         return s;
260 }
261
262 static const String *read_string(ImportContext &ctx)
263 {
264         off_t pos = ftello(ctx.f);
265         int pad = ((pos + 3) & ~3) - pos;
266
267         if (pad != 0 && fseeko(ctx.f, pad, SEEK_CUR) != 0)
268                 throw IOError(ctx.f);
269
270         int32_t length;
271         if (fread(&length, sizeof(length), 1, ctx.f) != 1)
272                 throw IOError(ctx.f);
273         
274         return read_string_raw(ctx, swap32(length, ctx.swap));
275 }
276
277 StrList::StrList(const String *input, char delimeter)
278 {
279         const char *cur = input->c_str();
280         
281         do {
282                 char *next = strchr(cur, delimeter);
283                 String *s = new String();
284                 s->token = TOK_IDENT;
285                 
286                 if (next == cur || *cur == 0)
287                         throw InvalidArgument();
288                 
289                 if (next) {
290                         s->append(cur, next - cur);
291                         cur = next + 1;
292                 } else {
293                         s->append(cur);
294                         cur = NULL;
295                 }
296                 
297                 push_back(s);
298         } while (cur);
299 }
300
301 // FIXME: Handle illegal recursion.
302 static Symbol *lookup_sym_noyyerror(ImportContext &ctx, const String *name)
303 {
304         StrList *sl;
305
306         try {
307                 sl = new StrList(name);
308         }
309
310         catch (InvalidArgument) {
311                 fprintf(stderr, "\"%s\" is not a valid object "
312                                 "(\"%s\" is not a valid identifier).\n",
313                         ctx.filename, name->c_str());
314                 throw UserError();
315         }
316
317         try {
318                 return lookup_sym(toplevel, sl, toplevel);
319         }
320         
321         catch (UserError) {
322                 fprintf(stderr, "\"%s\" is not a valid object "
323                                 "(\"%s\" could not be loaded).\n",
324                         ctx.filename, name->c_str());
325
326                 throw UserError();
327         }
328 }
329
330 // FIXME: Handle illegal recursion.
331 static Symbol *lookup_sym_in_ns_noyyerror(ImportContext &ctx,
332                                           NameSpace *ns,
333                                           const String *name)
334 {
335         Symbol *sym = ns->lookup_noex(name);
336         if (!sym) {
337                 fprintf(stderr, "\"%s\" is not a valid object "
338                                 "(\"%s\" is not found).\n",
339                         ctx.filename, name->c_str());
340
341                 throw UserError();
342         }
343         
344         return sym;
345 }
346
347 Datum *Datum::import(ImportContext &ctx)
348 {
349         if (ctx.is_dir) {
350                 fprintf(stderr, "\"%s\" is not a valid object "
351                                 "(a Datum must be a file).\n",
352                         ctx.filename);
353                 throw UserError();
354         }
355
356         CompiledDatum def;
357         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
358                 throw IOError(ctx.f);
359
360         Datum *d = new Datum(ctx.name);
361         d->def.flags.raw = swap32(def.flags.raw, ctx.swap);
362
363         if (d->def.flags.field.Invalid) {
364                 fprintf(stderr, "\"%s\" is not a valid object "
365                                 "(bad flags 0x%08x).\n",
366                         ctx.filename, d->def.flags.raw);
367                 throw UserError();
368         }
369
370         ctx.parent->add_import(d, ctx.filename);
371
372         d->def.type.length = swap32(def.type.length, ctx.swap);
373
374         if (d->def.type.length != 0) {
375                 d->type_fq_name = read_string_raw(ctx, d->def.type.length);
376                 Symbol *sym = lookup_sym_noyyerror(ctx, d->type_fq_name);
377                 d->type = dynamic_cast<Type *>(sym);
378                 
379                 if (!d->type) {
380                         fprintf(stderr, "\"%s\" is not a valid object "
381                                         "(\"%s\" is not a type).\n",
382                                 ctx.filename, d->type_fq_name->c_str());
383                         throw UserError();
384                 }
385                 
386                 import_and_validate_array(ctx, d->def.basictype.array,
387                                           def.basictype.array);
388         
389                 BasicType *bt = dynamic_cast<BasicType *>(*d->type);
390                 if (bt)
391                         d->cbt = &bt->def;
392                 else
393                         d->cbt = NULL;
394         } else {
395                 d->basic = true;
396                 import_and_validate_basictype(ctx, d->def.basictype,
397                                               def.basictype);
398                 d->cbt = &d->def.basictype;
399         }
400
401         d->def.ucon = swap64(def.ucon, ctx.swap);
402
403         if (d->def.flags.field.Const) {
404                 if (!d->cbt) {
405                         fprintf(stderr, "\"%s\" is not a valid object "
406                                         "(constant, but non-basic type).\n",
407                                 ctx.filename);
408                         throw UserError();
409                 }
410         
411                 if (d->cbt->flags.field.Float)
412                         d->con_type = TOK_FCON;
413                 else if (!d->cbt->flags.field.Unsigned)
414                         d->con_type = TOK_ICON;
415                 else
416                         d->con_type = TOK_UCON;
417         
418                 if (!d->verify_const()) {
419                         fprintf(stderr, "\"%s\" is not a valid object "
420                                         "(bad constant).\n",
421                                 ctx.filename);
422                         throw UserError();
423                 }
424         } else {
425                 d->con_type = TOK_NONE;
426         }
427
428         return d;
429 }
430
431 // OPT: Importing methods and supers isn't necessary for idlc to
432 // work; it could be made optional to speed up large compilations.
433
434 Interface *Interface::import(ImportContext &ctx)
435 {
436         if (!ctx.is_dir) {
437                 fprintf(stderr, "\"%s\" is not a valid object "
438                                 "(an Interface must be a directory).\n",
439                         ctx.filename);
440                 throw UserError();
441         }
442
443         CompiledInterface def;
444         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
445                 throw IOError(ctx.f);
446
447         Interface *iface = new Interface(ctx.name);
448         iface->path = new String(ctx.filename);
449         ctx.parent->add_import(iface, ctx.filename);
450         
451         iface->def.guid[0] = def.guid[0];
452         iface->def.guid[1] = def.guid[1];
453
454         int32_t num_methods = swap32(def.num_methods, ctx.swap);
455         int32_t num_supers = swap32(def.num_supers, ctx.swap);
456         
457         if (num_methods < 0 || num_methods > 4096) {
458                 fprintf(stderr, "\"%s\" is not a valid object "
459                                 "(unreasonable num_methods %d).\n",
460                         ctx.filename, num_methods);
461                 throw UserError();
462         }
463
464         if (num_supers < 0 || num_supers > 4096) {
465                 fprintf(stderr, "\"%s\" is not a valid object "
466                                 "(unreasonable num_supers %d).\n",
467                         ctx.filename, num_supers);
468                 throw UserError();
469         }
470         
471         for (int32_t i = 0; i < num_methods; i++) {
472                 const String *str = read_string(ctx);
473                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, iface, str);
474                 Method *m = dynamic_cast<Method *>(sym);
475                 
476                 if (!m) {
477                         fprintf(stderr, "\"%s\" is not a valid object "
478                                         "(\"%s\" is not a method).\n",
479                                 ctx.filename, str->c_str());
480                         throw UserError();
481                 }
482                 
483                 iface->add_elem(m);
484         }
485
486         // FIXME: Check for bad recursion again
487
488         for (int32_t i = 0; i < num_supers; i++) {
489                 const String *str = read_string(ctx);
490                 Symbol *sym = lookup_sym_noyyerror(ctx, str);
491                 Interface *super = dynamic_cast<Interface *>(sym);
492         
493                 if (!super) {
494                         fprintf(stderr, "\"%s\" is not a valid object "
495                                         "(\"%s\" is not an interface).\n",
496                                 ctx.filename, str->c_str());
497                         throw UserError();
498                 }
499         
500                 iface->add_super(super);
501         }
502         
503         assert(num_methods == iface->def.num_methods);
504         assert(num_supers == iface->def.num_supers);
505
506         iface->sort_chains();
507         return iface;
508 }
509
510 Method *Method::import(ImportContext &ctx)
511 {
512         if (!ctx.is_dir) {
513                 fprintf(stderr, "\"%s\" is not a valid object "
514                                 "(a Method must be a directory).\n",
515                         ctx.filename);
516                 throw UserError();
517         }
518
519         CompiledMethod def;
520         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
521                 throw IOError(ctx.f);
522
523         Method *m = new Method(ctx.name);
524         m->path = new String(ctx.filename);
525         ctx.parent->add_import(m, ctx.filename);
526
527         int32_t num_entries = swap32(def.num_entries, ctx.swap);
528
529         m->def.flags.raw = swap32(def.flags.raw, ctx.swap);
530         
531         if (num_entries < 0 || num_entries > 4096) {
532                 fprintf(stderr, "\"%s\" is not a valid object "
533                                 "(unreasonable num_entries %d).\n",
534                         ctx.filename, num_entries);
535                 throw UserError();
536         }
537
538         for (int32_t i = 0; i < num_entries; i++) {
539                 const String *str = read_string(ctx);
540                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, m, str);
541                 Param *p = dynamic_cast<Param *>(sym);
542                 
543                 if (!p) {
544                         fprintf(stderr, "\"%s\" is not a valid object "
545                                         "(\"%s\" is not a parameter).\n",
546                                 ctx.filename, str->c_str());
547                         throw UserError();
548                 }
549                 
550                 m->add_elem(p);
551         }
552
553         assert(num_entries == m->def.num_entries);
554         return m;
555 }
556
557 Param *Param::import(ImportContext &ctx)
558 {
559         if (ctx.is_dir) {
560                 fprintf(stderr, "\"%s\" is not a valid object "
561                                 "(a Parameter must be a file).\n",
562                         ctx.filename);
563                 throw UserError();
564         }
565
566         CompiledParam def;
567         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
568                 throw IOError(ctx.f);
569
570         Param *p = new Param(ctx.name);
571         ctx.parent->add_import(p, ctx.filename);
572
573         p->def.type.length = swap32(def.type.length, ctx.swap);
574
575         if (p->def.type.length != 0) {
576                 p->type_fq_name = read_string_raw(ctx, p->def.type.length);
577                 Symbol *sym = lookup_sym_noyyerror(ctx, p->type_fq_name);
578                 p->type = dynamic_cast<Type *>(sym);
579                 
580                 if (!p->type) {
581                         fprintf(stderr, "\"%s\" is not a valid object "
582                                         "(\"%s\" is not a type).\n",
583                                 ctx.filename, p->type_fq_name->c_str());
584                         throw UserError();
585                 }
586                 
587                 import_and_validate_array(ctx, p->def.basictype.array,
588                                           def.basictype.array);
589         } else {
590                 import_and_validate_basictype(ctx, p->def.basictype,
591                                               def.basictype);
592         }
593
594         p->def.flags.raw = swap32(def.flags.raw, ctx.swap);
595         return p;
596 }
597
598 // OPT: Importing data and super isn't necessary for idlc to
599 // work; it could be made optional to speed up large compilations.
600
601 Struct *Struct::import(ImportContext &ctx)
602 {
603         if (!ctx.is_dir) {
604                 fprintf(stderr, "\"%s\" is not a valid object "
605                                 "(a Struct must be a directory).\n",
606                         ctx.filename);
607                 throw UserError();
608         }
609
610         CompiledStruct def;
611         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
612                 throw IOError(ctx.f);
613
614         Struct *s = new Struct(ctx.name);
615         s->path = new String(ctx.filename);
616         ctx.parent->add_import(s, ctx.filename);
617         
618         s->def.guid[0] = def.guid[0];
619         s->def.guid[1] = def.guid[1];
620         
621         int32_t num_entries = swap32(def.num_entries, ctx.swap);
622
623         s->def.flags.raw = swap32(def.flags.raw, ctx.swap);
624         
625         if (num_entries < 0 || num_entries > 4096) {
626                 fprintf(stderr, "\"%s\" is not a valid object "
627                                 "(unreasonable num_entries %d).\n",
628                         ctx.filename, num_entries);
629                 throw UserError();
630         }
631
632         for (int32_t i = 0; i < num_entries; i++) {
633                 const String *str = read_string(ctx);
634                 
635                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, s, str);
636                 Datum *d = dynamic_cast<Datum *>(sym);
637                 
638                 if (!d) {
639                         fprintf(stderr, "\"%s\" is not a valid object "
640                                         "(\"%s\" is not a datum).\n",
641                                 ctx.filename, str->c_str());
642                         throw UserError();
643                 }
644                 
645                 s->add_elem(d);
646         }
647         
648         if (s->def.flags.field.Super) {
649                 const String *str = read_string(ctx);
650                 Symbol *sym = lookup_sym_noyyerror(ctx, str);
651                 Struct *super = dynamic_cast<Struct *>(sym);
652                 
653                 if (!super) {
654                         fprintf(stderr, "\"%s\" is not a valid object "
655                                         "(\"%s\" is not a struct).\n",
656                                 ctx.filename, str->c_str());
657                         throw UserError();
658                 }
659                 
660                 s->super = super;
661                 
662                 if (super->is_virtual() && !s->is_virtual()) {
663                         fprintf(stderr, "\"%s\" is not a valid object "
664                                         "(not virtual but parent is).\n",
665                                 ctx.filename);
666                         throw UserError();
667                 }
668         }
669
670         assert(num_entries == s->def.num_entries);
671         return s;
672 }
673
674 // OPT: Importing elements isn't necessary for idlc to work (at
675 // least, not unless inheritance is implemented); it could be made
676 // optional to speed up large compilations.
677
678 BitField *BitField::import(ImportContext &ctx)
679 {
680         if (!ctx.is_dir) {
681                 fprintf(stderr, "\"%s\" is not a valid object "
682                                 "(a BitField must be a directory).\n",
683                         ctx.filename);
684                 throw UserError();
685         }
686
687         CompiledBitField def;
688         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
689                 throw IOError(ctx.f);
690
691         BitField *bf = new BitField(ctx.name);
692         bf->path = new String(ctx.filename);
693         ctx.parent->add_import(bf, ctx.filename);
694
695         int32_t num_entries = swap32(def.num_entries, ctx.swap);
696         bf->def.bits = swap32(def.bits, ctx.swap);
697
698         if (num_entries < 0 || num_entries > 4096) {
699                 fprintf(stderr, "\"%s\" is not a valid object "
700                                 "(unreasonable num_entries %d).\n",
701                         ctx.filename, num_entries);
702                 throw UserError();
703         }
704         
705         // FIXME: bits can only be 16, 32, or 64 when not in another bitfield.
706
707         if (bf->def.bits < 1 || bf->def.bits > 64) {
708                 fprintf(stderr, "\"%s\" is not a valid object "
709                                 "(unreasonable bits %d).\n",
710                         ctx.filename, num_entries);
711                 throw UserError();
712         }
713
714         for (int32_t i = 0; i < num_entries; i++) {
715                 const String *str = read_string(ctx);
716                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, bf, str);
717                 Datum *d = dynamic_cast<Datum *>(sym);
718                 
719                 if (!d) {
720                         fprintf(stderr, "\"%s\" is not a valid object "
721                                         "(\"%s\" is not a datum).\n",
722                                 ctx.filename, str->c_str());
723                         throw UserError();
724                 }
725                 
726                 bf->add_elem(d);
727         }
728         
729         assert(num_entries == bf->def.num_entries);
730         return bf;
731 }
732
733 // OPT: Importing elements isn't necessary for idlc to work (at
734 // least, not unless inheritance is implemented); it could be made
735 // optional to speed up large compilations.
736
737 Enum *Enum::import(ImportContext &ctx)
738 {
739         if (!ctx.is_dir) {
740                 fprintf(stderr, "\"%s\" is not a valid object "
741                                 "(a Enum must be a directory).\n",
742                         ctx.filename);
743                 throw UserError();
744         }
745
746         CompiledEnum def;
747         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
748                 throw IOError(ctx.f);
749
750         Enum *e = new Enum(ctx.name);
751         e->path = new String(ctx.filename);
752         ctx.parent->add_import(e, ctx.filename);
753
754         int32_t num_entries = swap32(def.num_entries, ctx.swap);
755         e->def.bits = swap32(def.bits, ctx.swap);
756
757         if (num_entries < 0 || num_entries > 4096) {
758                 fprintf(stderr, "\"%s\" is not a valid object "
759                                 "(unreasonable num_entries %d).\n",
760                         ctx.filename, num_entries);
761                 throw UserError();
762         }
763         
764         // FIXME: bits can only be 16, 32, or 64 when not in another bitfield.
765
766         if (e->def.bits < 1 || e->def.bits > 64) {
767                 fprintf(stderr, "\"%s\" is not a valid object "
768                                 "(unreasonable bits %d).\n",
769                         ctx.filename, num_entries);
770                 throw UserError();
771         }
772
773         for (int32_t i = 0; i < num_entries; i++) {
774                 const String *str = read_string(ctx);
775                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, e, str);
776                 Datum *d = dynamic_cast<Datum *>(sym);
777                 
778                 if (!d) {
779                         fprintf(stderr, "\"%s\" is not a valid object "
780                                         "(\"%s\" is not a datum).\n",
781                                 ctx.filename, str->c_str());
782                         throw UserError();
783                 }
784                 
785                 e->add_elem(d);
786         }
787         
788         assert(num_entries == e->def.num_entries);
789         return e;
790 }
791
792 Alias *Alias::import(ImportContext &ctx)
793 {
794         if (ctx.is_dir) {
795                 fprintf(stderr, "\"%s\" is not a valid object "
796                                 "(an Alias must be a file).\n",
797                         ctx.filename);
798                 throw UserError();
799         }
800
801         Alias *a = new Alias(ctx.name);
802         ctx.parent->add_import(a, ctx.filename);
803
804         const String *str = read_string(ctx);
805         Symbol *sym = lookup_sym_noyyerror(ctx, str);
806         
807         Alias *aptr = dynamic_cast<Alias *>(sym);
808         if (aptr) {
809                 fprintf(stderr, "\"%s\" is not a valid object "
810                                 "(points to \"%s\", which is an alias).\n",
811                         ctx.filename, str->c_str());
812                 throw UserError();
813         }
814
815         a->real_sym = sym;
816         a->sym_fq_name = str;
817         a->def.length = str->length();
818
819         return a;
820 }
821
822 TypeDef *TypeDef::import(ImportContext &ctx)
823 {
824         if (ctx.is_dir) {
825                 fprintf(stderr, "\"%s\" is not a valid object "
826                                 "(a TypeDef must be a file).\n",
827                         ctx.filename);
828                 throw UserError();
829         }
830
831         TypeDef *td = new TypeDef(ctx.name);
832         ctx.parent->add_import(td, ctx.filename);
833
834         const String *str = read_string(ctx);
835         Symbol *sym = lookup_sym_noyyerror(ctx, str);
836         
837         Alias *aptr = dynamic_cast<Alias *>(sym);
838         if (aptr) {
839                 fprintf(stderr, "\"%s\" is not a valid object "
840                                 "(points to \"%s\", which is an alias).\n",
841                         ctx.filename, str->c_str());
842                 throw UserError();
843         }
844
845         td->real_sym = sym;
846         td->sym_fq_name = str;
847         td->def.length = str->length();
848
849         return td;
850 }
851
852 UserNameSpace *UserNameSpace::import(ImportContext &ctx)
853 {
854         if (!ctx.is_dir) {
855                 fprintf(stderr, "\"%s\" is not a valid object "
856                                 "(a NameSpace must be a directory).\n",
857                         ctx.filename);
858                 throw UserError();
859         }
860
861         UserNameSpace *uns = new UserNameSpace();
862         uns->path = new String(ctx.filename);
863         
864         // FIXME: sanity check the mount point
865
866         uns->name = ctx.name;   
867         ctx.parent->add_import(uns, ctx.filename);
868         return uns;
869 }
870
871 Symbol *do_load(ImportContext &ctx)
872 {
873         int type = swap32(ctx.hdr.type, ctx.swap);
874         
875         switch (type) {
876                 case CompiledDefHeader::NameSpace:
877                         return UserNameSpace::import(ctx);
878                         break;
879                 
880                 case CompiledDefHeader::BasicType:
881                         return BasicType::import(ctx);
882                         break;
883
884                 case CompiledDefHeader::Datum:
885                         return Datum::import(ctx);
886                         break;
887
888                 case CompiledDefHeader::Interface:
889                         return Interface::import(ctx);
890                         break;
891
892                 case CompiledDefHeader::Method:
893                         return Method::import(ctx);
894                         break;
895
896                 case CompiledDefHeader::Param:
897                         return Param::import(ctx);
898                         break;
899
900                 case CompiledDefHeader::Struct:
901                         return Struct::import(ctx);
902                         break;
903
904                 case CompiledDefHeader::Enum:
905                         return Enum::import(ctx);
906                         break;
907
908                 case CompiledDefHeader::BitField:
909                         return BitField::import(ctx);
910                         break;
911
912                 case CompiledDefHeader::Alias:
913                         ctx.is_typedef = false;
914                         return Alias::import(ctx);
915                         break;
916
917                 case CompiledDefHeader::TypeDef:
918                         ctx.is_typedef = true;
919                         return TypeDef::import(ctx);
920                         break;
921
922                 default:
923                         fprintf(stderr, "\"%s\" is not a valid object "
924                                         "(bad type %08x).\n",
925                                 ctx.filename, type);
926                         
927                         throw UserError();
928         }
929 }
930
931 Symbol *NameSpace::load(const String *symname)
932 {
933         ImportContext ctx;
934         const String *mangled = mangle(symname);
935
936         ctx.name = new String(*symname);
937         ctx.is_dir = true;
938         ctx.parent = this;
939
940         string filename_no_self(path);
941         filename_no_self += '/';
942         filename_no_self += *mangled;
943         ctx.filename = filename_no_self.c_str();
944
945         string filename(filename_no_self);
946         filename.append("/.self");
947
948         ctx.f = fopen(filename.c_str(), "rb");
949         if (!ctx.f) {
950                 ctx.is_dir = false;
951                 ctx.f = fopen(ctx.filename, "rb");
952                 
953                 if (!ctx.f)
954                         return NULL;
955                 
956                 filename = filename_no_self;
957         }
958         
959         Symbol *ret;
960         try {
961                 if (fread(&ctx.hdr, sizeof(ctx.hdr), 1, ctx.f) != 1)
962                         throw IOError(ctx.f);
963
964                 if (ctx.hdr.magic == CompiledDefHeader::magic_normal)
965                         ctx.swap = false;
966                 else if (ctx.hdr.magic == CompiledDefHeader::magic_reversed)
967                         ctx.swap = true;
968                 else {
969                         fprintf(stderr, "\"%s\" is not a valid object "
970                                         "(bad magic 0x%08x).\n",
971                                 ctx.filename, ctx.hdr.magic);
972         
973                         throw UserError();
974                 }
975                 
976                 ret = do_load(ctx);
977         }
978         
979         catch (IOError &e) {
980                 fprintf(stderr, "Cannot read from file \"%s\": %s.\n",
981                         ctx.filename, e.msg);
982         
983                 throw UserError();
984         }
985         
986         return ret;
987 }
988
989 NameSpace *check_for_imports(NameSpace *ns)
990 {
991         while (ns) {
992                 if (ns->get_path())
993                         return ns;
994
995                 ns = ns->get_ns();
996         }
997         
998         return NULL;
999 }
1000
1001 void UserNameSpace::declare_import(const char *path)
1002 {
1003         ImportContext ctx;
1004         UserNameSpace *ns = new UserNameSpace();
1005         string filename(path);
1006         filename.append("/.self");
1007
1008         ctx.parent = NULL;
1009         ctx.filename = path;
1010         
1011         ctx.f = fopen(filename.c_str(), "rb");
1012         if (!ctx.f) {
1013                 fprintf(stderr, "Cannot import \"%s\": %s.\n",
1014                         path, strerror(errno));
1015         
1016                 throw UserError();
1017         }
1018         
1019         const String *mount;
1020         try {
1021                 if (fread(&ctx.hdr, sizeof(ctx.hdr), 1, ctx.f) != 1)
1022                         throw IOError(ctx.f);
1023         
1024                 if (ctx.hdr.magic == CompiledDefHeader::magic_normal)
1025                         ctx.swap = false;
1026                 else if (ctx.hdr.magic == CompiledDefHeader::magic_reversed)
1027                         ctx.swap = true;
1028                 else {
1029                         fprintf(stderr, "\"%s\" is not a valid object "
1030                                         "(bad magic 0x%08x).\n",
1031                                 ctx.filename, ctx.hdr.magic);
1032         
1033                         throw UserError();
1034                 }
1035                 
1036                 mount = read_string(ctx);
1037         }
1038         
1039         catch (IOError &e) {
1040                 fprintf(stderr, "Cannot read from file \"%s\": %s.\n",
1041                         ctx.filename, e.msg);
1042         
1043                 throw UserError();
1044         }
1045
1046         StrList *strl = new StrList(mount);
1047         if (!strl) {
1048                 fprintf(stderr, "\"%s\" is not a valid object "
1049                                 "(mount point \"%s\" is not valid).\n",
1050                         ctx.filename, mount->c_str());
1051
1052                 throw UserError();
1053         }
1054
1055         ns->name = strl->back();
1056         strl->pop_back();
1057         
1058         if (strl->size() != 0) {
1059                 ctx.parent = add_nspace(strl, false);
1060
1061                 if (!ctx.parent)
1062                         BUG();
1063                 
1064                 NameSpace *conflict = check_for_imports(ctx.parent);
1065                 if (conflict) {         
1066                         fprintf(stderr, "Import \"%s\" conflicts"
1067                                         " with \"%s\" at \"%s\".\n",
1068                                 path, conflict->get_fq_name()->flatten()->c_str(),
1069                                 conflict->get_path()->c_str());
1070
1071                         throw UserError();
1072                 }
1073         } else {
1074                 ctx.parent = toplevel;
1075         }
1076         
1077         ns->path = new String(path);
1078         ctx.parent->add_import(ns, ctx.filename);
1079 }
1080
1081 void NameSpace::import_all()
1082 {
1083         if (!path)
1084                 return;
1085         
1086         DIR *dir = opendir(path->c_str());
1087         if (!dir) {
1088                 fprintf(stderr, "Cannot open directory \"%s\".\n", path->c_str());
1089                 throw UserError();
1090         }
1091         
1092         struct dirent ent, *entp;
1093         while (true) {
1094                 // readdir_r is buggy on osx 10.2, and will fail if errno is
1095                 // non-zero.
1096                 errno = 0;
1097                 
1098                 if (readdir_r(dir, &ent, &entp)) {
1099                         fprintf(stderr, "1 Cannot readdir on \"%s\": %s.\n",
1100                                 path->c_str(), strerror(errno));
1101
1102                         closedir(dir);
1103                         throw UserError();
1104                 }
1105                 
1106                 if (!entp)
1107                         break;
1108                 
1109                 // Ignore ".", "..", and ".self".
1110                 if (ent.d_name[0] == '.')
1111                         continue;
1112                 
1113                 try {
1114                         char *under = strchr(ent.d_name, '_');
1115                         if (!under) {
1116                                 fprintf(stderr, "\"%s\" is not a valid namespace "
1117                                                 "(bad member \"%s\").\n",
1118                                         path->c_str(), ent.d_name);
1119
1120                                 throw UserError();
1121                         }
1122                         
1123                         String *s = new String(under + 1);
1124                         lookup(s);
1125                 }
1126                 
1127                 catch (SymbolNotFound) {
1128                         fprintf(stderr, "\"%s\" is not a valid namespace "
1129                                         "(member \"%s\" disappeared).\n",
1130                                 path->c_str(), ent.d_name);
1131
1132                         closedir(dir);
1133                         throw UserError();
1134                 }
1135         }
1136         
1137         closedir(dir);
1138 }
1139
1140 void NameSpace::import_all_recursive()
1141 {
1142         import_all();
1143         
1144         for (const_iterator i = begin(); i != end(); ++i) {
1145                 Symbol *sym = (*i).second;
1146                 NameSpace *ns = dynamic_cast<NameSpace *>(sym);
1147                 if (ns)
1148                         ns->import_all_recursive();
1149         }
1150 }