diff --git a/.gitattributes b/.gitattributes
index 70afb34c903b57ddae877faaa78fbb4c9e5c0b27..c07832fce6edce995d05ee1d213ec0f78cee8e4e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -145,6 +145,7 @@ testfont binary
 /lib/modules/MIME.pmod foreign_ident
 /lib/modules/MIME.pmod/ext_to_media_type.pmod foreign_ident
 /lib/modules/MIME.pmod/module.pmod foreign_ident
+/lib/modules/Object.pmod foreign_ident
 /lib/modules/Parser.pmod/C.pmod foreign_ident
 /lib/modules/Parser.pmod/LR.pmod/GrammarParser.pmod foreign_ident
 /lib/modules/Parser.pmod/LR.pmod/lr.pike foreign_ident
diff --git a/lib/modules/Object.pmod b/lib/modules/Object.pmod
new file mode 100644
index 0000000000000000000000000000000000000000..4cf4d31799bb3592ac367c40b9d1f70815b31dee
--- /dev/null
+++ b/lib/modules/Object.pmod
@@ -0,0 +1,14 @@
+//! $Id: Object.pmod,v 1.1 2005/02/09 16:35:50 mast Exp $
+
+#pike __REAL_VERSION__
+#pragma strict_types
+
+constant DESTRUCT_EXPLICIT = __builtin.DESTRUCT_EXPLICIT;
+constant DESTRUCT_NO_REFS = __builtin.DESTRUCT_NO_REFS;
+constant DESTRUCT_GC = __builtin.DESTRUCT_GC;
+constant DESTRUCT_CLEANUP = __builtin.DESTRUCT_CLEANUP;
+//! Flags passed to @[lfun::destroy].
+//!
+//! @note
+//! @[Object.DESTRUCT_EXPLICIT] is @expr{0@} and
+//! @[Object.DESTRUCT_CLEANUP] is @expr{1@} for compatibility.
diff --git a/src/builtin_functions.c b/src/builtin_functions.c
index 3dee9142037f529d3bd0081fb53becb8d48b7acc..ccae321d19b090e553e4f5399855d1a59a4cb9fb 100644
--- a/src/builtin_functions.c
+++ b/src/builtin_functions.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: builtin_functions.c,v 1.586 2004/12/23 13:44:44 grubba Exp $
+|| $Id: builtin_functions.c,v 1.587 2005/02/09 16:35:50 mast Exp $
 */
 
 #include "global.h"
@@ -2377,7 +2377,7 @@ PMOD_EXPORT void f_destruct(INT32 args)
     Pike_error("Destruct permission denied.\n");
 #endif
   debug_malloc_touch(o);
-  destruct(o);
+  destruct_object (o, DESTRUCT_EXPLICIT);
   pop_n_elems(args);
   destruct_objects_to_destruct();
 }
@@ -8697,4 +8697,9 @@ void init_builtin_efuns(void)
   ADD_INT_CONSTANT("__FLOAT_PRECISION_FLOAT__",1,0);
 #endif
 #endif
+
+  ADD_INT_CONSTANT ("DESTRUCT_EXPLICIT", DESTRUCT_EXPLICIT, 0);
+  ADD_INT_CONSTANT ("DESTRUCT_NO_REFS", DESTRUCT_NO_REFS, 0);
+  ADD_INT_CONSTANT ("DESTRUCT_GC", DESTRUCT_GC, 0);
+  ADD_INT_CONSTANT ("DESTRUCT_CLEANUP", DESTRUCT_CLEANUP, 0);
 }
diff --git a/src/gc.c b/src/gc.c
index ac9052458c18d08f74d6981e4fe0722205e72960..52bdf6a0ab1dcb0a0909c022b567eb61b84bb5c4 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: gc.c,v 1.261 2004/09/30 12:12:10 mast Exp $
+|| $Id: gc.c,v 1.262 2005/02/09 16:43:35 mast Exp $
 */
 
 #include "global.h"
@@ -3041,39 +3041,46 @@ size_t do_gc(void *ignored, int explicit_call)
 #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP)
   destroy_count = 0;
 #endif
