1 /* compiler.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2005-2009 */
2
3 /*
4
5 Copyright (c) 2005-2009 Made to Order Software Corp.
6
7 Permission is hereby granted, free of charge, to any
8 person obtaining a copy of this software and
9 associated documentation files (the "Software"), to
10 deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify,
12 merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom
14 the Software is furnished to do so, subject to the
15 following conditions:
16
17 The above copyright notice and this permission notice
18 shall be included in all copies or substantial
19 portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
22 ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
23 LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
24 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
28 ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 SOFTWARE.
31
32 */
33
34 #include "compiler.h"
35
36 #ifdef WIN32
37 #include <windows.h>
38 #endif
39
40
41 namespace sswf
42 {
43 namespace as
44 {
45
46 #ifdef _MSVC
47 // replicate the Unix directory functions
48 struct DIRECTORY {
49 const char *path;
50 struct _finddata_t info;
51 long handle;
52 };
53
54 typedef struct DIRECTORY DIR;
55
56
57 struct dirent {
58 char d_name[128];
59 };
60
opendir(const char * path)61 DIR *opendir(const char *path)
62 {
63 static DIR dir;
64
65 memset(&dir, 0, sizeof(dir));
66 dir.path = path;
67 dir.handle = -1;
68
69 return (&dir);
70 }
71
72
readdir(DIR * dir)73 struct dirent *readdir(DIR *dir)
74 {
75 static struct dirent entry;
76
77 long err, len;
78 char *name;
79
80 memset(&entry, 0, sizeof(entry));
81
82 if(dir->handle == -1) {
83 len = strlen(dir->path) + 3;
84 name = new char[len];
85 memcpy(name, dir->path, len - 3);
86 name[len - 3] = '\\';
87 name[len - 2] = '*';
88 name[len - 1] = '\0';
89 dir->handle = _findfirst(
90 name,
91 &dir->info);
92 delete [] name;
93 if(dir->handle < 0) {
94 err = -1;
95 }
96 else {
97 err = 0;
98 }
99 }
100 else {
101 err = _findnext(dir->handle,
102 &dir->info);
103 }
104
105 if(err < 0) { /* end of directory */
106 return (NULL);
107 }
108
109 strncpy(entry.d_name, dir->info.name,
110 sizeof(entry.d_name));
111
112 return (&entry);
113 }
114
115
closedir(DIR * dir)116 void closedir(DIR *dir)
117 {
118 if(dir->handle != -1) {
119 _findclose(dir->handle);
120 }
121
122 return;
123 }
124 #endif /* #if _MSVC */
125
126
127 /**********************************************************************/
128 /**********************************************************************/
129 /*** COMPILER CREATOR ***********************************************/
130 /**********************************************************************/
131 /**********************************************************************/
132
CreateCompiler(InputRetriever * input)133 Compiler *Compiler::CreateCompiler(InputRetriever *input)
134 {
135 return new IntCompiler(input);
136 }
137
138
Version(void)139 const char *Compiler::Version(void)
140 {
141 return TO_STR(SSWF_VERSION);
142 }
143
144
145 /**********************************************************************/
146 /**********************************************************************/
147 /*** INTERNAL COMPILER (Basics) *************************************/
148 /**********************************************************************/
149 /**********************************************************************/
150
151
152 IntCompiler::rc_t IntCompiler::g_rc;
153 NodePtr IntCompiler::g_global_import;
154 NodePtr IntCompiler::g_system_import;
155 NodePtr IntCompiler::g_native_import;
156
157
IntCompiler(InputRetriever * input)158 IntCompiler::IntCompiler(InputRetriever *input)
159 {
160 // TODO: under MS-Windows, we may want to use something such
161 // as a SHGetFolder() call
162 f_home = getenv("HOME");
163
164 //f_default_error_stream -- auto-init
165 f_error_stream = &f_default_error_stream;
166 //f_optimizer -- auto-init
167 f_optimizer.SetErrorStream(f_default_error_stream);
168 f_options = 0;
169 f_input_retriever = input;
170 //f_program -- auto-init
171 f_time = time(NULL);
172 f_err_flags = 0;
173 //f_scope -- auto-init
174 f_db = 0;
175 f_db_size = 0;
176 f_db_data = 0;
177 f_db_count = 0;
178 f_db_max = 0;
179 f_db_packages = 0;
180 f_mod_count = 0;
181 f_mod_max = 0;
182 f_modules = 0;
183
184 InternalImports();
185 }
186
187
~IntCompiler()188 IntCompiler::~IntCompiler()
189 {
190 if(f_db != 0) {
191 fclose(f_db);
192 }
193 delete [] f_db_data;
194
195 // delete packages we added in this session
196 for(size_t idx = 0; idx < f_db_count; ++idx) {
197 if(f_db_packages[idx] < f_db_data
198 || f_db_packages[idx] > f_db_data + f_db_size) {
199 delete [] f_db_packages[idx];
200 }
201 }
202
203 delete [] f_db_packages;
204 }
205
206
SetInputRetriever(InputRetriever * retriever)207 InputRetriever *IntCompiler::SetInputRetriever(InputRetriever *retriever)
208 {
209 InputRetriever *old(f_input_retriever);
210
211 f_input_retriever = retriever;
212
213 return old;
214 }
215
216
SetErrorStream(ErrorStream & error_stream)217 void IntCompiler::SetErrorStream(ErrorStream& error_stream)
218 {
219 f_error_stream = &error_stream;
220 f_optimizer.SetErrorStream(error_stream);
221 }
222
223
SetOptions(Options & options)224 void IntCompiler::SetOptions(Options& options)
225 {
226 f_options = &options;
227 f_optimizer.SetOptions(options);
228 }
229
230
231
232
233
isspace(int c)234 bool IntCompiler::isspace(int c)
235 {
236 return c == ' ' || c == '\t';
237 }
238
239
240
241
FindRC(const String & home,bool accept_if_missing)242 void IntCompiler::rc_t::FindRC(const String& home, bool accept_if_missing)
243 {
244 // first try to find a place with a .rc
245 // (at this time, these directories are kind of random...)
246 static const char *directories[] = {
247 // Mainly for Unices
248 ".",
249 "include/sswf/scripts",
250 "@include/sswf/scripts", // For Win32
251 ".sswf",
252 "~/.sswf",
253 "~/sswf",
254 "/etc",
255 "/etc/sswf",
256 "/usr/include/sswf/scripts",
257 "/usr/share/sswf/scripts",
258 0
259 };
260
261 const char **dir, *d;
262
263 for(dir = directories; *dir != 0; ++dir) {
264 d = *dir;
265 if(*d == '@') {
266 // by default I assume I cannot apply this function properly
267 f_filename[0] = '\0';
268 #ifdef WIN32
269 char fullpath[MAX_PATH * 5];
270
271 fullpath[0] = '\0';
272 DWORD sz = GetModuleFileNameA(NULL, fullpath, MAX_PATH * 5);
273 if(sz < MAX_PATH * 5) {
274 char *s = fullpath + strlen(fullpath);
275 while(s > fullpath) {
276 if(s[-1] == '\\') {
277 s--;
278 while(s > fullpath) {
279 if(s[-1] == '\\') {
280 s[0] = '\0';
281 snprintf(f_filename, sizeof(f_filename),
282 "%s%s\\sswf.rc", fullpath, *dir + 1);
283 break;
284 }
285 s--;
286 }
287 break;
288 }
289 s--;
290 }
291 }
292 #endif
293 }
294 else if(*d == '~') {
295 if(home.IsEmpty()) {
296 // no valid HOME directory
297 continue;
298 }
299 char buf[256];
300 size_t sz = sizeof(buf);
301 home.ToUTF8(buf, sz);
302 snprintf(f_filename, sizeof(f_filename), "%s/%s/sswf.rc", buf, *dir + 1);
303 }
304 else {
305 snprintf(f_filename, sizeof(f_filename), "%s/sswf.rc", *dir);
306 }
307 if(f_filename[0] != '\0') {
308 #ifdef WIN32
309 // Win98 can have problems with / in filenames
310 char *s = f_filename;
311 while(*s != '\0') {
312 if(*s == '/') {
313 *s = '\\';
314 }
315 s++;
316 }
317 #endif
318 f_f = fopen(f_filename, "rb");
319 if(f_f != 0) {
320 break;
321 }
322 }
323 }
324 if(f_f == 0) {
325 if(!accept_if_missing) {
326 fprintf(stderr, "INSTALLATION ERROR: cannot find the sswf.rc file; it is usually put in "
327 #ifdef WIN32
328 "include/sswf/scripts found under the folder of the sswf executable.\n"
329 #else
330 "/etc/sswf/sswf.rc\n"
331 #endif
332 );
333 exit(1);
334 }
335 // if we want everything internal, we'll just use working defaults
336 f_path = "include/sswf/scripts";
337 f_db = "tmp/asc_packages.db";
338 strcpy(f_filename, "internal.rc");
339 }
340 }
341
342
ReadRC(void)343 void IntCompiler::rc_t::ReadRC(void)
344 {
345 char buf[256], *s, *name, *param;
346 int l, line, quote;
347
348 // if f_f is null, we already have the defaults
349 // and f_input_retriever is not NULL
350 if(f_f == 0) {
351 return;
352 }
353
354 line = 0;
355 while(fgets(buf, sizeof(buf), f_f) != 0) {
356 line++;
357 s = buf;
358 while(isspace(*s)) {
359 s++;
360 }
361 if(*s == '#' || *s == '\n' || *s == '\0') {
362 continue;
363 }
364 name = s;
365 while(*s != '\0' && *s != '=' && !isspace(*s)) {
366 s++;
367 }
368 l = s - name;
369 while(isspace(*s)) {
370 s++;
371 }
372 if(*s != '=') {
373 fprintf(stderr, "%s:%d: syntax error; expected an equal sign\n", f_filename, line);
374 continue;
375 }
376 // skip the = and following spaces
377 s++;
378 while(isspace(*s)) {
379 s++;
380 }
381 if(*s == '"' || *s == '\'') {
382 quote = *s++;
383 param = s;
384 while(*s != '\0' && *s != quote && *s != '\n') {
385 s++;
386 }
387 }
388 else {
389 param = s;
390 while(*s != '\0' && *s != '\n') {
391 s++;
392 }
393 }
394 *s = '\0';
395 if(l == 7 && strncmp(name, "version", 7) == 0) {
396 // TODO: check that we understand this version
397 continue;
398 }
399 if(l == 8 && strncmp(name, "asc_path", 8) == 0) {
400 //fprintf(stderr, "Got path! [%s]\n", param);
401 f_path = param;
402 continue;
403 }
404 if(l == 6 && strncmp(name, "asc_db", 6) == 0) {
405 f_db = param;
406 continue;
407 }
408 }
409 }
410
411
412
413
GetPackageFilename(const char * package_info)414 String IntCompiler::GetPackageFilename(const char *package_info)
415 {
416 int cnt;
417
418 cnt = 0;
419 while(*package_info != '\0') {
420 package_info++;
421 if(package_info[-1] == ' ') {
422 cnt++;
423 if(cnt >= 3) {
424 break;
425 }
426 }
427 }
428 if(*package_info != '"') {
429 return "";
430 }
431 package_info++;
432 const char *name = package_info;
433 while(*package_info != '"' && *package_info != '\0') {
434 package_info++;
435 }
436
437 String result;
438 result.FromUTF8(name, package_info - name);
439
440 return result;
441 }
442
443
444 /** \fn InputRetriever::Retrieve(const char *filename) = 0
445 *
446 * \brief Function you want to overload to provide your own input file to the loader.
447 *
448 * The IntCompiler::FindModule() function calls this function to retrieve an
449 * input file. The input file is used to load the module specified by
450 * \p filename.
451 *
452 * This is used by the libasc extension that enables you to put all the system
453 * ASC files in your binary and thus avoid you having to deal with a directory
454 * structure.
455 *
456 * \bug
457 * The returned pointer MUST be a class derived from Input and MUST be allocated.
458 * If the file cannot be opened by your retriever, then return NULL.
459 *
460 * \param[in] filename The name of the module to be loaded
461 *
462 * \return NULL or an object derived from the Input class. You MUST allocate this object
463 * since the IntCompiler::FindModule() will delete it once the module was loaded.
464 */
465
FindModule(const String & filename,NodePtr & result)466 bool IntCompiler::FindModule(const String& filename, NodePtr& result)
467 {
468 int i, j, p, r;
469 module_t *mod;
470
471 p = 0; // avoid warnings
472 j = f_mod_count;
473 if(j < 4) {
474 // lack of precision forces us to do this with 0 to 3 elements
475 p = 0;
476 while(p < j) {
477 r = filename.Compare(f_modules[p].f_filename);
478 if(r == 0) {
479 result = f_modules[p].f_node;
480 return true;
481 }
482 // when filename < f_modules.f_filename,
483 // we won't find anything anymore; also
484 // p is at the right place to add the
485 // file!
486 if(r < 0) {
487 break;
488 }
489 p++;
490 }
491 }
492 else {
493 // this is really the binary search
494 // (see the / 2 below?)
495 i = 0;
496 while(i < j) {
497 p = i + (j - i) / 2;
498 r = filename.Compare(f_modules[p].f_filename);
499 if(r == 0) {
500 result = f_modules[p].f_node;
501 return true;
502 }
503 if(r > 0) {
504 p++;
505 i = p;
506 }
507 else {
508 j = p;
509 }
510 }
511 }
512
513 // we couldn't find this entry, when n != 0 insert a new row
514 // otherwise try to load the module
515 if(!result.HasNode()) {
516 FileInput file_input;
517 Input *in(NULL);
518 char *fn = filename.GetUTF8();
519 if(f_input_retriever != NULL) {
520 in = f_input_retriever->Retrieve(fn);
521 }
522 if(in == NULL) {
523 if(!file_input.Open(fn)) {
524 fprintf(stderr, "FATAL ERROR: cannot open module file \"%s\".\n", fn);
525 delete [] fn;
526 exit(1);
527 }
528 in = &file_input;
529 }
530
531 Parser *parser = Parser::CreateParser();
532 if(f_options != 0) {
533 parser->SetOptions(*f_options);
534 }
535 parser->SetInput(*in);
536
537 result = parser->Parse();
538
539 delete parser;
540 if(in != &file_input) {
541 // This is ugly, we should have a shared pointer or
542 // some other feature to handle that one!
543 delete in;
544 }
545
546 #if 0 && (defined(_DEBUG) || defined(DEBUG))
547 fprintf(stderr, "%s module:\n", fn);
548 result.Display(stderr);
549 #endif
550
551 if(!result.HasNode()) {
552 fprintf(stderr, "FATAL ERROR: cannot compile module file \"%s\".\n", fn);
553 delete [] fn;
554 exit(1);
555 }
556
557 delete [] fn;
558 }
559
560 // we need to enlarge the f_db_packages buffer
561 if(f_mod_count >= f_mod_max) {
562 // enlarge buffer
563 f_mod_max += 250;
564 mod = new module_t[f_mod_max];
565 for(i = 0; (size_t) i < f_mod_count; ++i) {
566 mod[i] = f_modules[i];
567 }
568 delete [] f_modules;
569 f_modules = mod;
570 }
571
572 i = f_mod_count;
573 while(i > p) {
574 f_modules[i] = f_modules[i - 1];
575 i--;
576 }
577 f_mod_count++;
578
579 f_modules[p].f_filename = filename;
580 f_modules[p].f_node = result;
581
582 return true;
583 }
584
585
586
LoadModule(const char * module,const char * file)587 NodePtr IntCompiler::LoadModule(const char *module, const char *file)
588 {
589 char buf[256];
590 char path[256];
591 size_t sz;
592 NodePtr result;
593
594 // create the name of the module
595 sz = sizeof(path);
596 g_rc.GetPath().ToUTF8(path, sz);
597 sz = snprintf(buf, sizeof(buf), "%s/%s/%s", path, module, file);
598 if(sz >= sizeof(buf)) {
599 fprintf(stderr, "FATAL ERROR: filename too long; cannot load module.\n");
600 exit(1);
601 }
602
603 // search for the module; if not already loaded, load it
604 FindModule(buf, result);
605 return result;
606 }
607
608
ReadDB(void)609 void IntCompiler::ReadDB(void)
610 {
611 Input *in(NULL);
612
613 if(f_db != 0) {
614 fclose(f_db);
615 f_db = 0;
616 }
617
618 // the input retriever defines the packages database?
619 if(f_input_retriever != NULL) {
620 // if we have a retriever query the retriever for the database
621 // in this case the database is read only (i.e. f_db remains set
622 // to zero)
623 in = f_input_retriever->Retrieve("asc_packages.db");
624 }
625 if(in != NULL) {
626 f_db_size = in->GetSize();
627 delete [] f_db_data;
628 f_db_data = new char[f_db_size + 2];
629 // TODO: we do not currently support UTF-8 here, only ASCII...
630 for(size_t i = 0; i < f_db_size; ++i) {
631 f_db_data[i] = in->GetC();
632 }
633 // we're done with the input
634 delete in;
635
636 f_db_data[f_db_size] = '\0';
637 }
638 else {
639 // get resource filename if any
640 String db = g_rc.GetDB();
641 if(db.GetLength() == 0) {
642 // internal default
643 db = "~/.sswf/asc_packages.db";
644 }
645
646 // check for user path
647 const long *path = db.Get();
648 long len = db.GetLength();
649 if(len > 1
650 && path[0] == '~'
651 && (path[1] == '/' || path[1] == '\\')) {
652 String s = f_home;
653 s.AppendStr(path + 1, len - 1);
654 db = s;
655 }
656
657 // open the file
658 char filename[256];
659 size_t sz = sizeof(filename);
660 db.ToUTF8(filename, sz);
661 f_db = fopen(filename, "rb+");
662 if(f_db == 0) {
663 // if it fails, try to create possibly missing directories
664 char *fn;
665 fn = filename;
666 while(*fn != '\0') {
667 if(*fn == '/' || *fn == '\\') {
668 char c = *fn;
669 *fn = '\0';
670 #ifdef WIN32
671 mkdir(filename);
672 #else
673 mkdir(filename, 0700);
674 #endif
675 *fn = c;
676 do {
677 fn++;
678 } while(*fn == '/' || *fn == '\\');
679 }
680 else {
681 fn++;
682 }
683 }
684 // now try again to open the file...
685 f_db = fopen(filename, "wb+");
686 if(f_db == 0) {
687 // we can't create the database!
688 fprintf(stderr, "FATAL ERROR: can't open or create database file \"%s\" for package information.\n", filename);
689 exit(1);
690 }
691 }
692
693 // read the file at once [we need it this way to do binary searches]
694 // mmap would be a pain to manage since we would need to remap all the
695 // time whenever we insert new lines in the DB.
696 fseek(f_db, 0, SEEK_END);
697 f_db_size = ftell(f_db);
698 fseek(f_db, 0, SEEK_SET);
699
700 if(f_db_size == 0) {
701 // when the file just got created, print out a comment
702 fprintf(f_db, "# Database of SSWF ActionScript Compiler (asc)\n");
703 fprintf(f_db, "# DO NOT EDIT UNLESS YOU KNOW WHAT YOU ARE DOING\n");
704 fprintf(f_db, "# Copyright (c) 2005-2009 by Made to Order Software Corp.\n");
705 fprintf(f_db, "# WARNING: package names below MUST be sorted\n");
706 fprintf(f_db, "# This file is written in UTF-8\n");
707 fprintf(f_db, "# You can safely modify it with an editor which supports UTF-8\n");
708 fprintf(f_db, "# package name + element name + type + file name + line number\n");
709 fflush(f_db);
710
711 fseek(f_db, 0, SEEK_END);
712 f_db_size = ftell(f_db);
713 fseek(f_db, 0, SEEK_SET);
714 }
715
716 delete [] f_db_data;
717 f_db_data = new char[f_db_size + 2];
718 if(fread(f_db_data, 1, f_db_size, f_db) != f_db_size) {
719 fprintf(stderr, "FATAL ERROR: can't read the database file: \"%s\".\n", filename);
720 exit(1);
721 }
722
723 f_db_data[f_db_size] = '\0';
724 }
725
726 // "parse" the file and create the sorted array
727 // [the file is assumed to already be sorted]
728 // count the lines which aren't commented out
729 // and we remove empty lines and change new lines to just '\n'
730 f_db_count = 0;
731 char c, *s, *d;
732 s = d = f_db_data;
733 c = *s;
734 while(c != '\0') {
735 // skip leading spaces and empty lines
736 // (those are lost)
737 while(isspace(*s) || *s == '\n' || *s == '\r') {
738 s++;
739 }
740 // don't count comments (we don't need them in our array)
741 if(*s != '#') {
742 f_db_count++;
743 }
744 while(*s != '\n' && *s != '\r' && *s != '\0') {
745 *d++ = *s++;
746 }
747 // We need to skip this here otherwise a comment on
748 // the last line generates an error!
749 while(*s == '\n' || *s == '\r') {
750 s++;
751 }
752 c = *s;
753 // we only want '\n', no '\r\n' nor just '\r'
754 *d++ = '\n';
755 }
756 *d = '\0';
757 // f_db_size can now be smaller or 1 byte bigger
758 f_db_size = d - f_db_data;
759 //fprintf(stderr, "Count = %ld\n", (long) f_db_count);
760
761 if(f_db_count < 1000) {
762 f_db_max = 1000;
763 }
764 else {
765 f_db_max = f_db_count + 100;
766 }
767
768 // save the start of each line which isn't commented
769 f_db_packages = new char *[f_db_max];
770 s = f_db_data;
771 char **p = f_db_packages;
772 while(*s != '\0') {
773 // ignore comments
774 if(*s != '#') {
775 *p++ = s;
776 }
777 // TODO: we really need to check the validity of each
778 // entry and skip invalid lines as comments
779 // go to the next line
780 while(*s != '\0') {
781 s++;
782 if(s[-1] == '\n') {
783 break;
784 }
785 }
786 }
787
788 // it's ready.
789 }
790
791
792
793
WriteDB(void)794 void IntCompiler::WriteDB(void)
795 {
796 const char *s, *start;
797 size_t idx;
798
799 // if we are in ReadOnly mode, we simply cannot save the database
800 if(f_db == 0) {
801 return;
802 }
803
804 // clear the file
805 fseek(f_db, 0, SEEK_SET);
806 #ifdef WIN32
807 _chsize(fileno(f_db), 0);
808 #else
809 ftruncate(fileno(f_db), 0);
810 #endif
811
812 // save all the comments at the start of the file
813 s = f_db_data;
814 while(*s != '\0') {
815 start = s;
816 // ignore lines that are not comments
817 if(*s != '#') {
818 continue;
819 }
820 while(*s != '\n' && *s != '\0') {
821 s++;
822 }
823 fprintf(f_db, "%.*s\n", s - start, start);
824 while(*s == '\n') {
825 s++;
826 }
827 }
828
829 // now save the list of rows
830 for(idx = 0; idx < f_db_count; ++idx) {
831 start = s = f_db_packages[idx];
832 while(*s != '\n' && *s != '\0') {
833 s++;
834 }
835 fprintf(f_db, "%.*s\n", s - start, start);
836 }
837
838 // make sure it's written on disk
839 fflush(f_db);
840 }
841
842
843
844 // s2 ends with a space (name from database)
845 // s1 ends with a null terminator
pckcmp(const char * s1,const char * s2)846 int pckcmp(const char *s1, const char *s2)
847 {
848 int r, cnt;
849
850 cnt = 0;
851 while(*s1 != '\0' && *s2 != '\n' && *s2 != '\0') {
852 if(*s2 == ' ') {
853 cnt++;
854 if(cnt == 2) {
855 break;
856 }
857 if(*s1 != ' ') {
858 r = *s1 - *s2;
859 return r < 0 ? -1 : 1;
860 }
861 s1++;
862 s2++;
863 if(s1[0] == '*' && s1[1] == '\0') {
864 return 0;
865 }
866 continue;
867 }
868 r = *s1 - *s2;
869 if(r != 0) {
870 return r < 0 ? -1 : 1;
871 }
872 s1++;
873 s2++;
874 }
875
876 if(*s1 == '\0' && *s2 == ' ') {
877 return 0;
878 }
879
880 return *s1 != '\0' ? 1 : -1;
881 }
882
883
884 // Search for a named element:
885 // <package name>{.<package name>}.<class, function, variable name>
886 // TODO: add support for '*' in <package name>
FindElement(const String & package_name,const String & element_name,NodePtr * element,const char * type)887 const char *IntCompiler::FindElement(const String& package_name, const String& element_name, NodePtr *element, const char *type)
888 {
889 int i, j, p, r, len;
890 size_t sz;
891 char buf[16], **pck;
892
893 // transform the name in a UTF-8 string as supported by the database
894 len = package_name.GetUTF8Length() + element_name.GetUTF8Length();
895 if(len < 0) {
896 fprintf(stderr, "INTERNAL ERROR: UTF8 convertion failed! (1)\n");
897 exit(1);
898 }
899 len += 3;
900 #ifdef _MSVC
901 // alloca() not available with cl
902 class AutoDelete {
903 public: AutoDelete(char *ptr) { f_ptr = ptr; }
904 ~AutoDelete() { delete f_ptr; }
905 private: char *f_ptr;
906 };
907 char *n = new char[len];
908 AutoDelete ad_n(n);
909 #else
910 char n[len];
911 #endif
912 sz = len;
913 package_name.ToUTF8(n, sz);
914 n[len - sz] = ' ';
915 sz--;
916 element_name.ToUTF8(n + len - sz, sz);
917
918 // the f_db_packages list of strings are sorted
919 p = i = 0;
920 j = f_db_count;
921 if(j < 4) {
922 // lack of precision forces us to do this with 0 to 3 elements
923 p = 0;
924 while(p < j) {
925 r = pckcmp(n, f_db_packages[p]);
926 if(r == 0) {
927 goto found;
928 }
929 // when n < f_db_packages, we won't find anything
930 if(r < 0) {
931 break;
932 }
933 p++;
934 }
935 }
936 else {
937 // this is really the binary search
938 // (see the / 2 below?)
939 while(i < j) {
940 p = i + (j - i) / 2;
941 r = pckcmp(n, f_db_packages[p]);
942 if(r == 0) {
943 goto found;
944 }
945 if(r > 0) {
946 p++;
947 i = p;
948 }
949 else {
950 j = p;
951 }
952 }
953 }
954
955 // we couldn't find this entry, when type != 0 insert a new row
956 if(type == 0) {
957 return 0;
958 }
959
960 // we need to enlarge the f_db_packages buffer
961 if(f_db_count >= f_db_max) {
962 // enlarge buffer
963 f_db_max += 250;
964 pck = new char *[f_db_max];
965 memcpy(pck, f_db_packages, sizeof(char *) * f_db_count);
966 delete [] f_db_packages;
967 f_db_packages = pck;
968 }
969
970 if((int) f_db_count - p > 0) {
971 memmove(f_db_packages + p + 1, f_db_packages + p, sizeof(char *) * (f_db_count - p));
972 }
973 f_db_count++;
974
975 // build the string which is:
976 // <package name> <element name> <type> "<filename>" <line>
977 {
978 String entry(package_name);
979 entry += " ";
980 entry += element_name;
981 entry += " ";
982 entry += type;
983 entry += " \"";
984 entry += element->GetFilename();
985 entry += "\" ";
986 snprintf(buf, sizeof(buf), "%ld", element->GetLine());
987 entry += buf;
988 entry += "\n";
989
990 len = entry.GetUTF8Length();
991 if(len < 0) {
992 fprintf(stderr, "INTERNAL ERROR: UTF8 convertion failed! (2)\n");
993 exit(1);
994 }
995 len += 2;
996 f_db_packages[p] = new char[len];
997 sz = len;
998 if(entry.ToUTF8(f_db_packages[p], sz) < 0) {
999 fprintf(stderr, "INTERNAL ERROR: UTF8 convertion failed! (3)\n");
1000 exit(1);
1001 }
1002 //fflush(stdout);
1003 //fprintf(stderr, "Add [%s]\n", f_db_packages[p]);
1004 }
1005
1006 return f_db_packages[p];
1007
1008 found:
1009 // TODO: if type != 0 we want to test that the filename referenced
1010 // is the same as the one in the database
1011 return f_db_packages[p];
1012 }
1013
1014
FindPackages_AddDatabaseEntry(const String & package_name,NodePtr & element,const char * type)1015 void IntCompiler::FindPackages_AddDatabaseEntry(const String& package_name, NodePtr& element, const char *type)
1016 {
1017 // here, we totally ignore internal, private
1018 // and false entries right away
1019 unsigned long attr = GetAttributes(element);
1020 if((attr & (NODE_ATTR_PRIVATE | NODE_ATTR_FALSE | NODE_ATTR_INTERNAL)) != 0) {
1021 return;
1022 }
1023
1024 Data& data = element.GetData();
1025 FindElement(package_name, data.f_str, &element, type);
1026 }
1027
1028
1029
1030 // This function searches a list of directives for class, functions
1031 // and variables which are defined in a package. Their names are
1032 // then saved in the import database for fast search.
FindPackages_SavePackageElements(NodePtr & package,const String & package_name)1033 void IntCompiler::FindPackages_SavePackageElements(NodePtr& package, const String& package_name)
1034 {
1035 int max = package.GetChildCount();
1036 for(int idx = 0; idx < max; ++idx) {
1037 NodePtr& child = package.GetChild(idx);
1038 Data& data = child.GetData();
1039 if(data.f_type == NODE_DIRECTIVE_LIST) {
1040 FindPackages_SavePackageElements(child, package_name);
1041 }
1042 else if(data.f_type == NODE_CLASS) {
1043 FindPackages_AddDatabaseEntry(
1044 package_name,
1045 child,
1046 "class"
1047 );
1048 }
1049 else if(data.f_type == NODE_FUNCTION) {
1050 // we don't save prototypes, that's tested later
1051 int flg = data.f_int.Get();
1052 const char *type;
1053 if((flg & NODE_FUNCTION_FLAG_GETTER) != 0) {
1054 type = "getter";
1055 }
1056 else if((flg & NODE_FUNCTION_FLAG_SETTER) != 0) {
1057 type = "setter";
1058 }
1059 else {
1060 type = "function";
1061 }
1062 FindPackages_AddDatabaseEntry(
1063 package_name,
1064 child,
1065 type
1066 );
1067 }
1068 else if(data.f_type == NODE_VAR) {
1069 int vcnt = child.GetChildCount();
1070 for(int v = 0; v < vcnt; ++v) {
1071 NodePtr& variable = child.GetChild(v);
1072 // we don't save the variable type,
1073 // it wouldn't help resolution
1074 FindPackages_AddDatabaseEntry(
1075 package_name,
1076 variable,
1077 "variable"
1078 );
1079 }
1080 }
1081 else if(data.f_type == NODE_PACKAGE) {
1082 // sub packages
1083 NodePtr& list = child.GetChild(0);
1084 String name = package_name;
1085 name += ".";
1086 name += data.f_str;
1087 FindPackages_SavePackageElements(list, name);
1088 }
1089 }
1090 }
1091
1092
1093 // this function searches the tree for packages (it stops at classes,
1094 // functions, and other such blocks)
FindPackages_DirectiveList(NodePtr & list)1095 void IntCompiler::FindPackages_DirectiveList(NodePtr& list)
1096 {
1097 int max = list.GetChildCount();
1098 for(int idx = 0; idx < max; ++idx) {
1099 NodePtr& child = list.GetChild(idx);
1100 Data& data = child.GetData();
1101 if(data.f_type == NODE_DIRECTIVE_LIST) {
1102 FindPackages_DirectiveList(child);
1103 }
1104 else if(data.f_type == NODE_PACKAGE) {
1105 // Found a package! Save all the functions
1106 // variables and classes in the database
1107 // if not there yet.
1108 NodePtr& directive_list = child.GetChild(0);
1109 FindPackages_SavePackageElements(directive_list, data.f_str);
1110 }
1111 }
1112 }
1113
1114
FindPackages(NodePtr & program)1115 void IntCompiler::FindPackages(NodePtr& program)
1116 {
1117 Data& data = program.GetData();
1118 if(data.f_type != NODE_PROGRAM) {
1119 return;
1120 }
1121
1122 FindPackages_DirectiveList(program);
1123 }
1124
1125
LoadInternalPackages(const char * module)1126 void IntCompiler::LoadInternalPackages(const char *module)
1127 {
1128 #ifndef PATH_MAX
1129 #define PATH_MAX 259
1130 #endif
1131 char buf[PATH_MAX], path[PATH_MAX];
1132 size_t sz;
1133 DIR *dir;
1134
1135 sz = sizeof(path);
1136 g_rc.GetPath().ToUTF8(path, sz);
1137 snprintf(buf, sizeof(buf), "%s/%s", path, module);
1138 dir = opendir(buf);
1139 if(dir == 0) {
1140 #ifdef WIN32
1141 if(buf[0] != '/' && buf[0] != '\\'
1142 && (((buf[0] < 'a' || buf[0] > 'z')
1143 && (buf[0] < 'A' || buf[0] > 'Z'))
1144 || buf[1] != ':')) {
1145 char fullpath[MAX_PATH * 5];
1146
1147 fullpath[0] = '\0';
1148 fullpath[sizeof(fullpath) - 1] = '\0';
1149 DWORD sz = GetModuleFileNameA(NULL, fullpath, sizeof(fullpath));
1150 if(sz < MAX_PATH * 5) {
1151 char *s = fullpath + strlen(fullpath);
1152 while(s > fullpath) {
1153 if(s[-1] == '\\') {
1154 s--;
1155 while(s > fullpath) {
1156 if(s[-1] == '\\') {
1157 strncpy(s, buf, (sizeof(fullpath) - 1) - (s - fullpath));
1158 dir = opendir(fullpath);
1159 break;
1160 }
1161 s--;
1162 }
1163 break;
1164 }
1165 s--;
1166 }
1167 }
1168 }
1169 #endif
1170 if(dir == 0) {
1171 fprintf(stderr, "INSTALLATION ERROR: cannot read directory \"%s\".\n", buf);
1172 exit(1);
1173 }
1174 }
1175
1176 struct dirent *ent;
1177 while((ent = readdir(dir)) != 0) {
1178 const char *s = ent->d_name;
1179 const char *e = 0;
1180 while(*s != '\0') {
1181 if(*s == '.') {
1182 e = s;
1183 }
1184 s++;
1185 }
1186 // TODO: under MS-Windows and other such file systems,
1187 // a filename is not case sensitive
1188 if(e == 0 || strcmp(e, ".asc") != 0
1189 || strcmp(ent->d_name, "as_init.asc") == 0) {
1190 continue;
1191 }
1192 // we've got a file of interest
1193 // TODO: we want to keep this package in RAM since
1194 // we already parsed it!
1195 NodePtr p = LoadModule(module, ent->d_name);
1196 // now we can search the package in the actual code
1197 FindPackages(p);
1198 }
1199
1200 // avoid leaks
1201 closedir(dir);
1202 }
1203
1204
InternalImports(void)1205 void IntCompiler::InternalImports(void)
1206 {
1207 if(!g_global_import.HasNode()) {
1208 #if defined(_DEBUG) || defined(DEBUG)
1209 fflush(stdout);
1210 #endif
1211 // Read the resource file
1212 g_rc.FindRC(f_home, f_input_retriever != NULL);
1213 g_rc.ReadRC();
1214 g_rc.Close();
1215
1216 g_global_import = LoadModule("global", "as_init.asc");
1217 g_system_import = LoadModule("system", "as_init.asc");
1218 g_native_import = LoadModule("native", "as_init.asc");
1219 }
1220
1221 ReadDB();
1222
1223 if(f_db_count == 0) {
1224 LoadInternalPackages("global");
1225 LoadInternalPackages("system");
1226 LoadInternalPackages("native");
1227
1228 // this saves the internal packages info
1229 WriteDB();
1230 }
1231 }
1232
1233
1234
1235 }; // namespace as
1236 }; // namespace sswf
1237