diff options
| author | Gerd Moellmann | 1999-07-21 21:43:52 +0000 |
|---|---|---|
| committer | Gerd Moellmann | 1999-07-21 21:43:52 +0000 |
| commit | 41c28a3753d17471b669cabb85c3bd5a375e78d7 (patch) | |
| tree | bf708c4ef6d27375ea283a42008ae44ae32931b1 /src/alloc.c | |
| parent | ecfd95532daefab697b130da736ecdc7cb292169 (diff) | |
| download | emacs-41c28a3753d17471b669cabb85c3bd5a375e78d7.tar.gz emacs-41c28a3753d17471b669cabb85c3bd5a375e78d7.zip | |
(gc_sweep): Call sweep_weak_hash_tables.
(survives_gc_p): New.
(mark_object): Mark objects referenced from glyphs, hash tables,
toolbar date, toolbar window, face caches, menu bar window.
Mark windows specially.
(Fgarbage_collect): Use message3_nolog.
(mark_face_cache): New.
(NSTATICS): Increased to 1024.
(mark_glyph_matrix): New.
Diffstat (limited to 'src/alloc.c')
| -rw-r--r-- | src/alloc.c | 291 |
1 files changed, 287 insertions, 4 deletions
diff --git a/src/alloc.c b/src/alloc.c index 79e3278680b..1ae6cdd153a 100644 --- a/src/alloc.c +++ b/src/alloc.c | |||
| @@ -192,9 +192,17 @@ int ignore_warnings; | |||
| 192 | 192 | ||
| 193 | Lisp_Object Qgc_cons_threshold, Qchar_table_extra_slots; | 193 | Lisp_Object Qgc_cons_threshold, Qchar_table_extra_slots; |
| 194 | 194 | ||
| 195 | static void mark_object (), mark_buffer (), mark_kboards (); | 195 | static void mark_buffer (), mark_kboards (); |
| 196 | static void clear_marks (), gc_sweep (); | 196 | static void clear_marks (), gc_sweep (); |
| 197 | static void compact_strings (); | 197 | static void compact_strings (); |
| 198 | static void mark_glyph_matrix P_ ((struct glyph_matrix *)); | ||
| 199 | static void mark_face_cache P_ ((struct face_cache *)); | ||
| 200 | |||
| 201 | #ifdef HAVE_WINDOW_SYSTEM | ||
| 202 | static void mark_image P_ ((struct image *)); | ||
| 203 | static void mark_image_cache P_ ((struct frame *)); | ||
| 204 | #endif /* HAVE_WINDOW_SYSTEM */ | ||
| 205 | |||
| 198 | 206 | ||
| 199 | extern int message_enable_multibyte; | 207 | extern int message_enable_multibyte; |
| 200 | 208 | ||
| @@ -1667,7 +1675,7 @@ Does not copy symbols.") | |||
| 1667 | 1675 | ||
| 1668 | struct gcpro *gcprolist; | 1676 | struct gcpro *gcprolist; |
| 1669 | 1677 | ||
| 1670 | #define NSTATICS 768 | 1678 | #define NSTATICS 1024 |
| 1671 | 1679 | ||
| 1672 | Lisp_Object *staticvec[NSTATICS] = {0}; | 1680 | Lisp_Object *staticvec[NSTATICS] = {0}; |
| 1673 | 1681 | ||
| @@ -1739,15 +1747,19 @@ Garbage collection happens automatically if you cons more than\n\ | |||
| 1739 | register struct backtrace *backlist; | 1747 | register struct backtrace *backlist; |
| 1740 | register Lisp_Object tem; | 1748 | register Lisp_Object tem; |
| 1741 | char *omessage = echo_area_glyphs; | 1749 | char *omessage = echo_area_glyphs; |
| 1750 | Lisp_Object omessage_string = echo_area_message; | ||
| 1742 | int omessage_length = echo_area_glyphs_length; | 1751 | int omessage_length = echo_area_glyphs_length; |
| 1743 | int oldmultibyte = message_enable_multibyte; | 1752 | int oldmultibyte = message_enable_multibyte; |
| 1744 | char stack_top_variable; | 1753 | char stack_top_variable; |
| 1745 | register int i; | 1754 | register int i; |
| 1755 | struct gcpro gcpro1; | ||
| 1746 | 1756 | ||
| 1747 | /* In case user calls debug_print during GC, | 1757 | /* In case user calls debug_print during GC, |
| 1748 | don't let that cause a recursive GC. */ | 1758 | don't let that cause a recursive GC. */ |
| 1749 | consing_since_gc = 0; | 1759 | consing_since_gc = 0; |
| 1750 | 1760 | ||
| 1761 | GCPRO1 (omessage_string); | ||
| 1762 | |||
| 1751 | /* Save a copy of the contents of the stack, for debugging. */ | 1763 | /* Save a copy of the contents of the stack, for debugging. */ |
| 1752 | #if MAX_SAVE_STACK > 0 | 1764 | #if MAX_SAVE_STACK > 0 |
| 1753 | if (NILP (Vpurify_flag)) | 1765 | if (NILP (Vpurify_flag)) |
| @@ -1930,12 +1942,15 @@ Garbage collection happens automatically if you cons more than\n\ | |||
| 1930 | 1942 | ||
| 1931 | if (garbage_collection_messages) | 1943 | if (garbage_collection_messages) |
| 1932 | { | 1944 | { |
| 1945 | if (STRINGP (omessage_string)) | ||
| 1946 | message3_nolog (omessage_string, omessage_length, oldmultibyte); | ||
| 1933 | if (omessage || minibuf_level > 0) | 1947 | if (omessage || minibuf_level > 0) |
| 1934 | message2_nolog (omessage, omessage_length, oldmultibyte); | 1948 | message2_nolog (omessage, omessage_length, oldmultibyte); |
| 1935 | else | 1949 | else |
| 1936 | message1_nolog ("Garbage collecting...done"); | 1950 | message1_nolog ("Garbage collecting...done"); |
| 1937 | } | 1951 | } |
| 1938 | 1952 | ||
| 1953 | UNGCPRO; | ||
| 1939 | return Fcons (Fcons (make_number (total_conses), | 1954 | return Fcons (Fcons (make_number (total_conses), |
| 1940 | make_number (total_free_conses)), | 1955 | make_number (total_free_conses)), |
| 1941 | Fcons (Fcons (make_number (total_symbols), | 1956 | Fcons (Fcons (make_number (total_symbols), |
| @@ -2019,6 +2034,95 @@ clear_marks () | |||
| 2019 | } | 2034 | } |
| 2020 | } | 2035 | } |
| 2021 | #endif | 2036 | #endif |
| 2037 | |||
| 2038 | /* Mark Lisp objects in glyph matrix MATRIX. */ | ||
| 2039 | |||
| 2040 | static void | ||
| 2041 | mark_glyph_matrix (matrix) | ||
| 2042 | struct glyph_matrix *matrix; | ||
| 2043 | { | ||
| 2044 | struct glyph_row *row = matrix->rows; | ||
| 2045 | struct glyph_row *end = row + matrix->nrows; | ||
| 2046 | |||
| 2047 | while (row < end) | ||
| 2048 | { | ||
| 2049 | if (row->enabled_p) | ||
| 2050 | { | ||
| 2051 | int area; | ||
| 2052 | for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area) | ||
| 2053 | { | ||
| 2054 | struct glyph *glyph = row->glyphs[area]; | ||
| 2055 | struct glyph *end_glyph = glyph + row->used[area]; | ||
| 2056 | |||
| 2057 | while (glyph < end_glyph) | ||
| 2058 | { | ||
| 2059 | if (/* OBJECT Is zero for face extending glyphs, padding | ||
| 2060 | spaces and such. */ | ||
| 2061 | glyph->object | ||
| 2062 | /* Marking the buffer itself should not be necessary. */ | ||
| 2063 | && !BUFFERP (glyph->object)) | ||
| 2064 | mark_object (&glyph->object); | ||
| 2065 | ++glyph; | ||
| 2066 | } | ||
| 2067 | } | ||
| 2068 | } | ||
| 2069 | |||
| 2070 | ++row; | ||
| 2071 | } | ||
| 2072 | } | ||
| 2073 | |||
| 2074 | /* Mark Lisp faces in the face cache C. */ | ||
| 2075 | |||
| 2076 | static void | ||
| 2077 | mark_face_cache (c) | ||
| 2078 | struct face_cache *c; | ||
| 2079 | { | ||
| 2080 | if (c) | ||
| 2081 | { | ||
| 2082 | int i, j; | ||
| 2083 | for (i = 0; i < c->used; ++i) | ||
| 2084 | { | ||
| 2085 | struct face *face = FACE_FROM_ID (c->f, i); | ||
| 2086 | |||
| 2087 | if (face) | ||
| 2088 | { | ||
| 2089 | for (j = 0; j < LFACE_VECTOR_SIZE; ++j) | ||
| 2090 | mark_object (&face->lface[j]); | ||
| 2091 | mark_object (&face->registry); | ||
| 2092 | } | ||
| 2093 | } | ||
| 2094 | } | ||
| 2095 | } | ||
| 2096 | |||
| 2097 | |||
| 2098 | #ifdef HAVE_WINDOW_SYSTEM | ||
| 2099 | |||
| 2100 | /* Mark Lisp objects in image IMG. */ | ||
| 2101 | |||
| 2102 | static void | ||
| 2103 | mark_image (img) | ||
| 2104 | struct image *img; | ||
| 2105 | { | ||
| 2106 | mark_object (&img->spec); | ||
| 2107 | |||
| 2108 | if (!NILP (img->data.lisp_val)) | ||
| 2109 | mark_object (&img->data.lisp_val); | ||
| 2110 | } | ||
| 2111 | |||
| 2112 | |||
| 2113 | /* Mark Lisp objects in image cache of frame F. It's done this way so | ||
| 2114 | that we don't have to include xterm.h here. */ | ||
| 2115 | |||
| 2116 | static void | ||
| 2117 | mark_image_cache (f) | ||
| 2118 | struct frame *f; | ||
| 2119 | { | ||
| 2120 | forall_images_in_image_cache (f, mark_image); | ||
| 2121 | } | ||
| 2122 | |||
| 2123 | #endif /* HAVE_X_WINDOWS */ | ||
| 2124 | |||
| 2125 | |||
| 2022 | 2126 | ||
| 2023 | /* Mark reference to a Lisp_Object. | 2127 | /* Mark reference to a Lisp_Object. |
| 2024 | If the object referred to has not been seen yet, recursively mark | 2128 | If the object referred to has not been seen yet, recursively mark |
| @@ -2034,7 +2138,7 @@ clear_marks () | |||
| 2034 | Lisp_Object *last_marked[LAST_MARKED_SIZE]; | 2138 | Lisp_Object *last_marked[LAST_MARKED_SIZE]; |
| 2035 | int last_marked_index; | 2139 | int last_marked_index; |
| 2036 | 2140 | ||
| 2037 | static void | 2141 | void |
| 2038 | mark_object (argptr) | 2142 | mark_object (argptr) |
| 2039 | Lisp_Object *argptr; | 2143 | Lisp_Object *argptr; |
| 2040 | { | 2144 | { |
| @@ -2144,6 +2248,16 @@ mark_object (argptr) | |||
| 2144 | mark_object (&ptr->menu_bar_vector); | 2248 | mark_object (&ptr->menu_bar_vector); |
| 2145 | mark_object (&ptr->buffer_predicate); | 2249 | mark_object (&ptr->buffer_predicate); |
| 2146 | mark_object (&ptr->buffer_list); | 2250 | mark_object (&ptr->buffer_list); |
| 2251 | mark_object (&ptr->menu_bar_window); | ||
| 2252 | mark_object (&ptr->toolbar_window); | ||
| 2253 | mark_face_cache (ptr->face_cache); | ||
| 2254 | #ifdef HAVE_WINDOW_SYSTEM | ||
| 2255 | mark_image_cache (ptr); | ||
| 2256 | mark_object (&ptr->desired_toolbar_items); | ||
| 2257 | mark_object (&ptr->current_toolbar_items); | ||
| 2258 | mark_object (&ptr->desired_toolbar_string); | ||
| 2259 | mark_object (&ptr->current_toolbar_string); | ||
| 2260 | #endif /* HAVE_WINDOW_SYSTEM */ | ||
| 2147 | } | 2261 | } |
| 2148 | else if (GC_BOOL_VECTOR_P (obj)) | 2262 | else if (GC_BOOL_VECTOR_P (obj)) |
| 2149 | { | 2263 | { |
| @@ -2153,6 +2267,76 @@ mark_object (argptr) | |||
| 2153 | break; /* Already marked */ | 2267 | break; /* Already marked */ |
| 2154 | ptr->size |= ARRAY_MARK_FLAG; /* Else mark it */ | 2268 | ptr->size |= ARRAY_MARK_FLAG; /* Else mark it */ |
| 2155 | } | 2269 | } |
| 2270 | else if (GC_WINDOWP (obj)) | ||
| 2271 | { | ||
| 2272 | register struct Lisp_Vector *ptr = XVECTOR (obj); | ||
| 2273 | struct window *w = XWINDOW (obj); | ||
| 2274 | register EMACS_INT size = ptr->size; | ||
| 2275 | /* The reason we use ptr1 is to avoid an apparent hardware bug | ||
| 2276 | that happens occasionally on the FSF's HP 300s. | ||
| 2277 | The bug is that a2 gets clobbered by recursive calls to mark_object. | ||
| 2278 | The clobberage seems to happen during function entry, | ||
| 2279 | perhaps in the moveml instruction. | ||
| 2280 | Yes, this is a crock, but we have to do it. */ | ||
| 2281 | struct Lisp_Vector *volatile ptr1 = ptr; | ||
| 2282 | register int i; | ||
| 2283 | |||
| 2284 | /* Stop if already marked. */ | ||
| 2285 | if (size & ARRAY_MARK_FLAG) | ||
| 2286 | break; | ||
| 2287 | |||
| 2288 | /* Mark it. */ | ||
| 2289 | ptr->size |= ARRAY_MARK_FLAG; | ||
| 2290 | |||
| 2291 | /* There is no Lisp data above The member CURRENT_MATRIX in | ||
| 2292 | struct WINDOW. Stop marking when that slot is reached. */ | ||
| 2293 | for (i = 0; | ||
| 2294 | (char *) &ptr1->contents[i] < (char *) &w->current_matrix; | ||
| 2295 | i++) | ||
| 2296 | mark_object (&ptr1->contents[i]); | ||
| 2297 | |||
| 2298 | /* Mark glyphs for leaf windows. Marking window matrices is | ||
| 2299 | sufficient because frame matrices use the same glyph | ||
| 2300 | memory. */ | ||
| 2301 | if (NILP (w->hchild) | ||
| 2302 | && NILP (w->vchild) | ||
| 2303 | && w->current_matrix) | ||
| 2304 | { | ||
| 2305 | mark_glyph_matrix (w->current_matrix); | ||
| 2306 | mark_glyph_matrix (w->desired_matrix); | ||
| 2307 | } | ||
| 2308 | } | ||
| 2309 | else if (GC_HASH_TABLE_P (obj)) | ||
| 2310 | { | ||
| 2311 | struct Lisp_Hash_Table *h = XHASH_TABLE (obj); | ||
| 2312 | EMACS_INT size = h->size; | ||
| 2313 | |||
| 2314 | /* Stop if already marked. */ | ||
| 2315 | if (size & ARRAY_MARK_FLAG) | ||
| 2316 | break; | ||
| 2317 | |||
| 2318 | /* Mark it. */ | ||
| 2319 | h->size |= ARRAY_MARK_FLAG; | ||
| 2320 | |||
| 2321 | /* Mark contents. */ | ||
| 2322 | mark_object (&h->test); | ||
| 2323 | mark_object (&h->weak); | ||
| 2324 | mark_object (&h->rehash_size); | ||
| 2325 | mark_object (&h->rehash_threshold); | ||
| 2326 | mark_object (&h->hash); | ||
| 2327 | mark_object (&h->next); | ||
| 2328 | mark_object (&h->index); | ||
| 2329 | mark_object (&h->user_hash_function); | ||
| 2330 | mark_object (&h->user_cmp_function); | ||
| 2331 | |||
| 2332 | /* If hash table is not weak, mark all keys and values. | ||
| 2333 | For weak tables, mark only the vector. */ | ||
| 2334 | if (GC_NILP (h->weak)) | ||
| 2335 | mark_object (&h->key_and_value); | ||
| 2336 | else | ||
| 2337 | XVECTOR (h->key_and_value)->size |= ARRAY_MARK_FLAG; | ||
| 2338 | |||
| 2339 | } | ||
| 2156 | else | 2340 | else |
| 2157 | { | 2341 | { |
| 2158 | register struct Lisp_Vector *ptr = XVECTOR (obj); | 2342 | register struct Lisp_Vector *ptr = XVECTOR (obj); |
| @@ -2170,6 +2354,7 @@ mark_object (argptr) | |||
| 2170 | ptr->size |= ARRAY_MARK_FLAG; /* Else mark it */ | 2354 | ptr->size |= ARRAY_MARK_FLAG; /* Else mark it */ |
| 2171 | if (size & PSEUDOVECTOR_FLAG) | 2355 | if (size & PSEUDOVECTOR_FLAG) |
| 2172 | size &= PSEUDOVECTOR_SIZE_MASK; | 2356 | size &= PSEUDOVECTOR_SIZE_MASK; |
| 2357 | |||
| 2173 | for (i = 0; i < size; i++) /* and then mark its elements */ | 2358 | for (i = 0; i < size; i++) /* and then mark its elements */ |
| 2174 | mark_object (&ptr1->contents[i]); | 2359 | mark_object (&ptr1->contents[i]); |
| 2175 | } | 2360 | } |
| @@ -2187,7 +2372,7 @@ mark_object (argptr) | |||
| 2187 | mark_object (&ptr->function); | 2372 | mark_object (&ptr->function); |
| 2188 | mark_object (&ptr->plist); | 2373 | mark_object (&ptr->plist); |
| 2189 | XSETTYPE (*(Lisp_Object *) &ptr->name, Lisp_String); | 2374 | XSETTYPE (*(Lisp_Object *) &ptr->name, Lisp_String); |
| 2190 | mark_object (&ptr->name); | 2375 | mark_object ((Lisp_Object *) &ptr->name); |
| 2191 | /* Note that we do not mark the obarray of the symbol. | 2376 | /* Note that we do not mark the obarray of the symbol. |
| 2192 | It is safe not to do so because nothing accesses that | 2377 | It is safe not to do so because nothing accesses that |
| 2193 | slot except to check whether it is nil. */ | 2378 | slot except to check whether it is nil. */ |
| @@ -2403,12 +2588,104 @@ mark_kboards () | |||
| 2403 | mark_object (&kb->Vdefault_minibuffer_frame); | 2588 | mark_object (&kb->Vdefault_minibuffer_frame); |
| 2404 | } | 2589 | } |
| 2405 | } | 2590 | } |
| 2591 | |||
| 2592 | |||
| 2593 | /* Value is non-zero if OBJ will survive the current GC because it's | ||
| 2594 | either marked or does not need to be marked to survive. */ | ||
| 2595 | |||
| 2596 | int | ||
| 2597 | survives_gc_p (obj) | ||
| 2598 | Lisp_Object obj; | ||
| 2599 | { | ||
| 2600 | int survives_p; | ||
| 2601 | |||
| 2602 | switch (XGCTYPE (obj)) | ||
| 2603 | { | ||
| 2604 | case Lisp_Int: | ||
| 2605 | survives_p = 1; | ||
| 2606 | break; | ||
| 2607 | |||
| 2608 | case Lisp_Symbol: | ||
| 2609 | survives_p = XMARKBIT (XSYMBOL (obj)->plist); | ||
| 2610 | break; | ||
| 2611 | |||
| 2612 | case Lisp_Misc: | ||
| 2613 | switch (XMISCTYPE (obj)) | ||
| 2614 | { | ||
| 2615 | case Lisp_Misc_Marker: | ||
| 2616 | survives_p = XMARKBIT (obj); | ||
| 2617 | break; | ||
| 2618 | |||
| 2619 | case Lisp_Misc_Buffer_Local_Value: | ||
| 2620 | case Lisp_Misc_Some_Buffer_Local_Value: | ||
| 2621 | survives_p = XMARKBIT (XBUFFER_LOCAL_VALUE (obj)->realvalue); | ||
| 2622 | break; | ||
| 2623 | |||
| 2624 | case Lisp_Misc_Intfwd: | ||
| 2625 | case Lisp_Misc_Boolfwd: | ||
| 2626 | case Lisp_Misc_Objfwd: | ||
| 2627 | case Lisp_Misc_Buffer_Objfwd: | ||
| 2628 | case Lisp_Misc_Kboard_Objfwd: | ||
| 2629 | survives_p = 1; | ||
| 2630 | break; | ||
| 2631 | |||
| 2632 | case Lisp_Misc_Overlay: | ||
| 2633 | survives_p = XMARKBIT (XOVERLAY (obj)->plist); | ||
| 2634 | break; | ||
| 2635 | |||
| 2636 | default: | ||
| 2637 | abort (); | ||
| 2638 | } | ||
| 2639 | break; | ||
| 2640 | |||
| 2641 | case Lisp_String: | ||
| 2642 | { | ||
| 2643 | struct Lisp_String *s = XSTRING (obj); | ||
| 2644 | |||
| 2645 | if (s->size & MARKBIT) | ||
| 2646 | survives_p = s->size & ARRAY_MARK_FLAG; | ||
| 2647 | else | ||
| 2648 | survives_p = (s->size & ~DONT_COPY_FLAG) > STRING_BLOCK_SIZE; | ||
| 2649 | } | ||
| 2650 | break; | ||
| 2651 | |||
| 2652 | case Lisp_Vectorlike: | ||
| 2653 | if (GC_BUFFERP (obj)) | ||
| 2654 | survives_p = XMARKBIT (XBUFFER (obj)->name); | ||
| 2655 | else if (GC_SUBRP (obj)) | ||
| 2656 | survives_p = 1; | ||
| 2657 | else | ||
| 2658 | survives_p = XVECTOR (obj)->size & ARRAY_MARK_FLAG; | ||
| 2659 | break; | ||
| 2660 | |||
| 2661 | case Lisp_Cons: | ||
| 2662 | survives_p = XMARKBIT (XCAR (obj)); | ||
| 2663 | break; | ||
| 2664 | |||
| 2665 | #ifdef LISP_FLOAT_TYPE | ||
| 2666 | case Lisp_Float: | ||
| 2667 | survives_p = XMARKBIT (XFLOAT (obj)->type); | ||
| 2668 | break; | ||
| 2669 | #endif /* LISP_FLOAT_TYPE */ | ||
| 2670 | |||
| 2671 | default: | ||
| 2672 | abort (); | ||
| 2673 | } | ||
| 2674 | |||
| 2675 | return survives_p; | ||
| 2676 | } | ||
| 2677 | |||
| 2678 | |||
| 2406 | 2679 | ||
| 2407 | /* Sweep: find all structures not marked, and free them. */ | 2680 | /* Sweep: find all structures not marked, and free them. */ |
| 2408 | 2681 | ||
| 2409 | static void | 2682 | static void |
| 2410 | gc_sweep () | 2683 | gc_sweep () |
| 2411 | { | 2684 | { |
| 2685 | /* Remove or mark entries in weak hash tables. | ||
| 2686 | This must be done before any object is unmarked. */ | ||
| 2687 | sweep_weak_hash_tables (); | ||
| 2688 | |||
| 2412 | total_string_size = 0; | 2689 | total_string_size = 0; |
| 2413 | compact_strings (); | 2690 | compact_strings (); |
| 2414 | 2691 | ||
| @@ -2746,6 +3023,11 @@ gc_sweep () | |||
| 2746 | while (vector) | 3023 | while (vector) |
| 2747 | if (!(vector->size & ARRAY_MARK_FLAG)) | 3024 | if (!(vector->size & ARRAY_MARK_FLAG)) |
| 2748 | { | 3025 | { |
| 3026 | #if 0 | ||
| 3027 | if ((vector->size & (PSEUDOVECTOR_FLAG | PVEC_HASH_TABLE)) | ||
| 3028 | == (PSEUDOVECTOR_FLAG | PVEC_HASH_TABLE)) | ||
| 3029 | fprintf (stderr, "Freeing hash table %p\n", vector); | ||
| 3030 | #endif | ||
| 2749 | if (prev) | 3031 | if (prev) |
| 2750 | prev->next = vector->next; | 3032 | prev->next = vector->next; |
| 2751 | else | 3033 | else |
| @@ -2754,6 +3036,7 @@ gc_sweep () | |||
| 2754 | lisp_free (vector); | 3036 | lisp_free (vector); |
| 2755 | n_vectors--; | 3037 | n_vectors--; |
| 2756 | vector = next; | 3038 | vector = next; |
| 3039 | |||
| 2757 | } | 3040 | } |
| 2758 | else | 3041 | else |
| 2759 | { | 3042 | { |