1 //
2 // class.cc
3 //
4 // Copyright (C) 1996 Limit Point Systems, Inc.
5 //
6 // Author: Curtis Janssen <cljanss@limitpt.com>
7 // Maintainer: LPS
8 //
9 // This file is part of the SC Toolkit.
10 //
11 // The SC Toolkit is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU Library General Public License as published by
13 // the Free Software Foundation; either version 2, or (at your option)
14 // any later version.
15 //
16 // The SC Toolkit is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Library General Public License for more details.
20 //
21 // You should have received a copy of the GNU Library General Public License
22 // along with the SC Toolkit; see the file COPYING.LIB. If not, write to
23 // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 //
25 // The U.S. Government is granted a limited license as per AL 91-7.
26 //
27
28 #ifdef __GNUG__
29 #pragma implementation
30 #endif
31
32 #ifdef HAVE_CONFIG_H
33 #include <scconfig.h>
34 #endif
35
36 #include <string>
37
38 #include <stdlib.h>
39 #include <string.h>
40 #if defined(HAVE_DLFCN_H)
41 #include <dlfcn.h>
42 #endif // HAVE_DLFCN_H
43
44 #include <util/misc/formio.h>
45
46 #include <util/class/class.h>
47 #include <util/class/proxy.h>
48
49 using namespace std;
50 using namespace sc;
51
52 std::map<std::string,ClassDescP>* ClassDesc::all_ = 0;
53 std::map<type_info_key,ClassDescP>* ClassDesc::type_info_all_ = 0;
54 char * ClassDesc::classlib_search_path_ = 0;
55 std::set<std::string>* ClassDesc::unresolved_parents_ = 0;
56
57 /////////////////////////////////////////////////////////////////
58
59 static sc::ClassDesc DescribedClassProxy_cd(
60 typeid(sc::DescribedClassProxy), "DescribedClassProxy",1,"public DescribedClass");
61
62 /////////////////////////////////////////////////////////////////
63
ParentClass(ClassDesc * classdesc,Access access,int is_virtual)64 ParentClass::ParentClass(ClassDesc*classdesc,Access access,int is_virtual):
65 _access(access),
66 _is_virtual(is_virtual),
67 _classdesc(classdesc)
68 {
69 }
70
ParentClass(const ParentClass & p)71 ParentClass::ParentClass(const ParentClass&p):
72 _access(p._access),
73 _is_virtual(p._is_virtual),
74 _classdesc(p._classdesc)
75 {
76 }
77
~ParentClass()78 ParentClass::~ParentClass()
79 {
80 }
81
is_virtual() const82 int ParentClass::is_virtual() const
83 {
84 return _is_virtual;
85 }
86
classdesc() const87 const ClassDesc* ParentClass::classdesc() const
88 {
89 return _classdesc;
90 }
91
change_classdesc(ClassDesc * n)92 void ParentClass::change_classdesc(ClassDesc*n)
93 {
94 _classdesc = n;
95 }
96
97 /////////////////////////////////////////////////////////////////
98
ParentClasses()99 ParentClasses::ParentClasses():
100 _n(0),
101 _classes(0)
102 {
103 }
104
105 void
init(const char * parents)106 ParentClasses::init(const char* parents)
107 {
108 // if parents is empty then we are done
109 if (!parents || strlen(parents) == 0) return;
110
111 char* tokens = ::strcpy(new char[strlen(parents)+1],parents);
112 const char* whitesp = "\t\n,() ";
113 char* token;
114 int is_virtual = 0;
115 ParentClass::Access access = ParentClass::Private;
116 for (token = ::strtok(tokens,whitesp);
117 token;
118 token = ::strtok(0,whitesp)) {
119 if (!strcmp(token,"virtual")) {
120 is_virtual = 1;
121 }
122 else if (!strcmp(token,"public")) {
123 access = ParentClass::Public;
124 }
125 else if (!strcmp(token,"protected")) {
126 access = ParentClass::Protected;
127 }
128 else if (!strcmp(token,"private")) {
129 access = ParentClass::Private;
130 }
131 else {
132 std::string parentkey(token);
133 // if the parents class desc does not exist create a temporary
134 // the temporary will be incorrect,because it does not have the
135 // parent's parents
136 if (ClassDesc::all().find(parentkey) == ClassDesc::all().end()) {
137 ClassDesc *tmp_classdesc = new ClassDesc(token);
138 ClassDesc::all()[parentkey] = tmp_classdesc;
139 if (ClassDesc::unresolved_parents_ == 0) {
140 ClassDesc::unresolved_parents_ = new std::set<std::string>;
141 }
142 ClassDesc::unresolved_parents_->insert(token);
143 }
144 ParentClass* p = new ParentClass(ClassDesc::all()[parentkey],
145 access,
146 is_virtual);
147 add(p);
148 access = ParentClass::Private;
149 is_virtual = 0;
150 }
151 }
152 delete[] tokens;
153
154 }
155
~ParentClasses()156 ParentClasses::~ParentClasses()
157 {
158 for (int i=0; i<_n; i++) delete _classes[i];
159
160 if (_classes) delete[] _classes;
161 _classes = 0;
162 _n = 0;
163 }
164
165 void
add(ParentClass * p)166 ParentClasses::add(ParentClass*p)
167 {
168 ParentClass** newpp = new ParentClass*[_n+1];
169 for (int i=0; i<_n; i++) newpp[i] = _classes[i];
170 newpp[_n] = p;
171 _n++;
172 delete[] _classes;
173 _classes = newpp;
174 }
175
176 void
change_parent(ClassDesc * oldcd,ClassDesc * newcd)177 ParentClasses::change_parent(ClassDesc*oldcd,ClassDesc*newcd)
178 {
179 for (int i=0; i<_n; i++) {
180 if (parent(i).classdesc() == oldcd) parent(i).change_classdesc(newcd);
181 }
182 }
183
184 ////////////////////////////////////////////////////////////////////////
185
186 type_info_key&
operator =(const type_info_key & t)187 type_info_key::operator=(const type_info_key&t)
188 {
189 ti_ = t.ti_;
190 return *this;
191 }
192
193 int
operator ==(const type_info_key & t) const194 type_info_key::operator==(const type_info_key&t) const
195 {
196 if (!ti_ && !t.ti_) return 1;
197 if (!ti_ || !t.ti_) return 0;
198
199 return *ti_ == *t.ti_;
200 }
201
202 int
operator <(const type_info_key & t) const203 type_info_key::operator<(const type_info_key&t) const
204 {
205 if (!ti_ && !t.ti_) return 0;
206 if (!ti_) return 0;
207 if (!t.ti_) return 1;
208
209 return ti_->before(*t.ti_);
210 }
211
212 int
cmp(const type_info_key & t) const213 type_info_key::cmp(const type_info_key&t) const
214 {
215 if (*this == t) return 0;
216 if (*this < t) return -1;
217 return 1;
218 }
219
220 ////////////////////////////////////////////////////////////////////////
221
ClassDesc(const type_info & ti,const char * name,int version,const char * parents,DescribedClass * (* ctor)(),DescribedClass * (* keyvalctor)(const Ref<KeyVal> &),DescribedClass * (* stateinctor)(StateIn &))222 ClassDesc::ClassDesc(const type_info &ti,
223 const char* name, int version,
224 const char* parents,
225 DescribedClass* (*ctor)(),
226 DescribedClass* (*keyvalctor)(const Ref<KeyVal>&),
227 DescribedClass* (*stateinctor)(StateIn&)
228 )
229 {
230 if (!type_info_all_) {
231 type_info_all_ = new std::map<type_info_key,ClassDescP>;
232 }
233 type_info_key key(&ti);
234 if (type_info_all_->find(key) != type_info_all_->end()) {
235 ExEnv::err0() << indent
236 << "ERROR: duplicate ClassDesc detected for class "
237 << name << " type_info name = " << ti.name() << endl;
238 abort();
239 }
240 else {
241 if (type_info_all_->find(key) == type_info_all_->end()) {
242 (*type_info_all_)[key] = this;
243 }
244 else {
245 // this should never happen
246 }
247 }
248
249 // test the version number to see if it is valid
250 if (version <= 0) {
251 ExEnv::errn() << "ERROR: ClassDesc ctor: version <= 0" << endl;
252 exit(1);
253 }
254
255 init(name,version,parents,&ti,ctor,keyvalctor,stateinctor);
256 }
257
ClassDesc(const char * name)258 ClassDesc::ClassDesc(const char* name)
259 {
260 init(name, 0);
261 }
262
263 void
init(const char * name,int version,const char * parents,const type_info * ti,DescribedClass * (* ctor)(),DescribedClass * (* keyvalctor)(const Ref<KeyVal> &),DescribedClass * (* stateinctor)(StateIn &))264 ClassDesc::init(const char* name, int version,
265 const char* parents,
266 const type_info *ti,
267 DescribedClass* (*ctor)(),
268 DescribedClass* (*keyvalctor)(const Ref<KeyVal>&),
269 DescribedClass* (*stateinctor)(StateIn&))
270 {
271 classname_ = 0;
272 version_ = version;
273 children_ = 0;
274 ctor_ = ctor;
275 keyvalctor_ = keyvalctor;
276 stateinctor_ = stateinctor;
277 ti_ = ti;
278
279 // make sure that the static members have been initialized
280 if (!all_) {
281 all_ = new std::map<std::string,ClassDescP>;
282 const char* tmp = getenv("LD_LIBRARY_PATH");
283 if (tmp) {
284 // Needed for misbehaving getenv's.
285 if (strncmp(tmp, "LD_LIBRARY_PATH=", 16) == 0) {
286 tmp = ::strchr(tmp,'=');
287 tmp++;
288 }
289 }
290 else tmp = ".";
291 classlib_search_path_ = ::strcpy(new char[strlen(tmp)+1],tmp);
292 }
293
294 // see if I'm already in the list
295 ClassDesc *me = name_to_class_desc(name);
296 int temp_copy_present = 0;
297 if (me && me->version() != 0) {
298 ExEnv::err0()
299 << indent
300 << "ERROR: ClassDesc ctor: ClassDesc already initialized for "
301 << name << endl;
302 abort();
303 }
304 else if (me) {
305 temp_copy_present = 1;
306 }
307
308 parents_.init(parents);
309
310 if (!temp_copy_present && name_to_class_desc(name)) {
311 // I wasn't in the list before, but am in it now
312 ExEnv::err0()
313 << indent
314 << "ERROR: ClassDesc ctor: inheritance loop detected for "
315 << name << endl;
316 abort();
317 }
318
319 classname_ = ::strcpy(new char[strlen(name)+1],name);
320
321 std::string key(name);
322
323 // let each of the parents know that this is a child
324 for (int i=0; i<parents_.n(); i++) {
325 std::string parentkey(parents_[i].classdesc()->name());
326 if (!(*all_)[parentkey]->children_)
327 (*all_)[parentkey]->children_ = new std::set<std::string>;
328 // let the parents know about the child
329 ((*all_)[parentkey]->children_)->insert(key);
330 }
331
332 // if this class is aleady in all_, then it was put there by a child
333 // preserve children info, destroy the old entry, and put this there
334 if (all_->find(key) != all_->end()) {
335 children_ = (*all_)[key]->children_;
336 (*all_)[key]->children_ = 0;
337
338 if (!children_) {
339 ExEnv::err0()
340 << indent
341 << "ERROR: ClassDesc: inconsistency in initialization for "
342 << key
343 << "--perhaps a duplicated CTOR call" << endl;
344 abort();
345 }
346
347 // go thru the list of children and correct their
348 // parent class descriptors
349 for (std::set<std::string>::iterator i=children_->begin();
350 i!=children_->end(); i++) {
351 (*all_)[*i]->change_parent((*all_)[key],this);
352 }
353
354 delete (*all_)[key];
355 unresolved_parents_->erase(key);
356 if (unresolved_parents_->size() == 0) {
357 delete unresolved_parents_;
358 unresolved_parents_ = 0;
359 }
360 }
361 (*all_)[key] = this;
362 }
363
~ClassDesc()364 ClassDesc::~ClassDesc()
365 {
366 // remove references to this class descriptor
367 if (children_) {
368 for (std::set<std::string>::iterator i=children_->begin();
369 i!=children_->end(); i++) {
370 if (all_->find(*i) != all_->end()) {
371 (*all_)[*i]->change_parent(this,0);
372 }
373 }
374 }
375 // delete this ClassDesc from the list of all ClassDesc's
376 std::string key(classname_);
377 all_->erase(key);
378
379 // if the list of all ClassDesc's is empty, delete it
380 if (all_->size() == 0) {
381 delete all_;
382 all_ = 0;
383 delete[] classlib_search_path_;
384 classlib_search_path_ = 0;
385 }
386
387 // delete this ClassDesc entry from the type_info map
388 if (ti_ != 0) {
389 type_info_key key(ti_);
390 type_info_all_->erase(key);
391 if (type_info_all_->size() == 0) {
392 delete type_info_all_;
393 type_info_all_ = 0;
394 }
395 }
396
397 // delete local data
398 delete[] classname_;
399 if (children_) delete children_;
400 }
401
402 ClassDesc*
class_desc(const type_info & ti)403 ClassDesc::class_desc(const type_info &ti)
404 {
405 if (type_info_all_->find(type_info_key(&ti))
406 == type_info_all_->end()) return 0;
407 return (*type_info_all_)[type_info_key(&ti)];
408 }
409
410 std::map<std::string,ClassDescP>&
all()411 ClassDesc::all()
412 {
413 if (!all_) {
414 ExEnv::errn() << "ClassDesc::all(): all not initialized" << endl;
415 abort();
416 }
417 return *all_;
418 }
419
420 ClassDesc*
name_to_class_desc(const char * name)421 ClassDesc::name_to_class_desc(const char* name)
422 {
423 std::string key(name);
424 if (all_->find(key) == all_->end()) return 0;
425 return (*all_)[key];
426 }
427
428 DescribedClass*
create() const429 ClassDesc::create() const
430 {
431 if (ctor_) return (*ctor_)();
432 return 0;
433 }
434
435 DescribedClass*
create(const Ref<KeyVal> & keyval) const436 ClassDesc::create(const Ref<KeyVal>&keyval) const
437 {
438 DescribedClass* result;
439 if (keyvalctor_) {
440 result = (*keyvalctor_)(keyval);
441 }
442 else result = 0;
443 return result;
444 }
445
446 DescribedClass*
create(StateIn & statein) const447 ClassDesc::create(StateIn&statein) const
448 {
449 if (stateinctor_) return (*stateinctor_)(statein);
450 return 0;
451 }
452
453 void
change_parent(ClassDesc * oldcd,ClassDesc * newcd)454 ClassDesc::change_parent(ClassDesc*oldcd,ClassDesc*newcd)
455 {
456 parents_.change_parent(oldcd,newcd);
457 }
458
459 void
list_all_classes()460 ClassDesc::list_all_classes()
461 {
462 ExEnv::out0() << "Listing all classes:" << endl;
463 for (std::map<std::string,ClassDescP>::iterator ind=all_->begin();
464 ind!=all_->end(); ind++) {
465 ClassDesc* classdesc = ind->second;
466 ExEnv::out0() << "class " << classdesc->name() << endl;
467 ParentClasses& parents = classdesc->parents_;
468 if (parents.n()) {
469 ExEnv::out0() << " parents:";
470 for (int i=0; i<parents.n(); i++) {
471 if (parents[i].is_virtual()) {
472 ExEnv::out0() << " virtual";
473 }
474 if (parents[i].access() == ParentClass::Public) {
475 ExEnv::out0() << " public";
476 }
477 else if (parents[i].access() == ParentClass::Protected) {
478 ExEnv::out0() << " protected";
479 }
480 if (parents[i].classdesc() == 0) {
481 ExEnv::errn() << endl
482 << "ERROR: parent " << i
483 << " for " << classdesc->name()
484 << " is missing" << endl;
485 abort();
486 }
487 const char *n = parents[i].classdesc()->name();
488 ExEnv::out0() << " " << parents[i].classdesc()->name();
489 }
490 ExEnv::out0() << endl;
491 }
492 std::set<std::string>* children = classdesc->children_;
493 if (children) {
494 ExEnv::out0() << " children:";
495 for (std::set<std::string>::iterator pind=children->begin();
496 pind!=children->end(); pind++) {
497 ExEnv::out0() << " " << (*pind);
498 }
499 ExEnv::out0() << endl;
500 }
501 }
502 }
503
create_described_class() const504 DescribedClass* ClassDesc::create_described_class() const
505 {
506 return create();
507 }
508
509 // Returns 0 for success and -1 for failure.
510 int
load_class(const char * classname)511 ClassDesc::load_class(const char* classname)
512 {
513 // See if the class has already been loaded.
514 if (name_to_class_desc(classname) != 0) {
515 return 0;
516 }
517
518 #if HAVE_DLFCN_H
519 // make a copy of the library search list
520 char* path = new char[strlen(classlib_search_path_) + 1];
521 strcpy(path, classlib_search_path_);
522
523 // go through each directory in the library search list
524 char* dir = strtok(path,":");
525 while (dir) {
526 // find the 'classes' files
527 char* filename = new char[strlen(dir) + 8 + 1];
528 strcpy(filename,dir);
529 strcat(filename,"/classes");
530 ExEnv::outn() << "ClassDesc::load_class looking for \"" << filename << "\""
531 << endl;
532 FILE* fp = fopen(filename, "r");
533 delete[] filename;
534
535 if (fp) {
536 // read the lines in the classes file
537 const int bufsize = 10000;
538 char buf[bufsize];
539 while(fgets(buf, bufsize, fp)) {
540 if (buf[0] != '\0' && buf[strlen(buf)-1] == '\n') {
541 buf[strlen(buf)-1] = '\0';
542 }
543 char* lib = strtok(buf," ");
544 char* testclassname = strtok(0," ");
545 ExEnv::outn() << "lib = \"" << lib << "\"" << endl;
546 while(testclassname) {
547 ExEnv::outn() << "classname = \"" << testclassname << "\"" << endl;
548 if (strcmp(testclassname,classname) == 0) {
549 // found it
550 char* libname = new char[strlen(lib) + strlen(dir) + 2];
551 strcpy(libname, dir);
552 strcat(libname, "/");
553 strcat(libname, lib);
554 // load the libraries this lib depends upon
555
556 // i should look in the library's .dep file to
557 // get the dependencies, but this makes it a little
558 // difficult to make sure the same library doesn't
559 // get loaded twice (which is important) so for now
560 // i'll just wait until after i load the library and
561 // then look in the unresolved parents set
562 // and load parents until nothing is left
563
564 // load the library
565 ExEnv::outn() << "loading \"" << libname << "\"" << endl;
566 dlopen(libname, RTLD_LAZY);
567
568 // load code for parents
569 while (unresolved_parents_
570 && unresolved_parents_->size()) {
571 load_class((*unresolved_parents_->begin()).c_str());
572 }
573
574 fclose(fp);
575 delete[] path;
576 // make sure it worked.
577 if (name_to_class_desc(classname) == 0) {
578 ExEnv::errn() << "load of \"" << classname << "\" from \""
579 << libname << "\" failed" << endl;
580 delete[] libname;
581 return -1;
582 }
583 ExEnv::outn() << "loaded \"" << classname << "\" from \""
584 << libname << "\"" << endl;
585 delete[] libname;
586 return 0;
587 }
588 testclassname = strtok(0," ");
589 }
590 }
591 fclose(fp);
592 }
593
594 dir = strtok(0, ":");
595 }
596
597 delete[] path;
598 #endif // HAVE_DLFCN_H
599
600 ExEnv::outn() << "ClassDesc::load_class(\"" << classname << "\"): load failed"
601 << endl
602 << "Either \"" << classname << "\" is an invalid class name or the code"
603 << endl
604 << "for \"" << classname << "\" was not linked into the executable."
605 << endl;
606
607 return -1;
608 }
609
610 ////////////////////////////////////////////////////
611
612 static ClassDesc DescribedClass_cd(
613 typeid(DescribedClass),"DescribedClass");
614
DescribedClass()615 DescribedClass::DescribedClass()
616 {
617 }
618
DescribedClass(const DescribedClass &)619 DescribedClass::DescribedClass(const DescribedClass&) {}
operator =(const DescribedClass &)620 DescribedClass& DescribedClass::operator=(const DescribedClass&)
621 {
622 return *this;
623 }
624
~DescribedClass()625 DescribedClass::~DescribedClass()
626 {
627 }
628
629 ClassDesc*
class_desc() const630 DescribedClass::class_desc() const throw()
631 {
632 ClassDesc *cd;
633 try {
634 cd = ClassDesc::class_desc(typeid(*this));
635 }
636 catch (...) {
637 cd = 0;
638 }
639 return cd;
640 }
641
class_name() const642 const char* DescribedClass::class_name() const
643 {
644 return class_desc()->name();
645 }
646
class_version() const647 int DescribedClass::class_version() const
648 {
649 return class_desc()->version();
650 }
651
652 void
print(ostream & o) const653 DescribedClass::print(ostream &o) const
654 {
655 o << indent << "Object of type " << class_name() << endl;
656 }
657
658 ostream &
operator <<(ostream & o,const RefBase & ref)659 operator <<(ostream&o, const RefBase &ref)
660 {
661 DescribedClass *dc = dynamic_cast<DescribedClass*>(ref.parentpointer());
662 if (dc) {
663 dc->print(o);
664 }
665 else {
666 o << indent << "reference to null" << endl;
667 }
668
669 return o;
670 }
671
672 #ifdef EXPLICIT_TEMPLATE_INSTANTIATION
673
674 template class std::map<std::string,ClassDescP>;
675
676 template class std::map<std::string,int>;
677 template class std::set<std::string>;
678
679 #endif
680
681 /////////////////////////////////////////////////////////////////////////////
682
683 // Local Variables:
684 // mode: c++
685 // c-file-style: "CLJ"
686 // End:
687