Mercurial > projects > ddmd
annotate dmd/SwitchStatement.d @ 5:63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
author | dkoroskin <> |
---|---|
date | Tue, 08 Dec 2009 21:39:17 +0300 |
parents | d706d958e4e8 |
children | a8b50ff7f201 |
rev | line source |
---|---|
0 | 1 module dmd.SwitchStatement; |
2 | |
3 import dmd.Statement; | |
4 import dmd.Expression; | |
5 import dmd.DefaultStatement; | |
6 import dmd.TryFinallyStatement; | |
7 import dmd.Array; | |
8 import dmd.Loc; | |
9 import dmd.Scope; | |
10 import dmd.OutBuffer; | |
11 import dmd.HdrGenState; | |
12 import dmd.InlineScanState; | |
13 import dmd.IRState; | |
14 import dmd.InterState; | |
15 import dmd.BE; | |
16 import dmd.TY; | |
17 import dmd.WANT; | |
18 import dmd.GotoCaseStatement; | |
19 import dmd.CaseStatement; | |
20 import dmd.ArrayTypes; | |
21 import dmd.CompoundStatement; | |
22 import dmd.Global; | |
23 import dmd.SwitchErrorStatement; | |
24 import dmd.Type; | |
25 import dmd.HaltExp; | |
26 import dmd.ExpStatement; | |
27 import dmd.BreakStatement; | |
28 import dmd.EnumDeclaration; | |
29 import dmd.TypeEnum; | |
30 import dmd.Dsymbol; | |
31 import dmd.EnumMember; | |
32 import dmd.TypeTypedef; | |
33 import dmd.TOK; | |
34 import dmd.StringExp; | |
35 | |
36 import dmd.backend.Util; | |
37 import dmd.backend.block; | |
38 import dmd.backend.Blockx; | |
39 import dmd.backend.elem; | |
40 import dmd.backend.OPER; | |
41 import dmd.backend.TYM; | |
42 import dmd.backend.BC; | |
43 import dmd.backend.dt_t; | |
44 import dmd.backend.Symbol; | |
45 import dmd.backend.SC; | |
46 import dmd.backend.FL; | |
47 import dmd.backend.RTLSYM; | |
48 import dmd.backend.targ_types; | |
49 | |
4 | 50 import core.memory; |
2 | 51 |
5
63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
dkoroskin <>
parents:
4
diff
changeset
|
52 import core.stdc.stdlib; |
63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
dkoroskin <>
parents:
4
diff
changeset
|
53 |
0 | 54 class SwitchStatement : Statement |
55 { | |
56 Expression condition; | |
57 Statement body_; | |
58 bool isFinal; | |
59 | |
60 DefaultStatement sdefault = null; | |
61 TryFinallyStatement tf = null; | |
62 Array gotoCases; // array of unresolved GotoCaseStatement's | |
63 Array cases; // array of CaseStatement's | |
64 int hasNoDefault = 0; // !=0 if no default statement | |
65 int hasVars = 0; // !=0 if has variable case values | |
66 | |
67 this(Loc loc, Expression c, Statement b, bool isFinal) | |
68 { | |
69 super(loc); | |
70 | |
71 this.condition = c; | |
72 this.body_ = b; | |
73 this.isFinal = isFinal; | |
74 | |
75 gotoCases = new Array(); | |
76 } | |
77 | |
78 Statement syntaxCopy() | |
79 { | |
80 assert(false); | |
81 } | |
82 | |
83 Statement semantic(Scope sc) | |
84 { | |
85 //printf("SwitchStatement.semantic(%p)\n", this); | |
86 tf = sc.tf; | |
87 assert(!cases); // ensure semantic() is only run once | |
88 condition = condition.semantic(sc); | |
89 condition = resolveProperties(sc, condition); | |
90 if (condition.type.isString()) | |
91 { | |
92 // If it's not an array, cast it to one | |
93 if (condition.type.ty != Tarray) | |
94 { | |
95 condition = condition.implicitCastTo(sc, condition.type.nextOf().arrayOf()); | |
96 } | |
97 condition.type = condition.type.constOf(); | |
98 } | |
99 else | |
100 { | |
101 condition = condition.integralPromotions(sc); | |
102 condition.checkIntegral(); | |
103 } | |
104 condition = condition.optimize(WANTvalue); | |
105 | |
106 sc = sc.push(); | |
107 sc.sbreak = this; | |
108 sc.sw = this; | |
109 | |
110 cases = new Array(); | |
111 sc.noctor++; // BUG: should use Scope.mergeCallSuper() for each case instead | |
112 body_ = body_.semantic(sc); | |
113 sc.noctor--; | |
114 | |
115 // Resolve any goto case's with exp | |
116 for (int i = 0; i < gotoCases.dim; i++) | |
117 { | |
118 GotoCaseStatement gcs = cast(GotoCaseStatement)gotoCases.data[i]; | |
119 | |
120 if (!gcs.exp) | |
121 { | |
122 gcs.error("no case statement following goto case;"); | |
123 break; | |
124 } | |
125 | |
126 for (Scope scx = sc; scx; scx = scx.enclosing) | |
127 { | |
128 if (!scx.sw) | |
129 continue; | |
130 for (int j = 0; j < scx.sw.cases.dim; j++) | |
131 { | |
132 CaseStatement cs = cast(CaseStatement)scx.sw.cases.data[j]; | |
133 | |
134 if (cs.exp.equals(gcs.exp)) | |
135 { | |
136 gcs.cs = cs; | |
137 goto Lfoundcase; | |
138 } | |
139 } | |
140 } | |
141 gcs.error("case %s not found", gcs.exp.toChars()); | |
142 | |
143 Lfoundcase: | |
144 ; | |
145 } | |
146 | |
147 if (!sc.sw.sdefault && !isFinal) | |
148 { | |
149 hasNoDefault = 1; | |
150 | |
151 warning("switch statement has no default"); | |
152 | |
153 // Generate runtime error if the default is hit | |
154 Statements a = new Statements(); | |
155 CompoundStatement cs; | |
156 Statement s; | |
157 | |
158 if (global.params.useSwitchError) | |
159 s = new SwitchErrorStatement(loc); | |
160 else | |
161 { | |
162 Expression e = new HaltExp(loc); | |
163 s = new ExpStatement(loc, e); | |
164 } | |
165 | |
166 a.reserve(4); | |
167 a.push(cast(void*)body_); | |
168 a.push(cast(void*)new BreakStatement(loc, null)); | |
169 sc.sw.sdefault = new DefaultStatement(loc, s); | |
170 a.push(cast(void*)sc.sw.sdefault); | |
171 cs = new CompoundStatement(loc, a); | |
172 body_ = cs; | |
173 } | |
174 | |
175 version (DMDV2) { | |
176 if (isFinal) | |
177 { | |
178 Type t = condition.type; | |
179 while (t.ty == Ttypedef) | |
180 { | |
181 // Don't use toBasetype() because that will skip past enums | |
182 t = (cast(TypeTypedef)t).sym.basetype; | |
183 } | |
184 if (condition.type.ty == Tenum) | |
185 { | |
186 TypeEnum te = cast(TypeEnum)condition.type; | |
187 EnumDeclaration ed = te.toDsymbol(sc).isEnumDeclaration(); | |
188 assert(ed); | |
189 size_t dim = ed.members.dim; | |
190 for (size_t i = 0; i < dim; i++) | |
191 { | |
192 EnumMember em = (cast(Dsymbol)ed.members.data[i]).isEnumMember(); | |
193 if (em) | |
194 { | |
195 for (size_t j = 0; j < cases.dim; j++) | |
196 { | |
197 CaseStatement cs = cast(CaseStatement)cases.data[j]; | |
198 if (cs.exp.equals(em.value)) | |
199 goto L1; | |
200 } | |
201 error("enum member %s not represented in final switch", em.toChars()); | |
202 } | |
203 L1: | |
204 ; | |
205 } | |
206 } | |
207 } | |
208 } | |
209 | |
210 sc.pop(); | |
211 return this; | |
212 } | |
213 | |
214 bool hasBreak() | |
215 { | |
216 assert(false); | |
217 } | |
218 | |
219 bool usesEH() | |
220 { | |
221 assert(false); | |
222 } | |
223 | |
224 BE blockExit() | |
225 { | |
226 BE result = BE.BEnone; | |
227 if (condition.canThrow()) | |
228 result |= BE.BEthrow; | |
229 | |
230 if (body_) | |
231 { result |= body_.blockExit(); | |
232 if (result & BE.BEbreak) | |
233 { | |
234 result |= BE.BEfallthru; | |
235 result &= ~BE.BEbreak; | |
236 } | |
237 } | |
238 else | |
239 result |= BE.BEfallthru; | |
240 | |
241 return result; | |
242 } | |
243 | |
244 Expression interpret(InterState* istate) | |
245 { | |
246 assert(false); | |
247 } | |
248 | |
249 void toCBuffer(OutBuffer buf, HdrGenState* hgs) | |
250 { | |
251 assert(false); | |
252 } | |
253 | |
254 Statement inlineScan(InlineScanState* iss) | |
255 { | |
256 //printf("SwitchStatement.inlineScan()\n"); | |
257 condition = condition.inlineScan(iss); | |
258 body_ = body_ ? body_.inlineScan(iss) : null; | |
259 if (sdefault) | |
260 sdefault = cast(DefaultStatement)sdefault.inlineScan(iss); | |
261 if (cases) | |
262 { | |
263 for (int i = 0; i < cases.dim; i++) | |
264 { | |
265 Statement s = cast(Statement)cases.data[i]; | |
266 cases.data[i] = cast(void*)s.inlineScan(iss); | |
267 } | |
268 } | |
269 return this; | |
270 } | |
271 | |
272 void toIR(IRState* irs) | |
273 { | |
274 int string; | |
275 Blockx* blx = irs.blx; | |
276 | |
277 //printf("SwitchStatement.toIR()\n"); | |
278 IRState mystate = IRState(irs,this); | |
279 | |
280 mystate.switchBlock = blx.curblock; | |
281 | |
282 /* Block for where "break" goes to | |
283 */ | |
284 mystate.breakBlock = block_calloc(blx); | |
285 | |
286 /* Block for where "default" goes to. | |
287 * If there is a default statement, then that is where default goes. | |
288 * If not, then do: | |
289 * default: break; | |
290 * by making the default block the same as the break block. | |
291 */ | |
292 mystate.defaultBlock = sdefault ? block_calloc(blx) : mystate.breakBlock; | |
293 | |
294 int numcases = 0; | |
295 if (cases) | |
296 numcases = cases.dim; | |
297 | |
298 incUsage(irs, loc); | |
299 elem* econd = condition.toElem(&mystate); | |
300 | |
301 version (DMDV2) { | |
302 if (hasVars) | |
303 { | |
304 /* Generate a sequence of if-then-else blocks for the cases. | |
305 */ | |
306 if (econd.Eoper != OPvar) | |
307 { | |
308 elem* e = exp2_copytotemp(econd); | |
309 block_appendexp(mystate.switchBlock, e); | |
310 econd = e.E2; | |
311 } | |
312 | |
313 for (int i = 0; i < numcases; i++) | |
314 { | |
315 CaseStatement cs = cast(CaseStatement)cases.data[i]; | |
316 | |
317 elem* ecase = cs.exp.toElem(&mystate); | |
318 elem* e = el_bin(OPeqeq, TYbool, el_copytree(econd), ecase); | |
319 block* b = blx.curblock; | |
320 block_appendexp(b, e); | |
321 block* bcase = block_calloc(blx); | |
322 cs.cblock = bcase; | |
323 block_next(blx, BCiftrue, null); | |
324 list_append(&b.Bsucc, bcase); | |
325 list_append(&b.Bsucc, blx.curblock); | |
326 } | |
327 | |
328 /* The final 'else' clause goes to the default | |
329 */ | |
330 block* b = blx.curblock; | |
331 block_next(blx, BCgoto, null); | |
332 list_append(&b.Bsucc, mystate.defaultBlock); | |
333 | |
334 body_.toIR(&mystate); | |
335 | |
336 /* Have the end of the switch body fall through to the block | |
337 * following the switch statement. | |
338 */ | |
339 block_goto(blx, BCgoto, mystate.breakBlock); | |
340 return; | |
341 } | |
342 } | |
343 | |
344 if (condition.type.isString()) | |
345 { | |
346 // Number the cases so we can unscramble things after the sort() | |
347 for (int i = 0; i < numcases; i++) | |
348 { | |
349 CaseStatement cs = cast(CaseStatement)cases.data[i]; | |
350 cs.index = i; | |
351 } | |
352 | |
353 cases.sort(); | |
354 | |
355 /* Create a sorted array of the case strings, and si | |
356 * will be the symbol for it. | |
357 */ | |
358 dt_t* dt = null; | |
359 Symbol* si = symbol_generate(SCstatic,type_fake(TYullong)); | |
360 version (MACHOBJ) { | |
361 si.Sseg = Segment.DATA; | |
362 } | |
363 dtdword(&dt, numcases); | |
364 dtxoff(&dt, si, 8, TYnptr); | |
365 | |
366 for (int i = 0; i < numcases; i++) | |
367 { | |
368 CaseStatement cs = cast(CaseStatement)cases.data[i]; | |
369 | |
370 if (cs.exp.op != TOKstring) | |
371 { | |
372 error("case '%s' is not a string", cs.exp.toChars()); // BUG: this should be an assert | |
373 } | |
374 else | |
375 { | |
376 StringExp se = cast(StringExp)(cs.exp); | |
377 uint len = se.len; | |
378 dtdword(&dt, len); | |
379 dtabytes(&dt, TYnptr, 0, se.len * se.sz, cast(char*)se.string_); | |
380 } | |
381 } | |
382 | |
383 si.Sdt = dt; | |
384 si.Sfl = FLdata; | |
385 outdata(si); | |
386 | |
387 /* Call: | |
388 * _d_switch_string(string[] si, string econd) | |
389 */ | |
390 elem* eparam = el_param(econd, el_var(si)); | |
391 switch (condition.type.nextOf().ty) | |
392 { | |
393 case Tchar: | |
394 econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_STRING]), eparam); | |
395 break; | |
396 case Twchar: | |
397 econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_USTRING]), eparam); | |
398 break; | |
399 case Tdchar: // BUG: implement | |
400 econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_DSTRING]), eparam); | |
401 break; | |
402 default: | |
403 assert(0); | |
404 } | |
405 elem_setLoc(econd, loc); | |
406 string = 1; | |
407 } | |
408 else | |
409 string = 0; | |
410 block_appendexp(mystate.switchBlock, econd); | |
411 block_next(blx,BCswitch,null); | |
412 | |
5
63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
dkoroskin <>
parents:
4
diff
changeset
|
413 /// |
63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
dkoroskin <>
parents:
4
diff
changeset
|
414 targ_llong* pu = cast(targ_llong*) malloc(targ_llong.sizeof * (numcases + 1)); |
0 | 415 mystate.switchBlock.Bswitch = pu; |
416 /* First pair is the number of cases, and the default block | |
417 */ | |
418 *pu++ = numcases; | |
419 list_append(&mystate.switchBlock.Bsucc, mystate.defaultBlock); | |
420 | |
421 /* Fill in the first entry in each pair, which is the case value. | |
422 * CaseStatement.toIR() will fill in | |
423 * the second entry for each pair with the block. | |
424 */ | |
425 for (int i = 0; i < numcases; i++) | |
426 { | |
427 CaseStatement cs = cast(CaseStatement)cases.data[i]; | |
428 if (string) | |
429 { | |
430 pu[cs.index] = i; | |
431 } | |
432 else | |
433 { | |
434 pu[i] = cs.exp.toInteger(); | |
435 } | |
436 } | |
437 | |
438 body_.toIR(&mystate); | |
439 | |
440 /* Have the end of the switch body fall through to the block | |
441 * following the switch statement. | |
442 */ | |
443 block_goto(blx, BCgoto, mystate.breakBlock); | |
444 } | |
445 } |