/*\ ||| This file a part of Pike, and is copyright by Fredrik Hubinette ||| Pike is distributed as GPL (General Public License) ||| See the files COPYING and DISCLAIMER for more information. \*/ #include "global.h" RCSID("$Id: object.c,v 1.15 1997/03/12 12:15:28 hubbe Exp $"); #include "object.h" #include "dynamic_buffer.h" #include "interpret.h" #include "program.h" #include "stralloc.h" #include "svalue.h" #include "macros.h" #include "memory.h" #include "error.h" #include "main.h" #include "array.h" #include "gc.h" #include "backend.h" #include "callback.h" struct object *master_object = 0; struct program *master_program =0; struct object *first_object; struct object fake_object = { 1 }; /* start with one reference */ void setup_fake_object() { fake_object.prog=&fake_program; fake_object.next=0; fake_object.refs=0xffffff; } struct object *low_clone(struct program *p) { int e; struct object *o; struct frame frame; GC_ALLOC(); o=(struct object *)xalloc(sizeof(struct object)-1+p->storage_needed); o->prog=p; p->refs++; o->next=first_object; o->prev=0; if(first_object) first_object->prev=o; first_object=o; o->refs=1; frame.parent_frame=fp; frame.current_object=o; frame.locals=0; frame.fun=-1; frame.pc=0; fp= & frame; frame.current_object->refs++; /* clear globals and call C initializers */ for(e=p->num_inherits-1; e>=0; e--) { int d; frame.context=p->inherits[e]; frame.context.prog->refs++; frame.current_storage=o->storage+frame.context.storage_offset; for(d=0;d<(int)frame.context.prog->num_identifiers;d++) { if(!IDENTIFIER_IS_VARIABLE(frame.context.prog->identifiers[d].flags)) continue; if(frame.context.prog->identifiers[d].run_time_type == T_MIXED) { struct svalue *s; s=(struct svalue *)(frame.current_storage + frame.context.prog->identifiers[d].func.offset); s->type=T_INT; s->u.integer=0; s->subtype=0; }else{ union anything *u; u=(union anything *)(frame.current_storage + frame.context.prog->identifiers[d].func.offset); MEMSET((char *)u,0,sizeof(*u)); } } if(frame.context.prog->init) frame.context.prog->init(o); free_program(frame.context.prog); } free_object(frame.current_object); fp = frame.parent_frame; return o; } static void init_object(struct object *o, int args) { apply_lfun(o,LFUN___INIT,0); pop_stack(); apply_lfun(o,LFUN_CREATE,args); pop_stack(); } struct object *clone_object(struct program *p, int args) { struct object *o=low_clone(p); init_object(o,args); return o; } struct object *get_master() { extern char *master_file; struct pike_string *master_name; static int inside=0; if(master_object && master_object->prog) return master_object; if(inside) return 0; if(master_object) { free_object(master_object); master_object=0; } inside = 1; if(!master_program) { master_name=make_shared_string(master_file); master_program=compile_file(master_name); free_string(master_name); if(!master_program) return 0; } master_object=clone_object(master_program,0); apply_lfun(master_object,LFUN___INIT,0); pop_stack(); apply_lfun(master_object,LFUN_CREATE,0); pop_stack(); inside = 0; return master_object; } struct object *master() { struct object *o; o=get_master(); if(!o) fatal("Couldn't load master object.\n"); return o; } void destruct(struct object *o) { int e; struct frame frame; struct program *p; #ifdef DEBUG if(d_flag > 20) do_debug(); #endif if(!o || !(p=o->prog)) return; /* Object already destructed */ o->refs++; if(o->prog->lfuns[LFUN_DESTROY] != -1) { safe_apply_low(o, o->prog->lfuns[LFUN_DESTROY], 0); pop_stack(); } /* destructed in destroy() */ if(!o->prog) { free_object(o); return; } o->prog=0; frame.parent_frame=fp; frame.current_object=o; /* refs already updated */ frame.locals=0; frame.fun=-1; frame.pc=0; fp= & frame; /* free globals and call C de-initializers */ for(e=p->num_inherits-1; e>=0; e--) { int d; frame.context=p->inherits[e]; frame.context.prog->refs++; frame.current_storage=o->storage+frame.context.storage_offset; if(frame.context.prog->exit) frame.context.prog->exit(o); for(d=0;d<(int)frame.context.prog->num_identifiers;d++) { if(!IDENTIFIER_IS_VARIABLE(frame.context.prog->identifiers[d].flags)) continue; if(frame.context.prog->identifiers[d].run_time_type == T_MIXED) { struct svalue *s; s=(struct svalue *)(frame.current_storage + frame.context.prog->identifiers[d].func.offset); free_svalue(s); }else{ union anything *u; u=(union anything *)(frame.current_storage + frame.context.prog->identifiers[d].func.offset); free_short_svalue(u, frame.context.prog->identifiers[d].run_time_type); } } free_program(frame.context.prog); } free_object(frame.current_object); fp = frame.parent_frame; free_program(p); } static struct object *objects_to_destruct = 0; static struct callback *destruct_object_evaluator_callback =0; /* This function destructs the objects that are scheduled to be * destructed by really_free_object. It links the object back into the * list of objects first. Adds a reference, destructs it and then frees it. */ void destruct_objects_to_destruct() { struct object *o, *next; while(o=objects_to_destruct) { /* Link object back to list of objects */ objects_to_destruct=o->next; if(first_object) first_object->prev=o; o->next=first_object; first_object=o; o->prev=0; o->refs++; /* Don't free me now! */ destruct(o); free_object(o); } objects_to_destruct=0; if(destruct_object_evaluator_callback) { remove_callback(destruct_object_evaluator_callback); destruct_object_evaluator_callback=0; } } /* really_free_objects: * 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. */ void really_free_object(struct object *o) { if(o->prev) o->prev->next=o->next; else first_object=o->next; if(o->next) o->next->prev=o->prev; if(o->prog) { if(!objects_to_destruct) { destruct_object_evaluator_callback= add_to_callback(&evaluator_callbacks, (callback_func)destruct_objects_to_destruct, 0,0); } o->next=objects_to_destruct; o->prev=0; objects_to_destruct=o; } else { free((char *)o); GC_FREE(); } } void low_object_index_no_free(struct svalue *to, struct object *o, INT32 f) { struct identifier *i; struct program *p=o->prog; if(!p) error("Cannot access global variables in destructed object.\n"); i=ID_FROM_INT(p, f); switch(i->flags & (IDENTIFIER_FUNCTION | IDENTIFIER_CONSTANT)) { case IDENTIFIER_FUNCTION: case IDENTIFIER_C_FUNCTION: case IDENTIFIER_PIKE_FUNCTION: to->type=T_FUNCTION; to->subtype=f; to->u.object=o; o->refs++; break; case IDENTIFIER_CONSTANT: { struct svalue *s; s=PROG_FROM_INT(p,f)->constants + i->func.offset; check_destructed(s); assign_svalue_no_free(to, s); break; } case 0: if(i->run_time_type == T_MIXED) { struct svalue *s; s=(struct svalue *)LOW_GET_GLOBAL(o,f,i); check_destructed(s); assign_svalue_no_free(to, s); } else { union anything *u; u=(union anything *)LOW_GET_GLOBAL(o,f,i); check_short_destructed(u,i->run_time_type); assign_from_short_svalue_no_free(to, u, i->run_time_type); } } } void object_index_no_free2(struct svalue *to, struct object *o, struct svalue *index) { struct program *p; int f; if(!o || !(p=o->prog)) { error("Lookup in destructed object.\n"); return; /* make gcc happy */ } if(index->type != T_STRING) error("Lookup on non-string value.\n"); f=find_shared_string_identifier(index->u.string, p); 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) void object_index_no_free(struct svalue *to, struct object *o, struct svalue *index) { struct program *p; int lfun; if(!o || !(p=o->prog)) { error("Lookup in destructed object.\n"); return; /* make gcc happy */ } lfun=ARROW_INDEX_P(index) ? LFUN_ARROW : LFUN_INDEX; if(p->lfuns[lfun] != -1) { push_svalue(index); apply_lfun(o,lfun,1); to=sp; sp--; } else { object_index_no_free2(to,o,index); } } static void object_low_set_index(struct object *o, int f, struct svalue *from) { struct identifier *i; struct program *p; if(!o || !(p=o->prog)) { error("Lookup in destructed object.\n"); return; /* make gcc happy */ } check_destructed(from); i=ID_FROM_INT(p, f); if(!IDENTIFIER_IS_VARIABLE(i->flags)) { error("Cannot assign functions or constants.\n"); } else if(i->run_time_type == T_MIXED) { assign_svalue((struct svalue *)LOW_GET_GLOBAL(o,f,i),from); } else { assign_to_short_svalue((union anything *) LOW_GET_GLOBAL(o,f,i), i->run_time_type, from); } } void object_set_index2(struct object *o, struct svalue *index, struct svalue *from) { struct program *p; int f; if(!o || !(p=o->prog)) { error("Lookup in destructed object.\n"); return; /* make gcc happy */ } if(index->type != T_STRING) error("Lookup on non-string value.\n"); f=find_shared_string_identifier(index->u.string, p); if(f < 0) { error("No such variable (%s) in object.\n", index->u.string->str); }else{ object_low_set_index(o, f, from); } } void object_set_index(struct object *o, struct svalue *index, struct svalue *from) { struct program *p; int lfun; if(!o || !(p=o->prog)) { error("Lookup in destructed object.\n"); return; /* make gcc happy */ } lfun=ARROW_INDEX_P(index) ? LFUN_ASSIGN_ARROW : LFUN_ASSIGN_INDEX; if(p->lfuns[lfun] != -1) { push_svalue(index); push_svalue(from); apply_lfun(o,lfun,2); pop_stack(); } else { object_set_index2(o,index,from); } } static union anything *object_low_get_item_ptr(struct object *o, int f, TYPE_T type) { struct identifier *i; struct program *p; if(!o || !(p=o->prog)) { error("Lookup in destructed object.\n"); return 0; /* make gcc happy */ } i=ID_FROM_INT(p, f); if(!IDENTIFIER_IS_VARIABLE(i->flags)) { 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, struct svalue *index, TYPE_T type) { struct program *p; int f; if(!o || !(p=o->prog)) { error("Lookup in destructed object.\n"); return 0; /* make gcc happy */ } f=ARROW_INDEX_P(index) ? LFUN_ASSIGN_ARROW : LFUN_ASSIGN_INDEX; if(p->lfuns[f] != -1) error("Cannot do incremental operations on overloaded index (yet).\n"); if(index->type != T_STRING) error("Lookup on non-string value.\n"); f=find_shared_string_identifier(index->u.string, p); if(f < 0) { error("No such variable in object.\n"); }else{ return object_low_get_item_ptr(o, f, type); } return 0; } #ifdef DEBUG void verify_all_objects() { struct object *o; struct frame frame; for(o=first_object;o;o=o->next) { if(o->next && o->next->prev !=o) fatal("Object check: o->next->prev != o\n"); if(o->prev) { if(o->prev->next != o) fatal("Object check: o->prev->next != o\n"); if(o == first_object) fatal("Object check: o->prev !=0 && first_object == o\n"); } else { if(first_object != o) fatal("Object check: o->prev ==0 && first_object != o\n"); } if(o->refs <= 0) fatal("Object refs <= zero.\n"); if(o->prog) { extern struct program *first_program; struct program *p; int e; for(p=first_program;p!=o->prog;p=p->next) if(!p) fatal("Object's program not in program list.\n"); for(e=0;e<(int)o->prog->num_identifiers;e++) { struct identifier *i; i=ID_FROM_INT(o->prog, e); if(!IDENTIFIER_IS_VARIABLE(i->flags)) continue; if(i->run_time_type == T_MIXED) { check_svalue((struct svalue *)LOW_GET_GLOBAL(o,e,i)); }else{ check_short_svalue((union anything *)LOW_GET_GLOBAL(o,e,i), i->run_time_type); } } frame.parent_frame=fp; frame.current_object=o; frame.locals=0; frame.fun=-1; frame.pc=0; fp= & frame; frame.current_object->refs++; for(e=0;e<(int)o->prog->num_inherits;e++) { frame.context=o->prog->inherits[e]; frame.context.prog->refs++; frame.current_storage=o->storage+frame.context.storage_offset; } free_object(frame.current_object); fp = frame.parent_frame; } } } #endif 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; if(a->prog) { int e; for(e=0;e<(int)a->prog->num_identifiers;e++) { struct identifier *i; i=ID_FROM_INT(a->prog, e); if(!IDENTIFIER_IS_VARIABLE(i->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; } void cleanup_objects() { struct object *o, *next; for(o=first_object;o;o=next) { o->refs++; destruct(o); next=o->next; free_object(o); } destruct_objects_to_destruct(); free_object(master_object); master_object=0; free_program(master_program); master_program=0; } struct array *object_indices(struct object *o) { struct program *p; struct array *a; int e; p=o->prog; if(!p) error("indices() on destructed object.\n"); if(p->lfuns[LFUN__INDICES]==-1) { a=allocate_array_no_init(p->num_identifier_indexes,0); for(e=0;e<(int)p->num_identifier_indexes;e++) { copy_shared_string(ITEM(a)[e].u.string, ID_FROM_INT(p,p->identifier_index[e])->name); ITEM(a)[e].type=T_STRING; } }else{ apply_lfun(o, LFUN__INDICES, 0); if(sp[-1].type != T_ARRAY) error("Bad return type from o->_indices()\n"); a=sp[-1].u.array; sp--; } return a; } struct array *object_values(struct object *o) { struct program *p; struct array *a; int e; p=o->prog; if(!p) error("values() on destructed object.\n"); if(p->lfuns[LFUN__INDICES]==-1) { a=allocate_array_no_init(p->num_identifier_indexes,0); for(e=0;e<(int)p->num_identifier_indexes;e++) { low_object_index_no_free(ITEM(a)+e, o, p->identifier_index[e]); } }else{ apply_lfun(o, LFUN__VALUES, 0); if(sp[-1].type != T_ARRAY) error("Bad return type from o->_values()\n"); a=sp[-1].u.array; sp--; } return a; } #ifdef GC2 void gc_mark_object_as_referenced(struct object *o) { if(gc_mark(o)) { if(o->prog) { INT32 e; for(e=0;e<(int)o->prog->num_identifier_indexes;e++) { struct identifier *i; i=ID_FROM_INT(o->prog, e); if(!IDENTIFIER_IS_VARIABLE(i->flags)) continue; if(i->run_time_type == T_MIXED) { gc_mark_svalues((struct svalue *)LOW_GET_GLOBAL(o,e,i),1); }else{ gc_mark_short_svalue((union anything *)LOW_GET_GLOBAL(o,e,i), i->run_time_type); } } } } } void gc_check_all_objects() { struct object *o; for(o=first_object;o;o=o->next) { if(o->prog) { INT32 e; for(e=0;e<(int)o->prog->num_identifier_indexes;e++) { struct identifier *i; i=ID_FROM_INT(o->prog, e); if(!IDENTIFIER_IS_VARIABLE(i->flags)) continue; if(i->run_time_type == T_MIXED) { gc_check_svalues((struct svalue *)LOW_GET_GLOBAL(o,e,i),1); }else{ gc_check_short_svalue((union anything *)LOW_GET_GLOBAL(o,e,i), i->run_time_type); } } } } } void gc_mark_all_objects() { struct object *o; for(o=first_object;o;o=o->next) if(gc_is_referenced(o)) gc_mark_object_as_referenced(o); } void gc_free_all_unreferenced_objects() { struct object *o,*next; for(o=first_object;o;o=next) { if(gc_do_free(o)) { o->refs++; destruct(o); next=o->next; free_object(o); }else{ next=o->next; } } } #endif /* GC2 */ void count_memory_in_objects(INT32 *num_, INT32 *size_) { INT32 num=0, size=0; struct object *o; for(o=first_object;o;o=o->next) { num++; if(o->prog) { size+=sizeof(struct object)-1+o->prog->storage_needed; }else{ size+=sizeof(struct object); } } for(o=objects_to_destruct;o;o=o->next) { num++; if(o->prog) { size+=sizeof(struct object)-1+o->prog->storage_needed; }else{ size+=sizeof(struct object); } } *num_=num; *size_=size; }