-  while (kill_list) {
-    struct gc_frame *next = NEXT(kill_list);
-    struct object *o = (struct object *) kill_list->data;
-#ifdef PIKE_DEBUG
-    if (!(kill_list->frameflags & GC_ON_KILL_LIST))
-      gc_fatal(o, 0, "Kill list element hasn't got the proper flag.\n");
-    if ((get_marker(kill_list->data)->flags & (GC_LIVE|GC_LIVE_OBJ)) !=
-	(GC_LIVE|GC_LIVE_OBJ))
-      gc_fatal(o, 0, "Invalid object on kill list.\n");
-    if (o->prog && (o->prog->flags & PROGRAM_USES_PARENT) &&
-	PARENT_INFO(o)->parent &&
-	!PARENT_INFO(o)->parent->prog &&
-	get_marker(PARENT_INFO(o)->parent)->flags & GC_LIVE_OBJ)
-      gc_fatal(o, 0, "GC destructed parent prematurely.\n");
-#endif
-    GC_VERBOSE_DO(
-      fprintf(stderr, "|   Killing %p with %d refs", o, o->refs);
-      if (o->prog) {
-	INT32 line;
-	struct pike_string *file = get_program_line (o->prog, &line);
-	fprintf(stderr, ", prog %s:%d\n", file->str, line);
-	free_string(file);
-      }
-      else fputs(", is destructed\n", stderr);
-    );
-    destruct(o);
-    free_object(o);
-    gc_free_extra_ref(o);
+  {
+    enum object_destruct_reason reason =
+#ifdef DO_PIKE_CLEANUP
+      gc_destruct_everything ? DESTRUCT_CLEANUP :
+#endif
+      DESTRUCT_GC;
+    while (kill_list) {
+      struct gc_frame *next = NEXT(kill_list);
+      struct object *o = (struct object *) kill_list->data;
+#ifdef PIKE_DEBUG
+      if (!(kill_list->frameflags & GC_ON_KILL_LIST))
+	gc_fatal(o, 0, "Kill list element hasn't got the proper flag.\n");
+      if ((get_marker(kill_list->data)->flags & (GC_LIVE|GC_LIVE_OBJ)) !=
+	  (GC_LIVE|GC_LIVE_OBJ))
+	gc_fatal(o, 0, "Invalid object on kill list.\n");
+      if (o->prog && (o->prog->flags & PROGRAM_USES_PARENT) &&
+	  PARENT_INFO(o)->parent &&
+	  !PARENT_INFO(o)->parent->prog &&
+	  get_marker(PARENT_INFO(o)->parent)->flags & GC_LIVE_OBJ)
+	gc_fatal(o, 0, "GC destructed parent prematurely.\n");
+#endif
+      GC_VERBOSE_DO(
+	fprintf(stderr, "|   Killing %p with %d refs", o, o->refs);
+	if (o->prog) {
+	  INT32 line;
+	  struct pike_string *file = get_program_line (o->prog, &line);
+	  fprintf(stderr, ", prog %s:%d\n", file->str, line);
+	  free_string(file);
+	}
+	else fputs(", is destructed\n", stderr);
+      );
+      destruct_object (o, reason);
+      free_object(o);
+      gc_free_extra_ref(o);
 #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP)
-    destroy_count++;
+      destroy_count++;
 #endif
-    debug_really_free_gc_frame(kill_list);
-    kill_list = next;
+      debug_really_free_gc_frame(kill_list);
+      kill_list = next;
+    }
   }
 
   GC_VERBOSE_DO(fprintf(stderr, "| kill: %u objects killed, %d things really freed\n",
diff --git a/src/object.c b/src/object.c
index 9e37da28734b3d187424084a5a7aaea891aa8e0c..14377221a46eec5ef9a53c860f39a1ff51208984 100644
--- a/src/object.c
+++ b/src/object.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: object.c,v 1.264 2005/01/20 14:29:25 nilsson Exp $
+|| $Id: object.c,v 1.265 2005/02/09 16:35:50 mast Exp $
 */
 
 #include "global.h"
@@ -669,7 +669,7 @@ PMOD_EXPORT struct program *get_program_for_object_being_destructed(struct objec
   return 0;
 }
 
-static void call_destroy(struct object *o, int foo)
+static void call_destroy(struct object *o, enum object_destruct_reason reason)
 {
   volatile int e;
 
@@ -731,8 +731,8 @@ static void call_destroy(struct object *o, int foo)
       }
 
       else {
-	if(foo) push_int(1);
-	apply_low(o, e, foo?1:0);
+	push_int (reason);
+	apply_low(o, e, 1);
 	pop_stack();
 	UNSETJMP (jmp);
       }
@@ -753,7 +753,7 @@ static void call_destroy(struct object *o, int foo)
 }
 
 
-void destruct(struct object *o)
+PMOD_EXPORT void destruct_object (struct object *o, enum object_destruct_reason reason)
 {
   int e;
   struct program *p;
@@ -782,7 +782,7 @@ void destruct(struct object *o)
   }
 #endif
   add_ref( o );
-  call_destroy(o,0);
+  call_destroy(o, reason);
 
   /* destructed in destroy() */
   if(!(p=o->prog))
@@ -936,7 +936,7 @@ void low_destruct_objects_to_destruct(void)
 
       /* call destroy, keep one ref */
       add_ref(o);
-      destruct(o);
+      destruct_object (o, DESTRUCT_NO_REFS);
       free_object(o);
     } while ((o = next));
   }
