Skip to content
Snippets Groups Projects
Select Git revision
  • afbf0ae3eba7b5aa1edb768eef0ff1b214e02a85
  • master default protected
  • 9.0
  • 8.0
  • 7.8
  • 7.6
  • 7.4
  • 7.2
  • 7.0
  • 0.6
  • rosuav/latex-markdown-renderer
  • rxnpatch/rxnpatch
  • marcus/gobject-introspection
  • rxnpatch/8.0
  • rosuav/pre-listening-ports
  • nt-tools
  • rosuav/async-annotations
  • rosuav/pgsql-ssl
  • rxnpatch/rxnpatch-broken/2023-10-06T094250
  • grubba/fdlib
  • grubba/wip/sakura/8.0
  • v8.0.2000
  • v8.0.1998
  • v8.0.1996
  • v8.0.1994
  • v8.0.1992
  • v8.0.1990
  • v8.0.1988
  • v8.0.1986
  • rxnpatch/clusters/8.0/2025-04-29T124414
  • rxnpatch/2025-04-29T124414
  • v8.0.1984
  • v8.0.1982
  • v8.0.1980
  • v8.0.1978
  • v8.0.1976
  • v8.0.1974
  • v8.0.1972
  • v8.0.1970
  • v8.0.1968
  • v8.0.1966
41 results

builtin_functions.c

