|
|
|
|
|
|
#define NO_PIKE_SHORTHAND |
#include "global.h" |
#include "pike_macros.h" |
#include "pike_error.h" |
#include "interpret.h" |
#include "stralloc.h" |
#include "builtin_functions.h" |
#include "array.h" |
#include "object.h" |
#include "main.h" |
#include "builtin_functions.h" |
#include "backend.h" |
#include "operators.h" |
#include "module_support.h" |
#include "threads.h" |
#include "gc.h" |
|
RCSID("$Id: error.c,v 1.67 2000/12/05 21:08:17 per Exp $"); |
|
#undef ATTRIBUTE |
#define ATTRIBUTE(X) |
|
|
|
|
|
struct pike_backtrace |
{ |
struct pike_frame *trace; |
struct pike_frame *iterator; |
int iterator_index; |
}; |
|
static struct program *frame_program = NULL; |
|
#define THIS_BT ((struct pike_backtrace *)Pike_fp->current_storage) |
|
static void init_backtrace(struct object *o) |
{ |
MEMSET(THIS_BT, 0, sizeof(struct pike_backtrace)); |
} |
|
static void exit_backtrace(struct object *o) |
{ |
if (THIS_BT->trace) { |
free_pike_frame(THIS_BT->trace); |
THIS_BT->trace = NULL; |
THIS_BT->iterator = NULL; |
} |
} |
|
|
void f_bt_create(INT32 args) |
{ |
if (THIS_BT->trace) { |
free_pike_frame(THIS_BT->trace); |
} |
add_ref(THIS_BT->trace = Pike_fp); |
THIS_BT->iterator = THIS_BT->trace; |
THIS_BT->iterator_index = 0; |
pop_n_elems(args); |
push_int(0); |
} |
|
|
static void f_bt__sizeof(INT32 args) |
{ |
int size = 0; |
struct pike_frame *f = THIS_BT->iterator; |
size = THIS_BT->iterator_index; |
while (f) { |
size++; |
f = f->next; |
} |
pop_n_elems(args); |
push_int(size); |
} |
|
|
static void f_bt__indices(INT32 args) |
{ |
|
f_bt__sizeof(args); |
f_allocate(1); |
f_indices(1); |
} |
|
|
static void f_bt__values(INT32 args) |
{ |
struct pike_frame *f = THIS_BT->trace; |
struct array *a; |
int i; |
int sz; |
f_bt__sizeof(args); |
f_allocate(1); |
for (i=0, a=Pike_sp[-1].u.array, sz=a->size; (i < sz) && f; i++, f=f->next) { |
struct object *o = low_clone(frame_program); |
call_c_initializers(o); |
add_ref(*((struct pike_frame **)(o->storage)) = f); |
push_object(o); |
a->item[i] = Pike_sp[-1]; |
Pike_sp--; |
} |
} |
|
|
static void f_bt__index(INT32 args) |
{ |
INT_TYPE index; |
int i; |
struct pike_frame *f; |
struct object *o; |
|
get_all_args("backtrace->`[]", args, "%i", &index); |
|
if (index < 0) { |
|
index_error("backtrace->`[]", Pike_sp-args, args, NULL, Pike_sp-args, |
"Indexing with negative value (%d).\n", index); |
} |
|
if (index < THIS_BT->iterator_index) { |
THIS_BT->iterator = THIS_BT->trace; |
THIS_BT->iterator_index = 0; |
} |
|
f = THIS_BT->iterator; |
i = THIS_BT->iterator_index; |
|
while (f && (i < index)) { |
f = f->next; |
i++; |
} |
if (!f) { |
index_error("backtrace->`[]", Pike_sp-args, args, NULL, Pike_sp-args, |
"Index out of range [0..%d].\n", i-1); |
} |
THIS_BT->iterator = f; |
THIS_BT->iterator_index = i; |
o = low_clone(frame_program); |
call_c_initializers(o); |
add_ref(*((struct pike_frame **)(o->storage)) = f); |
pop_n_elems(args); |
push_object(o); |
} |
|
|
|
|
|
#define THIS_PF ((struct pike_frame **)Pike_fp->current_storage) |
|
static void init_pike_frame(struct object *o) |
{ |
THIS_PF[0] = NULL; |
} |
|
static void exit_pike_frame(struct object *o) |
{ |
if (THIS_PF[0]) { |
free_pike_frame(THIS_PF[0]); |
THIS_PF[0] = NULL; |
} |
} |
|
|
static void f_pf__index(INT32 args) |
{ |
INT_TYPE index; |
struct pike_frame *f; |
|
get_all_args("pike_frame->`[]", args, "%i", &index); |
|
f = THIS_PF[0]; |
|
if (!f) { |
index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args, |
"Indexing the empty array with %d.\n", index); |
} |
if (index < 0) { |
index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args, |
"Indexing with negative index (%d)\n", index); |
} |
if (!(f->current_object && f->context.prog)) { |
index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args, |
"Indexing the NULL value with %d.\n", index); |
} |
switch(index) { |
case 0: |
case 1: |
break; |
case 2: |
if (f->current_object->prog) { |
pop_n_elems(args); |
ref_push_object(f->current_object); |
Pike_sp[-1].subtype = f->fun; |
Pike_sp[-1].type = PIKE_T_FUNCTION; |
} else { |
pop_n_elems(args); |
push_int(0); |
Pike_sp[-1].subtype = NUMBER_DESTRUCTED; |
} |
return; |
default: |
break; |
} |
} |
|
|
|
|
|
JMP_BUF *recoveries=0; |
|
#ifdef PIKE_DEBUG |
PMOD_EXPORT void check_recovery_context(void) |
{ |
char foo; |
#define TESTILITEST ((((char *)Pike_interpreter.recoveries)-((char *)&foo))*STACK_DIRECTION) |
if(Pike_interpreter.recoveries && TESTILITEST > 0) { |
fprintf(stderr, "Recoveries is out biking (Pike_interpreter.recoveries=%p, Pike_sp=%p, %ld)!\n", |
Pike_interpreter.recoveries, &foo, |
DO_NOT_WARN((long)TESTILITEST)); |
fprintf(stderr, "Last recovery was added at %s:%d\n", |
Pike_interpreter.recoveries->file, |
Pike_interpreter.recoveries->line); |
fatal("Recoveries is out biking (Pike_interpreter.recoveries=%p, Pike_sp=%p, %ld)!\n", |
Pike_interpreter.recoveries, &foo, |
DO_NOT_WARN((long)TESTILITEST)); |
} |
|
|
} |
|
PMOD_EXPORT void pike_gdb_breakpoint(void) |
{ |
} |
#endif |
|
PMOD_EXPORT JMP_BUF *init_recovery(JMP_BUF *r DEBUG_LINE_ARGS) |
{ |
check_recovery_context(); |
#ifdef PIKE_DEBUG |
r->line=line; |
r->file=file; |
OED_FPRINTF((stderr, "init_recovery(%p) %s:%d\n", r, file, line)); |
#endif |
r->frame_pointer=Pike_fp; |
r->stack_pointer=Pike_sp-Pike_interpreter.evaluator_stack; |
r->mark_sp=Pike_mark_sp - Pike_interpreter.mark_stack; |
r->previous=Pike_interpreter.recoveries; |
r->onerror=0; |
r->severity=THROW_ERROR; |
Pike_interpreter.recoveries=r; |
check_recovery_context(); |
return r; |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void pike_throw(void) ATTRIBUTE((noreturn)) |
{ |
while(Pike_interpreter.recoveries && throw_severity > Pike_interpreter.recoveries->severity) |
{ |
while(Pike_interpreter.recoveries->onerror) |
{ |
(*Pike_interpreter.recoveries->onerror->func)(Pike_interpreter.recoveries->onerror->arg); |
Pike_interpreter.recoveries->onerror=Pike_interpreter.recoveries->onerror->previous; |
} |
|
Pike_interpreter.recoveries=Pike_interpreter.recoveries->previous; |
} |
|
if(!Pike_interpreter.recoveries) |
fatal("No error recovery context.\n"); |
|
#ifdef PIKE_DEBUG |
if(Pike_sp - Pike_interpreter.evaluator_stack < Pike_interpreter.recoveries->stack_pointer) |
fatal("Stack error in error.\n"); |
#endif |
|
while(Pike_fp != Pike_interpreter.recoveries->frame_pointer) |
{ |
#ifdef PIKE_DEBUG |
if(!Pike_fp) |
fatal("Popped out of stack frames.\n"); |
#endif |
POP_PIKE_FRAME(); |
} |
|
pop_n_elems(Pike_sp - Pike_interpreter.evaluator_stack - Pike_interpreter.recoveries->stack_pointer); |
Pike_mark_sp = Pike_interpreter.mark_stack + Pike_interpreter.recoveries->mark_sp; |
|
while(Pike_interpreter.recoveries->onerror) |
{ |
(*Pike_interpreter.recoveries->onerror->func)(Pike_interpreter.recoveries->onerror->arg); |
Pike_interpreter.recoveries->onerror=Pike_interpreter.recoveries->onerror->previous; |
} |
|
longjmp(Pike_interpreter.recoveries->recovery,1); |
} |
|
PMOD_EXPORT void push_error(char *description) |
{ |
push_text(description); |
f_backtrace(0); |
f_aggregate(2); |
} |
|
PMOD_EXPORT struct svalue throw_value = { PIKE_T_INT }; |
int throw_severity; |
static const char *in_error; |
|
PMOD_EXPORT DECLSPEC(noreturn) void low_error(const char *buf) ATTRIBUTE((noreturn)) |
{ |
push_error(buf); |
free_svalue(& throw_value); |
throw_value = *--Pike_sp; |
throw_severity = THROW_ERROR; |
in_error=0; |
pike_throw(); |
} |
|
|
|
|
void DECLSPEC(noreturn) va_error(const char *fmt, va_list args) ATTRIBUTE((noreturn)) |
{ |
char buf[4096]; |
SWAP_IN_THREAD_IF_REQUIRED(); |
if(in_error) |
{ |
const char *tmp=in_error; |
in_error=0; |
fatal("Recursive error() calls, original error: %s",tmp); |
} |
|
in_error=buf; |
|
#ifdef HAVE_VSNPRINTF |
vsnprintf(buf, 4090, fmt, args); |
#else /* !HAVE_VSNPRINTF */ |
VSPRINTF(buf, fmt, args); |
#endif /* HAVE_VSNPRINTF */ |
|
if(!Pike_interpreter.recoveries) |
{ |
#ifdef PIKE_DEBUG |
dump_backlog(); |
#endif |
|
fprintf(stderr,"No error recovery context!\n%s",buf); |
exit(99); |
} |
|
if((size_t)strlen(buf) >= (size_t)sizeof(buf)) |
fatal("Buffer overflow in error()\n"); |
|
low_error(buf); |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void new_error(const char *name, const char *text, struct svalue *oldsp, |
INT32 args, const char *file, int line) ATTRIBUTE((noreturn)) |
{ |
int i; |
|
ASSERT_THREAD_SWAPPED_IN(); |
|
if(in_error) |
{ |
const char *tmp=in_error; |
in_error=0; |
fatal("Recursive error() calls, original error: %s",tmp); |
} |
|
in_error=text; |
|
if(!Pike_interpreter.recoveries) |
{ |
#ifdef PIKE_DEBUG |
dump_backlog(); |
#endif |
|
fprintf(stderr,"No error recovery context!\n%s():%s",name,text); |
if(file) |
fprintf(stderr,"at %s:%d\n",file,line); |
exit(99); |
} |
|
push_text(text); |
|
f_backtrace(0); |
|
if (file) { |
push_text(file); |
push_int(line); |
} else { |
push_int(0); |
push_int(0); |
} |
push_text(name); |
|
for (i=-args; i; i++) { |
push_svalue(oldsp + i); |
} |
|
f_aggregate(args + 3); |
f_aggregate(1); |
|
f_add(2); |
|
f_aggregate(2); |
|
free_svalue(& throw_value); |
throw_value = *--Pike_sp; |
throw_severity=THROW_ERROR; |
|
in_error=0; |
pike_throw(); |
} |
|
PMOD_EXPORT void exit_on_error(void *msg) |
{ |
ONERROR tmp; |
SET_ONERROR(tmp,fatal_on_error,"Fatal in exit_on_error!"); |
d_flag=0; |
|
fprintf(stderr,"%s\n",(char *)msg); |
#ifdef PIKE_DEBUG |
dump_backlog(); |
#endif |
fprintf(stderr,"%s\n",(char *)msg); |
#ifdef PIKE_DEBUG |
{ |
char *s; |
fprintf(stderr,"Attempting to dump raw error: (may fail)\n"); |
init_buf(); |
describe_svalue(&throw_value,0,0); |
s=simple_free_buf(); |
fprintf(stderr,"%s\n",s); |
free(s); |
} |
#endif |
exit(1); |
} |
|
#ifdef __NT__ |
|
static void do_abort() |
{ |
if (!d_flag && !getenv("PIKE_DEBUG")) { |
exit(-6); |
} |
abort(); |
} |
#else /* !__NT__ */ |
#define do_abort() abort() |
#endif /* __NT__ */ |
|
PMOD_EXPORT void fatal_on_error(void *msg) |
{ |
#ifdef PIKE_DEBUG |
dump_backlog(); |
#endif |
fprintf(stderr,"%s\n",(char *)msg); |
do_abort(); |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void Pike_error(const char *fmt,...) ATTRIBUTE((noreturn,format (printf, 1, 2))) |
{ |
va_list args; |
va_start(args,fmt); |
va_error(fmt,args); |
va_end(args); |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void debug_fatal(const char *fmt, ...) ATTRIBUTE((noreturn,format (printf, 1, 2))) |
{ |
va_list args; |
static int in_fatal = 0; |
|
va_start(args,fmt); |
|
if (in_fatal) |
{ |
(void)VFPRINTF(stderr, fmt, args); |
do_abort(); |
} |
|
in_fatal = 1; |
#ifdef PIKE_DEBUG |
dump_backlog(); |
#endif |
|
{ |
extern int Pike_in_gc; |
if(Pike_in_gc) |
{ |
fprintf(stderr,"Pike was in GC stage %d when this fatal occured:\n",Pike_in_gc); |
Pike_in_gc=0; |
} |
} |
|
(void)VFPRINTF(stderr, fmt, args); |
|
d_flag=t_flag=0; |
if(Pike_sp && Pike_interpreter.evaluator_stack) |
{ |
fprintf(stderr,"Attempting to dump backlog (may fail)...\n"); |
push_error("Backtrace at time of fatal:\n"); |
APPLY_MASTER("describe_backtrace",1); |
if(Pike_sp[-1].type==PIKE_T_STRING) |
write_to_stderr(Pike_sp[-1].u.string->str, Pike_sp[-1].u.string->len); |
}else{ |
fprintf(stderr,"No stack - no backtrace.\n"); |
} |
fflush(stderr); |
do_abort(); |
} |
|
#if 1 |
|
#define ERR_DECLARE |
#include "errors.h" |
|
|
void f_error_cast(INT32 args) |
{ |
char *s; |
get_all_args("error->cast",args,"%s",&s); |
if(!strncmp(s,"array",5)) |
{ |
pop_n_elems(args); |
ref_push_string(GENERIC_ERROR_THIS->desc); |
ref_push_array(GENERIC_ERROR_THIS->backtrace); |
f_aggregate(2); |
}else{ |
SIMPLE_BAD_ARG_ERROR("error->cast", 1, "the value \"array\""); |
} |
} |
|
void f_error_index(INT32 args) |
{ |
INT_TYPE ind; |
get_all_args("error->`[]",args,"%i",&ind); |
|
switch(ind) |
{ |
case 0: |
pop_n_elems(args); |
ref_push_string(GENERIC_ERROR_THIS->desc); |
break; |
case 1: |
pop_n_elems(args); |
ref_push_array(GENERIC_ERROR_THIS->backtrace); |
break; |
default: |
index_error("error->`[]", Pike_sp-args, args, NULL, Pike_sp-args, |
"Index %d is out of range 0 - 1.\n", ind); |
break; |
} |
} |
|
|
void f_error_describe(INT32 args) |
{ |
pop_n_elems(args); |
ref_push_object(Pike_fp->current_object); |
APPLY_MASTER("describe_backtrace",1); |
} |
|
void f_error_backtrace(INT32 args) |
{ |
pop_n_elems(args); |
ref_push_array(GENERIC_ERROR_THIS->backtrace); |
} |
|
#ifdef ERROR_DEBUG |
#define DWERROR(X) fprintf X |
#else /* !ERROR_DEBUG */ |
#define DWERROR(X) |
#endif /* ERROR_DEBUG */ |
|
#define INIT_ERROR(FEL)\ |
va_list foo; \ |
struct object *o; \ |
va_start(foo,desc); \ |
ASSERT_THREAD_SWAPPED_IN(); \ |
o=low_clone(PIKE_CONCAT(FEL,_error_program)); \ |
DWERROR((stderr, "%s(): Throwing a " #FEL " error\n", func)) |
|
#define ERROR_DONE(FOO) \ |
PIKE_CONCAT(FOO,_error_va(o,func, \ |
base_sp, args, \ |
desc,foo)); \ |
va_end(foo) |
|
#define ERROR_STRUCT(STRUCT,O) \ |
((struct PIKE_CONCAT(STRUCT,_error_struct) *)((O)->storage + PIKE_CONCAT(STRUCT,_error_offset))) |
|
#define ERROR_COPY(STRUCT,X) \ |
ERROR_STRUCT(STRUCT,o)->X=X |
|
#define ERROR_COPY_SVALUE(STRUCT,X) do { \ |
if (X) { \ |
assign_svalue_no_free( & ERROR_STRUCT(STRUCT,o)->X, X); \ |
} else { \ |
ERROR_STRUCT(STRUCT, o)->X.type = PIKE_T_INT; \ |
ERROR_STRUCT(STRUCT, o)->X.subtype = 0; \ |
ERROR_STRUCT(STRUCT, o)->X.u.integer = 0; \ |
} \ |
} while (0) |
|
|
#define ERROR_COPY_REF(STRUCT,X) \ |
add_ref( ERROR_STRUCT(STRUCT,o)->X=X ) |
|
|
DECLSPEC(noreturn) void generic_error_va(struct object *o, |
char *func, |
struct svalue *base_sp, int args, |
char *fmt, |
va_list foo) |
ATTRIBUTE((noreturn)) |
{ |
char buf[8192]; |
struct pike_string *desc; |
struct array *backtrace; |
int i; |
|
#ifdef HAVE_VSNPRINTF |
vsnprintf(buf, sizeof(buf)-1, fmt, foo); |
#else /* !HAVE_VSNPRINTF */ |
|
buf[sizeof(buf)-1] = '\0'; |
|
VSPRINTF(buf, fmt, foo); |
|
if(buf[sizeof(buf)-1]) |
fatal("Buffer overflow in error()\n"); |
#endif /* HAVE_VSNPRINTF */ |
in_error=buf; |
|
if (!master_program) { |
fprintf(stderr, "ERROR: %s\n", buf); |
} |
|
ERROR_STRUCT(generic,o)->desc=make_shared_string(buf); |
f_backtrace(0); |
|
if(func) |
{ |
push_int(0); |
push_int(0); |
push_text(func); |
|
for (i=0;i<args;i++) |
push_svalue(base_sp + i); |
f_aggregate(args + 3); |
f_aggregate(1); |
f_add(2); |
} |
|
if(Pike_sp[-1].type!=PIKE_T_ARRAY) |
fatal("Error failed to generate a backtrace!\n"); |
|
ERROR_STRUCT(generic,o)->backtrace=Pike_sp[-1].u.array; |
Pike_sp--; |
dmalloc_touch_svalue(Pike_sp); |
|
free_svalue(& throw_value); |
throw_value.type=PIKE_T_OBJECT; |
throw_value.u.object=o; |
throw_severity = THROW_ERROR; |
in_error=0; |
pike_throw(); |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void generic_error( |
char *func, |
struct svalue *base_sp, int args, |
char *desc, ...) ATTRIBUTE((noreturn,format (printf, 4, 5))) |
{ |
INIT_ERROR(generic); |
ERROR_DONE(generic); |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void index_error( |
char *func, |
struct svalue *base_sp, int args, |
struct svalue *val, |
struct svalue *ind, |
char *desc, ...) ATTRIBUTE((noreturn,format (printf, 6, 7))) |
{ |
INIT_ERROR(index); |
ERROR_COPY_SVALUE(index, val); |
ERROR_COPY_SVALUE(index, ind); |
ERROR_DONE(generic); |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void bad_arg_error( |
char *func, |
struct svalue *base_sp, int args, |
int which_arg, |
char *expected_type, |
struct svalue *got, |
char *desc, ...) ATTRIBUTE((noreturn,format (printf, 7, 8))) |
{ |
INIT_ERROR(bad_arg); |
ERROR_COPY(bad_arg, which_arg); |
ERROR_STRUCT(bad_arg,o)->expected_type=make_shared_string(expected_type); |
if(got) |
{ |
ERROR_COPY_SVALUE(bad_arg, got); |
}else{ |
ERROR_STRUCT(bad_arg,o)->got.type=PIKE_T_INT; |
ERROR_STRUCT(bad_arg,o)->got.subtype=NUMBER_UNDEFINED; |
ERROR_STRUCT(bad_arg,o)->got.u.integer=0; |
} |
DWERROR((stderr, "%s():Bad arg %d (expected %s)\n", |
func, which_arg, expected_type)); |
ERROR_DONE(generic); |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void math_error( |
char *func, |
struct svalue *base_sp, int args, |
struct svalue *number, |
char *desc, ...) ATTRIBUTE((noreturn,format (printf, 5, 6))) |
{ |
INIT_ERROR(math); |
if(number) |
{ |
ERROR_COPY_SVALUE(math, number); |
}else{ |
ERROR_STRUCT(math,o)->number.type=PIKE_T_INT; |
ERROR_STRUCT(math,o)->number.subtype=NUMBER_UNDEFINED; |
ERROR_STRUCT(math,o)->number.u.integer=0; |
} |
ERROR_DONE(generic); |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void resource_error( |
char *func, |
struct svalue *base_sp, int args, |
char *resource_type, |
size_t howmuch_, |
char *desc, ...) ATTRIBUTE((noreturn,format (printf, 6, 7))) |
{ |
INT_TYPE howmuch = DO_NOT_WARN((INT_TYPE)howmuch_); |
INIT_ERROR(resource); |
ERROR_COPY(resource, howmuch); |
ERROR_STRUCT(resource,o)->resource_type=make_shared_string(resource_type); |
ERROR_DONE(generic); |
} |
|
PMOD_EXPORT DECLSPEC(noreturn) void permission_error( |
char *func, |
struct svalue *base_sp, int args, |
char *permission_type, |
char *desc, ...) ATTRIBUTE((noreturn, format(printf, 5, 6))) |
{ |
INIT_ERROR(permission); |
ERROR_STRUCT(permission,o)->permission_type= |
make_shared_string(permission_type); |
ERROR_DONE(generic); |
} |
|
PMOD_EXPORT void wrong_number_of_args_error(char *name, int args, int expected) |
{ |
char *msg; |
if(expected>args) |
{ |
msg="Too few arguments"; |
}else{ |
msg="Too many arguments"; |
} |
|
new_error(name, msg, Pike_sp-args, args, 0,0); |
} |
|
#ifdef PIKE_DEBUG |
static void gc_check_throw_value(struct callback *foo, void *bar, void *gazonk) |
{ |
debug_gc_xmark_svalues(&throw_value,1," in the throw value"); |
} |
#endif |
|
void init_error(void) |
{ |
#define ERR_SETUP |
#include "errors.h" |
|
#ifdef PIKE_DEBUG |
dmalloc_accept_leak(add_gc_callback(gc_check_throw_value,0,0)); |
#endif |
} |
|
void cleanup_error(void) |
{ |
#define ERR_CLEANUP |
#include "errors.h" |
} |
#endif |
|
|