implemented asset resolving
[swftools.git] / lib / as3 / initcode.c
1 /* initcode.c
2
3    Routines for handling/compiling 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 <assert.h>
25 #include "../q.h"
26 #include "abc.h"
27 #include "code.h"
28 #include "common.h"
29 #include "registry.h"
30 #include "initcode.h"
31
32 int compare_parsedclass(const void *_v1, const void *_v2)
33 {
34     parsedclass_t*p1 = *(parsedclass_t**)_v1;
35     parsedclass_t*p2 = *(parsedclass_t**)_v2;
36     if((p1->cls->flags^p2->cls->flags)&FLAG_INTERFACE) {
37         return (int)(p2->cls->flags&FLAG_INTERFACE) - (int)(p1->cls->flags&FLAG_INTERFACE);
38     }
39     classinfo_t*c2 = dict_lookup(&p1->parents, p2);
40     classinfo_t*c1 = dict_lookup(&p2->parents, p1);
41     assert(!c1 || !c2); // otherwise we would have a loop
42     assert(!c1 || c1==p1->cls);
43     assert(!c2 || c2==p2->cls);
44
45     if(c1) {
46         return -1;
47     }
48     if(c2) {
49         return 1;
50     }
51     
52     c2 = dict_lookup(&p1->usedclasses_deep, p2);
53     c1 = dict_lookup(&p2->usedclasses_deep, p1);
54     assert(!c1 || !c2);
55     assert(!c1 || c1==p1->cls);
56     assert(!c2 || c2==p2->cls);
57     if(c1) {
58         return -1;
59     }
60     if(c2) {
61         return 1;
62     }
63
64     return 0;
65 }
66
67 static void add_parent(parsedclass_t*p, classinfo_t*c, dict_t*s2p, char soft)
68 {
69     dict_t*parents = soft?(&p->usedclasses_deep):(&p->parents);
70     int t;
71     if(dict_contains(parents, p)) {
72         if(soft) {
73             as3_warning("circular reference: class %s references self (through static code)", p->cls->name);
74             return;
75         } else {
76             syntaxerror("circular reference: class %s references self", p->cls->name);
77         }
78     }
79
80     if(c) {
81         parsedclass_t*n = dict_lookup(s2p, c);
82         if(n && !dict_contains(parents, n)) {
83             assert(n->cls == c);
84             dict_put(parents, n, c);
85         }
86     } else {
87         c = p->cls;
88     }
89
90     if(soft && dict_contains(s2p, c)) {
91         parsedclass_t*pp = dict_lookup(s2p, c);
92         DICT_ITERATE_KEY(&pp->usedclasses, classinfo_t*, cc) {
93             add_parent(p, cc, s2p, soft);
94         }
95     }
96     if(c->superclass) {
97         add_parent(p, c->superclass, s2p, soft);
98     }
99     for(t=0;c->interfaces[t];t++) {
100         add_parent(p, c->interfaces[t], s2p, soft);
101     }
102 }
103
104 parsedclass_t* parsedclass_new(classinfo_t*cls, abc_class_t*abc)
105 {
106     NEW(parsedclass_t,p);
107     p->cls = cls;
108     p->abc = abc;
109     dict_init2(&p->parents, &ptr_type, 1);
110     dict_init2(&p->usedclasses, &ptr_type, 1);
111     dict_init2(&p->usedclasses_deep, &ptr_type, 1);
112     return p;
113 }
114
115 /* sort classes so that 
116    (a) interfaces appear before classes
117    (b) base classes always appear before their subclasses
118    (c) classes appear after the classes they use in static code
119 */
120 parsedclass_t** initcode_sort_classlist(parsedclass_list_t*classes)
121 {
122     dict_t* s2p = dict_new2(&ptr_type);
123
124     /* create hash tables */
125     int count = 0;
126     parsedclass_list_t*l;
127     for(l=classes;l;l=l->next) {
128         dict_put(s2p, l->parsedclass->cls, l->parsedclass);
129         count++;
130     }
131     for(l=classes;l;l=l->next) {
132         add_parent(l->parsedclass, 0, s2p, 0);
133         DICT_ITERATE_KEY(&l->parsedclass->usedclasses, classinfo_t*, c) {
134             add_parent(l->parsedclass, c, s2p, 1);
135         }
136     }
137
138     parsedclass_t**list = malloc(sizeof(parsedclass_t*)*count);
139
140     /* build an array for each class */
141     int i = 0;
142     for(l=classes;l;l=l->next) {
143         list[i++] = l->parsedclass;
144     }
145     
146     /* sort and flatten.
147        We unfortunately need to do insertion sort O(n^2) as
148        our dependencies are only partially ordered */
149     int j;
150     for(i=0;i<count;i++) {
151         for(j=i+1;j<count;j++) {
152             int r = compare_parsedclass(list+i,list+j);
153             if(r>0) {
154                 parsedclass_t*p1 = list[i];
155                 parsedclass_t*p2 = list[j];
156                 list[i] = p2;
157                 list[j] = p1;
158             }
159         }
160     }
161
162     parsedclass_t**list2 = malloc(sizeof(parsedclass_t*)*(count+1));
163     for(i=0;i<count;i++) {
164         list2[i] = (parsedclass_t*)list[i];
165 #ifdef DEBUG
166         parsedclass_t*p = list[i];
167         printf("%s\n", p->cls->name);
168         if(p->cls->superclass)
169             printf("  extends %s\n", p->cls->superclass->name);
170         int t;
171         for(t=0;p->cls->interfaces[t];t++)
172             printf("  interface %s\n", p->cls->interfaces[t]->name);
173         DICT_ITERATE_KEY(&p->usedclasses, classinfo_t*, c) {
174             printf("  uses %s\n", c->name);
175         }
176         DICT_ITERATE_KEY(&p->parents, parsedclass_t*, pp) {
177             printf("  depends on (deep) %s\n", pp->cls->name);
178         }
179         DICT_ITERATE_KEY(&p->usedclasses_deep, parsedclass_t*, px) {
180             printf("  uses (deep) %s\n", px->cls->name);
181         }
182         printf("\n");
183 #endif
184     }
185     list2[count]=0;
186     free(list);
187
188     dict_destroy(s2p);
189     return list2;
190 }
191
192 void parsedclass_add_dependency(parsedclass_t*p, classinfo_t*c)
193 {
194     if(!dict_contains(&p->usedclasses, c)) {
195         dict_put(&p->usedclasses, c, c);
196     }
197 }
198
199 void initcode_add_classlist(abc_script_t*init, parsedclass_list_t*_classes)
200 {
201     code_t*c = 0;
202
203     c = abc_getlocal_0(c);
204     c = abc_pushscope(c);
205   
206     parsedclass_t**classes = initcode_sort_classlist(_classes);
207
208     int t;
209     for(t=0;classes[t];t++) {
210         abc_class_t*abc = classes[t]->abc;
211         classinfo_t*cls = classes[t]->cls;
212         
213         array_append(init->file->classes, "", abc);
214
215         /* write the construction code for this class to the global init
216            function */
217         MULTINAME(classname2,cls);
218         trait_t*trait = abc_initscript_addClassTrait(init, &classname2, abc);
219
220         c = abc_getglobalscope(c);
221         classinfo_t*s = cls->superclass;
222
223         int count=0;
224
225         while(s) {
226             //TODO: take a look at the current scope stack, maybe 
227             //      we can re-use something
228             s = s->superclass;
229             if(!s) 
230             break;
231            
232             multiname_t*s2 = sig2mname(s);
233             c = abc_getlex2(c, s2);
234             multiname_destroy(s2);
235
236             c = abc_pushscope(c); count++;
237             c = c->prev->prev; // invert
238         }
239         /* continue appending after last op end */
240         while(c && c->next) c = c->next; 
241
242         multiname_t*extends2 = sig2mname(cls->superclass);
243         /* TODO: if this is one of *our* classes, we can also 
244                  do a getglobalscope/getslot <nr> (which references
245                  the init function's slots) */
246         if(extends2) {
247             c = abc_getlex2(c, extends2);
248             c = abc_dup(c);
249             /* notice: we get a Verify Error #1107 if the top elemnt on the scope
250                stack is not the superclass */
251             c = abc_pushscope(c);count++;
252         } else {
253             c = abc_pushnull(c);
254             /* notice: we get a verify error #1107 if the top element on the scope 
255                stack is not the global object */
256             c = abc_getlocal_0(c);
257             c = abc_pushscope(c);count++;
258         }
259         c = abc_newclass(c,abc);
260         while(count--) {
261             c = abc_popscope(c);
262         }
263         c = abc_setslot(c, trait->slot_id);
264         multiname_destroy(extends2);
265     }
266     c = abc_returnvoid(c);
267
268     free(classes);
269
270     init->method->body->code = c;
271 }