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