Mercurial > projects > ldc
comparison druntime/src/compiler/ldc/genobj.d @ 759:d3eb054172f9
Added copy of druntime from DMD 2.020 modified for LDC.
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Tue, 11 Nov 2008 01:52:37 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
758:f04dde6e882c | 759:d3eb054172f9 |
---|---|
1 /** | |
2 * Part of the D programming language runtime library. | |
3 * Forms the symbols available to all D programs. Includes | |
4 * Object, which is the root of the class object hierarchy. | |
5 * | |
6 * This module is implicitly imported. | |
7 * Macros: | |
8 * WIKI = Object | |
9 */ | |
10 | |
11 /* | |
12 * Copyright (C) 2004-2007 by Digital Mars, www.digitalmars.com | |
13 * Written by Walter Bright | |
14 * | |
15 * This software is provided 'as-is', without any express or implied | |
16 * warranty. In no event will the authors be held liable for any damages | |
17 * arising from the use of this software. | |
18 * | |
19 * Permission is granted to anyone to use this software for any purpose, | |
20 * including commercial applications, and to alter it and redistribute it | |
21 * freely, in both source and binary form, subject to the following | |
22 * restrictions: | |
23 * | |
24 * o The origin of this software must not be misrepresented; you must not | |
25 * claim that you wrote the original software. If you use this software | |
26 * in a product, an acknowledgment in the product documentation would be | |
27 * appreciated but is not required. | |
28 * o Altered source versions must be plainly marked as such, and must not | |
29 * be misrepresented as being the original software. | |
30 * o This notice may not be removed or altered from any source | |
31 * distribution. | |
32 */ | |
33 | |
34 /* | |
35 * Modified by Sean Kelly for use with the D Runtime Project | |
36 */ | |
37 | |
38 /* | |
39 * Modified by Tomas Lindquist Olsen for use with the LLVM D Compiler | |
40 */ | |
41 | |
42 module object; | |
43 | |
44 private | |
45 { | |
46 import stdc.string; | |
47 import stdc.stdlib; | |
48 import util.string; | |
49 debug(PRINTF) import stdc.stdio; | |
50 | |
51 extern (C) void onOutOfMemoryError(); | |
52 extern (C) Object _d_newclass(ClassInfo ci); | |
53 } | |
54 | |
55 // NOTE: For some reason, this declaration method doesn't work | |
56 // in this particular file (and this file only). It must | |
57 // be a DMD thing. | |
58 //alias typeof(int.sizeof) size_t; | |
59 //alias typeof(cast(void*)0 - cast(void*)0) ptrdiff_t; | |
60 | |
61 version(X86_64) | |
62 { | |
63 alias ulong size_t; | |
64 alias long ptrdiff_t; | |
65 } | |
66 else | |
67 { | |
68 alias uint size_t; | |
69 alias int ptrdiff_t; | |
70 } | |
71 | |
72 alias size_t hash_t; | |
73 alias bool equals_t; | |
74 | |
75 alias invariant(char)[] string; | |
76 alias invariant(wchar)[] wstring; | |
77 alias invariant(dchar)[] dstring; | |
78 | |
79 /** | |
80 * All D class objects inherit from Object. | |
81 */ | |
82 class Object | |
83 { | |
84 /** | |
85 * Convert Object to a human readable string. | |
86 */ | |
87 string toString() | |
88 { | |
89 return this.classinfo.name; | |
90 } | |
91 | |
92 /** | |
93 * Compute hash function for Object. | |
94 */ | |
95 hash_t toHash() | |
96 { | |
97 // BUG: this prevents a compacting GC from working, needs to be fixed | |
98 return cast(hash_t)cast(void*)this; | |
99 } | |
100 | |
101 /** | |
102 * Compare with another Object obj. | |
103 * Returns: | |
104 * $(TABLE | |
105 * $(TR $(TD this < obj) $(TD < 0)) | |
106 * $(TR $(TD this == obj) $(TD 0)) | |
107 * $(TR $(TD this > obj) $(TD > 0)) | |
108 * ) | |
109 */ | |
110 int opCmp(Object o) | |
111 { | |
112 // BUG: this prevents a compacting GC from working, needs to be fixed | |
113 //return cast(int)cast(void*)this - cast(int)cast(void*)o; | |
114 | |
115 throw new Exception("need opCmp for class " ~ this.classinfo.name); | |
116 //return this !is o; | |
117 } | |
118 | |
119 /** | |
120 * Returns !=0 if this object does have the same contents as obj. | |
121 */ | |
122 equals_t opEquals(Object o) | |
123 { | |
124 return this is o; | |
125 } | |
126 | |
127 interface Monitor | |
128 { | |
129 void lock(); | |
130 void unlock(); | |
131 } | |
132 | |
133 /** | |
134 * Create instance of class specified by classname. | |
135 * The class must either have no constructors or have | |
136 * a default constructor. | |
137 * Returns: | |
138 * null if failed | |
139 */ | |
140 static Object factory(string classname) | |
141 { | |
142 auto ci = ClassInfo.find(classname); | |
143 if (ci) | |
144 { | |
145 return ci.create(); | |
146 } | |
147 return null; | |
148 } | |
149 } | |
150 | |
151 /** | |
152 * Information about an interface. | |
153 * When an object is accessed via an interface, an Interface* appears as the | |
154 * first entry in its vtbl. | |
155 */ | |
156 struct Interface | |
157 { | |
158 ClassInfo classinfo; /// .classinfo for this interface (not for containing class) | |
159 void*[] vtbl; | |
160 ptrdiff_t offset; /// offset to Interface 'this' from Object 'this' | |
161 } | |
162 | |
163 /** | |
164 * Runtime type information about a class. Can be retrieved for any class type | |
165 * or instance by using the .classinfo property. | |
166 * A pointer to this appears as the first entry in the class's vtbl[]. | |
167 */ | |
168 class ClassInfo : Object | |
169 { | |
170 byte[] init; /** class static initializer | |
171 * (init.length gives size in bytes of class) | |
172 */ | |
173 string name; /// class name | |
174 void*[] vtbl; /// virtual function pointer table | |
175 Interface[] interfaces; /// interfaces this class implements | |
176 ClassInfo base; /// base class | |
177 void* destructor; | |
178 void function(Object) classInvariant; | |
179 uint flags; | |
180 // 1: // is IUnknown or is derived from IUnknown | |
181 // 2: // has no possible pointers into GC memory | |
182 // 4: // has offTi[] member | |
183 // 8: // has constructors | |
184 // 16: // has xgetMembers member | |
185 void* deallocator; | |
186 OffsetTypeInfo[] offTi; | |
187 void function(Object) defaultConstructor; // default Constructor | |
188 const(MemberInfo[]) function(in char[]) xgetMembers; | |
189 | |
190 /** | |
191 * Search all modules for ClassInfo corresponding to classname. | |
192 * Returns: null if not found | |
193 */ | |
194 static ClassInfo find(in char[] classname) | |
195 { | |
196 foreach (m; ModuleInfo) | |
197 { | |
198 //writefln("module %s, %d", m.name, m.localClasses.length); | |
199 foreach (c; m.localClasses) | |
200 { | |
201 //writefln("\tclass %s", c.name); | |
202 if (c.name == classname) | |
203 return c; | |
204 } | |
205 } | |
206 return null; | |
207 } | |
208 | |
209 /** | |
210 * Create instance of Object represented by 'this'. | |
211 */ | |
212 Object create() | |
213 { | |
214 if (flags & 8 && !defaultConstructor) | |
215 return null; | |
216 Object o = _d_newclass(this); | |
217 if (flags & 8 && defaultConstructor) | |
218 { | |
219 defaultConstructor(o); | |
220 } | |
221 return o; | |
222 } | |
223 | |
224 /** | |
225 * Search for all members with the name 'name'. | |
226 * If name[] is null, return all members. | |
227 */ | |
228 const(MemberInfo[]) getMembers(in char[] name) | |
229 { | |
230 if (flags & 16 && xgetMembers) | |
231 return xgetMembers(name); | |
232 return null; | |
233 } | |
234 } | |
235 | |
236 /** | |
237 * Array of pairs giving the offset and type information for each | |
238 * member in an aggregate. | |
239 */ | |
240 struct OffsetTypeInfo | |
241 { | |
242 size_t offset; /// Offset of member from start of object | |
243 TypeInfo ti; /// TypeInfo for this member | |
244 } | |
245 | |
246 /** | |
247 * Runtime type information about a type. | |
248 * Can be retrieved for any type using a | |
249 * <a href="../expression.html#typeidexpression">TypeidExpression</a>. | |
250 */ | |
251 class TypeInfo | |
252 { | |
253 override hash_t toHash() | |
254 { | |
255 hash_t hash; | |
256 | |
257 foreach (char c; this.toString()) | |
258 hash = hash * 9 + c; | |
259 return hash; | |
260 } | |
261 | |
262 override int opCmp(Object o) | |
263 { | |
264 if (this is o) | |
265 return 0; | |
266 TypeInfo ti = cast(TypeInfo)o; | |
267 if (ti is null) | |
268 return 1; | |
269 return dstrcmp(this.toString(), ti.toString()); | |
270 } | |
271 | |
272 override equals_t opEquals(Object o) | |
273 { | |
274 /* TypeInfo instances are singletons, but duplicates can exist | |
275 * across DLL's. Therefore, comparing for a name match is | |
276 * sufficient. | |
277 */ | |
278 if (this is o) | |
279 return true; | |
280 TypeInfo ti = cast(TypeInfo)o; | |
281 return ti && this.toString() == ti.toString(); | |
282 } | |
283 | |
284 /// Returns a hash of the instance of a type. | |
285 hash_t getHash(in void* p) { return cast(hash_t)p; } | |
286 | |
287 /// Compares two instances for equality. | |
288 equals_t equals(in void* p1, in void* p2) { return p1 == p2; } | |
289 | |
290 /// Compares two instances for <, ==, or >. | |
291 int compare(in void* p1, in void* p2) { return 0; } | |
292 | |
293 /// Returns size of the type. | |
294 size_t tsize() { return 0; } | |
295 | |
296 /// Swaps two instances of the type. | |
297 void swap(void* p1, void* p2) | |
298 { | |
299 size_t n = tsize(); | |
300 for (size_t i = 0; i < n; i++) | |
301 { | |
302 byte t = (cast(byte *)p1)[i]; | |
303 (cast(byte*)p1)[i] = (cast(byte*)p2)[i]; | |
304 (cast(byte*)p2)[i] = t; | |
305 } | |
306 } | |
307 | |
308 /// Get TypeInfo for 'next' type, as defined by what kind of type this is, | |
309 /// null if none. | |
310 TypeInfo next() { return null; } | |
311 | |
312 /// Return default initializer, null if default initialize to 0 | |
313 void[] init() { return null; } | |
314 | |
315 /// Get flags for type: 1 means GC should scan for pointers | |
316 uint flags() { return 0; } | |
317 | |
318 /// Get type information on the contents of the type; null if not available | |
319 OffsetTypeInfo[] offTi() { return null; } | |
320 /// Run the destructor on the object and all its sub-objects | |
321 void destroy(void* p) {} | |
322 /// Run the postblit on the object and all its sub-objects | |
323 void postblit(void* p) {} | |
324 } | |
325 | |
326 class TypeInfo_Typedef : TypeInfo | |
327 { | |
328 override string toString() { return name; } | |
329 | |
330 override equals_t opEquals(Object o) | |
331 { | |
332 TypeInfo_Typedef c; | |
333 return this is o || | |
334 ((c = cast(TypeInfo_Typedef)o) !is null && | |
335 this.name == c.name && | |
336 this.base == c.base); | |
337 } | |
338 | |
339 override hash_t getHash(in void* p) { return base.getHash(p); } | |
340 override equals_t equals(in void* p1, in void* p2) { return base.equals(p1, p2); } | |
341 override int compare(in void* p1, in void* p2) { return base.compare(p1, p2); } | |
342 override size_t tsize() { return base.tsize(); } | |
343 override void swap(void* p1, void* p2) { return base.swap(p1, p2); } | |
344 | |
345 override TypeInfo next() { return base.next(); } | |
346 override uint flags() { return base.flags(); } | |
347 override void[] init() { return m_init.length ? m_init : base.init(); } | |
348 | |
349 TypeInfo base; | |
350 string name; | |
351 void[] m_init; | |
352 } | |
353 | |
354 class TypeInfo_Enum : TypeInfo_Typedef | |
355 { | |
356 | |
357 } | |
358 | |
359 class TypeInfo_Pointer : TypeInfo | |
360 { | |
361 override string toString() { return m_next.toString() ~ "*"; } | |
362 | |
363 override equals_t opEquals(Object o) | |
364 { | |
365 TypeInfo_Pointer c; | |
366 return this is o || | |
367 ((c = cast(TypeInfo_Pointer)o) !is null && | |
368 this.m_next == c.m_next); | |
369 } | |
370 | |
371 override hash_t getHash(in void* p) | |
372 { | |
373 return cast(hash_t)*cast(void**)p; | |
374 } | |
375 | |
376 override equals_t equals(in void* p1, in void* p2) | |
377 { | |
378 return *cast(void**)p1 == *cast(void**)p2; | |
379 } | |
380 | |
381 override int compare(in void* p1, in void* p2) | |
382 { | |
383 if (*cast(void**)p1 < *cast(void**)p2) | |
384 return -1; | |
385 else if (*cast(void**)p1 > *cast(void**)p2) | |
386 return 1; | |
387 else | |
388 return 0; | |
389 } | |
390 | |
391 override size_t tsize() | |
392 { | |
393 return (void*).sizeof; | |
394 } | |
395 | |
396 override void swap(void* p1, void* p2) | |
397 { | |
398 void* tmp = *cast(void**)p1; | |
399 *cast(void**)p1 = *cast(void**)p2; | |
400 *cast(void**)p2 = tmp; | |
401 } | |
402 | |
403 override TypeInfo next() { return m_next; } | |
404 override uint flags() { return 1; } | |
405 | |
406 TypeInfo m_next; | |
407 } | |
408 | |
409 class TypeInfo_Array : TypeInfo | |
410 { | |
411 override string toString() { return value.toString() ~ "[]"; } | |
412 | |
413 override equals_t opEquals(Object o) | |
414 { | |
415 TypeInfo_Array c; | |
416 return this is o || | |
417 ((c = cast(TypeInfo_Array)o) !is null && | |
418 this.value == c.value); | |
419 } | |
420 | |
421 override hash_t getHash(in void* p) | |
422 { | |
423 size_t sz = value.tsize(); | |
424 hash_t hash = 0; | |
425 void[] a = *cast(void[]*)p; | |
426 for (size_t i = 0; i < a.length; i++) | |
427 hash += value.getHash(a.ptr + i * sz); | |
428 return hash; | |
429 } | |
430 | |
431 override equals_t equals(in void* p1, in void* p2) | |
432 { | |
433 void[] a1 = *cast(void[]*)p1; | |
434 void[] a2 = *cast(void[]*)p2; | |
435 if (a1.length != a2.length) | |
436 return false; | |
437 size_t sz = value.tsize(); | |
438 for (size_t i = 0; i < a1.length; i++) | |
439 { | |
440 if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz)) | |
441 return false; | |
442 } | |
443 return true; | |
444 } | |
445 | |
446 override int compare(in void* p1, in void* p2) | |
447 { | |
448 void[] a1 = *cast(void[]*)p1; | |
449 void[] a2 = *cast(void[]*)p2; | |
450 size_t sz = value.tsize(); | |
451 size_t len = a1.length; | |
452 | |
453 if (a2.length < len) | |
454 len = a2.length; | |
455 for (size_t u = 0; u < len; u++) | |
456 { | |
457 int result = value.compare(a1.ptr + u * sz, a2.ptr + u * sz); | |
458 if (result) | |
459 return result; | |
460 } | |
461 return cast(int)a1.length - cast(int)a2.length; | |
462 } | |
463 | |
464 override size_t tsize() | |
465 { | |
466 return (void[]).sizeof; | |
467 } | |
468 | |
469 override void swap(void* p1, void* p2) | |
470 { | |
471 void[] tmp = *cast(void[]*)p1; | |
472 *cast(void[]*)p1 = *cast(void[]*)p2; | |
473 *cast(void[]*)p2 = tmp; | |
474 } | |
475 | |
476 TypeInfo value; | |
477 | |
478 override TypeInfo next() | |
479 { | |
480 return value; | |
481 } | |
482 | |
483 override uint flags() { return 1; } | |
484 } | |
485 | |
486 class TypeInfo_StaticArray : TypeInfo | |
487 { | |
488 override string toString() | |
489 { | |
490 char[10] tmp = void; | |
491 return cast(string)(value.toString() ~ "[" ~ tmp.intToString(len) ~ "]"); | |
492 } | |
493 | |
494 override equals_t opEquals(Object o) | |
495 { | |
496 TypeInfo_StaticArray c; | |
497 return this is o || | |
498 ((c = cast(TypeInfo_StaticArray)o) !is null && | |
499 this.len == c.len && | |
500 this.value == c.value); | |
501 } | |
502 | |
503 override hash_t getHash(in void* p) | |
504 { | |
505 size_t sz = value.tsize(); | |
506 hash_t hash = 0; | |
507 for (size_t i = 0; i < len; i++) | |
508 hash += value.getHash(p + i * sz); | |
509 return hash; | |
510 } | |
511 | |
512 override equals_t equals(in void* p1, in void* p2) | |
513 { | |
514 size_t sz = value.tsize(); | |
515 | |
516 for (size_t u = 0; u < len; u++) | |
517 { | |
518 if (!value.equals(p1 + u * sz, p2 + u * sz)) | |
519 return false; | |
520 } | |
521 return true; | |
522 } | |
523 | |
524 override int compare(in void* p1, in void* p2) | |
525 { | |
526 size_t sz = value.tsize(); | |
527 | |
528 for (size_t u = 0; u < len; u++) | |
529 { | |
530 int result = value.compare(p1 + u * sz, p2 + u * sz); | |
531 if (result) | |
532 return result; | |
533 } | |
534 return 0; | |
535 } | |
536 | |
537 override size_t tsize() | |
538 { | |
539 return len * value.tsize(); | |
540 } | |
541 | |
542 override void swap(void* p1, void* p2) | |
543 { | |
544 void* tmp; | |
545 size_t sz = value.tsize(); | |
546 ubyte[16] buffer; | |
547 void* pbuffer; | |
548 | |
549 if (sz < buffer.sizeof) | |
550 tmp = buffer.ptr; | |
551 else | |
552 tmp = pbuffer = (new void[sz]).ptr; | |
553 | |
554 for (size_t u = 0; u < len; u += sz) | |
555 { size_t o = u * sz; | |
556 memcpy(tmp, p1 + o, sz); | |
557 memcpy(p1 + o, p2 + o, sz); | |
558 memcpy(p2 + o, tmp, sz); | |
559 } | |
560 if (pbuffer) | |
561 delete pbuffer; | |
562 } | |
563 | |
564 override void[] init() { return value.init(); } | |
565 override TypeInfo next() { return value; } | |
566 override uint flags() { return value.flags(); } | |
567 | |
568 override void destroy(void* p) | |
569 { | |
570 auto sz = value.tsize(); | |
571 p += sz * len; | |
572 foreach (i; 0 .. len) | |
573 { | |
574 p -= sz; | |
575 value.destroy(p); | |
576 } | |
577 } | |
578 | |
579 override void postblit(void* p) | |
580 { | |
581 auto sz = value.tsize(); | |
582 foreach (i; 0 .. len) | |
583 { | |
584 value.postblit(p); | |
585 p += sz; | |
586 } | |
587 } | |
588 | |
589 TypeInfo value; | |
590 size_t len; | |
591 } | |
592 | |
593 class TypeInfo_AssociativeArray : TypeInfo | |
594 { | |
595 override string toString() | |
596 { | |
597 return cast(string)(next.toString() ~ "[" ~ key.toString() ~ "]"); | |
598 } | |
599 | |
600 override equals_t opEquals(Object o) | |
601 { | |
602 TypeInfo_AssociativeArray c; | |
603 return this is o || | |
604 ((c = cast(TypeInfo_AssociativeArray)o) !is null && | |
605 this.key == c.key && | |
606 this.value == c.value); | |
607 } | |
608 | |
609 // BUG: need to add the rest of the functions | |
610 | |
611 override size_t tsize() | |
612 { | |
613 return (char[int]).sizeof; | |
614 } | |
615 | |
616 override TypeInfo next() { return value; } | |
617 override uint flags() { return 1; } | |
618 | |
619 TypeInfo value; | |
620 TypeInfo key; | |
621 } | |
622 | |
623 class TypeInfo_Function : TypeInfo | |
624 { | |
625 override string toString() | |
626 { | |
627 return cast(string)(next.toString() ~ "()"); | |
628 } | |
629 | |
630 override equals_t opEquals(Object o) | |
631 { | |
632 TypeInfo_Function c; | |
633 return this is o || | |
634 ((c = cast(TypeInfo_Function)o) !is null && | |
635 this.next == c.next); | |
636 } | |
637 | |
638 // BUG: need to add the rest of the functions | |
639 | |
640 override size_t tsize() | |
641 { | |
642 return 0; // no size for functions | |
643 } | |
644 | |
645 TypeInfo next; | |
646 } | |
647 | |
648 class TypeInfo_Delegate : TypeInfo | |
649 { | |
650 override string toString() | |
651 { | |
652 return cast(string)(next.toString() ~ " delegate()"); | |
653 } | |
654 | |
655 override equals_t opEquals(Object o) | |
656 { | |
657 TypeInfo_Delegate c; | |
658 return this is o || | |
659 ((c = cast(TypeInfo_Delegate)o) !is null && | |
660 this.next == c.next); | |
661 } | |
662 | |
663 // BUG: need to add the rest of the functions | |
664 | |
665 override size_t tsize() | |
666 { | |
667 alias int delegate() dg; | |
668 return dg.sizeof; | |
669 } | |
670 | |
671 override uint flags() { return 1; } | |
672 | |
673 TypeInfo next; | |
674 } | |
675 | |
676 class TypeInfo_Class : TypeInfo | |
677 { | |
678 override string toString() { return info.name; } | |
679 | |
680 override equals_t opEquals(Object o) | |
681 { | |
682 TypeInfo_Class c; | |
683 return this is o || | |
684 ((c = cast(TypeInfo_Class)o) !is null && | |
685 this.info.name == c.classinfo.name); | |
686 } | |
687 | |
688 override hash_t getHash(in void* p) | |
689 { | |
690 Object o = *cast(Object*)p; | |
691 return o ? o.toHash() : 0; | |
692 } | |
693 | |
694 override equals_t equals(in void* p1, in void* p2) | |
695 { | |
696 Object o1 = *cast(Object*)p1; | |
697 Object o2 = *cast(Object*)p2; | |
698 | |
699 return (o1 is o2) || (o1 && o1.opEquals(o2)); | |
700 } | |
701 | |
702 override int compare(in void* p1, in void* p2) | |
703 { | |
704 Object o1 = *cast(Object*)p1; | |
705 Object o2 = *cast(Object*)p2; | |
706 int c = 0; | |
707 | |
708 // Regard null references as always being "less than" | |
709 if (o1 !is o2) | |
710 { | |
711 if (o1) | |
712 { | |
713 if (!o2) | |
714 c = 1; | |
715 else | |
716 c = o1.opCmp(o2); | |
717 } | |
718 else | |
719 c = -1; | |
720 } | |
721 return c; | |
722 } | |
723 | |
724 override size_t tsize() | |
725 { | |
726 return Object.sizeof; | |
727 } | |
728 | |
729 override uint flags() { return 1; } | |
730 | |
731 override OffsetTypeInfo[] offTi() | |
732 { | |
733 return (info.flags & 4) ? info.offTi : null; | |
734 } | |
735 | |
736 ClassInfo info; | |
737 } | |
738 | |
739 class TypeInfo_Interface : TypeInfo | |
740 { | |
741 override string toString() { return info.name; } | |
742 | |
743 override equals_t opEquals(Object o) | |
744 { | |
745 TypeInfo_Interface c; | |
746 return this is o || | |
747 ((c = cast(TypeInfo_Interface)o) !is null && | |
748 this.info.name == c.classinfo.name); | |
749 } | |
750 | |
751 override hash_t getHash(in void* p) | |
752 { | |
753 Interface* pi = **cast(Interface ***)*cast(void**)p; | |
754 Object o = cast(Object)(*cast(void**)p - pi.offset); | |
755 assert(o); | |
756 return o.toHash(); | |
757 } | |
758 | |
759 override equals_t equals(in void* p1, in void* p2) | |
760 { | |
761 Interface* pi = **cast(Interface ***)*cast(void**)p1; | |
762 Object o1 = cast(Object)(*cast(void**)p1 - pi.offset); | |
763 pi = **cast(Interface ***)*cast(void**)p2; | |
764 Object o2 = cast(Object)(*cast(void**)p2 - pi.offset); | |
765 | |
766 return o1 == o2 || (o1 && o1.opCmp(o2) == 0); | |
767 } | |
768 | |
769 override int compare(in void* p1, in void* p2) | |
770 { | |
771 Interface* pi = **cast(Interface ***)*cast(void**)p1; | |
772 Object o1 = cast(Object)(*cast(void**)p1 - pi.offset); | |
773 pi = **cast(Interface ***)*cast(void**)p2; | |
774 Object o2 = cast(Object)(*cast(void**)p2 - pi.offset); | |
775 int c = 0; | |
776 | |
777 // Regard null references as always being "less than" | |
778 if (o1 != o2) | |
779 { | |
780 if (o1) | |
781 { | |
782 if (!o2) | |
783 c = 1; | |
784 else | |
785 c = o1.opCmp(o2); | |
786 } | |
787 else | |
788 c = -1; | |
789 } | |
790 return c; | |
791 } | |
792 | |
793 override size_t tsize() | |
794 { | |
795 return Object.sizeof; | |
796 } | |
797 | |
798 override uint flags() { return 1; } | |
799 | |
800 ClassInfo info; | |
801 } | |
802 | |
803 class TypeInfo_Struct : TypeInfo | |
804 { | |
805 override string toString() { return name; } | |
806 | |
807 override equals_t opEquals(Object o) | |
808 { | |
809 TypeInfo_Struct s; | |
810 return this is o || | |
811 ((s = cast(TypeInfo_Struct)o) !is null && | |
812 this.name == s.name && | |
813 this.init.length == s.init.length); | |
814 } | |
815 | |
816 override hash_t getHash(in void* p) | |
817 { | |
818 assert(p); | |
819 if (xtoHash) | |
820 { | |
821 debug(PRINTF) printf("getHash() using xtoHash\n"); | |
822 return (*xtoHash)(p); | |
823 } | |
824 else | |
825 { | |
826 hash_t h; | |
827 debug(PRINTF) printf("getHash() using default hash\n"); | |
828 // A sorry hash algorithm. | |
829 // Should use the one for strings. | |
830 // BUG: relies on the GC not moving objects | |
831 auto q = cast(const(ubyte)*)p; | |
832 for (size_t i = 0; i < init.length; i++) | |
833 { | |
834 h = h * 9 + *q; | |
835 q++; | |
836 } | |
837 return h; | |
838 } | |
839 } | |
840 | |
841 override equals_t equals(in void* p1, in void* p2) | |
842 { | |
843 if (p1 == p2) | |
844 return true; | |
845 else if (!p1 || !p2) | |
846 return false; | |
847 else if (xopEquals) | |
848 return (*xopEquals)(p1, p2); | |
849 else | |
850 // BUG: relies on the GC not moving objects | |
851 return memcmp(p1, p2, init.length) == 0; | |
852 } | |
853 | |
854 override int compare(in void* p1, in void* p2) | |
855 { | |
856 // Regard null references as always being "less than" | |
857 if (p1 != p2) | |
858 { | |
859 if (p1) | |
860 { | |
861 if (!p2) | |
862 return true; | |
863 else if (xopCmp) | |
864 return (*xopCmp)(p2, p1); | |
865 else | |
866 // BUG: relies on the GC not moving objects | |
867 return memcmp(p1, p2, init.length); | |
868 } | |
869 else | |
870 return -1; | |
871 } | |
872 return 0; | |
873 } | |
874 | |
875 override size_t tsize() | |
876 { | |
877 return init.length; | |
878 } | |
879 | |
880 override void[] init() { return m_init; } | |
881 | |
882 override uint flags() { return m_flags; } | |
883 | |
884 override void destroy(void* p) | |
885 { | |
886 if (xdtor) | |
887 (*xdtor)(p); | |
888 } | |
889 | |
890 override void postblit(void* p) | |
891 { | |
892 if (xpostblit) | |
893 (*xpostblit)(p); | |
894 } | |
895 | |
896 string name; | |
897 void[] m_init; // initializer; init.ptr == null if 0 initialize | |
898 | |
899 hash_t function(in void*) xtoHash; | |
900 equals_t function(in void*, in void*) xopEquals; | |
901 int function(in void*, in void*) xopCmp; | |
902 char[] function(in void*) xtoString; | |
903 | |
904 uint m_flags; | |
905 | |
906 const(MemberInfo[]) function(in char[]) xgetMembers; | |
907 void function(void*) xdtor; | |
908 void function(void*) xpostblit; | |
909 } | |
910 | |
911 class TypeInfo_Tuple : TypeInfo | |
912 { | |
913 TypeInfo[] elements; | |
914 | |
915 override string toString() | |
916 { | |
917 string s = "("; | |
918 foreach (i, element; elements) | |
919 { | |
920 if (i) | |
921 s ~= ','; | |
922 s ~= element.toString(); | |
923 } | |
924 s ~= ")"; | |
925 return s; | |
926 } | |
927 | |
928 override equals_t opEquals(Object o) | |
929 { | |
930 if (this is o) | |
931 return true; | |
932 | |
933 auto t = cast(TypeInfo_Tuple)o; | |
934 if (t && elements.length == t.elements.length) | |
935 { | |
936 for (size_t i = 0; i < elements.length; i++) | |
937 { | |
938 if (elements[i] != t.elements[i]) | |
939 return false; | |
940 } | |
941 return true; | |
942 } | |
943 return false; | |
944 } | |
945 | |
946 override hash_t getHash(in void* p) | |
947 { | |
948 assert(0); | |
949 } | |
950 | |
951 override equals_t equals(in void* p1, in void* p2) | |
952 { | |
953 assert(0); | |
954 } | |
955 | |
956 override int compare(in void* p1, in void* p2) | |
957 { | |
958 assert(0); | |
959 } | |
960 | |
961 override size_t tsize() | |
962 { | |
963 assert(0); | |
964 } | |
965 | |
966 override void swap(void* p1, void* p2) | |
967 { | |
968 assert(0); | |
969 } | |
970 | |
971 override void destroy(void* p) | |
972 { | |
973 assert(0); | |
974 } | |
975 | |
976 override void postblit(void* p) | |
977 { | |
978 assert(0); | |
979 } | |
980 } | |
981 | |
982 class TypeInfo_Const : TypeInfo | |
983 { | |
984 override string toString() | |
985 { | |
986 return cast(string) ("const(" ~ base.toString() ~ ")"); | |
987 } | |
988 | |
989 override equals_t opEquals(Object o) { return base.opEquals(o); } | |
990 override hash_t getHash(in void *p) { return base.getHash(p); } | |
991 override equals_t equals(in void *p1, in void *p2) { return base.equals(p1, p2); } | |
992 override int compare(in void *p1, in void *p2) { return base.compare(p1, p2); } | |
993 override size_t tsize() { return base.tsize(); } | |
994 override void swap(void *p1, void *p2) { return base.swap(p1, p2); } | |
995 | |
996 override TypeInfo next() { return base.next(); } | |
997 override uint flags() { return base.flags(); } | |
998 override void[] init() { return base.init(); } | |
999 | |
1000 TypeInfo base; | |
1001 } | |
1002 | |
1003 class TypeInfo_Invariant : TypeInfo_Const | |
1004 { | |
1005 override string toString() | |
1006 { | |
1007 return cast(string) ("invariant(" ~ base.toString() ~ ")"); | |
1008 } | |
1009 } | |
1010 | |
1011 abstract class MemberInfo | |
1012 { | |
1013 string name(); | |
1014 } | |
1015 | |
1016 class MemberInfo_field : MemberInfo | |
1017 { | |
1018 this(string name, TypeInfo ti, size_t offset) | |
1019 { | |
1020 m_name = name; | |
1021 m_typeinfo = ti; | |
1022 m_offset = offset; | |
1023 } | |
1024 | |
1025 override string name() { return m_name; } | |
1026 TypeInfo typeInfo() { return m_typeinfo; } | |
1027 size_t offset() { return m_offset; } | |
1028 | |
1029 string m_name; | |
1030 TypeInfo m_typeinfo; | |
1031 size_t m_offset; | |
1032 } | |
1033 | |
1034 class MemberInfo_function : MemberInfo | |
1035 { | |
1036 this(string name, TypeInfo ti, void* fp, uint flags) | |
1037 { | |
1038 m_name = name; | |
1039 m_typeinfo = ti; | |
1040 m_fp = fp; | |
1041 m_flags = flags; | |
1042 } | |
1043 | |
1044 override string name() { return m_name; } | |
1045 TypeInfo typeInfo() { return m_typeinfo; } | |
1046 void* fp() { return m_fp; } | |
1047 uint flags() { return m_flags; } | |
1048 | |
1049 string m_name; | |
1050 TypeInfo m_typeinfo; | |
1051 void* m_fp; | |
1052 uint m_flags; | |
1053 } | |
1054 | |
1055 | |
1056 /////////////////////////////////////////////////////////////////////////////// | |
1057 // Throwable | |
1058 /////////////////////////////////////////////////////////////////////////////// | |
1059 | |
1060 | |
1061 class Throwable : Object | |
1062 { | |
1063 interface TraceInfo | |
1064 { | |
1065 int opApply(int delegate(inout char[])); | |
1066 } | |
1067 | |
1068 string msg; | |
1069 string file; | |
1070 size_t line; | |
1071 TraceInfo info; | |
1072 Throwable next; | |
1073 | |
1074 this(string msg, Throwable next = null) | |
1075 { | |
1076 this.msg = msg; | |
1077 this.next = next; | |
1078 this.info = traceContext(); | |
1079 } | |
1080 | |
1081 this(string msg, string file, size_t line, Throwable next = null) | |
1082 { | |
1083 this(msg, next); | |
1084 this.file = file; | |
1085 this.line = line; | |
1086 this.info = traceContext(); | |
1087 } | |
1088 | |
1089 override string toString() | |
1090 { | |
1091 char[10] tmp = void; | |
1092 char[] buf; | |
1093 | |
1094 for (Throwable e = this; e !is null; e = e.next) | |
1095 { | |
1096 if (e.file) | |
1097 { | |
1098 buf ~= e.classinfo.name ~ "@" ~ e.file ~ "(" ~ tmp.intToString(e.line) ~ "): " ~ e.msg; | |
1099 } | |
1100 else | |
1101 { | |
1102 buf ~= e.classinfo.name ~ ": " ~ e.msg; | |
1103 } | |
1104 if (e.info) | |
1105 { | |
1106 buf ~= "\n----------------"; | |
1107 foreach (t; e.info) | |
1108 buf ~= "\n" ~ t; | |
1109 } | |
1110 if (e.next) | |
1111 buf ~= "\n"; | |
1112 } | |
1113 return cast(string) buf; | |
1114 } | |
1115 } | |
1116 | |
1117 | |
1118 alias Throwable.TraceInfo function(void* ptr = null) TraceHandler; | |
1119 private TraceHandler traceHandler = null; | |
1120 | |
1121 | |
1122 /** | |
1123 * Overrides the default trace hander with a user-supplied version. | |
1124 * | |
1125 * Params: | |
1126 * h = The new trace handler. Set to null to use the default handler. | |
1127 */ | |
1128 extern (C) void rt_setTraceHandler(TraceHandler h) | |
1129 { | |
1130 traceHandler = h; | |
1131 } | |
1132 | |
1133 | |
1134 /** | |
1135 * This function will be called when an exception is constructed. The | |
1136 * user-supplied trace handler will be called if one has been supplied, | |
1137 * otherwise no trace will be generated. | |
1138 * | |
1139 * Params: | |
1140 * ptr = A pointer to the location from which to generate the trace, or null | |
1141 * if the trace should be generated from within the trace handler | |
1142 * itself. | |
1143 * | |
1144 * Returns: | |
1145 * An object describing the current calling context or null if no handler is | |
1146 * supplied. | |
1147 */ | |
1148 Throwable.TraceInfo traceContext(void* ptr = null) | |
1149 { | |
1150 if (traceHandler is null) | |
1151 return null; | |
1152 return traceHandler(ptr); | |
1153 } | |
1154 | |
1155 | |
1156 class Exception : Throwable | |
1157 { | |
1158 this(string msg, Throwable next = null) | |
1159 { | |
1160 super(msg, next); | |
1161 } | |
1162 | |
1163 this(string msg, string file, size_t line, Throwable next = null) | |
1164 { | |
1165 super(msg, file, line, next); | |
1166 } | |
1167 } | |
1168 | |
1169 | |
1170 class Error : Throwable | |
1171 { | |
1172 this(string msg, Throwable next = null) | |
1173 { | |
1174 super(msg, next); | |
1175 } | |
1176 | |
1177 this(string msg, string file, size_t line, Throwable next = null) | |
1178 { | |
1179 super(msg, file, line, next); | |
1180 } | |
1181 } | |
1182 | |
1183 | |
1184 /////////////////////////////////////////////////////////////////////////////// | |
1185 // ModuleInfo | |
1186 /////////////////////////////////////////////////////////////////////////////// | |
1187 | |
1188 | |
1189 enum | |
1190 { | |
1191 MIctorstart = 1, // we've started constructing it | |
1192 MIctordone = 2, // finished construction | |
1193 MIstandalone = 4, // module ctor does not depend on other module | |
1194 // ctors being done first | |
1195 MIhasictor = 8, // has ictor member | |
1196 } | |
1197 | |
1198 | |
1199 class ModuleInfo | |
1200 { | |
1201 string name; | |
1202 ModuleInfo[] importedModules; | |
1203 ClassInfo[] localClasses; | |
1204 uint flags; | |
1205 | |
1206 void function() ctor; // module static constructor (order dependent) | |
1207 void function() dtor; // module static destructor | |
1208 void function() unitTest; // module unit tests | |
1209 | |
1210 void* xgetMembers; // module getMembers() function | |
1211 | |
1212 void function() ictor; // module static constructor (order independent) | |
1213 | |
1214 static int opApply(int delegate(inout ModuleInfo) dg) | |
1215 { | |
1216 int ret = 0; | |
1217 | |
1218 foreach (m; _moduleinfo_array) | |
1219 { | |
1220 ret = dg(m); | |
1221 if (ret) | |
1222 break; | |
1223 } | |
1224 return ret; | |
1225 } | |
1226 } | |
1227 | |
1228 | |
1229 // Windows: this gets initialized by minit.asm | |
1230 // linux: this gets initialized in _moduleCtor() | |
1231 extern (C) ModuleInfo[] _moduleinfo_array; | |
1232 | |
1233 | |
1234 version (linux) | |
1235 { | |
1236 // This linked list is created by a compiler generated function inserted | |
1237 // into the .ctor list by the compiler. | |
1238 struct ModuleReference | |
1239 { | |
1240 ModuleReference* next; | |
1241 ModuleInfo mod; | |
1242 } | |
1243 | |
1244 extern (C) ModuleReference* _Dmodule_ref; // start of linked list | |
1245 } | |
1246 | |
1247 ModuleInfo[] _moduleinfo_dtors; | |
1248 uint _moduleinfo_dtors_i; | |
1249 | |
1250 // Register termination function pointers | |
1251 extern (C) int _fatexit(void*); | |
1252 | |
1253 /** | |
1254 * Initialize the modules. | |
1255 */ | |
1256 | |
1257 extern (C) void _moduleCtor() | |
1258 { | |
1259 debug(PRINTF) printf("_moduleCtor()\n"); | |
1260 version (linux) | |
1261 { | |
1262 int len = 0; | |
1263 ModuleReference *mr; | |
1264 | |
1265 for (mr = _Dmodule_ref; mr; mr = mr.next) | |
1266 len++; | |
1267 _moduleinfo_array = new ModuleInfo[len]; | |
1268 len = 0; | |
1269 for (mr = _Dmodule_ref; mr; mr = mr.next) | |
1270 { _moduleinfo_array[len] = mr.mod; | |
1271 len++; | |
1272 } | |
1273 } | |
1274 | |
1275 version (Windows) | |
1276 { | |
1277 // Ensure module destructors also get called on program termination | |
1278 //_fatexit(&_STD_moduleDtor); | |
1279 } | |
1280 | |
1281 _moduleinfo_dtors = new ModuleInfo[_moduleinfo_array.length]; | |
1282 debug(PRINTF) printf("_moduleinfo_dtors = x%x\n", cast(void*)_moduleinfo_dtors); | |
1283 _moduleIndependentCtors(); | |
1284 _moduleCtor2(_moduleinfo_array, 0); | |
1285 } | |
1286 | |
1287 extern (C) void _moduleIndependentCtors() | |
1288 { | |
1289 debug(PRINTF) printf("_moduleIndependentCtors()\n"); | |
1290 foreach (m; _moduleinfo_array) | |
1291 { | |
1292 if (m && m.flags & MIhasictor && m.ictor) | |
1293 { | |
1294 (*m.ictor)(); | |
1295 } | |
1296 } | |
1297 } | |
1298 | |
1299 void _moduleCtor2(ModuleInfo[] mi, int skip) | |
1300 { | |
1301 debug(PRINTF) printf("_moduleCtor2(): %d modules\n", mi.length); | |
1302 for (uint i = 0; i < mi.length; i++) | |
1303 { | |
1304 ModuleInfo m = mi[i]; | |
1305 | |
1306 debug(PRINTF) printf("\tmodule[%d] = '%p'\n", i, m); | |
1307 if (!m) | |
1308 continue; | |
1309 debug(PRINTF) printf("\tmodule[%d] = '%.*s'\n", i, m.name); | |
1310 if (m.flags & MIctordone) | |
1311 continue; | |
1312 debug(PRINTF) printf("\tmodule[%d] = '%.*s', m = x%x\n", i, m.name, m); | |
1313 | |
1314 if (m.ctor || m.dtor) | |
1315 { | |
1316 if (m.flags & MIctorstart) | |
1317 { if (skip || m.flags & MIstandalone) | |
1318 continue; | |
1319 throw new Exception("Cyclic dependency in module " ~ m.name); | |
1320 } | |
1321 | |
1322 m.flags |= MIctorstart; | |
1323 _moduleCtor2(m.importedModules, 0); | |
1324 if (m.ctor) | |
1325 (*m.ctor)(); | |
1326 m.flags &= ~MIctorstart; | |
1327 m.flags |= MIctordone; | |
1328 | |
1329 // Now that construction is done, register the destructor | |
1330 //printf("\tadding module dtor x%x\n", m); | |
1331 assert(_moduleinfo_dtors_i < _moduleinfo_dtors.length); | |
1332 _moduleinfo_dtors[_moduleinfo_dtors_i++] = m; | |
1333 } | |
1334 else | |
1335 { | |
1336 m.flags |= MIctordone; | |
1337 _moduleCtor2(m.importedModules, 1); | |
1338 } | |
1339 } | |
1340 } | |
1341 | |
1342 /** | |
1343 * Destruct the modules. | |
1344 */ | |
1345 | |
1346 // Starting the name with "_STD" means under linux a pointer to the | |
1347 // function gets put in the .dtors segment. | |
1348 | |
1349 extern (C) void _moduleDtor() | |
1350 { | |
1351 debug(PRINTF) printf("_moduleDtor(): %d modules\n", _moduleinfo_dtors_i); | |
1352 | |
1353 for (uint i = _moduleinfo_dtors_i; i-- != 0;) | |
1354 { | |
1355 ModuleInfo m = _moduleinfo_dtors[i]; | |
1356 | |
1357 debug(PRINTF) printf("\tmodule[%d] = '%.*s', x%x\n", i, m.name, m); | |
1358 if (m.dtor) | |
1359 { | |
1360 (*m.dtor)(); | |
1361 } | |
1362 } | |
1363 debug(PRINTF) printf("_moduleDtor() done\n"); | |
1364 } | |
1365 | |
1366 /////////////////////////////////////////////////////////////////////////////// | |
1367 // Monitor | |
1368 /////////////////////////////////////////////////////////////////////////////// | |
1369 | |
1370 alias Object.Monitor IMonitor; | |
1371 alias void delegate(Object) DEvent; | |
1372 | |
1373 // NOTE: The dtor callback feature is only supported for monitors that are not | |
1374 // supplied by the user. The assumption is that any object with a user- | |
1375 // supplied monitor may have special storage or lifetime requirements and | |
1376 // that as a result, storing references to local objects within Monitor | |
1377 // may not be safe or desirable. Thus, devt is only valid if impl is | |
1378 // null. | |
1379 struct Monitor | |
1380 { | |
1381 IMonitor impl; | |
1382 /* internal */ | |
1383 DEvent[] devt; | |
1384 /* stuff */ | |
1385 } | |
1386 | |
1387 Monitor* getMonitor(Object h) | |
1388 { | |
1389 return cast(Monitor*) (cast(void**) h)[1]; | |
1390 } | |
1391 | |
1392 void setMonitor(Object h, Monitor* m) | |
1393 { | |
1394 (cast(void**) h)[1] = m; | |
1395 } | |
1396 | |
1397 extern (C) void _d_monitor_create(Object); | |
1398 extern (C) void _d_monitor_destroy(Object); | |
1399 extern (C) void _d_monitor_lock(Object); | |
1400 extern (C) int _d_monitor_unlock(Object); | |
1401 | |
1402 extern (C) void _d_monitordelete(Object h, bool det) | |
1403 { | |
1404 Monitor* m = getMonitor(h); | |
1405 | |
1406 if (m !is null) | |
1407 { | |
1408 IMonitor i = m.impl; | |
1409 if (i is null) | |
1410 { | |
1411 _d_monitor_devt(m, h); | |
1412 _d_monitor_destroy(h); | |
1413 setMonitor(h, null); | |
1414 return; | |
1415 } | |
1416 if (det && (cast(void*) i) !is (cast(void*) h)) | |
1417 delete i; | |
1418 setMonitor(h, null); | |
1419 } | |
1420 } | |
1421 | |
1422 extern (C) void _d_monitorenter(Object h) | |
1423 { | |
1424 Monitor* m = getMonitor(h); | |
1425 | |
1426 if (m is null) | |
1427 { | |
1428 _d_monitor_create(h); | |
1429 m = getMonitor(h); | |
1430 } | |
1431 | |
1432 IMonitor i = m.impl; | |
1433 | |
1434 if (i is null) | |
1435 { | |
1436 _d_monitor_lock(h); | |
1437 return; | |
1438 } | |
1439 i.lock(); | |
1440 } | |
1441 | |
1442 extern (C) void _d_monitorexit(Object h) | |
1443 { | |
1444 Monitor* m = getMonitor(h); | |
1445 IMonitor i = m.impl; | |
1446 | |
1447 if (i is null) | |
1448 { | |
1449 _d_monitor_unlock(h); | |
1450 return; | |
1451 } | |
1452 i.unlock(); | |
1453 } | |
1454 | |
1455 extern (C) void _d_monitor_devt(Monitor* m, Object h) | |
1456 { | |
1457 if (m.devt.length) | |
1458 { | |
1459 DEvent[] devt; | |
1460 | |
1461 synchronized (h) | |
1462 { | |
1463 devt = m.devt; | |
1464 m.devt = null; | |
1465 } | |
1466 foreach (v; devt) | |
1467 { | |
1468 if (v) | |
1469 v(h); | |
1470 } | |
1471 free(devt.ptr); | |
1472 } | |
1473 } | |
1474 | |
1475 extern (C) void rt_attachDisposeEvent(Object h, DEvent e) | |
1476 { | |
1477 synchronized (h) | |
1478 { | |
1479 Monitor* m = getMonitor(h); | |
1480 assert(m.impl is null); | |
1481 | |
1482 foreach (inout v; m.devt) | |
1483 { | |
1484 if (v is null || v == e) | |
1485 { | |
1486 v = e; | |
1487 return; | |
1488 } | |
1489 } | |
1490 | |
1491 auto len = m.devt.length + 4; // grow by 4 elements | |
1492 auto pos = m.devt.length; // insert position | |
1493 auto p = realloc(m.devt.ptr, DEvent.sizeof * len); | |
1494 if (!p) | |
1495 onOutOfMemoryError(); | |
1496 m.devt = (cast(DEvent*)p)[0 .. len]; | |
1497 m.devt[pos+1 .. len] = null; | |
1498 m.devt[pos] = e; | |
1499 } | |
1500 } | |
1501 | |
1502 extern (C) void rt_detachDisposeEvent(Object h, DEvent e) | |
1503 { | |
1504 synchronized (h) | |
1505 { | |
1506 Monitor* m = getMonitor(h); | |
1507 assert(m.impl is null); | |
1508 | |
1509 foreach (p, v; m.devt) | |
1510 { | |
1511 if (v == e) | |
1512 { | |
1513 memmove(&m.devt[p], | |
1514 &m.devt[p+1], | |
1515 (m.devt.length - p - 1) * DEvent.sizeof); | |
1516 m.devt[$ - 1] = null; | |
1517 return; | |
1518 } | |
1519 } | |
1520 } | |
1521 } |