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