@@ -981,7 +981,7 @@ PMOD_EXPORT void schedule_really_free_object(struct object *o)
   if(o->prog && (o->prog->flags & PROGRAM_DESTRUCT_IMMEDIATE))
   {
     add_ref(o);
-    destruct(o);
+    destruct_object (o, DESTRUCT_NO_REFS);
     if(sub_ref(o)) return;
   }
 
@@ -1981,6 +1981,11 @@ size_t gc_free_all_unreferenced_objects(void)
 {
   struct object *o,*next;
   size_t unreferenced = 0;
+  enum object_destruct_reason reason =
+#ifdef DO_PIKE_CLEANUP
+    gc_destruct_everything ? DESTRUCT_CLEANUP :
+#endif
+    DESTRUCT_GC;
 
   for(o=gc_internal_object; o; o=next)
   {
@@ -1993,7 +1998,7 @@ size_t gc_free_all_unreferenced_objects(void)
 	gc_fatal(o,0,"Can't free a live object in gc_free_all_unreferenced_objects().\n");
 #endif
       debug_malloc_touch(o);
-      destruct(o);
+      destruct_object (o, reason);
 
       gc_free_extra_ref(o);
       SET_NEXT_AND_FREE(o,free_object);
@@ -2418,7 +2423,7 @@ void exit_object(void)
   master_is_cleaned_up = 1;
   if (master_object) {
     call_destroy (master_object, 1);
-    destruct (master_object);
+    destruct_object (master_object, DESTRUCT_CLEANUP);
     free_object(master_object);
     master_object=0;
   }
diff --git a/src/object.h b/src/object.h
index a922f860b7375d4dee61357b624e04204626a5e2..88ea536b00bceb447973086cc7a2577d14dd753e 100644
--- a/src/object.h
+++ b/src/object.h
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: object.h,v 1.89 2004/12/03 14:41:14 grubba Exp $
+|| $Id: object.h,v 1.90 2005/02/09 16:35:50 mast Exp $
 */
 
 #ifndef OBJECT_H
@@ -66,6 +66,13 @@ void gc_check_zapped (void *a, TYPE_T type, const char *file, int line);
 
 #define this_object() (add_ref(Pike_fp->current_object), Pike_fp->current_object)
 
+enum object_destruct_reason {
+  DESTRUCT_EXPLICIT = 0,
+  DESTRUCT_CLEANUP = 1,
+  DESTRUCT_NO_REFS,
+  DESTRUCT_GC
+};
+
 #include "block_alloc_h.h"
 /* Prototypes begin here */
 BLOCK_ALLOC_FILL_PAGES(object, 2)
@@ -87,7 +94,8 @@ PMOD_EXPORT struct object *debug_master(void);
 struct destroy_called_mark;
 PTR_HASH_ALLOC(destroy_called_mark,128)
 PMOD_EXPORT struct program *get_program_for_object_being_destructed(struct object * o);
-void destruct(struct object *o);
+PMOD_EXPORT void destruct_object (struct object *o, enum object_destruct_reason reason);
+#define destruct(o) destruct_object (o, DESTRUCT_EXPLICIT)
 void low_destruct_objects_to_destruct(void);
 void destruct_objects_to_destruct_cb(void);
 PMOD_EXPORT void schedule_really_free_object(struct object *o);
diff --git a/src/program.c b/src/program.c
index 9dbe322aafb9a6d39c77b71e9a0358a87587d2c0..47eb038e3eaa4084fe9818aa0af9f4f9080455aa 100644
--- a/src/program.c
+++ b/src/program.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: program.c,v 1.585 2005/01/20 00:27:17 nilsson Exp $
+|| $Id: program.c,v 1.586 2005/02/09 16:35:50 mast Exp $
 */
 
 #include "global.h"
@@ -163,7 +163,7 @@ static struct mapping *lfun_types;
 static const char *const raw_lfun_types[] = {
   tFuncV(tNone,tVoid,tVoid),	/* "__INIT", */
   tFuncV(tNone,tZero,tVoid),	/* "create", */
-  tFuncV(tNone,tVoid,tVoid),	/* "destroy", */
+  tFuncV(tOr(tVoid,tInt),tVoid,tVoid), /* "destroy", */
   tFuncV(tZero,tZero,tMix),	/* "`+", */
   tFunc(tOr(tVoid,tZero),tMix),	/* "`-", */
   tFuncV(tNone,tZero,tMix),	/* "`&", */
@@ -329,17 +329,33 @@ static const char *const raw_lfun_types[] = {
  *!   @[lfun::__INIT()], @[lfun::destroy()]
  */
 
-/*! @decl void lfun::destroy()
+/*! @decl void lfun::destroy (void|int reason)
  *!
  *!   Object destruction callback.
  *!   
- *!   This function is called by @[predef::destruct()] right before it
- *!   zeroes all the object variables and destroys the object.
+ *!   This function is called right before the object is destructed.
+ *!   That can happen either through a call to @[predef::destruct()],
+ *!   when there are no more references to the object, or when the
+ *!   garbage collector discovers that it's part of a cyclic data
+ *!   structure that has become garbage.
  *!
- *! @note
- *!   Note that it's also called on implicit destruct, i.e. when there
- *!   are no more references to the object, or when the garbage
- *!   collector decides to destruct it.
+ *! @param reason
+ *!   A flag that tells why the object is destructed:
+ *!
+ *!   @int
+ *!     @value Object.DESTRUCT_EXPLICIT
+ *!       Destructed explicitly by @[predef::destruct].
+ *!     @value Object.DESTRUCT_NO_REFS
+ *!       Destructed due to running out of references.
+ *!     @value Object.DESTRUCT_GC
+ *!       Destructed by the garbage collector.
+ *!     @value Object.DESTRUCT_CLEANUP
+ *!       Destructed as part of the cleanup when the pike program
+ *!       exits. Note that Pike normally doesn't do any cleanup before
+ *!       exit - it must be compiled with the configure option
+ *!       @tt{--with-cleanup-on-exit@} to enable that. So this value
+ *!       normally doesn't occur.
+ *!   @endint
  *!
  *! @note
  *! Regarding destruction order during garbage collection:
@@ -409,8 +425,8 @@ static const char *const raw_lfun_types[] = {
  *! still intact. They are not freed if the @expr{destroy@} function
  *! adds external references to them. However, all objects with
  *! @[lfun::destroy] in the cycle are already scheduled for
- *! destruction and are thus be destroyed even if external references
- *! are added to them.
+ *! destruction and will therefore be destroyed even if external
+ *! references are added to them.
  *!
  *! @note
  *! The garbage collector had completely random destruct order in
@@ -469,7 +485,7 @@ static const char *const raw_lfun_types[] = {
  *!   Right side addition/concatenation callback.
  *!
  *!   This is used by @[predef::`+]. It's called with any arguments
- *!   that precedes this object in the argument list of the call to
+ *!   that precede this object in the argument list of the call to
  *!   @[predef::`+]. The returned value should be a new instance that
  *!   represents the addition/concatenation between the arguments in
  *!   the order they are given and this object.