as3: improved dependency handling
[swftools.git] / lib / as3 / compiler.c
1 /* compiler.h
2
3    Compiler for parsing Flash2 AVM2 ABC Actionscript
4
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2008/2009 Matthias Kramm <kramm@quiss.org>
9  
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
23
24 #include "common.h"
25 #include "tokenizer.h"
26 #include "files.h"
27 #include "parser.h"
28 #include "parser.tab.h"
29 #include "compiler.h"
30 #include "../os.h"
31 #ifdef HAVE_SYS_STAT_H
32 #include <sys/stat.h>
33 #endif
34 #ifdef HAVE_DIRENT_H
35 #include <dirent.h>
36 #endif
37
38 /* flex/bison definitions */
39 extern int a3_parse();
40 extern int as3_lex();
41 extern int as3_lex_destroy();
42
43 static char config_recurse = 0;
44
45 void as3_setverbosity(int level)
46 {
47     as3_verbosity=level;
48 }
49 void as3_add_include_dir(char*dir)
50 {
51     add_include_dir(dir);
52 }
53 void as3_set_option(const char*key, const char*value)
54 {
55     if(!strcmp(key, "recurse")) {
56         config_recurse=atoi(value);
57     }
58 }
59
60 static char registry_initialized = 0;
61 static char parser_initialized = 0;
62
63 //#define STORE_TOKENS
64
65 //#define DEBUG
66 #define DEBUG if(0)
67
68 int a3_lex()
69 {
70     as3_tokencount++;
71     return as3_lex();
72 }
73
74 typedef struct _compile_list {
75     const char*name;
76     const char*filename;
77     struct _compile_list*next;
78 } compile_list_t;
79 static compile_list_t*compile_list=0;
80
81 static void as3_parse_file_or_array(const char*name, const char*filename, const void*mem, int length)
82 {
83     if(!registry_initialized) {
84         registry_initialized = 1;
85         registry_init();
86     }
87     if(!parser_initialized) {
88         parser_initialized = 1;
89         initialize_parser();
90     }
91
92     FILE*fi = 0;
93     if(filename) {
94         if(as3_pass==1 && !mem) {
95             // record the fact that we compiled this file
96             compile_list_t*c = rfx_calloc(sizeof(compile_list_t));
97             c->next = compile_list;
98             c->name = strdup(name);
99             c->filename = strdup(filename);
100             compile_list = c;
101         }
102         DEBUG printf("[pass %d] parse file %s %s\n", as3_pass, name, filename);
103         fi = enter_file2(name, filename, 0);
104         as3_file_input(fi);
105     } else {
106         DEBUG printf("[pass %d] parse bytearray %s (%d bytes)\n", as3_pass, name, length);
107         enter_file(name, name, 0);
108         as3_buffer_input((void*)mem, length);
109     }
110
111     as3_tokencount=0;
112     initialize_file(name, filename);
113     a3_parse();
114     as3_lex_destroy();
115     finish_file();
116 }
117
118 typedef struct _scheduled_file {
119     char*name;
120     char*filename;
121     struct _scheduled_file*next;
122 } scheduled_file_t;
123
124 static scheduled_file_t*scheduled=0;
125 dict_t*scheduled_dict=0;
126
127 void as3_parse_scheduled()
128 {
129     DEBUG printf("[pass %d] parse scheduled\n", as3_pass);
130
131     while(scheduled) {
132         scheduled_file_t*s = scheduled;
133         scheduled = 0;
134         while(s) {
135             scheduled_file_t*old = s;
136             as3_parse_file_or_array(s->name, s->filename, 0,0);
137             s = s->next;
138
139             free(old->filename);
140             free(old->name);
141             old->filename = old->name = 0;
142             free(old);
143         }
144     }
145     if(scheduled_dict) {
146         dict_destroy(scheduled_dict);
147         scheduled_dict=0;
148     }
149 }
150
151 void as3_schedule_file(const char*name, const char*filename) 
152 {
153     if(!scheduled_dict) {
154         scheduled_dict = dict_new();
155     }
156
157     filename = normalize_path(filename);
158     
159     if(dict_contains(scheduled_dict, filename)) {
160         return; //already processed
161     } else {
162         dict_put(scheduled_dict, filename, 0);
163     }
164     DEBUG printf("[pass %d] schedule %s %s\n", as3_pass, name, filename);
165
166     NEW(scheduled_file_t, f);
167     f->name = strdup(name);
168     f->filename = strdup(filename);
169     f->next = scheduled; // dfs
170     scheduled = f;
171 }
172
173 void as3_parse_list()
174 {
175     while(compile_list) {
176         as3_parse_file_or_array(compile_list->name, compile_list->filename, 0,0);
177         compile_list = compile_list->next;
178     }
179 }
180
181 void as3_parse_bytearray(const char*name, const void*mem, int length)
182 {
183     as3_pass = 1;
184     as3_parse_file_or_array(name, 0, mem, length);
185     as3_parse_scheduled();
186     
187     registry_resolve_all();
188     
189     as3_pass = 2;
190     as3_parse_file_or_array(name, 0, mem, length);
191     as3_parse_list();
192 }
193
194 void as3_parse_file(const char*filename) 
195 {
196     char*fullfilename = find_file(filename, 1);
197     if(!fullfilename)
198         return; // not found
199
200     compile_list = 0;
201     as3_pass = 1;
202     as3_schedule_file(filename, fullfilename);
203     as3_parse_scheduled();
204     
205     registry_resolve_all();
206
207     as3_pass = 2;
208     as3_parse_list();
209
210     free(fullfilename);
211 }
212
213 void as3_parse_directory(const char*dir)
214 {
215     compile_list = 0;
216
217     as3_pass = 1;
218     as3_schedule_directory(dir);
219     if(!scheduled)
220         as3_warning("Directory %s doesn't contain any ActionScript files", dir);
221     as3_parse_scheduled();
222
223     registry_resolve_all();
224
225     as3_pass = 2;
226     as3_parse_list();
227 }
228
229 char as3_schedule_directory(const char*dirname)
230 {
231     DEBUG printf("[pass %d] schedule directory %s\n", as3_pass, dirname);
232     char ok=0;
233 #ifdef HAVE_DIRENT_H
234     include_dir_t*i = current_include_dirs;
235     while(i) {
236         char*fulldirname = concat_paths(i->path, dirname);
237         DEBUG printf("[pass %d] ... %s\n", as3_pass, fulldirname);
238         DIR*dir = opendir(fulldirname);
239         if(dir) {
240             ok = 1;
241             struct dirent*ent;
242             while(1) {
243                 ent = readdir(dir);
244                 if (!ent) 
245                     break;
246                 char*name = ent->d_name;
247                 char type = 0;
248                 if(!name) continue;
249                 int l=strlen(name);
250                 if(l<4)
251                     continue;
252                 if(strncasecmp(&name[l-3], ".as", 3)) 
253                     continue;
254                 char*fullfilename = concatPaths(fulldirname, name);
255                 as3_schedule_file(name, fullfilename);
256                 free(fullfilename);
257             }
258         }
259         free(fulldirname);
260         i = i->next;
261     }
262 #endif
263     return ok;
264 }
265
266 void as3_schedule_package(const char*package)
267 {
268     DEBUG printf("[pass %d] schedule package %s\n", as3_pass, package);
269     char*dirname = strdup(package);
270     int s=0;
271     while(dirname[s]) {
272         if(dirname[s]=='.') dirname[s]='/';
273         s++;
274     };
275     if(!as3_schedule_directory(dirname))
276         as3_softwarning("Could not find package %s in file system", package);
277 }
278
279 static void schedule_class(const char*package, const char*cls, char error)
280 {
281     if(error) {
282         DEBUG printf("[pass %d] schedule class %s.%s\n",  as3_pass, package, cls);
283     }
284     if(!cls) {
285         as3_schedule_package(package);
286         return;
287     }
288     int l1 = package?strlen(package):0;
289     int l2 = cls?strlen(cls):0;
290     char*filename = malloc(l1+l2+5);
291     int s=0,t=0;
292     while(package[s]) {
293         if(package[s]=='.')
294             filename[t++]='/';
295         else
296             filename[t++] = package[s];
297         s++;
298     }
299     if(t)
300         filename[t++] = '/';
301
302     strcpy(filename+t, cls);
303     strcpy(filename+t+l2, ".as");
304     char*f=0;
305     if(!(f=find_file(filename, error))) {
306         int i;
307         /* try lower case filename (not packagename!), too */
308         for(i=t;i<t+l2;i++) {
309             if(filename[i]>='A' && filename[i]<='Z')
310                 filename[i] += 'a'-'A';
311         }
312         if(!(f=find_file(filename, error))) {
313             if(error) {
314                 strcpy(filename+t, cls);
315                 strcpy(filename+t+l2, ".as");
316                 as3_warning("Could not open file %s", filename);
317             }
318             return;
319         }
320     }
321     as3_schedule_file(filename, f);
322 }
323
324 void as3_schedule_class(const char*package, const char*cls)
325 {
326     schedule_class(package, cls, 1);
327 }
328
329 void as3_schedule_class_noerror(const char*package, const char*cls)
330 {
331     if(config_recurse) {
332         schedule_class(package, cls, 0);
333     }
334 }
335
336
337 static void*as3code = 0;
338 void* as3_getcode()
339 {
340     if(parser_initialized) {
341         parser_initialized = 0;
342         as3code = finish_parser();
343     }
344     return as3code;
345 }
346 char* as3_getglobalclass()
347 {
348     return as3_globalclass;
349 }
350
351 void as3_destroy() 
352 {
353     if(parser_initialized) {
354         parser_initialized = 0;
355         swf_FreeABC(finish_parser());
356 #ifdef STORE_TOKENS
357         mem_clear(&tokens);
358 #endif
359     }
360     if(as3_globalclass) {
361         free(as3_globalclass);as3_globalclass=0;
362     }
363 }
364