void print_exception(VM *vm, VALUE result) { // TODO: fprintf to stderr and teach dump_titled to optionally fprintf to stderr too printf("====== Exception of type '%s' ======\n", obj_to_cstring(NGS_TYPE_NAME(NORMAL_TYPE_INSTANCE_TYPE(result)))); // TODO: maybe macro to iterate attributes VALUE fields = NGS_TYPE_FIELDS(NORMAL_TYPE_INSTANCE_TYPE(result)); HASH_OBJECT_ENTRY *e; for(e=HASH_HEAD(fields); e; e=e->insertion_order_next) { if(obj_is_of_type(ARRAY_ITEMS(NORMAL_TYPE_INSTANCE_FIELDS(result))[GET_INT(e->val)], vm->Backtrace)) { printf("=== [ backtrace ] ===\n"); // Backtrace.frames = [{"closure": ..., "ip": ...}, ...] VALUE backtrace = ARRAY_ITEMS(NORMAL_TYPE_INSTANCE_FIELDS(result))[GET_INT(e->val)]; VALUE frames; assert(get_normal_type_instace_attribute(backtrace, make_string("frames"), &frames) == METHOD_OK); unsigned int i; for(i = 0; i < OBJ_LEN(frames); i++) { VALUE frame, resolved_ip, ip; frame = ARRAY_ITEMS(frames)[i]; H(ip, frame, "ip"); resolved_ip = resolve_ip(vm, (IP)(GET_INT(ip) - 1)); if(IS_HASH(resolved_ip)) { VALUE file, first_line, first_column, last_line, last_column; HASH_OBJECT_ENTRY *closure_entry; char *closure_name = "<anonymous>"; H(file, resolved_ip, "file"); H(first_line, resolved_ip, "first_line"); H(first_column, resolved_ip, "first_column"); H(last_line, resolved_ip, "last_line"); H(last_column, resolved_ip, "last_column"); closure_entry = get_hash_key(frame, make_string("closure")); if(closure_entry && IS_CLOSURE(closure_entry->val) && (IS_HASH(CLOSURE_OBJ_ATTRS(closure_entry->val)))) { HASH_OBJECT_ENTRY *name_entry; name_entry = get_hash_key(CLOSURE_OBJ_ATTRS(closure_entry->val), make_string("name")); if(name_entry) { closure_name = obj_to_cstring(name_entry->val); } } // TODO: fix types printf("[Frame #%u] %s:%d:%d - %d:%d [in %s]\n", i, obj_to_cstring(file), (int) GET_INT(first_line), (int) GET_INT(first_column), (int) GET_INT(last_line), (int) GET_INT(last_column), closure_name); } else { printf("[Frame #%u] (no source location)\n", i); } } continue; } if(obj_is_of_type(ARRAY_ITEMS(NORMAL_TYPE_INSTANCE_FIELDS(result))[GET_INT(e->val)], vm->Exception)) { assert(IS_STRING(e->key)); printf("---8<--- %s - start ---8<---\n", obj_to_cstring(e->key)); print_exception(vm, ARRAY_ITEMS(NORMAL_TYPE_INSTANCE_FIELDS(result))[GET_INT(e->val)]); printf("---8<--- %s - end ---8<---\n", obj_to_cstring(e->key)); continue; } if(IS_STRING(e->key)) { dump_titled(obj_to_cstring(e->key), ARRAY_ITEMS(NORMAL_TYPE_INSTANCE_FIELDS(result))[GET_INT(e->val)]); } else { // Should not happen dump_titled("attribute key", e->key); dump_titled("attribute value", ARRAY_ITEMS(NORMAL_TYPE_INSTANCE_FIELDS(result))[GET_INT(e->val)]); } } }
static void _dump(VALUE v, int level) { char **symbols; void *symbols_buffer[1]; VALUE *ptr; size_t i; HASH_OBJECT_ENTRY *e; HASH_OBJECT_ENTRY **buckets; if(IS_NULL(v)) { printf("%*s* null\n", level << 1, ""); goto exit; } if(IS_TRUE(v)) { printf("%*s* true\n", level << 1, ""); goto exit; } if(IS_FALSE(v)) { printf("%*s* false\n", level << 1, ""); goto exit; } if(IS_UNDEF(v)) { printf("%*s* undef\n", level << 1, ""); goto exit; } if(IS_KWARGS_MARKER(v)){ printf("%*s* kwargs marker\n", level << 1, ""); goto exit; } if(IS_INT(v)) { printf("%*s* int %" VALUE_NUM_FMT "\n", level << 1, "", GET_INT(v)); goto exit; } if(IS_REAL(v)) { printf("%*s* real %g\n", level << 1, "", REAL_OBJECT_VAL(v)); goto exit; } if(IS_STRING(v)) { // TODO: properly handle // 1. non-printable characters // 2. zero character printf("%*s* string(len=%zu) %.*s\n", level << 1, "", OBJ_LEN(v), (int) OBJ_LEN(v), (char *)OBJ_DATA_PTR(v)); goto exit; } if(IS_NATIVE_METHOD(v)) { symbols_buffer[0] = OBJ_DATA_PTR(v); symbols = backtrace_symbols(symbols_buffer, 1); printf("%*s* native method %s at %p req_params=%d\n", level << 1, "", symbols[0], OBJ_DATA_PTR(v), NATIVE_METHOD_OBJ_N_REQ_PAR(v)); for(i=0; i<NATIVE_METHOD_OBJ_N_REQ_PAR(v); i++) { printf("%*s* required parameter %zu\n", (level+1) << 1, "", i+1); _dump(NATIVE_METHOD_OBJ_PARAMS(v)[i*2+0], level+2); _dump(NATIVE_METHOD_OBJ_PARAMS(v)[i*2+1], level+2); } goto exit; } if(IS_CLOSURE(v)) { printf("%*s* closure name=%s ip=%zu locals_including_params=%d req_params=%d opt_params=%d n_uplevels=%d params_flags=%d\n", level << 1, "", IS_NULL(CLOSURE_OBJ_NAME(v)) ? "(none)" : obj_to_cstring(CLOSURE_OBJ_NAME(v)), CLOSURE_OBJ_IP(v), CLOSURE_OBJ_N_LOCALS(v), CLOSURE_OBJ_N_REQ_PAR(v), CLOSURE_OBJ_N_OPT_PAR(v), CLOSURE_OBJ_N_UPLEVELS(v), CLOSURE_OBJ_PARAMS_FLAGS(v) ); for(i=0; i<CLOSURE_OBJ_N_REQ_PAR(v); i++) { printf("%*s* required parameter %zu (name and type follow)\n", (level+1) << 1, "", i+1); _dump(CLOSURE_OBJ_PARAMS(v)[i*2+0], level+2); _dump(CLOSURE_OBJ_PARAMS(v)[i*2+1], level+2); } for(i=0; i<CLOSURE_OBJ_N_OPT_PAR(v); i++) { printf("%*s* optional parameter %zu (name, type and default value follow)\n", (level+1) << 1, "", i+1); _dump(CLOSURE_OBJ_PARAMS(v)[CLOSURE_OBJ_N_REQ_PAR(v)*2 + i*3 + 0], level+2); _dump(CLOSURE_OBJ_PARAMS(v)[CLOSURE_OBJ_N_REQ_PAR(v)*2 + i*3 + 1], level+2); _dump(CLOSURE_OBJ_PARAMS(v)[CLOSURE_OBJ_N_REQ_PAR(v)*2 + i*3 + 2], level+2); } i = CLOSURE_OBJ_N_REQ_PAR(v)*2 + CLOSURE_OBJ_N_OPT_PAR(v)*3; if(CLOSURE_OBJ_PARAMS_FLAGS(v) & PARAMS_FLAG_ARR_SPLAT) { printf("%*s* array splat parameter\n", (level+1) << 1, ""); _dump(CLOSURE_OBJ_PARAMS(v)[i+0], level+2); _dump(CLOSURE_OBJ_PARAMS(v)[i+1], level+2); i+=3; } if(CLOSURE_OBJ_PARAMS_FLAGS(v) & PARAMS_FLAG_HASH_SPLAT) { printf("%*s* hash splat parameter\n", (level+1) << 1, ""); _dump(CLOSURE_OBJ_PARAMS(v)[i+0], level+2); _dump(CLOSURE_OBJ_PARAMS(v)[i+1], level+2); i+=3; } goto exit; } if(IS_ARRAY(v)) { printf("%*s* array of length %zu\n", level << 1, "", OBJ_LEN(v)); for(i=0, ptr=(VALUE *)OBJ_DATA_PTR(v); i<OBJ_LEN(v); i++, ptr++) { _dump(*ptr, level+1); } goto exit; } if(IS_HASH(v)) { printf("%*s* hash with total of %zu items in %zu buckets at %p\n", level << 1, "", OBJ_LEN(v), HASH_BUCKETS_N(v), OBJ_DATA_PTR(v)); buckets = OBJ_DATA_PTR(v); for(i=0; i<HASH_BUCKETS_N(v); i++) { if(!buckets[i]) { continue; } printf("%*s* bucket # %zu\n", (level+1) << 1, "", i); for(e=buckets[i]; e; e=e->bucket_next) { printf("%*s* item at %p with hash() of %u insertion_order_prev=%p insertion_order_next=%p \n", (level+2) << 1, "", (void *)e, e->hash, (void *)e->insertion_order_prev, (void *)e->insertion_order_next); printf("%*s* key\n", (level+3) << 1, ""); _dump(e->key, level+4); printf("%*s* value\n", (level+3) << 1, ""); _dump(e->val, level+4); } } goto exit; } if(IS_NGS_TYPE(v)) { printf("%*s* type (name and optionally constructors and parents follow) id=%" PRIdPTR " ptr=%p\n", level << 1, "", NGS_TYPE_ID(v), IS_NORMAL_TYPE(v) ? v.ptr : 0); _dump(NGS_TYPE_NAME(v), level + 1); if(level < 3) { _dump(NGS_TYPE_FIELDS(v), level + 1); _dump(NGS_TYPE_CONSTRUCTORS(v), level + 1); _dump(NGS_TYPE_PARENTS(v), level + 1); } goto exit; } if(IS_CLIB(v)) { printf("%*s* C library (name follows) ptr=%p\n", level << 1, "", OBJ_DATA_PTR(v)); _dump(CLIB_OBJECT_NAME(v), level + 1); goto exit; } if(IS_CSYM(v)) { printf("%*s* C symbol (name and libraray follow) ptr=%p\n", level << 1, "", OBJ_DATA_PTR(v)); _dump(CSYM_OBJECT_NAME(v), level + 1); _dump(CSYM_OBJECT_LIB(v), level + 1); goto exit; } if(IS_NORMAL_TYPE_CONSTRUCTOR(v)) { printf("%*s* user type constructor (type optionally follows)\n", level << 1, ""); if(level < 3) { _dump(OBJ_DATA(v), level + 1); } goto exit; } if(IS_NORMAL_TYPE_INSTANCE(v)) { printf("%*s* user type instance (type and fields optionally follow)\n", level << 1, ""); // level < 4 so that uncaught exception ImplNotFound could display the type of the arguments if(level < 4) { _dump(NORMAL_TYPE_INSTANCE_TYPE(v), level + 1); _dump(NORMAL_TYPE_INSTANCE_FIELDS(v), level + 1); } goto exit; } printf("%*s* (dump not implemented for the object at %p)\n", level << 1, "", OBJ_DATA_PTR(v)); exit: return; }