Mercurial > projects > ddmd
annotate dmd/SliceExp.d @ 178:e3afd1303184
Many small bugs fixed
Made all classes derive from TObject to detect memory leaks (functionality is disabled for now)
Began work on overriding backend memory allocations (to avoid memory leaks)
author | korDen |
---|---|
date | Sun, 17 Oct 2010 07:42:00 +0400 |
parents | af1bebfd96a4 |
children | b0d41ff5e0df |
rev | line source |
---|---|
72 | 1 module dmd.SliceExp; |
2 | |
114 | 3 import dmd.common; |
72 | 4 import dmd.Expression; |
5 import dmd.expression.ArrayLength; | |
6 import dmd.backend.elem; | |
7 import dmd.UnaExp; | |
8 import dmd.Identifier; | |
9 import dmd.IdentifierExp; | |
10 import dmd.ArrayExp; | |
11 import dmd.STC; | |
0 | 12 import dmd.InterState; |
13 import dmd.ScopeDsymbol; | |
14 import dmd.WANT; | |
178 | 15 import dmd.Util; |
0 | 16 import dmd.ArrayScopeSymbol; |
17 import dmd.CallExp; | |
18 import dmd.DotIdExp; | |
19 import dmd.Id; | |
20 import dmd.expression.Util; | |
21 import dmd.TypeTuple; | |
22 import dmd.TupleExp; | |
23 import dmd.TypeStruct; | |
24 import dmd.TypeClass; | |
25 import dmd.TY; | |
26 import dmd.Type; | |
72 | 27 import dmd.AggregateDeclaration; |
28 import dmd.OutBuffer; | |
29 import dmd.Loc; | |
30 import dmd.Scope; | |
31 import dmd.InlineCostState; | |
0 | 32 import dmd.VarDeclaration; |
33 import dmd.ErrorExp; | |
34 import dmd.TypeExp; | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
35 import dmd.Parameter; |
72 | 36 import dmd.ExpInitializer; |
37 import dmd.IRState; | |
38 import dmd.InlineDoState; | |
39 import dmd.ArrayTypes; | |
40 import dmd.HdrGenState; | |
41 import dmd.InlineScanState; | |
0 | 42 import dmd.TOK; |
43 import dmd.TypeSArray; | |
44 import dmd.GlobalExpressions; | |
45 import dmd.Global; | |
46 import dmd.PREC; | |
47 | |
48 import dmd.expression.Slice; | |
49 import dmd.expression.Util; | |
50 | |
51 import dmd.backend.Util; | |
52 import dmd.backend.Symbol; | |
53 import dmd.backend.OPER; | |
54 import dmd.backend.TYM; | |
55 import dmd.codegen.Util; | |
56 | |
72 | 57 import core.stdc.string; |
58 | |
0 | 59 class SliceExp : UnaExp |
60 { | |
61 Expression upr; // null if implicit 0 | |
62 Expression lwr; // null if implicit [length - 1] | |
63 | |
64 VarDeclaration lengthVar = null; | |
65 | |
66 this(Loc loc, Expression e1, Expression lwr, Expression upr) | |
72 | 67 { |
178 | 68 register(); |
0 | 69 super(loc, TOK.TOKslice, SliceExp.sizeof, e1); |
70 this.upr = upr; | |
71 this.lwr = lwr; | |
72 } | |
73 | |
72 | 74 override Expression syntaxCopy() |
0 | 75 { |
72 | 76 Expression lwr = null; |
77 if (this.lwr) | |
78 lwr = this.lwr.syntaxCopy(); | |
79 | |
80 Expression upr = null; | |
81 if (this.upr) | |
82 upr = this.upr.syntaxCopy(); | |
83 | |
53 | 84 return new SliceExp(loc, e1.syntaxCopy(), lwr, upr); |
0 | 85 } |
86 | |
72 | 87 override Expression semantic(Scope sc) |
0 | 88 { |
89 Expression e; | |
90 AggregateDeclaration ad; | |
91 //FuncDeclaration fd; | |
92 ScopeDsymbol sym; | |
93 | |
94 version (LOGSEMANTIC) { | |
95 printf("SliceExp.semantic('%s')\n", toChars()); | |
96 } | |
97 if (type) | |
98 return this; | |
99 | |
100 UnaExp.semantic(sc); | |
101 e1 = resolveProperties(sc, e1); | |
102 | |
103 e = this; | |
104 | |
105 Type t = e1.type.toBasetype(); | |
106 if (t.ty == Tpointer) | |
107 { | |
108 if (!lwr || !upr) | |
109 error("need upper and lower bound to slice pointer"); | |
110 } | |
111 else if (t.ty == Tarray) | |
112 { | |
113 } | |
114 else if (t.ty == Tsarray) | |
115 { | |
116 } | |
117 else if (t.ty == Tclass) | |
118 { | |
119 ad = (cast(TypeClass)t).sym; | |
120 goto L1; | |
121 } | |
122 else if (t.ty == Tstruct) | |
123 { | |
124 ad = (cast(TypeStruct)t).sym; | |
125 | |
126 L1: | |
127 if (search_function(ad, Id.slice)) | |
128 { | |
129 // Rewrite as e1.slice(lwr, upr) | |
130 e = new DotIdExp(loc, e1, Id.slice); | |
131 | |
132 if (lwr) | |
133 { | |
134 assert(upr); | |
135 e = new CallExp(loc, e, lwr, upr); | |
136 } | |
137 else | |
178 | 138 { |
0 | 139 assert(!upr); |
140 e = new CallExp(loc, e); | |
141 } | |
142 e = e.semantic(sc); | |
143 return e; | |
144 } | |
145 goto Lerror; | |
146 } | |
147 else if (t.ty == Ttuple) | |
148 { | |
149 if (!lwr && !upr) | |
150 return e1; | |
151 if (!lwr || !upr) | |
152 { error("need upper and lower bound to slice tuple"); | |
153 goto Lerror; | |
154 } | |
155 } | |
156 else | |
157 goto Lerror; | |
158 | |
159 { | |
160 Scope sc2 = sc; | |
161 if (t.ty == Tsarray || t.ty == Tarray || t.ty == Ttuple) | |
162 { | |
163 sym = new ArrayScopeSymbol(sc, this); | |
164 sym.loc = loc; | |
165 sym.parent = sc.scopesym; | |
166 sc2 = sc.push(sym); | |
167 } | |
168 | |
169 if (lwr) | |
178 | 170 { |
0 | 171 lwr = lwr.semantic(sc2); |
172 lwr = resolveProperties(sc2, lwr); | |
173 lwr = lwr.implicitCastTo(sc2, Type.tsize_t); | |
174 } | |
175 if (upr) | |
178 | 176 { |
0 | 177 upr = upr.semantic(sc2); |
178 upr = resolveProperties(sc2, upr); | |
179 upr = upr.implicitCastTo(sc2, Type.tsize_t); | |
180 } | |
181 | |
182 if (sc2 != sc) | |
183 sc2.pop(); | |
184 } | |
185 | |
186 if (t.ty == Ttuple) | |
187 { | |
188 lwr = lwr.optimize(WANTvalue); | |
189 upr = upr.optimize(WANTvalue); | |
190 ulong i1 = lwr.toUInteger(); | |
191 ulong i2 = upr.toUInteger(); | |
192 | |
193 size_t length; | |
194 TupleExp te; | |
195 TypeTuple tup; | |
196 | |
197 if (e1.op == TOKtuple) // slicing an expression tuple | |
178 | 198 { |
0 | 199 te = cast(TupleExp)e1; |
200 length = te.exps.dim; | |
201 } | |
202 else if (e1.op == TOKtype) // slicing a type tuple | |
178 | 203 { |
0 | 204 tup = cast(TypeTuple)t; |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
205 length = Parameter.dim(tup.arguments); |
0 | 206 } |
207 else | |
208 assert(0); | |
209 | |
210 if (i1 <= i2 && i2 <= length) | |
178 | 211 { |
0 | 212 size_t j1 = cast(size_t) i1; |
213 size_t j2 = cast(size_t) i2; | |
214 | |
215 if (e1.op == TOKtuple) | |
178 | 216 { |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
72
diff
changeset
|
217 auto exps = new Expressions; |
0 | 218 exps.setDim(j2 - j1); |
219 for (size_t i = 0; i < j2 - j1; i++) | |
178 | 220 { |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
72
diff
changeset
|
221 auto e2 = te.exps[j1 + i]; |
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
72
diff
changeset
|
222 exps[i] = e2; |
0 | 223 } |
224 e = new TupleExp(loc, exps); | |
225 } | |
226 else | |
178 | 227 { |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
228 auto args = new Parameters; |
0 | 229 args.reserve(j2 - j1); |
230 for (size_t i = j1; i < j2; i++) | |
178 | 231 { |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
232 auto arg = Parameter.getNth(tup.arguments, i); |
126
1765f3ef917d
ClassDeclarations, Arguments -> Vector
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
114
diff
changeset
|
233 args.push(arg); |
0 | 234 } |
235 e = new TypeExp(e1.loc, new TypeTuple(args)); | |
236 } | |
237 e = e.semantic(sc); | |
238 } | |
239 else | |
240 { | |
241 error("string slice [%ju .. %ju] is out of bounds", i1, i2); | |
242 e = new ErrorExp(); | |
243 } | |
244 return e; | |
245 } | |
246 | |
247 if (t.ty == Tarray) | |
248 { | |
249 type = e1.type; | |
250 } | |
251 else | |
252 type = t.nextOf().arrayOf(); | |
253 return e; | |
254 | |
255 Lerror: | |
256 string s; | |
257 if (t.ty == Tvoid) | |
258 s = e1.toChars(); | |
259 else | |
260 s = t.toChars(); | |
261 error("%s cannot be sliced with []", s); | |
262 e = new ErrorExp(); | |
263 return e; | |
264 } | |
265 | |
72 | 266 override void checkEscape() |
0 | 267 { |
268 e1.checkEscape(); | |
269 } | |
178 | 270 |
135 | 271 override void checkEscapeRef() |
272 { | |
273 e1.checkEscapeRef(); | |
274 } | |
0 | 275 |
276 version (DMDV2) { | |
72 | 277 override int isLvalue() |
0 | 278 { |
279 return 1; | |
280 } | |
281 } | |
72 | 282 override Expression toLvalue(Scope sc, Expression e) |
0 | 283 { |
284 return this; | |
285 } | |
286 | |
72 | 287 override Expression modifiableLvalue(Scope sc, Expression e) |
0 | 288 { |
289 error("slice expression %s is not a modifiable lvalue", toChars()); | |
290 return this; | |
291 } | |
292 | |
72 | 293 override void toCBuffer(OutBuffer buf, HdrGenState* hgs) |
0 | 294 { |
295 expToCBuffer(buf, hgs, e1, precedence[op]); | |
296 buf.writeByte('['); | |
297 if (upr || lwr) | |
298 { | |
299 if (lwr) | |
300 expToCBuffer(buf, hgs, lwr, PREC.PREC_assign); | |
301 else | |
302 buf.writeByte('0'); | |
303 buf.writestring(".."); | |
304 if (upr) | |
305 expToCBuffer(buf, hgs, upr, PREC.PREC_assign); | |
306 else | |
307 buf.writestring("length"); // BUG: should be array.length | |
308 } | |
309 buf.writeByte(']'); | |
310 } | |
311 | |
72 | 312 override Expression optimize(int result) |
0 | 313 { |
314 Expression e; | |
315 | |
316 //printf("SliceExp::optimize(result = %d) %s\n", result, toChars()); | |
317 e = this; | |
318 e1 = e1.optimize(WANTvalue | (result & WANTinterpret)); | |
319 if (!lwr) | |
178 | 320 { |
0 | 321 if (e1.op == TOKstring) |
178 | 322 { |
0 | 323 // Convert slice of string literal into dynamic array |
324 Type t = e1.type.toBasetype(); | |
325 if (t.nextOf()) | |
326 e = e1.castTo(null, t.nextOf().arrayOf()); | |
327 } | |
328 return e; | |
329 } | |
330 e1 = fromConstInitializer(result, e1); | |
331 lwr = lwr.optimize(WANTvalue | (result & WANTinterpret)); | |
332 upr = upr.optimize(WANTvalue | (result & WANTinterpret)); | |
333 e = Slice(type, e1, lwr, upr); | |
334 if (e is EXP_CANT_INTERPRET) | |
335 e = this; | |
336 //printf("-SliceExp::optimize() %s\n", e->toChars()); | |
337 return e; | |
338 } | |
339 | |
72 | 340 override Expression interpret(InterState istate) |
0 | 341 { |
72 | 342 Expression e; |
343 Expression e1; | |
344 Expression lwr; | |
345 Expression upr; | |
346 | |
347 version (LOG) { | |
348 printf("SliceExp.interpret() %s\n", toChars()); | |
349 } | |
350 e1 = this.e1.interpret(istate); | |
351 if (e1 is EXP_CANT_INTERPRET) | |
352 goto Lcant; | |
353 if (!this.lwr) | |
354 { | |
355 e = e1.castTo(null, type); | |
356 return e.interpret(istate); | |
357 } | |
358 | |
359 /* Set the $ variable | |
360 */ | |
361 e = ArrayLength(Type.tsize_t, e1); | |
362 if (e is EXP_CANT_INTERPRET) | |
363 goto Lcant; | |
364 if (lengthVar) | |
365 lengthVar.value = e; | |
366 | |
367 /* Evaluate lower and upper bounds of slice | |
368 */ | |
369 lwr = this.lwr.interpret(istate); | |
370 if (lwr is EXP_CANT_INTERPRET) | |
371 goto Lcant; | |
372 upr = this.upr.interpret(istate); | |
373 if (upr is EXP_CANT_INTERPRET) | |
374 goto Lcant; | |
375 | |
376 return Slice(type, e1, lwr, upr); | |
377 | |
378 Lcant: | |
63 | 379 return EXP_CANT_INTERPRET; |
0 | 380 } |
381 | |
72 | 382 override void dump(int indent) |
0 | 383 { |
384 assert(false); | |
385 } | |
386 | |
72 | 387 override elem* toElem(IRState* irs) |
0 | 388 { |
389 //printf("SliceExp.toElem()\n"); | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
390 auto t1 = e1.type.toBasetype(); |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
391 auto e = e1.toElem(irs); |
0 | 392 if (lwr) |
393 { | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
394 auto einit = resolveLengthVar(lengthVar, &e, t1); |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
395 |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
396 int sz = cast(uint)t1.nextOf().size(); |
0 | 397 |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
398 auto elwr = lwr.toElem(irs); |
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
399 auto eupr = upr.toElem(irs); |
0 | 400 |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
401 auto elwr2 = el_same(&elwr); |
0 | 402 |
403 // Create an array reference where: | |
404 // length is (upr - lwr) | |
405 // pointer is (ptr + lwr*sz) | |
406 // Combine as (length pair ptr) | |
407 | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
408 if (irs.arrayBoundsCheck()) |
0 | 409 { |
410 // Checks (unsigned compares): | |
411 // upr <= array.length | |
412 // lwr <= upr | |
413 | |
414 elem *c1; | |
415 elem *c2; | |
416 elem *ea; | |
417 elem *eb; | |
418 elem *eupr2; | |
419 elem *elength; | |
420 | |
421 if (t1.ty == Tpointer) | |
422 { | |
423 // Just do lwr <= upr check | |
424 | |
425 eupr2 = el_same(&eupr); | |
426 eupr2.Ety = TYuint; // make sure unsigned comparison | |
427 c1 = el_bin(OPle, TYint, elwr2, eupr2); | |
428 c1 = el_combine(eupr, c1); | |
429 goto L2; | |
430 } | |
431 else if (t1.ty == Tsarray) | |
178 | 432 { |
0 | 433 TypeSArray tsa = cast(TypeSArray)t1; |
434 ulong length = tsa.dim.toInteger(); | |
435 | |
436 elength = el_long(TYuint, length); | |
437 goto L1; | |
438 } | |
439 else if (t1.ty == Tarray) | |
440 { | |
441 if (lengthVar) | |
442 elength = el_var(lengthVar.toSymbol()); | |
443 else | |
444 { | |
445 elength = e; | |
446 e = el_same(&elength); | |
447 elength = el_una(OP64_32, TYuint, elength); | |
448 } | |
449 L1: | |
450 eupr2 = el_same(&eupr); | |
451 c1 = el_bin(OPle, TYint, eupr, elength); | |
452 eupr2.Ety = TYuint; // make sure unsigned comparison | |
453 c2 = el_bin(OPle, TYint, elwr2, eupr2); | |
454 c1 = el_bin(OPandand, TYint, c1, c2); // (c1 && c2) | |
455 | |
456 L2: | |
457 // Construct: (c1 || ModuleArray(line)) | |
458 Symbol* sassert; | |
459 | |
460 sassert = irs.blx.module_.toModuleArray(); | |
461 ea = el_bin(OPcall,TYvoid,el_var(sassert), el_long(TYint, loc.linnum)); | |
462 eb = el_bin(OPoror,TYvoid,c1,ea); | |
463 elwr = el_combine(elwr, eb); | |
464 | |
465 elwr2 = el_copytree(elwr2); | |
466 eupr = el_copytree(eupr2); | |
467 } | |
468 } | |
469 | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
470 auto eptr = array_toPtr(e1.type, e); |
0 | 471 |
472 elem *elength = el_bin(OPmin, TYint, eupr, elwr2); | |
473 eptr = el_bin(OPadd, TYnptr, eptr, el_bin(OPmul, TYint, el_copytree(elwr2), el_long(TYint, sz))); | |
474 | |
475 e = el_pair(TYullong, elength, eptr); | |
476 e = el_combine(elwr, e); | |
477 e = el_combine(einit, e); | |
478 } | |
479 else if (t1.ty == Tsarray) | |
480 { | |
481 e = sarray_toDarray(loc, t1, null, e); | |
482 } | |
483 | |
484 el_setLoc(e,loc); | |
485 return e; | |
486 } | |
487 | |
72 | 488 override void scanForNestedRef(Scope sc) |
0 | 489 { |
72 | 490 e1.scanForNestedRef(sc); |
491 | |
492 if (lengthVar) | |
178 | 493 { |
72 | 494 //printf("lengthVar\n"); |
495 lengthVar.parent = sc.parent; | |
496 } | |
497 if (lwr) | |
498 lwr.scanForNestedRef(sc); | |
499 if (upr) | |
64 | 500 upr.scanForNestedRef(sc); |
0 | 501 } |
502 | |
72 | 503 override void buildArrayIdent(OutBuffer buf, Expressions arguments) |
0 | 504 { |
72 | 505 buf.writestring("Slice"); |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
72
diff
changeset
|
506 arguments.shift(this); |
0 | 507 } |
508 | |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
509 override Expression buildArrayLoop(Parameters fparams) |
0 | 510 { |
72 | 511 Identifier id = Identifier.generateId("p", fparams.dim); |
130
60bb0fe4563e
dmdfe 2.037 first main iteration
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
126
diff
changeset
|
512 auto param = new Parameter(STCconst, type, id, null); |
126
1765f3ef917d
ClassDeclarations, Arguments -> Vector
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
114
diff
changeset
|
513 fparams.shift(param); |
72 | 514 Expression e = new IdentifierExp(Loc(0), id); |
515 Expressions arguments = new Expressions(); | |
516 Expression index = new IdentifierExp(Loc(0), Id.p); | |
84
be2ab491772e
Expressions -> Vector!Expression
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
72
diff
changeset
|
517 arguments.push(index); |
72 | 518 e = new ArrayExp(Loc(0), e, arguments); |
12
832f71e6f96c
*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete)
korDen
parents:
0
diff
changeset
|
519 return e; |
0 | 520 } |
521 | |
72 | 522 override int inlineCost(InlineCostState* ics) |
0 | 523 { |
524 int cost = 1 + e1.inlineCost(ics); | |
525 if (lwr) | |
526 cost += lwr.inlineCost(ics); | |
527 if (upr) | |
528 cost += upr.inlineCost(ics); | |
529 return cost; | |
530 } | |
531 | |
72 | 532 override Expression doInline(InlineDoState ids) |
0 | 533 { |
534 SliceExp are = cast(SliceExp)copy(); | |
535 | |
536 are.e1 = e1.doInline(ids); | |
537 | |
538 if (lengthVar) | |
178 | 539 { |
0 | 540 //printf("lengthVar\n"); |
541 VarDeclaration vd = lengthVar; | |
542 ExpInitializer ie; | |
543 ExpInitializer ieto; | |
544 VarDeclaration vto; | |
545 | |
178 | 546 vto = cloneThis(vd); |
547 | |
0 | 548 vto.parent = ids.parent; |
549 vto.csym = null; | |
550 vto.isym = null; | |
551 | |
552 ids.from.push(cast(void*)vd); | |
553 ids.to.push(cast(void*)vto); | |
554 | |
555 if (vd.init) | |
556 { | |
557 ie = vd.init.isExpInitializer(); | |
558 assert(ie); | |
559 ieto = new ExpInitializer(ie.loc, ie.exp.doInline(ids)); | |
560 vto.init = ieto; | |
561 } | |
562 | |
563 are.lengthVar = vto; | |
564 } | |
565 | |
566 if (lwr) | |
567 are.lwr = lwr.doInline(ids); | |
568 if (upr) | |
569 are.upr = upr.doInline(ids); | |
570 return are; | |
571 } | |
572 | |
72 | 573 override Expression inlineScan(InlineScanState* iss) |
0 | 574 { |
575 e1 = e1.inlineScan(iss); | |
576 if (lwr) | |
577 lwr = lwr.inlineScan(iss); | |
578 if (upr) | |
579 upr = upr.inlineScan(iss); | |
580 return this; | |
581 } | |
582 } | |
583 |