Blame
  • object.c 70.28 KiB
    /*
    || 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.301 2008/07/24 17:51:23 mast Exp $
    */
    
    #include "global.h"
    #include "object.h"
    #include "dynamic_buffer.h"
    #include "interpret.h"
    #include "program.h"
    #include "stralloc.h"
    #include "svalue.h"
    #include "pike_macros.h"
    #include "pike_memory.h"
    #include "pike_error.h"
    #include "main.h"
    #include "array.h"
    #include "gc.h"
    #include "backend.h"
    #include "callback.h"
    #include "cpp.h"
    #include "builtin_functions.h"
    #include "cyclic.h"
    #include "pike_security.h"
    #include "module_support.h"
    #include "fdlib.h"
    #include "mapping.h"
    #include "multiset.h"
    #include "constants.h"
    #include "encode.h"
    #include "pike_types.h"
    #include "operators.h"
    
    #include "block_alloc.h"
    
    #ifdef HAVE_SYS_TYPES_H
    #include <sys/types.h>
    #endif /* HAVE_SYS_TYPES_H */
    
    #ifdef HAVE_SYS_FILE_H
    #include <sys/file.h>
    #endif /* HAVE_SYS_FILE_H */
    
    #include <sys/stat.h>
    
    #include "dmalloc.h"
    
    #define sp Pike_sp
    
    /* #define GC_VERBOSE */
    
    
    #ifndef SEEK_SET
    #ifdef L_SET
    #define SEEK_SET	L_SET
    #else /* !L_SET */
    #define SEEK_SET	0
    #endif /* L_SET */
    #endif /* SEEK_SET */
    #ifndef SEEK_CUR
    #ifdef L_INCR
    #define SEEK_SET	L_INCR
    #else /* !L_INCR */
    #define SEEK_CUR	1
    #endif /* L_INCR */
    #endif /* SEEK_CUR */
    #ifndef SEEK_END
    #ifdef L_XTND
    #define SEEK_END	L_XTND
    #else /* !L_XTND */
    #define SEEK_END	2
    #endif /* L_XTND */
    #endif /* SEEK_END */
    
    
    struct object *master_object = 0;
    struct program *master_program =0;
    static int master_is_cleaned_up = 0;
    PMOD_EXPORT struct object *first_object;
    
    struct object *gc_internal_object = 0;
    static struct object *gc_mark_object_pos = 0;
    
    #undef COUNT_OTHER
    
    #define COUNT_OTHER() do{			\
      struct object *o;                             \
      for(o=first_object;o;o=o->next)		\
        if(o->prog)					\
          size+=o->prog->storage_needed;		\
    						\
      for(o=objects_to_destruct;o;o=o->next)	\
        if(o->prog)					\
          size+=o->prog->storage_needed;		\
    }while(0)
    BLOCK_ALLOC_FILL_PAGES(object, 2)
    
    #undef COUNT_OTHER
    #define COUNT_OTHER()
    
    PMOD_EXPORT struct object *low_clone(struct program *p)
    {
      struct object *o;
    
      if(!(p->flags & PROGRAM_PASS_1_DONE))
        Pike_error("Attempting to clone an unfinished program\n");
    
    #ifdef PROFILING
      p->num_clones++;
    #endif /* PROFILING */
    
      o=alloc_object();
    
      o->storage=p->storage_needed ? (char *)xalloc(p->storage_needed) : (char *)NULL;
    
      GC_ALLOC(o);
    
    #ifdef DO_PIKE_CLEANUP
      if (exit_cleanup_in_progress) {
        INT32 line;
        struct pike_string *file = get_program_line (p, &line);
        fprintf (stderr,
    	     "Warning: Object %p created during exit cleanup from %s:%d\n",
    	     o, file->str, line);
      }
    #endif
    
    #ifdef DEBUG_MALLOC
      if(!debug_malloc_copy_names(o, p)) 
      {
        struct pike_string *tmp;
        INT32 line;
    
        tmp=get_program_line(p, &line);
        debug_malloc_name(o, tmp->str, line);
        free_string(tmp);
      }
      dmalloc_set_mmap_from_template(o,p);
    #endif
    
      add_ref( o->prog=p );
    
      if(p->flags & PROGRAM_USES_PARENT)
      {
        LOW_PARENT_INFO(o,p)->parent=0;
        LOW_PARENT_INFO(o,p)->parent_identifier=0;
      }
    
      DOUBLELINK(first_object,o);
    
      INIT_PIKE_MEMOBJ(o);
    
    #ifdef PIKE_DEBUG
      o->program_id=p->id;
    #endif
    
      return o;
    }
    
    #define LOW_PUSH_FRAME2(O, P)			\
      pike_frame=alloc_pike_frame();		\
      pike_frame->next=Pike_fp;			\
      pike_frame->current_object=O;			\
      pike_frame->current_program=P;		\
      pike_frame->locals=0;				\
      pike_frame->num_locals=0;			\
      pike_frame->fun=-1;				\
      pike_frame->pc=0;				\
      pike_frame->context=NULL;                     \
      Pike_fp = pike_frame
    
    #define LOW_PUSH_FRAME(O, P)	do{		\
        struct pike_frame *pike_frame;		\
        LOW_PUSH_FRAME2(O, P)
    
    
    #define PUSH_FRAME(O, P)			\
      LOW_PUSH_FRAME(O, P);				\
      add_ref(pike_frame->current_object);		\
      add_ref(pike_frame->current_program)
    
    #define PUSH_FRAME2(O, P) do{			\
        LOW_PUSH_FRAME2(O, P);			\
        add_ref(pike_frame->current_object);	\
        add_ref(pike_frame->current_program);	\
      }while(0)
    
    /* Note: there could be a problem with programs without functions */
    #define LOW_SET_FRAME_CONTEXT(X)					     \
      pike_frame->context=(X);						     \
      pike_frame->fun=pike_frame->context->identifier_level;		\
      pike_frame->current_storage=o->storage+pike_frame->context->storage_offset
    
    #define SET_FRAME_CONTEXT(X)						\
      LOW_SET_FRAME_CONTEXT(X)
      
    #define LOW_UNSET_FRAME_CONTEXT()		\
      pike_frame->context = NULL;			\
      pike_frame->current_storage = NULL
      
    
    #ifdef DEBUG
    #define CHECK_FRAME() do { \
        if(pike_frame != Pike_fp) \
          Pike_fatal("Frame stack out of whack.\n"); \
      } while(0)
    #else
    #define CHECK_FRAME()
    #endif
    
    #define POP_FRAME()				\
      CHECK_FRAME()					\
      Pike_fp=pike_frame->next;			\
      pike_frame->next=0;				\
      free_pike_frame(pike_frame); }while(0)
    
    #define POP_FRAME2()				\
      do{CHECK_FRAME()				\
      Pike_fp=pike_frame->next;			\
      pike_frame->next=0;				\
      free_pike_frame(pike_frame);}while(0)
    
    #define LOW_POP_FRAME()				\
      add_ref(Pike_fp->current_object); \
      add_ref(Pike_fp->current_program); \
      POP_FRAME();
    
    
    
    PMOD_EXPORT void call_c_initializers(struct object *o)
    {
      int e;
      struct program *p=o->prog;
      struct pike_frame *pike_frame=0;
      int frame_pushed = 0;
      
      /* clear globals and call C initializers */
      for(e=p->num_inherits-1; e>=0; e--)
      {
        struct program *prog = p->inherits[e].prog;
        int n=(int)prog->num_variable_index;
        if( n )
        {
          char *storage = o->storage+p->inherits[e].storage_offset;
          int q;
          for(q=0;q<n;q++)
          {
    	int d=prog->variable_index[q];
    
    	if (IDENTIFIER_IS_ALIAS(prog->identifiers[d].identifier_flags))
    	  continue;
    
    	if(prog->identifiers[d].run_time_type == T_MIXED)
    	{
    	  struct svalue *s;
    	  s=(struct svalue *)(storage + prog->identifiers[d].func.offset);
    	  s->type=T_INT;
    	  s->u.integer=0;
    	  s->subtype=0;
    	} else {
    	  union anything *u;
    	  u=(union anything *)(storage + prog->identifiers[d].func.offset);
    	  switch(prog->identifiers[d].run_time_type)
    	  {
    	    case PIKE_T_FREE:
    	    case PIKE_T_GET_SET:
    	      break;
    	    case T_INT: u->integer=0; break;
    	    case T_FLOAT: u->float_number=0.0; break;
    	    default: u->refs=0; break;
    	  }
    	}
    	(void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" clear_global"));
          }
        }
        if(prog->event_handler)
        {
          if( !frame_pushed )
          {
    	PUSH_FRAME2(o, p);
    	frame_pushed = 1;
          }
          SET_FRAME_CONTEXT(p->inherits + e);
          prog->event_handler(PROG_EVENT_INIT);
        }
      }
      if( frame_pushed )
        POP_FRAME2();
    }
    
    
    PMOD_EXPORT void call_prog_event(struct object *o, int event)
    {
      int e;
      struct program *p=o->prog;
      struct pike_frame *pike_frame=0;
      int frame_pushed = 0;
    
      /* call event handlers */
      for(e=p->num_inherits-1; e>=0; e--)
      {
        struct program *prog = p->inherits[e].prog;
        if(prog->event_handler)
        {
          if( !frame_pushed )
          {
    	PUSH_FRAME2(o, p);
    	frame_pushed = 1;
          }
          SET_FRAME_CONTEXT(p->inherits + e);
          prog->event_handler(event);
        }
      }
      if( frame_pushed )
        POP_FRAME2();
    }
    
    
    void call_pike_initializers(struct object *o, int args)
    {
      ptrdiff_t fun;
      struct program *p=o->prog;
      if(!p) return;
      STACK_LEVEL_START(args);
      fun=FIND_LFUN(p, LFUN___INIT);
      if(fun!=-1)
      {
        apply_low(o,fun,0);
        pop_stack();
      }
      STACK_LEVEL_CHECK(args);
      fun=FIND_LFUN(p, LFUN_CREATE);
      if(fun!=-1)
      {
        apply_low(o,fun,args);
        pop_stack();
      } else {
        pop_n_elems(args);
      }
      STACK_LEVEL_DONE(0);
    }
    
    PMOD_EXPORT void do_free_object(struct object *o)
    {
      if (o)
        free_object(o);
    }
    
    PMOD_EXPORT struct object *debug_clone_object(struct program *p, int args)
    {
      ONERROR tmp;
      struct object *o;
      if(p->flags & PROGRAM_NEEDS_PARENT)
        Pike_error("Parent lost, cannot clone program.\n");
    
      if(!(p->flags & PROGRAM_PASS_1_DONE))
        Pike_error("Attempting to clone an unfinished program\n");
    
      o=low_clone(p);
      if (!args) {
        push_object(o);
      } else {
        SET_ONERROR(tmp, do_free_object, o);
        debug_malloc_touch(o);
      }
      call_c_initializers(o);
      debug_malloc_touch(o);
      call_pike_initializers(o,args);
      debug_malloc_touch(o);
      debug_malloc_touch(o->storage);
      if (!args) {
        /* Pop o from the stack. */
        Pike_sp--;
      } else {
        UNSET_ONERROR(tmp);
      }
      return o;
    }
    
    /* Clone and initialize an object, but do not call the pike initializers.
     *
     * WARNING: Only use this function if you know what you are doing...
     */
    PMOD_EXPORT struct object *fast_clone_object(struct program *p)
    {
      struct object *o=low_clone(p);
      push_object(o);
      call_c_initializers(o);
      Pike_sp--;
      debug_malloc_touch(o);
      debug_malloc_touch(o->storage);
      return o;
    }
    
    PMOD_EXPORT struct object *parent_clone_object(struct program *p,
    					       struct object *parent,
    					       ptrdiff_t parent_identifier,
    					       int args)
    {
      ONERROR tmp;
      struct object *o=low_clone(p);
      if (!args) {
        push_object(o);
      } else {
        SET_ONERROR(tmp, do_free_object, o);
        debug_malloc_touch(o);
      }
    
      if(!(p->flags & PROGRAM_PASS_1_DONE))
        Pike_error("Attempting to clone an unfinished program\n");
    
      if(p->flags & PROGRAM_USES_PARENT)
      {
        add_ref( PARENT_INFO(o)->parent=parent );
        PARENT_INFO(o)->parent_identifier = DO_NOT_WARN((INT32)parent_identifier);
      }
      call_c_initializers(o);
      call_pike_initializers(o,args);
      if (!args) {
        Pike_sp--;
      } else {
        UNSET_ONERROR(tmp);
      }
      return o;
    }
    
    PMOD_EXPORT struct object *clone_object_from_object(struct object *o, int args)
    {
      if (o->prog->flags & PROGRAM_USES_PARENT)
        return parent_clone_object(o->prog,
    			       PARENT_INFO(o)->parent,
    			       PARENT_INFO(o)->parent_identifier,
    			       args);
      else
        return clone_object(o->prog, args);
    }
    
    /* BEWARE: This function does not call create() or __INIT() */
    struct object *decode_value_clone_object(struct svalue *prog)
    {
      struct object *o, *parent;
      ONERROR tmp;
      INT32 parent_identifier;
      struct program *p=program_from_svalue(prog);
      if(!p) return NULL;
    
      o=low_clone(p);
      SET_ONERROR(tmp, do_free_object, o);
      debug_malloc_touch(o);
      
      if(prog->type == T_FUNCTION)
      {
        parent=prog->u.object;
        parent_identifier=prog->subtype;
      }else{
        parent=0;
        parent_identifier=0;
      }
    
      if(p->flags & PROGRAM_USES_PARENT)
      {
        
        PARENT_INFO(o)->parent=parent;
        if(parent) add_ref(parent);
        PARENT_INFO(o)->parent_identifier = DO_NOT_WARN((INT32)parent_identifier);
      }
      call_c_initializers(o);
      UNSET_ONERROR(tmp);
      return o;
    }
    
    static struct pike_string *low_read_file(const char *file)
    {
      struct pike_string *s;
      PIKE_OFF_T len;
      FD f;
    
      while((f = fd_open(file,fd_RDONLY,0666)) <0 && errno==EINTR)
        check_threads_etc();
      if(f >= 0)
      {
        ptrdiff_t tmp, pos = 0;
    
        len = fd_lseek(f, 0, SEEK_END);
        fd_lseek(f, 0, SEEK_SET);
    
        if (len > MAX_INT32)
          Pike_fatal ("low_read_file(%s): File too large: %"PRINTPIKEOFFT"d b.\n",
    		  file, len);
    
        s = begin_shared_string (DO_NOT_WARN ((ptrdiff_t) len));
    
        while(pos<len)
        {
          tmp = fd_read(f,s->str+pos, DO_NOT_WARN ((ptrdiff_t) len) - pos);
          if(tmp<=0)
          {
    	if (tmp < 0) {
    	  if(errno==EINTR) {
    	    check_threads_etc();
    	    continue;
    	  }
    	  Pike_fatal("low_read_file(%s) failed, errno=%d\n",file,errno);
    	}
    	Pike_fatal("low_read_file(%s) failed, short read: "
    		   "%"PRINTPIKEOFFT"d < %"PRINTPIKEOFFT"d\n",
    		   file, pos, len);
          }
          pos+=tmp;
        }
        fd_close(f);
        return end_shared_string(s);
      }
      return 0;
    }
    
    static void get_master_cleanup (void *dummy)
    {
      if (master_object) {
        free_object (master_object);
        master_object = 0;
      }
    }
    
    PMOD_EXPORT struct object *get_master(void)
    {
      static int inside=0;
    
      if(master_object && master_object->prog)
        return master_object;
    
      if(inside || master_is_cleaned_up) return 0;
    
      if(master_object)
      {
        free_object(master_object);
        master_object=0;
      }
    
      inside = 1;
    
      /* fprintf(stderr, "Need a new master object...\n"); */
    
      if(!master_program)
      {
        struct pike_string *s;
        char *tmp;
        PIKE_STAT_T stat_buf;
    
        if(!get_builtin_constants() ||
           !simple_mapping_string_lookup(get_builtin_constants(),
    				     "_static_modules"))
        {
          inside = 0;
          /* fprintf(stderr, "Builtin_constants: %p\n", get_builtin_constants()); */
          /* fprintf(stderr,"Cannot load master object yet!\n"); */
          return 0;
        }
    
        /* fprintf(stderr, "Master file: \"%s\"\n", master_file); */
    
        tmp=xalloc(strlen(master_file)+3);
    
        MEMCPY(tmp, master_file, strlen(master_file)+1);
        strcat(tmp,".o");
    
        s = NULL;
        if (!fd_stat(tmp, &stat_buf)) {
          time_t ts1 = stat_buf.st_mtime;
          time_t ts2 = 0;
    
          if (!fd_stat(master_file, &stat_buf)) {
    	ts2 = stat_buf.st_mtime;
          }
    
          if (ts1 >= ts2) {
    	s = low_read_file(tmp);
          }
        }
        free(tmp);
        if(s)
        {
          JMP_BUF tmp;
    
          /* fprintf(stderr, "Trying precompiled file \"%s.o\"...\n",
           *         master_file);
           */
    
          /* Moved here to avoid gcc warning: "might be clobbered". */
          push_string(s);
          push_int(0);
    
          if(SETJMP_SP(tmp, 2))
          {
    #ifdef DEBUG
    	if(d_flag)
    	  debug_describe_svalue(&throw_value);
    #endif
    	/* do nothing */
    	UNSETJMP(tmp);
    	free_svalue(&throw_value);
    	mark_free_svalue (&throw_value);
          }else{
    	f_decode_value(2);
    	UNSETJMP(tmp);
    
    	if(sp[-1].type == T_PROGRAM)
    	  goto compiled;
    
    	pop_stack();
    
          }
    #ifdef DEBUG
          if(d_flag)
    	fprintf(stderr,"Failed to import dumped master!\n");
    #endif
    
        }
    
        /* fprintf(stderr, "Reading master: \"%s\"...\n", master_file); */
    
        s=low_read_file(master_file);
        if(s)
        {
          push_string(s);
          push_text(master_file);
    
          /* fprintf(stderr, "Calling cpp()...\n"); */
    
          f_cpp(2);
    
          /* fprintf(stderr, "Calling compile()...\n"); */
    
          f_compile(1);
    
        compiled:
          if(sp[-1].type != T_PROGRAM)
          {
    	pop_stack();
    	return 0;
          }
          master_program=sp[-1].u.program;
          sp--;
          dmalloc_touch_svalue(sp);
        }else{
          throw_error_object(fast_clone_object(master_load_error_program), 0, 0, 0,
    			 "Couldn't load master program from %s.\n", master_file);
        }
      }
    
      {
        ONERROR uwp;
    
        /* fprintf(stderr, "Cloning master...\n"); */
    
        master_object=low_clone(master_program);
        debug_malloc_touch(master_object);
        debug_malloc_touch(master_object->storage);
    
        /* Make sure master_object doesn't point to a broken master. */
        SET_ONERROR (uwp, get_master_cleanup, NULL);
    
        /* fprintf(stderr, "Initializing master...\n"); */
    
        call_c_initializers(master_object);
        call_pike_initializers(master_object,0);
      
        /* fprintf(stderr, "Master loaded.\n"); */
    
        UNSET_ONERROR (uwp);
      }
    
      inside = 0;
      return master_object;
    }
    
    PMOD_EXPORT struct object *debug_master(void)
    {
      struct object *o;
      o=get_master();
      if(!o) Pike_fatal("Couldn't load master object.\n");
      return o;
    }
    
    struct destroy_called_mark
    {
      struct destroy_called_mark *next;
      void *data;
      struct program *p; /* for magic */
    };
    
    PTR_HASH_ALLOC(destroy_called_mark,128)
    
    PMOD_EXPORT struct program *get_program_for_object_being_destructed(struct object * o)
    {
      struct destroy_called_mark * tmp;
      if(( tmp = find_destroy_called_mark(o)))
        return tmp->p;
      return 0;
    }
    
    static void call_destroy(struct object *o, enum object_destruct_reason reason)
    {
      volatile int e;
    
      debug_malloc_touch(o);
      if(!o || !o->prog) {
    #ifdef GC_VERBOSE
        if (Pike_in_gc > GC_PASS_PREPARE)
          fprintf(stderr, "|   Not calling destroy() in "
    	      "destructed %p with %d refs.\n", o, o->refs);
    #endif
        return; /* Object already destructed */
      }
    
      e=FIND_LFUN(o->prog,LFUN_DESTROY);
      if(e != -1
    #ifdef DO_PIKE_CLEANUP
         && Pike_interpreter.evaluator_stack
    #endif
        && (o->prog->flags & PROGRAM_FINISHED))
      {
    #ifdef PIKE_DEBUG
        if(Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_FREE)
          Pike_fatal("Calling destroy() inside gc.\n");
    #endif
        if(check_destroy_called_mark_semaphore(o))
        {
          JMP_BUF jmp;
    
    #ifdef GC_VERBOSE
          if (Pike_in_gc > GC_PASS_PREPARE)
    	fprintf(stderr, "|   Calling destroy() in %p with %d refs.\n",
    		o, o->refs);
    #endif
    
          free_svalue (&throw_value);
          mark_free_svalue (&throw_value);
    
          if (SETJMP (jmp)) {
    	UNSETJMP (jmp);
    	if (gc_destruct_everything) {
    	  struct svalue err;
    	  move_svalue (&err, &throw_value);
    	  mark_free_svalue (&throw_value);
    	  if (!SETJMP (jmp)) {
    	    push_svalue (&err);
    	    push_int (0);
    	    push_text ("Got error during final cleanup:\n");
    	    push_svalue (&err);
    	    push_int (0);
    	    o_index();
    	    f_add (2);
    	    f_index_assign (3);
    	    pop_stack();
    	  }
    	  UNSETJMP (jmp);
    	  move_svalue (&throw_value, &err);
    	}
    	call_handle_error();
          }
    
          else {
    	push_int (reason);
    	apply_low(o, e, 1);
    	pop_stack();
    	UNSETJMP (jmp);
          }
    
    #ifdef GC_VERBOSE
          if (Pike_in_gc > GC_PASS_PREPARE)
    	fprintf(stderr, "|   Called destroy() in %p with %d refs.\n",
    		o, o->refs);
    #endif
        }
      }
    #ifdef GC_VERBOSE
      else
        if (Pike_in_gc > GC_PASS_PREPARE)
          fprintf(stderr, "|   No destroy() to call in %p with %d refs.\n",
    	      o, o->refs);
    #endif
    }
    
    
    PMOD_EXPORT void destruct_object (struct object *o, enum object_destruct_reason reason)
    {
      int e;
      struct program *p;
      struct pike_frame *pike_frame=0;
      int frame_pushed = 0;
    
    #ifdef PIKE_DEBUG
      ONERROR uwp;
    
      fatal_check_c_stack(8192);
    
      SET_ONERROR(uwp, fatal_on_error,
    	      "Shouldn't get an exception in destruct().\n");
      if(d_flag > 20) do_debug();
    #endif
    #ifdef GC_VERBOSE
      if (Pike_in_gc > GC_PASS_PREPARE) {
        fprintf(stderr, "|   Destructing %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);
      }
    #endif
      add_ref( o );
      call_destroy(o, reason);
    
      /* destructed in destroy() */
      if(!(p=o->prog))
      {
        free_object(o);
    #ifdef PIKE_DEBUG
        UNSET_ONERROR(uwp);
    #endif
        return;
      }
      get_destroy_called_mark(o)->p=p;
    
      debug_malloc_touch(o);
      debug_malloc_touch(o->storage);
      debug_malloc_touch(p);
      o->prog=0;
    
      
    #ifdef GC_VERBOSE
      if (Pike_in_gc > GC_PASS_PREPARE)
        fprintf(stderr, "|   Zapping references in %p with %d refs.\n", o, o->refs);
    #endif
    
      /* free globals and call C de-initializers */
      for(e=p->num_inherits-1; e>=0; e--)
      {
        int q;
        struct program *prog = p->inherits[e].prog;
        char *storage = o->storage+p->inherits[e].storage_offset;
        
        if(prog->event_handler)
        {
          if( !frame_pushed )
          {
    	PUSH_FRAME2(o, p);
    	frame_pushed = 1;
          }
          SET_FRAME_CONTEXT(p->inherits + e);
          prog->event_handler(PROG_EVENT_EXIT);
        }
    
        for(q=0;q<(int)prog->num_variable_index;q++)
        {
          int d=prog->variable_index[q];
          struct identifier *id = prog->identifiers + d;
          int identifier_flags = id->identifier_flags;
          int rtt = id->run_time_type;
    
          if (IDENTIFIER_IS_ALIAS(identifier_flags))
    	continue;
    
          if(rtt == T_MIXED)
          {
    	struct svalue *s;
    	s=(struct svalue *)(storage + id->func.offset);
    	dmalloc_touch_svalue(s);
    	if ((s->type != T_OBJECT && s->type != T_FUNCTION) ||
    	    s->u.object != o ||
    	    !(identifier_flags & IDENTIFIER_NO_THIS_REF)) {
    	  (void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" free_global"));
    	  free_svalue(s);
    #ifdef DEBUG_MALLOC
    	} else {
    	  (void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" skip_global"));
    	  dmalloc_touch_svalue(s);
    #endif /* DEBUG_MALLOC */
    	}
          } else if ((rtt != PIKE_T_GET_SET) && (rtt != PIKE_T_FREE)) {
    	union anything *u;
    	u=(union anything *)(storage + id->func.offset);
    #ifdef DEBUG_MALLOC
    	if (rtt <= MAX_REF_TYPE) {debug_malloc_touch(u->refs);}
    #endif
    	if (rtt != T_OBJECT || u->object != o ||
    	    !(identifier_flags & IDENTIFIER_NO_THIS_REF)) {
    	  (void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" free_global"));
    	  free_short_svalue(u, rtt);
    #ifdef DEBUG_MALLOC
    	} else {
    	  (void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" skip_global"));
    #endif /* DEBUG_MALLOC */
    	}
    	DO_IF_DMALLOC(u->refs=(void *)-1);
          }
        }
      }
    
      debug_malloc_touch(o);
      debug_malloc_touch(o->storage);
    
      if(p->flags & PROGRAM_USES_PARENT)
      {
        if(LOW_PARENT_INFO(o,p)->parent)
        {
          debug_malloc_touch(o);
          /* fprintf(stderr, "destruct(): Zapping parent.\n"); */
          free_object(LOW_PARENT_INFO(o,p)->parent);
          LOW_PARENT_INFO(o,p)->parent=0;
        }
      }
    
      if( frame_pushed )
        POP_FRAME2();
    
      free_object( o );
      free_program(p);
    
      remove_destroy_called_mark(o);
    
    #ifdef PIKE_DEBUG
      UNSET_ONERROR(uwp);
    #endif
    }
    
    
    struct object *objects_to_destruct = NULL;
    static struct callback *destruct_object_evaluator_callback = NULL;
    
    /* This function destructs the objects that are scheduled to be
     * destructed by schedule_really_free_object. It links the object back into the
     * list of objects first. Adds a reference, destructs it and then frees it.
     */
    void low_destruct_objects_to_destruct(void)
    {
      struct object *o, *next;
    
    #ifdef PIKE_DEBUG
      if (Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_FREE)
        Pike_fatal("Can't meddle with the object link list in gc pass %d.\n", Pike_in_gc);
    #endif
    
      /* We unlink the list from objects_to_destruct before processing it,
       * to avoid that reentrant calls to this function go through all
       * objects instead of just the newly added ones. This way we avoid
       * extensive recursion in this function and also avoid destructing
       * the objects arbitrarily late. */
      while (objects_to_destruct) {
        o = objects_to_destruct, objects_to_destruct = 0;
    
        /* When we remove the object list from objects_to_destruct, it
         * becomes invisible to gc_touch_all_objects (and other list walk
         * functions), so the object count it returns would be wrong. We
         * therefore use this hack to avoid complaining about it in the
         * gc. */
        got_unlinked_things++;
    
        do {
    #ifdef GC_VERBOSE
          if (Pike_in_gc > GC_PASS_PREPARE)
    	fprintf(stderr, "|   Destructing %p on objects_to_destruct.\n", o);
    #endif
    
          next = o->next;
          debug_malloc_touch(o);
          debug_malloc_touch(next);
    
          /* Link object back to list of objects */
          DOUBLELINK(first_object,o);
    
          /* call destroy, keep one ref */
          add_ref(o);
          destruct_object (o, DESTRUCT_NO_REFS);
          free_object(o);
        } while ((o = next));
    
        got_unlinked_things--;
      }
    }
    
    void destruct_objects_to_destruct_cb(void)
    {
      low_destruct_objects_to_destruct();
      if(destruct_object_evaluator_callback)
      {
        remove_callback(destruct_object_evaluator_callback);
        destruct_object_evaluator_callback=0;
      }
    }
    
    
    /* schedule_really_free_object:
     * This function is called when an object runs out of references.
     * It frees the object if it is destructed, otherwise it moves it to
     * a separate list of objects which will be destructed later.
     */
    
    PMOD_EXPORT void schedule_really_free_object(struct object *o)
    {
    #ifdef PIKE_DEBUG
      if (o->refs) {
    #ifdef DEBUG_MALLOC
        if (o->refs > 0) {
          fprintf (stderr, "Object got %d references in schedule_really_free_object():\n", o->refs);
          describe_something(o, T_OBJECT, 0,2,0, NULL);
        }
    #endif
        Pike_fatal("Object got %d references in schedule_really_free_object().\n", o->refs);
      }
      if (Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_FREE && o->next != o)
        Pike_fatal("Freeing objects is not allowed inside the gc.\n");
    #endif
    
      debug_malloc_touch(o);
      debug_malloc_touch(o->storage);
    
      if(o->prog && (o->prog->flags & PROGRAM_DESTRUCT_IMMEDIATE))
      {
        add_ref(o);
        destruct_object (o, DESTRUCT_NO_REFS);
        if(sub_ref(o)) return;
      }
    
      debug_malloc_touch(o);
      debug_malloc_touch(o->storage);
    
      if (o->next != o) {
        /* Don't unlink fake objects... */
        DOUBLEUNLINK(first_object,o);
      }
    
      if(o->prog)
      {
        o->next = objects_to_destruct;
        DO_IF_DMALLOC(o->prev = (void *) -1);
        PIKE_MEM_WO(o->prev);
        objects_to_destruct = o;
    
    #ifdef GC_VERBOSE
        if (Pike_in_gc > GC_PASS_PREPARE)
          fprintf(stderr, "|   Putting %p in objects_to_destruct.\n", o);
    #endif
    
        if (Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_DESTRUCT)
          /* destruct_objects_to_destruct() called by gc instead. */
          return;
        if(!destruct_object_evaluator_callback)
        {
          destruct_object_evaluator_callback=
    	add_to_callback(&evaluator_callbacks,
    			(callback_func)destruct_objects_to_destruct_cb,
    			0,0);
        }
      } else {
    #if 0    /* Did I just cause a leak? -Hubbe */
        if(o->prog && o->prog->flags & PROGRAM_USES_PARENT)
        {
          if(PARENT_INFO(o)->parent)
          {
    	/* fprintf(stderr, "schedule_really_free_object(): Zapping parent.\n"); */
    
    	free_object(PARENT_INFO(o)->parent);
    	PARENT_INFO(o)->parent=0;
          }
        }
    #endif
    
    #ifdef GC_VERBOSE
        if (Pike_in_gc > GC_PASS_PREPARE)
          fprintf(stderr, "|   Freeing storage for %p.\n", o);
    #endif
    
        if (o->next != o)
          /* As far as the gc is concerned, the fake objects doesn't exist. */
          GC_FREE(o);
    
        EXIT_PIKE_MEMOBJ(o);
    
        if(o->storage)
        {
          free(o->storage);
          o->storage=0;
        }
        really_free_object(o);
      }
    }
    
    
    /* Get a variable through internal indexing, i.e. directly by
     * identifier index without going through `->= or `[]= lfuns.
     *
     * NOTE: This function may be called by the compiler on objects
     *       cloned from unfinished programs (ie placeholder
     *       objects). Degenerated cases may thus occur.
     */
    PMOD_EXPORT void low_object_index_no_free(struct svalue *to,
    					  struct object *o,
    					  ptrdiff_t f)
    {
      struct identifier *i;
      struct program *p = NULL;
    
      while(1) {
        struct external_variable_context loc;
    
        if(!o || !(p = o->prog)) {
          Pike_error("Cannot access variables in destructed object.\n");
        }
        debug_malloc_touch(o);
        debug_malloc_touch(o->storage);
    
        i=ID_FROM_INT(p, f);
    
        if (!IDENTIFIER_IS_ALIAS(i->identifier_flags)) break;
    
        loc.o = o;
        loc.inherit = INHERIT_FROM_INT(p, f);
        loc.parent_identifier = f;
        find_external_context(&loc, i->func.ext_ref.depth);
        f = i->func.ext_ref.id + loc.inherit->identifier_level;
        o = loc.o;
      }
    
      switch(i->identifier_flags & IDENTIFIER_TYPE_MASK)
      {
      case IDENTIFIER_PIKE_FUNCTION:
        if (i->func.offset == -1 && p->flags & PROGRAM_FINISHED) {
          /* Prototype. In the first pass we must be able to get a
           * function anyway.
           *
           * We also need to get a function anyway if we're currently
           * in the second pass of compiling this program, since the
           * function might be defined further ahead.
           *
           * FIXME: Consider looking at IDENTIFIER_HAS_BODY in pass 2?
           *
           * On the other hand, consider mixins.
           */
          to->type=T_INT;
          to->subtype=NUMBER_UNDEFINED;
          to->u.integer=0;
          break;
        }
        /* Fall through. */
    
      case IDENTIFIER_C_FUNCTION:
        to->type=T_FUNCTION;
        to->subtype = DO_NOT_WARN(f);
        to->u.object=o;
        add_ref(o);
        break;
    
      case IDENTIFIER_CONSTANT:
        {
          if (i->func.offset >= 0) {
    	struct svalue *s;
    	s=& PROG_FROM_INT(p,f)->constants[i->func.offset].sval;
    	if(s->type==T_PROGRAM &&
    	   (s->u.program->flags & PROGRAM_USES_PARENT))
    	{
    	  to->type=T_FUNCTION;
    	  to->subtype = DO_NOT_WARN(f);
    	  to->u.object=o;
    	  add_ref(o);
    	}else{
    	  check_destructed(s);
    	  assign_svalue_no_free(to, s);
    	}
          } else {
    	/* Prototype constant. */
    	to->type = T_INT;
    	to->subtype = NUMBER_NUMBER;
    	to->u.integer = 0;
          }
          break;
        }
    
      case IDENTIFIER_VARIABLE:
        if (i->run_time_type == PIKE_T_GET_SET) {
          struct reference *ref = p->identifier_references + f;
          int fun = i->func.gs_info.getter;
          if (fun >= 0) {
    	DECLARE_CYCLIC();
    	fun += p->inherits[ref->inherit_offset].identifier_level;
    	if (!BEGIN_CYCLIC(o, (size_t) fun)) {
    	  SET_CYCLIC_RET(1);
    	  apply_low(o, fun, 0);
    	} else {
    	  END_CYCLIC();
    	  Pike_error("Cyclic loop on getter for symbol %S.\n", i->name);
    	}
    	END_CYCLIC();
    	*to = *(--Pike_sp);
          } else {
    	Pike_error("No getter for variable %S.\n", i->name);
          }
        } else if ((i->run_time_type == PIKE_T_FREE) || !PIKE_OBJ_STORAGE(o)) {
          /* Variable storage not allocated. */
    #ifdef PIKE_DEBUG
          if ((i->run_time_type != PIKE_T_FREE) && (p->flags & PROGRAM_FINISHED)) {
    	Pike_fatal("Object without variable storage!\n");
          }
    #endif /* PIKE_DEBUG */
          to->type = T_INT;
          to->subtype = ((i->run_time_type == PIKE_T_FREE)?
    		     NUMBER_UNDEFINED:NUMBER_NUMBER);
          to->u.integer = 0;      
        } else {
          void *ptr=LOW_GET_GLOBAL(o,f,i);
          switch(i->run_time_type)
          {
          case T_MIXED:
    	{
    	  struct svalue *s=(struct svalue *)ptr;
    	  check_destructed(s);
    	  assign_svalue_no_free(to, s);
    	  break;
    	}
    
          case T_FLOAT:
    	to->type=T_FLOAT;
    	to->subtype=0;
    	to->u.float_number=*(FLOAT_TYPE *)ptr;
    	break;
    
          case T_INT:
    	to->type=T_INT;
    	to->subtype=0;
    	to->u.integer=*(INT_TYPE *)ptr;
    	break;
    
          default:
    	{
    	  struct ref_dummy *dummy;
    
    	  to->subtype=0;
    	  if((dummy=*(struct ref_dummy  **)ptr))
    	  {
    	    add_ref(to->u.dummy=dummy);
    	    to->type=i->run_time_type;
    	  }else{
    	    to->type=T_INT;
    	    to->u.integer=0;
    	  }
    	  break;
    	}
          }
        }
        break;
    
      default:;
    #ifdef PIKE_DEBUG
        Pike_fatal ("Unknown identifier type.\n");
    #endif
      }
    }
    
    /* Get a variable without going through `->= or `[]= lfuns. If index
     * is a string then the externally visible identifiers are indexed. If
     * index is T_OBJ_INDEX then any identifier is accessed through
     * identifier index. */
    PMOD_EXPORT void object_index_no_free2(struct svalue *to,
    				       struct object *o,
    				       int inherit_number,
    				       struct svalue *index)
    {
      struct program *p;
      struct inherit *inh;
      int f = -1;
    
      if(!o || !(p=o->prog))
      {
        Pike_error("Lookup in destructed object.\n");
        return; /* make gcc happy */
      }
    
      p = (inh = p->inherits + inherit_number)->prog;
    
      switch(index->type)
      {
      case T_STRING:
        f=find_shared_string_identifier(index->u.string, p);
        if (f >= 0) f += inh->identifier_level;
        break;
    
      case T_OBJ_INDEX:
        f=index->u.identifier;
        break;
    
      default:
    #ifdef PIKE_DEBUG
        if (index->type > MAX_TYPE)
          Pike_fatal ("Invalid type %d in index.\n", index->type);
    #endif
        Pike_error("Lookup in object with non-string index.\n");
      }
    
      if(f < 0)
      {
        to->type=T_INT;
        to->subtype=NUMBER_UNDEFINED;
        to->u.integer=0;
      }else{
        low_object_index_no_free(to, o, f);
      }
    }
    
    #define ARROW_INDEX_P(X) ((X)->type==T_STRING && (X)->subtype)
    
    /* Get a variable through external indexing, i.e. by going through
     * `-> or `[] lfuns, not seeing private and protected etc. */
    PMOD_EXPORT void object_index_no_free(struct svalue *to,
    				      struct object *o,
    				      int inherit_number,
    				      struct svalue *index)
    {
      struct program *p = NULL;
      struct inherit *inh;
      int lfun,l;
    
      if(!o || !(p=o->prog))
      {
        Pike_error("Lookup in destructed object.\n");
        return; /* make gcc happy */
      }
    
      p = (inh = p->inherits + inherit_number)->prog;
    
    #ifdef PIKE_DEBUG
      if (index->type > MAX_TYPE)
        Pike_fatal ("Invalid index type %d.\n", index->type);
    #endif
    
      lfun=ARROW_INDEX_P(index) ? LFUN_ARROW : LFUN_INDEX;
    
      if(p->flags & PROGRAM_FIXED)
      {
        l=p->lfuns[lfun];
      }else{
        if(!(p->flags & PROGRAM_PASS_1_DONE))
        {
          if(report_compiler_dependency(p))
          {
    #if 0
    	struct svalue tmp;
    	fprintf(stderr,"Placeholder deployed for %p when indexing ", p);
    	tmp.type = T_OBJECT;
    	tmp.subtype = 0;
    	tmp.u.object = o;
    	print_svalue (stderr, &tmp);
    	fputs (" with ", stderr);
    	print_svalue (stderr, index);
    	fputc ('\n', stderr);
    #endif
    	add_ref(to->u.object=placeholder_object);
    	to->type=T_OBJECT;
    	to->subtype = 0;
    	return;
          }
        }
        l=low_find_lfun(p, lfun);
      }
      if(l != -1)
      {
        l += inh->identifier_level;
        push_svalue(index);
        apply_lfun(o, lfun, 1);
        *to=sp[-1];
        sp--;
        dmalloc_touch_svalue(sp);
      } else {
        object_index_no_free2(to, o, inherit_number, index);
      }
    }
    
    
    /* Assign a variable through internal indexing, i.e. directly by
     * identifier index without going through `->= or `[]= lfuns. */
    PMOD_EXPORT void object_low_set_index(struct object *o,
    				      int f,
    				      struct svalue *from)
    {
      struct identifier *i;
      struct program *p = NULL;
      int rtt, id_flags;
    
      while(1) {
        struct external_variable_context loc;
    
        if(!o || !(p=o->prog))
        {
          Pike_error("Lookup in destructed object.\n");
          return; /* make gcc happy */
        }
    
        debug_malloc_touch(o);
        debug_malloc_touch(o->storage);
    
        i=ID_FROM_INT(p, f);
    
        if (!IDENTIFIER_IS_ALIAS(i->identifier_flags)) break;
    
        loc.o = o;
        loc.inherit = INHERIT_FROM_INT(p, f);
        loc.parent_identifier = f;
        find_external_context(&loc, i->func.ext_ref.depth);
        f = i->func.ext_ref.id + loc.inherit->identifier_level;
        o = loc.o;
      }
    
      check_destructed(from);
      rtt = i->run_time_type;
      id_flags = i->identifier_flags;
    
      if(!IDENTIFIER_IS_VARIABLE(id_flags))
      {
        Pike_error("Cannot assign functions or constants.\n");
      }
      else if(rtt == T_MIXED)
      {
        /* Don't count references to ourselves to help the gc. DDTAH. */
        struct svalue *to = (struct svalue *) LOW_GET_GLOBAL(o,f,i);
        dmalloc_touch_svalue(to);
        if ((to->type != T_OBJECT && to->type != T_FUNCTION) ||
    	to->u.object != o ||
    	!(id_flags & IDENTIFIER_NO_THIS_REF)) {
          (void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" free_global"));
          free_svalue(to);
    #ifdef DEBUG_MALLOC
        } else {
          (void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" skip_global"));
          dmalloc_touch_svalue (to);
    #endif /* DEBUG_MALLOC */
        }
        *to = *from;
        dmalloc_touch_svalue (to);
        if ((to->type != T_OBJECT && to->type != T_FUNCTION) ||
    	(to->u.object != o) ||
    	!(id_flags & IDENTIFIER_NO_THIS_REF)) {
          if(to->type <= MAX_REF_TYPE) {
    	(void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" store_global"));
    	add_ref(to->u.dummy);
    #ifdef DEBUG_MALLOC
          } else {
    	(void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" store_global"));
    #endif /* DEBUG_MALLOC */
          }
    #ifdef DEBUG_MALLOC
        } else {
          (void) debug_malloc_update_location(o, DMALLOC_NAMED_LOCATION(" self_global"));
          dmalloc_touch_svalue (to);
    #endif /* DEBUG_MALLOC */
        }
      }
      else if (rtt == PIKE_T_GET_SET)
      {
        /* Getter/setter type variable. */
        struct reference *ref = p->identifier_references + f;
        struct program *pp = p->inherits[ref->inherit_offset].prog;
        int fun = i->func.gs_info.setter;
        if (fun >= 0) {
          DECLARE_CYCLIC();
          fun += p->inherits[ref->inherit_offset].identifier_level;
          if (!BEGIN_CYCLIC(o, (size_t) fun)) {
    	SET_CYCLIC_RET(1);
    	push_svalue(from);
    	apply_low(o, fun, 1);
    	pop_stack();
          } else {
    	END_CYCLIC();
    	Pike_error("Cyclic loop on setter for symbol %S.\n", i->name);
          }
          END_CYCLIC();
        } else {
          Pike_error("No setter for variable %S.\n", i->name);
        }
      }
      else if (rtt == PIKE_T_FREE) {
        Pike_error("Attempt to store data in extern variable %S.\n", i->name);
      } else {
        /* Partial code duplication from assign_to_short_svalue. */
        union anything *u = (union anything *) LOW_GET_GLOBAL(o,f,i);
        if(from->type == rtt)
        {
          switch(rtt)
          {
    	case T_INT: u->integer=from->u.integer; break;
    	case T_FLOAT: u->float_number=from->u.float_number; break;
    	case T_OBJECT:
    	  if (id_flags & IDENTIFIER_NO_THIS_REF) {
    	    /* Don't count references to ourselves to help the gc. */
    	    debug_malloc_touch(u->object);
    	    if ((u->object != o) && u->refs && !sub_ref(u->dummy)) {
    	      debug_malloc_touch(o);
    	      really_free_short_svalue(u,rtt);
    #ifdef DEBUG_MALLOC
    	    } else {
    	      debug_malloc_touch(o);
    #endif /* DEBUG_MALLOC */
    	    }
    	    u->refs = from->u.refs;
    	    debug_malloc_touch(u->refs);
    	    if (u->object != o) {
    	      debug_malloc_touch(o);
    	      add_ref(u->dummy);
    #ifdef DEBUG_MALLOC
    	    } else {
    	      debug_malloc_touch(o);
    #endif /* DEBUG_MALLOC */
    	    }
    	    break;
    	  }
    	  /* FALL THROUGH */
    	default:
    	  debug_malloc_touch(u->refs);
    	  if(u->refs && !sub_ref(u->dummy))
    	    really_free_short_svalue(u,rtt);
    	  u->refs = from->u.refs;
    	  add_ref(u->dummy);
          }
        }else if(rtt<=MAX_REF_TYPE && UNSAFE_IS_ZERO(from)){
          if((rtt != T_OBJECT || u->object != o || !(id_flags & IDENTIFIER_NO_THIS_REF)) &&
    	 u->refs && !sub_ref(u->dummy)) {
    	debug_malloc_touch(u->ptr);
    	really_free_short_svalue(u,rtt);
          }
          debug_malloc_touch(u->ptr);
          u->refs=0;
        }else{
          Pike_error("Wrong type in assignment, expected %s, got %s.\n",
    		 get_name_of_type(rtt),
    		 get_name_of_type(from->type));
        }
      }
    }
    
    /* Assign a variable without going through `->= or `[]= lfuns. If
     * index is a string then the externally visible identifiers are
     * indexed. If index is T_OBJ_INDEX then any identifier is accessed
     * through identifier index. */
    PMOD_EXPORT void object_set_index2(struct object *o,
    				   int inherit_number,
    				   struct svalue *index,
    				   struct svalue *from)
    {
      struct program *p;
      struct inherit *inh;
      int f = -1;
    
      if(!o || !(p=o->prog))
      {
        Pike_error("Lookup in destructed object.\n");
        return; /* make gcc happy */
      }
    
      p = (inh = p->inherits + inherit_number)->prog;
    
      switch(index->type)
      {
      case T_STRING:
        f=find_shared_string_identifier(index->u.string, p);
        if (f >= 0) f += inh->identifier_level;
        break;
    
      case T_OBJ_INDEX:
        f=index->u.identifier;
        break;
    
      default:
    #ifdef PIKE_DEBUG
        if (index->type > MAX_TYPE)
          Pike_fatal ("Invalid type %d in index.\n", index->type);
    #endif
        Pike_error("Lookup on non-string value.\n");
      }
    
      if(f < 0)
      {
        if (index->type == T_STRING && index->u.string->len < 1024)
          Pike_error("No such variable (%S) in object.\n", index->u.string);
        else
          Pike_error("No such variable in object.\n");
      }else{
        object_low_set_index(o, f, from);
      }
    }
    
    /* Assign a variable through external indexing, i.e. by going through
     * `->= or `[]= lfuns, not seeing private and protected etc. */
    PMOD_EXPORT void object_set_index(struct object *o,
    				  int inherit_number,
    				  struct svalue *index,
    				  struct svalue *from)
    {
      int lfun;
      int fun;
      struct program *p = NULL;
      struct inherit *inh;
    
      if(!o || !(p=o->prog))
      {
        Pike_error("Lookup in destructed object.\n");
        return; /* make gcc happy */
      }
    
      p = (inh = p->inherits + inherit_number)->prog;
    
    #ifdef PIKE_DEBUG
      if (index->type > MAX_TYPE)
        Pike_fatal ("Invalid index type %d.\n", index->type);
    #endif
    
      lfun=ARROW_INDEX_P(index) ? LFUN_ASSIGN_ARROW : LFUN_ASSIGN_INDEX;
    
      if((fun = FIND_LFUN(p, lfun)) != -1)
      {
        push_svalue(index);
        push_svalue(from);
        apply_low(o, fun + inh->identifier_level, 2);
        pop_stack();
      } else {
        object_set_index2(o, inherit_number, index, from);
      }
    }
    
    static union anything *object_low_get_item_ptr(struct object *o,
    					       int f,
    					       TYPE_T type)
    {
      struct identifier *i;
      struct program *p;
    
      debug_malloc_touch(o);
    
      if(!o || !(p=o->prog))
      {
        Pike_error("Lookup in destructed object.\n");
        return 0; /* make gcc happy */
      }
    
      debug_malloc_touch(p);
    
      i=ID_FROM_INT(p, f);
    
    #ifdef PIKE_DEBUG
      if (type > MAX_TYPE)
        Pike_fatal ("Invalid type %d.\n", type);
      if (type == T_OBJECT || type == T_FUNCTION)
        Pike_fatal ("Dangerous with the refcount-less this-pointers.\n");
    #endif
    
      while (IDENTIFIER_IS_ALIAS(i->identifier_flags)) {
        struct external_variable_context loc;
        loc.o = o;
        loc.inherit = INHERIT_FROM_INT(p, f);
        loc.parent_identifier = 0;
        find_external_context(&loc, i->func.ext_ref.depth);
        f = i->func.ext_ref.id;
        p = (o = loc.o)->prog;
        i = ID_FROM_INT(p, f);
      }
    
      if(!IDENTIFIER_IS_VARIABLE(i->identifier_flags))
      {
        Pike_error("Cannot assign functions or constants.\n");
      } else if(i->run_time_type == T_MIXED)
      {
        struct svalue *s;
        s=(struct svalue *)LOW_GET_GLOBAL(o,f,i);
        if(s->type == type) return & s->u;
      }
      else if(i->run_time_type == type)
      {
        return (union anything *) LOW_GET_GLOBAL(o,f,i);
      }
      return 0;
    }
    
    
    union anything *object_get_item_ptr(struct object *o,
    				    int inherit_number,
    				    struct svalue *index,
    				    TYPE_T type)
    {
      struct program *p;
      struct inherit *inh;
      int f;
    
      debug_malloc_touch(o);
      dmalloc_touch_svalue(index);
    
      if(!o || !(p=o->prog))
      {
        Pike_error("Lookup in destructed object.\n");
        return 0; /* make gcc happy */
      }
    
      p = (inh = p->inherits + inherit_number)->prog;
    
      switch(index->type)
      {
      case T_STRING:
        f=ARROW_INDEX_P(index) ? LFUN_ASSIGN_ARROW : LFUN_ASSIGN_INDEX;
    
        if(FIND_LFUN(p,f) != -1)
        {
          return 0;
          
          /* Pike_error("Cannot do incremental operations on overloaded index (yet).\n");
           */
        }
        
        f=find_shared_string_identifier(index->u.string, p);
        if (f >= 0) f += inh->identifier_level;
        break;
    
      case T_OBJ_INDEX:
        f=index->u.identifier;
        break;
    
      default:
    #ifdef PIKE_DEBUG
        if (index->type > MAX_TYPE)
          Pike_fatal ("Invalid type %d in index.\n", index->type);
    #endif
    /*    Pike_error("Lookup on non-string value.\n"); */
        return 0;
      }
    
      if(f < 0)
      {
        if (index->type == T_STRING && index->u.string->len < 1024)
          Pike_error("No such variable (%S) in object.\n", index->u.string);
        else
          Pike_error("No such variable in object.\n");
      }else{
        return object_low_get_item_ptr(o, f, type);
      }
      return 0;
    }
    
    
    PMOD_EXPORT int object_equal_p(struct object *a, struct object *b, struct processing *p)
    {
      struct processing curr;
    
      if(a == b) return 1;
      if(a->prog != b->prog) return 0;
    
      curr.pointer_a = a;
      curr.pointer_b = b;
      curr.next = p;
    
      for( ;p ;p=p->next)
        if(p->pointer_a == (void *)a && p->pointer_b == (void *)b)
          return 1;
    
      /* NOTE: At this point a->prog and b->prog are equal (see test 2 above). */
      if(a->prog)
      {
        int e;
    
        if(a->prog->flags & PROGRAM_HAS_C_METHODS) return 0;
    
        for(e=0;e<(int)a->prog->num_identifier_references;e++)
        {
          struct identifier *i;
          i=ID_FROM_INT(a->prog, e);
    
          if(!IDENTIFIER_IS_VARIABLE(i->identifier_flags) ||
    	 IDENTIFIER_IS_ALIAS(i->identifier_flags))
    	continue;
    
          if(i->run_time_type == T_MIXED)
          {
    	if(!low_is_equal((struct svalue *)LOW_GET_GLOBAL(a,e,i),
    			 (struct svalue *)LOW_GET_GLOBAL(b,e,i),
    			 &curr))
    	  return 0;
          }else{
    	if(!low_short_is_equal((union anything *)LOW_GET_GLOBAL(a,e,i),
    			       (union anything *)LOW_GET_GLOBAL(b,e,i),
    			       i->run_time_type,
    			       &curr))
    	  return 0;
          }
        }
      }
    
      return 1;
    }
    
    PMOD_EXPORT struct array *object_indices(struct object *o, int inherit_number)
    {
      struct program *p;
      struct inherit *inh;
      struct array *a;
      int fun;
      int e;
    
      p=o->prog;
      if(!p)
        Pike_error("indices() on destructed object.\n");
    
      p = (inh = p->inherits + inherit_number)->prog;
    
      if((fun = FIND_LFUN(p, LFUN__INDICES)) == -1)
      {
        a=allocate_array_no_init(p->num_identifier_index,0);
        for(e=0;e<(int)p->num_identifier_index;e++)
        {
          copy_shared_string(ITEM(a)[e].u.string,
    			 ID_FROM_INT(p,p->identifier_index[e])->name);
          ITEM(a)[e].type=T_STRING;
        }
        a->type_field = BIT_STRING;
      }else{
        apply_low(o, fun + inh->identifier_level, 0);
        if(sp[-1].type != T_ARRAY)
          Pike_error("Bad return type from o->_indices()\n");
        a=sp[-1].u.array;
        sp--;
        dmalloc_touch_svalue(sp);
      }
      return a;
    }
    
    PMOD_EXPORT struct array *object_values(struct object *o, int inherit_number)
    {
      struct program *p;
      struct inherit *inh;
      struct array *a;
      int fun;
      int e;
      
      p=o->prog;
      if(!p)
        Pike_error("values() on destructed object.\n");
    
      p = (inh = p->inherits + inherit_number)->prog;
    
      if((fun = FIND_LFUN(p, LFUN__VALUES)) == -1)
      {
        TYPE_FIELD types = 0;
        a=allocate_array_no_init(p->num_identifier_index,0);
        for(e=0;e<(int)p->num_identifier_index;e++)
        {
          low_object_index_no_free(ITEM(a)+e, o,
    			       p->identifier_index[e] + inh->identifier_level);
          types |= 1 << ITEM(a)[e].type;
        }
        a->type_field = types;
      }else{
        apply_low(o, fun + inh->identifier_level, 0);
        if(sp[-1].type != T_ARRAY)
          Pike_error("Bad return type from o->_values()\n");
        a=sp[-1].u.array;
        sp--;
        dmalloc_touch_svalue(sp);
      }
      return a;
    }
    
    
    PMOD_EXPORT void visit_object (struct object *o, int action)
    {
      struct program *p = o->prog;
    
      if (o->next == o) return; /* Fake object used by compiler */
    
      switch (action) {
    #ifdef PIKE_DEBUG
        default:
          Pike_fatal ("Unknown visit action %d.\n", action);
        case VISIT_NORMAL:
        case VISIT_COMPLEX_ONLY:
          break;
    #endif
        case VISIT_COUNT_BYTES:
          mc_counted_bytes += sizeof (struct object);
          if (p) mc_counted_bytes += p->storage_needed;
          break;
      }
    
      if (PIKE_OBJ_INITED (o)) {
        struct pike_frame *pike_frame = NULL;
        struct inherit *inh = p->inherits;
        char *storage = o->storage;
        int e;
    
        debug_malloc_touch (p);
        debug_malloc_touch (o);
        debug_malloc_touch (storage);
    
        visit_program_ref (p, REF_TYPE_NORMAL);
    
        for (e = p->num_inherits - 1; e >= 0; e--) {
          struct program *inh_prog = inh[e].prog;
          unsigned INT16 *inh_prog_var_idxs = inh_prog->variable_index;
          struct identifier *inh_prog_ids = inh_prog->identifiers;
          char *inh_storage = storage + inh[e].storage_offset;
    
          int q, num_vars = (int) inh_prog->num_variable_index;
    
          for (q = 0; q < num_vars; q++) {
    	int d = inh_prog_var_idxs[q];
    	struct identifier *id = inh_prog_ids + d;
    	int id_flags = id->identifier_flags;
    	int rtt = id->run_time_type;
    	void *var;
    	union anything *u;
    
    	if (IDENTIFIER_IS_ALIAS (id_flags))
    	  continue;
    
    	var = inh_storage + id->func.offset;
    	u = (union anything *) var;
    #ifdef DEBUG_MALLOC
    	if (rtt <= MAX_REF_TYPE)
    	  debug_malloc_touch (u->ptr);
    #endif
    
    	switch (rtt) {
    	  case T_MIXED: {
    	    struct svalue *s = (struct svalue *) var;
    	    dmalloc_touch_svalue (s);
    	    if ((s->type != T_OBJECT && s->type != T_FUNCTION) ||
    		s->u.object != o ||
    		!(id_flags & IDENTIFIER_NO_THIS_REF))
    	      visit_svalue (s, REF_TYPE_NORMAL);
    	    break;
    	  }
    
    	  case T_ARRAY:
    	    if (u->array)
    	      visit_array_ref (u->array, REF_TYPE_NORMAL);
    	    break;
    	  case T_MAPPING:
    	    if (u->mapping)
    	      visit_mapping_ref (u->mapping, REF_TYPE_NORMAL);
    	    break;
    	  case T_MULTISET:
    	    if (u->multiset)
    	      visit_multiset_ref (u->multiset, REF_TYPE_NORMAL);
    	    break;
    	  case T_PROGRAM:
    	    if (u->program)
    	      visit_program_ref (u->program, REF_TYPE_NORMAL);
    	    break;
    
    	  case T_OBJECT:
    	    if (u->object && (u->object != o ||
    			      !(id_flags & IDENTIFIER_NO_THIS_REF)))
    	      visit_object_ref (u->object, REF_TYPE_NORMAL);
    	    break;
    
    	  case T_STRING:
    	    if (u->string && !(action & VISIT_COMPLEX_ONLY))
    	      visit_string_ref (u->string, REF_TYPE_NORMAL);
    	    break;
    	  case T_TYPE:
    	    if (u->type && !(action & VISIT_COMPLEX_ONLY))
    	      visit_type_ref (u->type, REF_TYPE_NORMAL);
    	    break;
    
    #ifdef PIKE_DEBUG
    	  case PIKE_T_GET_SET:
    	  case PIKE_T_FREE:
    	  case T_INT:
    	  case T_FLOAT:
    	    break;
    	  default:
    	    Pike_fatal ("Invalid runtime type %d.\n", rtt);
    #endif
    	}
          }
    
          if (inh_prog->event_handler) {
    	if (!pike_frame) PUSH_FRAME2 (o, p);
    	SET_FRAME_CONTEXT (inh + e);
    	inh_prog->event_handler (PROG_EVENT_GC_RECURSE);
          }
        }
    
        if (pike_frame) POP_FRAME2();
    
        /* Strong ref follows. It must be last. */
        if (p->flags & PROGRAM_USES_PARENT)
          if (PARENT_INFO (o)->parent)
    	visit_object_ref (PARENT_INFO (o)->parent, REF_TYPE_STRONG);
      }
    }
    
    PMOD_EXPORT void visit_function (struct svalue *s, int ref_type)
    {
    #ifdef PIKE_DEBUG
      if (s->type != T_FUNCTION)
        Pike_fatal ("Should only be called for a function svalue.\n");
    #endif
    
      if (s->subtype == FUNCTION_BUILTIN)
        /* Could avoid this if we had access to the action from the caller
         * and check if it's VISIT_COMPLEX_ONLY. However, visit_callable
         * will only return first thing. */
        visit_callable_ref (s->u.efun, ref_type);
      else
        visit_object_ref (s->u.object, ref_type);
    }
    
    static void gc_check_object(struct object *o);
    
    PMOD_EXPORT void gc_mark_object_as_referenced(struct object *o)
    {
      debug_malloc_touch(o);
      debug_malloc_touch(o->storage);
    
      if(gc_mark(o)) {
        if(o->next == o) return; /* Fake object used by compiler */
    
        GC_ENTER (o, T_OBJECT) {
          int e;
          struct program *p = o->prog;
    
          if (o == gc_mark_object_pos)
    	gc_mark_object_pos = o->next;
          if (o == gc_internal_object)
    	gc_internal_object = o->next;
          else {
    	DOUBLEUNLINK(first_object, o);
    	DOUBLELINK(first_object, o); /* Linked in first. */
          }
    
          if(p && PIKE_OBJ_INITED(o)) {
    	debug_malloc_touch(p);
    
    	gc_mark_program_as_referenced (p);
    
    	if(o->prog->flags & PROGRAM_USES_PARENT)
    	  if(PARENT_INFO(o)->parent)
    	    gc_mark_object_as_referenced(PARENT_INFO(o)->parent);
    
    	LOW_PUSH_FRAME(o, p);
    
    	for(e=p->num_inherits-1; e>=0; e--)
    	{
    	  int q;
          
    	  LOW_SET_FRAME_CONTEXT(p->inherits + e);
    
    	  for(q=0;q<(int)pike_frame->context->prog->num_variable_index;q++)
    	  {
    	    int d=pike_frame->context->prog->variable_index[q];
    	    struct identifier *id = pike_frame->context->prog->identifiers + d;
    	    int id_flags = id->identifier_flags;
    	    int rtt = id->run_time_type;
    
    	    if (IDENTIFIER_IS_ALIAS(id_flags) || (rtt == PIKE_T_GET_SET) ||
    		(rtt == PIKE_T_FREE))
    	      continue;
    	
    	    if(rtt == T_MIXED)
    	    {
    	      struct svalue *s;
    	      s=(struct svalue *)(pike_frame->current_storage + id->func.offset);
    	      dmalloc_touch_svalue(s);
    	      if ((s->type != T_OBJECT && s->type != T_FUNCTION) || s->u.object != o ||
    		  !(id_flags & IDENTIFIER_NO_THIS_REF))
    		gc_mark_svalues(s, 1);
    	    }else{
    	      union anything *u;
    	      u=(union anything *)(pike_frame->current_storage + id->func.offset);
    #ifdef DEBUG_MALLOC
    	      if (rtt <= MAX_REF_TYPE) debug_malloc_touch(u->refs);
    #endif
    	      if (rtt != T_OBJECT || u->object != o ||
    		  !(id_flags & IDENTIFIER_NO_THIS_REF))
    		gc_mark_short_svalue(u, rtt);
    	    }
    	  }
    
    	  if(pike_frame->context->prog->event_handler)
    	    pike_frame->context->prog->event_handler(PROG_EVENT_GC_RECURSE);
    
    	  LOW_UNSET_FRAME_CONTEXT();
    	}
    
    	LOW_POP_FRAME();
          }
        } GC_LEAVE;
      }
    }
    
    PMOD_EXPORT void real_gc_cycle_check_object(struct object *o, int weak)
    {
      if(o->next == o) return; /* Fake object used by compiler */
    
      GC_CYCLE_ENTER_OBJECT(o, weak) {
        int e;
        struct program *p = o->prog;
    
        if (p && PIKE_OBJ_INITED(o)) {
    #if 0
          struct object *o2;
          for (o2 = gc_internal_object; o2 && o2 != o; o2 = o2->next) {}
          if (!o2) Pike_fatal("Object not on gc_internal_object list.\n");
    #endif
    
          LOW_PUSH_FRAME(o, p);
    
          for(e=p->num_inherits-1; e>=0; e--)
          {
    	int q;
          
    	LOW_SET_FRAME_CONTEXT(p->inherits + e);
    
    	for(q=0;q<(int)pike_frame->context->prog->num_variable_index;q++)
    	{
    	  int d=pike_frame->context->prog->variable_index[q];
    	  struct identifier *id = pike_frame->context->prog->identifiers + d;
    	  int id_flags = id->identifier_flags;
    	  int rtt = id->run_time_type;
    	
    	  if (IDENTIFIER_IS_ALIAS(id_flags) || (rtt == PIKE_T_GET_SET) ||
    	      (rtt == PIKE_T_FREE))
    	    continue;
    	
    	  if(rtt == T_MIXED)
    	  {
    	    struct svalue *s;
    	    s=(struct svalue *)(pike_frame->current_storage + id->func.offset);
    	    dmalloc_touch_svalue(s);
    	    if ((s->type != T_OBJECT && s->type != T_FUNCTION) || s->u.object != o ||
    		!(id_flags & IDENTIFIER_NO_THIS_REF))
    	      gc_cycle_check_svalues(s, 1);
    	  }else{
    	    union anything *u;
    	    u=(union anything *)(pike_frame->current_storage + id->func.offset);
    #ifdef DEBUG_MALLOC
    	    if (rtt <= MAX_REF_TYPE) debug_malloc_touch(u->refs);
    #endif
    	    if (rtt != T_OBJECT || u->object != o ||
    		!(id_flags & IDENTIFIER_NO_THIS_REF))
    	      gc_cycle_check_short_svalue(u, rtt);
    	  }
    	}
    
    	if(pike_frame->context->prog->event_handler)
    	  pike_frame->context->prog->event_handler(PROG_EVENT_GC_RECURSE);
    
    	LOW_UNSET_FRAME_CONTEXT();
          }
        
          LOW_POP_FRAME();
    
          /* Even though it's essential that the program isn't freed
           * before the object, it doesn't need a strong link. That since
           * programs can't be destructed before they run out of
           * references. */
          gc_cycle_check_program (p, 0);
    
          /* Strong ref follows. It must be last. */
          if(o->prog->flags & PROGRAM_USES_PARENT)
    	if(PARENT_INFO(o)->parent)
    	  gc_cycle_check_object(PARENT_INFO(o)->parent, -1);
        }
      } GC_CYCLE_LEAVE;
    }
    
    static void gc_check_object(struct object *o)
    {
      int e;
      struct program *p;
    
      GC_ENTER (o, T_OBJECT) {
        if((p=o->prog) && PIKE_OBJ_INITED(o))
        {
          debug_malloc_touch(p);
          debug_gc_check (p, " as the program of an object");
    
          if(p->flags & PROGRAM_USES_PARENT && PARENT_INFO(o)->parent)
    	debug_gc_check (PARENT_INFO(o)->parent, " as parent of an object");
    
          LOW_PUSH_FRAME(o, p);
        
          for(e=p->num_inherits-1; e>=0; e--)
          {
    	int q;
    	LOW_SET_FRAME_CONTEXT(p->inherits + e);
    
    	for(q=0;q<(int)pike_frame->context->prog->num_variable_index;q++)
    	{
    	  int d=pike_frame->context->prog->variable_index[q];
    	  struct identifier *id = pike_frame->context->prog->identifiers + d;
    	  int id_flags = id->identifier_flags;
    	  int rtt = id->run_time_type;
    	
    	  if (IDENTIFIER_IS_ALIAS(id_flags) || (rtt == PIKE_T_GET_SET) ||
    	      (rtt == PIKE_T_FREE))
    	    continue;
    	
    	  if(rtt == T_MIXED)
    	  {
    	    struct svalue *s;
    	    s=(struct svalue *)(pike_frame->current_storage + id->func.offset);
    	    dmalloc_touch_svalue(s);
    	    if ((s->type != T_OBJECT && s->type != T_FUNCTION) || s->u.object != o ||
    		!(id_flags & IDENTIFIER_NO_THIS_REF))
    	      gc_check_svalues(s, 1);
    	  }else{
    	    union anything *u;
    	    u=(union anything *)(pike_frame->current_storage + id->func.offset);
    #ifdef DEBUG_MALLOC
    	    if (rtt <= MAX_REF_TYPE) debug_malloc_touch(u->refs);
    #endif
    	    if (rtt != T_OBJECT || u->object != o ||
    		!(id_flags & IDENTIFIER_NO_THIS_REF))
    	      gc_check_short_svalue(u, rtt);
    	  }
    	}
          
    	if(pike_frame->context->prog->event_handler)
    	  pike_frame->context->prog->event_handler(PROG_EVENT_GC_CHECK);
    
    	LOW_UNSET_FRAME_CONTEXT();
          }
          LOW_POP_FRAME();
        }
      } GC_LEAVE;
    }
    
    unsigned gc_touch_all_objects(void)
    {
      unsigned n = 0;
      struct object *o;
      if (first_object && first_object->prev)
        Pike_fatal("Error in object link list.\n");
      for (o = first_object; o; o = o->next) {
        debug_gc_touch(o);
        n++;
        if (o->next && o->next->prev != o)
          Pike_fatal("Error in object link list.\n");
      }
      for (o = objects_to_destruct; o; o = o->next) n++;
      return n;
    }
    
    void gc_check_all_objects(void)
    {
      struct object *o;
      for(o=first_object;o;o=o->next)
        gc_check_object(o);
    }
    
    void gc_mark_all_objects(void)
    {
      gc_mark_object_pos = gc_internal_object;
      while (gc_mark_object_pos) {
        struct object *o = gc_mark_object_pos;
        gc_mark_object_pos = o->next;
        if(o->refs && gc_is_referenced(o))
          /* Refs check since objects without refs are left around during
           * gc by schedule_really_free_object(). */
          gc_mark_object_as_referenced(o);
      }
    
    #ifdef PIKE_DEBUG
      if(d_flag) {
        struct object *o;
        for(o=objects_to_destruct;o;o=o->next)
          debug_malloc_touch(o);
      }
    #endif
    }
    
    void gc_cycle_check_all_objects(void)
    {
      struct object *o;
      for (o = gc_internal_object; o; o = o->next) {
        real_gc_cycle_check_object(o, 0);
        gc_cycle_run_queue();
      }
    }
    
    void gc_zap_ext_weak_refs_in_objects(void)
    {
      gc_mark_object_pos = first_object;
      while (gc_mark_object_pos != gc_internal_object && gc_ext_weak_refs) {
        struct object *o = gc_mark_object_pos;
        gc_mark_object_pos = o->next;
        if (o->refs)
          gc_mark_object_as_referenced(o);
      }
      gc_mark_discard_queue();
    }
    
    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)
      {
        if(gc_do_free(o))
        {
          /* Got an extra ref from gc_cycle_pop_object(). */
    #ifdef PIKE_DEBUG
          if (gc_object_is_live (o) &&
    	  !find_destroy_called_mark(o))
    	gc_fatal(o,0,"Can't free a live object in gc_free_all_unreferenced_objects().\n");
    #endif
          debug_malloc_touch(o);
          destruct_object (o, reason);
    
          gc_free_extra_ref(o);
          SET_NEXT_AND_FREE(o,free_object);
        }else{
          next=o->next;
        }
        unreferenced++;
      }
    
      return unreferenced;
    }
    
    struct magic_index_struct
    {
      struct inherit *inherit;
      struct object *o;
    };
    
    #define MAGIC_THIS ((struct magic_index_struct *)(CURRENT_STORAGE))
    #define MAGIC_O2S(o) ((struct magic_index_struct *)(o->storage))
    
    struct program *magic_index_program=0;
    struct program *magic_set_index_program=0;
    struct program *magic_indices_program=0;
    struct program *magic_values_program=0;
    
    void push_magic_index(struct program *type, int inherit_no, int parent_level)
    {
      struct external_variable_context loc;
      struct object *magic;
    
      loc.o=Pike_fp->current_object;
      if(!loc.o) Pike_error("Illegal magic index call.\n");
    
      loc.parent_identifier=Pike_fp->fun;
      if (loc.o->prog)
        loc.inherit=INHERIT_FROM_INT(loc.o->prog, loc.parent_identifier);
      find_external_context(&loc, parent_level);
    
      if (!loc.o->prog)
        Pike_error ("Cannot index in destructed parent object.\n");
    
      magic=low_clone(type);
      add_ref(MAGIC_O2S(magic)->o=loc.o);
      MAGIC_O2S(magic)->inherit = loc.inherit + inherit_no;
    #ifdef DEBUG
      if(loc.inherit + inherit_no >= loc.o->prog->inherits + loc.o->prog->num_inherit)
         Pike_fatal("Magic index blahonga!\n");
    #endif
      push_object(magic);
    }
    
    /*! @namespace ::
     *!
     *! Symbols implicitly inherited from the virtual base class.
     *!
     *! These symbols exist mainly to simplify implementation of
     *! the corresponding lfuns.
     *!
     *! @seealso
     *!   @[lfun::]
     */
    
    /* The type argument to the magic index functions is intentionally
     * undocumented since I'm not sure this API is satisfactory. The
     * argument would be explained as follows. /mast
     *
     * The indexing normally involves the externally accessable
     * identifiers (i.e. those which aren't protected or private) in the
     * current class and any inherited classes. If @[type] is 1 then
     * locally accessible identifiers are indexed too. If @[type] is 2
     * then all externally accessible identifiers in the object, i.e. also
     * those in inheriting classes, are indexed. And if @[type] is 3
     * then all locally accessible identifiers in the object, i.e. also
     * those in inheriting classes, are indexed.
     */
    
    
    /*! @decl mixed ::`->(string index)
     *!
     *! Builtin arrow operator.
     *!
     *! This function indexes the current object with the string @[index].
     *! This is useful when the arrow operator has been overloaded.
     *!
     *! @seealso
     *!   @[::`->=()]
     */
    static void f_magic_index(INT32 args)
    {
      struct inherit *inherit;
      int type = 0, f;
      struct pike_string *s;
      struct object *o;
    
      switch (args) {
        default:
        case 2:
          if (sp[-args+1].type != T_INT) SIMPLE_BAD_ARG_ERROR ("::`->", 2, "void|int");
          type = sp[-args+1].u.integer;
          /* FALL THROUGH */
        case 1:
          if (sp[-args].type != T_STRING) SIMPLE_BAD_ARG_ERROR ("::`->", 1, "string");
          s = sp[-args].u.string;
          break;
        case 0:
          SIMPLE_TOO_FEW_ARGS_ERROR ("::`->", 1);
      }
    
      if(!(o=MAGIC_THIS->o))
        Pike_error("Magic index error\n");
    
      if(!o->prog)
        Pike_error("::`-> on destructed object.\n");
    
      switch (type) {
        case 0:
          inherit=MAGIC_THIS->inherit;
          f=find_shared_string_identifier(s,inherit->prog);
          break;
        case 1:
          inherit = MAGIC_THIS->inherit;
          f = really_low_find_shared_string_identifier (s, inherit->prog, SEE_PROTECTED);
          break;
        case 2:
          inherit = o->prog->inherits + 0;
          f = find_shared_string_identifier (s, inherit->prog);
          break;
        case 3:
          inherit = o->prog->inherits + 0;
          f = really_low_find_shared_string_identifier (s, inherit->prog, SEE_PROTECTED);
          break;
        default:
          Pike_error("Unknown indexing type: %d.\n", type);
      }
    
      pop_n_elems(args);
      if(f<0)
      {
        push_int(0);
        sp[-1].subtype=NUMBER_UNDEFINED;
      }else{
        struct svalue sval;
        low_object_index_no_free(&sval,o,f+
    			     inherit->identifier_level);
        *sp=sval;
        sp++;
        dmalloc_touch_svalue(Pike_sp-1);
      }
    }
    
    /*! @decl void ::`->=(string index, mixed value)
     *!
     *! Builtin arrow set operator.
     *!
     *! This function indexes the current object with the string @[index],
     *! and sets it to @[value].
     *! This is useful when the arrow set operator has been overloaded.
     *!
     *! @seealso
     *!   @[::`->()]
     */
    static void f_magic_set_index(INT32 args)
    {
      int type = 0, f;
      struct pike_string *s;
      struct object *o;
      struct svalue *val;
      struct inherit *inherit;
    
      switch (args) {
        default:
        case 3:
          if (sp[-args+2].type != T_INT) SIMPLE_BAD_ARG_ERROR ("::`->=", 3, "void|int");
          type = sp[-args+2].u.integer;
          /* FALL THROUGH */
        case 2:
          val = sp-args+1;
          if (sp[-args].type != T_STRING) SIMPLE_BAD_ARG_ERROR ("::`->=", 1, "string");
          s = sp[-args].u.string;
          break;
        case 1:
        case 0:
          SIMPLE_TOO_FEW_ARGS_ERROR ("::`->=", 2);
      }
    
      if(!(o=MAGIC_THIS->o))
        Pike_error("Magic index error\n");
    
      if(!o->prog)
        Pike_error("::`->= on destructed object.\n");
    
      switch (type) {
        case 0:
          inherit=MAGIC_THIS->inherit;
          f=find_shared_string_identifier(s,inherit->prog);
          break;
        case 1:
          inherit = MAGIC_THIS->inherit;
          f = really_low_find_shared_string_identifier (s, inherit->prog, SEE_PROTECTED);
          break;
        case 2:
          inherit = o->prog->inherits + 0;
          f = find_shared_string_identifier (s, inherit->prog);
          break;
        case 3:
          inherit = o->prog->inherits + 0;
          f = really_low_find_shared_string_identifier (s, inherit->prog, SEE_PROTECTED);
          break;
        default:
          Pike_error("Unknown indexing type.\n");
      }
    
      if(f<0)
      {
        if (s->len < 1024)
          Pike_error("No such variable (%S) in object.\n", s);
        else
          Pike_error("No such variable in object.\n");
      }else{
        object_low_set_index(o, f+inherit->identifier_level,
    			 val);
        pop_n_elems(args);
        push_int(0);
      }
    }
    
    /*! @decl mixed ::_indices()
     *!
     *! Builtin function to list the identifiers of an object.
     *! This is useful when @[lfun::_indices] has been overloaded.
     *!
     *! @seealso
     *!   @[::_values, ::`->]
     */
    static void f_magic_indices (INT32 args)
    {
      struct object *obj;
      struct program *prog;
      struct array *res;
      int type = 0, e, i;
    
      if (args >= 1) {
        if (sp[-args].type != T_INT) SIMPLE_BAD_ARG_ERROR ("::_indices", 1, "void|int");
        type = sp[-args].u.integer;
      }
    
      if (!(obj = MAGIC_THIS->o)) Pike_error ("Magic index error\n");
      if (!obj->prog) Pike_error ("Object is destructed.\n");
    
      /* FIXME: Both type 0 and 1 have somewhat odd behaviors if there are
       * local identifiers before inherits that are overridden by them
       * (e.g. listing duplicate identifiers). But then again, this is not
       * the only place where that gives strange effects, imho. /mast */
    
      switch (type) {
        case 0:
          prog = MAGIC_THIS->inherit->prog;
          break;
        case 1:
        case 3:
          if (type == 1) {
    	prog = MAGIC_THIS->inherit->prog;
          } else {
    	prog = obj->prog;
          }
          pop_n_elems (args);
          push_array (res = allocate_array_no_init (prog->num_identifier_references, 0));
          for (e = i = 0; e < (int) prog->num_identifier_references; e++) {
    	struct reference *ref = prog->identifier_references + e;
    	struct identifier *id = ID_FROM_PTR (prog, ref);
    	if (ref->id_flags & ID_HIDDEN) continue;
    	if ((ref->id_flags & (ID_INHERITED|ID_PRIVATE)) ==
    	    (ID_INHERITED|ID_PRIVATE)) continue;
    	copy_shared_string (ITEM(res)[i].u.string, ID_FROM_PTR (prog, ref)->name);
    	ITEM(res)[i++].type = T_STRING;
          }
          res->type_field = BIT_STRING;
          sp[-1].u.array = resize_array (res, i);
          return;
        case 2:
          prog = obj->prog;
          break;
        default:
          Pike_error("Unknown indexing type.\n");
      }
    
      pop_n_elems (args);
      push_array (res = allocate_array_no_init (prog->num_identifier_index, 0));
      for (e = 0; e < (int) prog->num_identifier_index; e++) {
        copy_shared_string (ITEM(res)[e].u.string,
    			ID_FROM_INT (prog, prog->identifier_index[e])->name);
        ITEM(res)[e].type = T_STRING;
      }
      res->type_field = BIT_STRING;
    }
    
    /*! @decl mixed ::_values()
     *!
     *! Builtin function to list the values of the identifiers of an
     *! object. This is useful when @[lfun::_values] has been overloaded.
     *!
     *! @seealso
     *!   @[::_indices, ::`->]
     */
    static void f_magic_values (INT32 args)
    {
      struct object *obj;
      struct program *prog;
      struct inherit *inherit;
      struct array *res;
      TYPE_FIELD types;
      int type = 0, e, i;
    
      if (args >= 1) {
        if (sp[-args].type != T_INT) SIMPLE_BAD_ARG_ERROR ("::_indices", 1, "void|int");
        type = sp[-args].u.integer;
      }
    
      if (!(obj = MAGIC_THIS->o)) Pike_error ("Magic index error\n");
      if (!obj->prog) Pike_error ("Object is destructed.\n");
    
      /* FIXME: Both type 0 and 1 have somewhat odd behaviors if there are
       * local identifiers before inherits that are overridden by them
       * (e.g. listing duplicate identifiers). But then again, this is not
       * the only place where that gives strange effects, imho. /mast */
    
      switch (type) {
        case 0:
          inherit = MAGIC_THIS->inherit;
          prog = inherit->prog;
          break;
        case 1:
        case 3:
          if (type == 1) {
    	inherit = MAGIC_THIS->inherit;
    	prog = inherit->prog;
          } else {
    	prog = obj->prog;
    	inherit = prog->inherits + 0;
          }
          pop_n_elems (args);
          push_array (res = allocate_array_no_init (prog->num_identifier_references, 0));
          types = 0;
          for (e = i = 0; e < (int) prog->num_identifier_references; e++, i++) {
    	struct reference *ref = prog->identifier_references + e;
    	struct identifier *id = ID_FROM_PTR (prog, ref);
    	if (ref->id_flags & ID_HIDDEN) continue;
    	if ((ref->id_flags & (ID_INHERITED|ID_PRIVATE)) ==
    	    (ID_INHERITED|ID_PRIVATE)) continue;
    	low_object_index_no_free (ITEM(res) + i, obj,
    				  e + inherit->identifier_level);
    	types |= 1 << ITEM(res)[i].type;
          }
          res->type_field = types;
          sp[-1].u.array = resize_array (res, i);
          return;
        case 2:
          prog = obj->prog;
          inherit = prog->inherits + 0;
          break;
        default:
          Pike_error("Unknown indexing type.\n");
      }
    
      pop_n_elems (args);
      push_array (res = allocate_array_no_init (prog->num_identifier_index, 0));
      types = 0;
      for (e = 0; e < (int) prog->num_identifier_index; e++) {
        low_object_index_no_free (ITEM(res) + e, obj,
    			      prog->identifier_index[e] + inherit->identifier_level);
        types |= 1 << ITEM(res)[e].type;
      }
      res->type_field = types;
    }
    
    /*! @endnamespace
     */
    
    void low_init_object(void)
    {
      init_destroy_called_mark_hash();
    }
    
    void init_object(void)
    {
      ptrdiff_t offset;
    
      enter_compiler(NULL, 0);
    
      start_new_program();
      offset=ADD_STORAGE(struct magic_index_struct);
      MAP_VARIABLE("__obj", tObj, ID_PROTECTED,
    	       offset + OFFSETOF(magic_index_struct, o), T_OBJECT);
      ADD_FUNCTION("`()",f_magic_index,tFunc(tStr tOr(tVoid,tInt),tMix),0);
      magic_index_program=end_program();
    
      start_new_program();
      offset=ADD_STORAGE(struct magic_index_struct);
      MAP_VARIABLE("__obj", tObj, ID_PROTECTED,
    	       offset + OFFSETOF(magic_index_struct, o), T_OBJECT);
      ADD_FUNCTION("`()",f_magic_set_index,
    	       tFunc(tStr tMix tOr(tVoid,tInt),tVoid),0);
      magic_set_index_program=end_program();
    
      start_new_program();
      offset=ADD_STORAGE(struct magic_index_struct);
      MAP_VARIABLE("__obj", tObj, ID_PROTECTED,
    	       offset + OFFSETOF(magic_index_struct, o), T_OBJECT);
      ADD_FUNCTION("`()",f_magic_indices,tFunc(tOr(tVoid,tInt),tArr(tStr)),0);
      magic_indices_program=end_program();
    
      start_new_program();
      offset=ADD_STORAGE(struct magic_index_struct);
      MAP_VARIABLE("__obj", tObj, ID_PROTECTED,
    	       offset + OFFSETOF(magic_index_struct, o), T_OBJECT);
      ADD_FUNCTION("`()",f_magic_values,tFunc(tOr(tVoid,tInt),tArray),0);
      magic_values_program=end_program();
    
      exit_compiler();
    }
    
    void exit_object(void)
    {
      if (destruct_object_evaluator_callback) {
        remove_callback(destruct_object_evaluator_callback);
        destruct_object_evaluator_callback = NULL;
      }
    
      master_is_cleaned_up = 1;
      if (master_object) {
        call_destroy (master_object, 1);
        destruct_object (master_object, DESTRUCT_CLEANUP);
        free_object(master_object);
        master_object=0;
      }
    
      if (master_program) {
        free_program(master_program);
        master_program=0;
      }
    
      destruct_objects_to_destruct();
    
      if(magic_index_program)
      {
        free_program(magic_index_program);
        magic_index_program=0;
      }
    
      if(magic_set_index_program)
      {
        free_program(magic_set_index_program);
        magic_set_index_program=0;
      }
    
      if(magic_indices_program)
      {
        free_program(magic_indices_program);
        magic_indices_program=0;
      }
    
      if(magic_values_program)
      {
        free_program(magic_values_program);
        magic_values_program=0;
      }
    }
    
    #ifdef PIKE_DEBUG
    void check_object_context(struct object *o,
    			  struct program *context_prog,
    			  char *current_storage)
    {
      int q;
      if(o == Pike_compiler->fake_object) return;
      if( ! o->prog ) return; /* Variables are already freed */
      for(q=0;q<(int)context_prog->num_variable_index;q++)
      {
        int d=context_prog->variable_index[q];
        struct identifier *id = context_prog->identifiers + d;
    
        if(d<0 || d>=context_prog->num_identifiers)
          Pike_fatal("Illegal index in variable_index!\n");
    
        if(id->run_time_type == T_MIXED)
        {
          struct svalue *s;
          s=(struct svalue *)(current_storage + id->func.offset);
          check_svalue(s);
        }else{
          union anything *u;
          u=(union anything *)(current_storage + id->func.offset);
          if ((id->run_time_type != PIKE_T_GET_SET) &&
    	  (id->run_time_type != PIKE_T_FREE))
    	check_short_svalue(u, id->run_time_type);
        }
      }
    }
    
    void check_object(struct object *o)
    {
      int e;
      struct program *p;
      debug_malloc_touch(o);
      debug_malloc_touch(o->storage);
    
      if(o == Pike_compiler->fake_object) return;
    
      if(o->next)
      {
        if (o->next == o)
        {
          describe(o);
          Pike_fatal("Object check: o->next == o\n");
        }
        if (o->next->prev !=o)
        {
          describe(o);
          Pike_fatal("Object check: o->next->prev != o\n");
        }
      }
    
      if(o->prev)
      {
        if (o->prev == o)
        {
          describe(o);
          Pike_fatal("Object check: o->prev == o\n");
        }
        if(o->prev->next != o)
        {
          describe(o);
          Pike_fatal("Object check: o->prev->next != o\n");
        }
        
        if(o == first_object)
          Pike_fatal("Object check: o->prev !=0 && first_object == o\n");
      } else {
        if(first_object != o)
          Pike_fatal("Object check: o->prev ==0 && first_object != o\n");
      }
      
      if(o->refs <= 0)
        Pike_fatal("Object refs <= zero.\n");
    
      if(!(p=o->prog)) return;
    
      if(id_to_program(o->prog->id) != o->prog)
        Pike_fatal("Object's program not in program list.\n");
    
      if(!(o->prog->flags & PROGRAM_PASS_1_DONE)) return;
    
      for(e=p->num_inherits-1; e>=0; e--)
      {
        check_object_context(o,
    			 p->inherits[e].prog,
    			 o->storage + p->inherits[e].storage_offset);
      }
    }
    
    void check_all_objects(void)
    {
      struct object *o, *next;
      for(o=first_object;o;o=next)
      {
        add_ref(o);
        check_object(o);
        SET_NEXT_AND_FREE(o,free_object);
      }
    
      for(o=objects_to_destruct;o;o=o->next)
        if(o->refs)
          Pike_fatal("Object to be destructed has references.\n");
    }
    
    #endif