/* * Get the source file information for a given code pool offset. If debug * records aren't available for the given location, returns nil. Returns * a list containing the source file information: the first element is a * string giving the name of the file, and the second element is an * integer giving the line number in the file. Returns nil if no source * information is available for the given byte code location. */ void CVmBifT3::get_source_info(VMG_ const uchar *entry_addr, ulong method_ofs, vm_val_t *retval) { CVmFuncPtr func_ptr; CVmDbgLinePtr line_ptr; const uchar *stm_start, *stm_end; CVmObjList *lst; vm_val_t ele; CVmSrcfEntry *srcf; CVmObjString *str; const char *fname; size_t map_len; /* presume we won't be able to find source information for the location */ retval->set_nil(); /* set up a debug table pointer for the function or method */ func_ptr.set(entry_addr); /* get the debug information for the given location */ if (!CVmRun::get_stm_bounds(vmg_ &func_ptr, method_ofs, &line_ptr, &stm_start, &stm_end)) { /* no source information available - return failure */ return; } /* get the source file record - if we can't find it, return failure */ srcf = (G_srcf_table != 0 ? G_srcf_table->get_entry(line_ptr.get_source_id()) : 0); if (srcf == 0) return; /* * Create a list for the return value. The return list has two * elements: the name of the source file containing this code, and the * line number in the file. */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 2)); lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* clear the list, in case gc runs during construction */ lst->cons_clear(); /* push the list for gc protection */ G_stk->push(retval); /* get the filename string */ fname = srcf->get_name(); /* * determine how long the string will be when translated to utf8 from * the local filename character set */ map_len = G_cmap_from_fname->map_str(0, 0, fname); /* * create a string value to hold the filename, and store it in the * first element of the return list (note that this automatically * protects the new string from garbage collection, by virtue of the * list referencing the string and the list itself being protected) */ ele.set_obj(CVmObjString::create(vmg_ FALSE, map_len)); lst->cons_set_element(0, &ele); /* map the string into the buffer we allocated for it */ str = (CVmObjString *)vm_objp(vmg_ ele.val.obj); G_cmap_from_fname->map_str(str->cons_get_buf(), map_len, fname); /* set the second element of the list to the source line number */ ele.set_int(line_ptr.get_source_line()); lst->cons_set_element(1, &ele); /* discard our gc protection */ G_stk->discard(); }
void CVmBifT3::get_stack_trace(VMG_ uint argc) { int single_level = 0; int level; vm_val_t *fp; vm_val_t lst_val; CVmObjList *lst; const uchar *entry_addr; ulong method_ofs; vm_val_t stack_info_cls; int want_named_args = FALSE; int want_locals = FALSE; int want_frefs = FALSE; int flags = 0; const vm_rcdesc *rc; /* check arguments */ check_argc_range(vmg_ argc, 0, 2); /* get the imported stack information class */ stack_info_cls.set_obj(G_predef->stack_info_cls); if (stack_info_cls.val.obj == VM_INVALID_OBJ) { /* * there's no stack information class - we can't return any * meaningful information, so just return nil */ retval_nil(vmg0_); return; } /* * look up T3StackInfo.construct() to determine how many arguments it * wants */ { int min_args, opt_args, varargs; if (vm_objp(vmg_ stack_info_cls.val.obj)->get_prop_interface( vmg_ stack_info_cls.val.obj, G_predef->obj_construct, min_args, opt_args, varargs)) { /* check to see how many extra arguments they want */ want_named_args = (min_args + opt_args >= 7 || varargs); want_locals = (min_args + opt_args >= 8 || varargs); want_frefs = (min_args + opt_args >= 9 || varargs); } } /* check to see if we're fetching a single level or the full trace */ if (argc >= 1) { /* * Get the single level, and adjust to a 0 base. If the level is * nil, we're still getting all levels. */ if (G_stk->get(0)->typ == VM_NIL) { /* it's nil - get all levels */ G_stk->discard(); } else { /* get the level number */ single_level = pop_int_val(vmg0_); /* make sure it's in range */ if (single_level <= 0) err_throw(VMERR_BAD_VAL_BIF); /* we won't need a return list */ lst_val.set_obj_or_nil(VM_INVALID_OBJ); lst = 0; } } /* get the flags argument, if present */ if (argc >= 2) flags = pop_int_val(vmg0_); /* if we're not doing a single level, we need a list for the result */ if (!single_level) { /* * We're returning a full list, so we need to allocate the list for * the return value. First, count stack levels to see how big a * list we'll need. */ fp = G_interpreter->get_frame_ptr(); entry_addr = G_interpreter->get_entry_ptr(); method_ofs = G_interpreter->get_method_ofs(); for (level = 0 ; fp != 0 ; fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp), ++level) { /* add an extra level for each system call */ if (method_ofs == 0 && entry_addr != 0) ++level; /* get the return address */ entry_addr = G_interpreter->get_enclosing_entry_ptr_from_frame(vmg_ fp); method_ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp); } /* create the list */ lst_val.set_obj(CVmObjList::create(vmg_ FALSE, level)); lst = (CVmObjList *)vm_objp(vmg_ lst_val.val.obj); /* * we create other objects while building this list, so the gc * could run - clear the list to ensure it contains valid data */ lst->cons_clear(); /* protect the list from garbage collection while we work */ G_stk->push(&lst_val); /* flag that we're doing the whole stack */ single_level = -1; } else { /* adjust the level to a 0-based index */ single_level -= 1; } /* set up at the current function */ fp = G_interpreter->get_frame_ptr(); entry_addr = G_interpreter->get_entry_ptr(); method_ofs = G_interpreter->get_method_ofs(); rc = 0; /* traverse the frames */ for (level = 0 ; fp != 0 ; ++level) { int fr_argc; int i; vm_obj_id_t def_obj; vm_val_t info_self; vm_val_t info_func; vm_val_t info_obj; vm_val_t info_prop; vm_val_t info_args; vm_val_t info_locals; vm_val_t info_srcloc; vm_val_t info_frameref; CVmObjList *arglst; vm_val_t ele; CVmFuncPtr func_ptr; int gc_cnt = 0; int info_argc = 0; /* if we're looking for a single level, and this isn't it, skip it */ if (single_level >= 0 && level != single_level) goto done_with_level; /* * start with the information values to nil - we'll set the * appropriate ones when we find out what we have */ info_func.set_nil(); info_obj.set_nil(); info_prop.set_nil(); info_self.set_nil(); info_locals.set_nil(); info_frameref.set_nil(); /* get the number of arguments to the function in this frame */ fr_argc = G_interpreter->get_argc_from_frame(vmg_ fp); /* set up a function pointer for the method's entry address */ func_ptr.set(entry_addr); /* get the current frame's defining object */ def_obj = G_interpreter->get_defining_obj_from_frame(vmg_ fp); /* check for special method offsets */ switch (method_ofs) { case VMRUN_RET_OP: /* the real return address is one past the last argument */ method_ofs = G_interpreter->get_param_from_frame(vmg_ fp, argc) ->val.intval; break; case VMRUN_RET_OP_ASILCL: /* the real return address is two past the last argument */ method_ofs = G_interpreter->get_param_from_frame(vmg_ fp, argc+1) ->val.intval; break; } /* determine whether it's an object.prop or a function call */ if (method_ofs == 0) { /* * A zero method offset indicates a recursive VM invocation * from a native function. Presume we have no information on * the caller. */ info_self.set_nil(); fr_argc = 0; /* check for a native caller context */ if (rc != 0) { /* check which kind of native caller we have */ if (rc->bifptr.typ != VM_NIL) { /* we have a built-in function at this level */ info_func = rc->bifptr; } else if (rc->self.typ != VM_NIL) { /* it's an intrinsic class method - get the 'self' */ info_obj = info_self = rc->self; /* get the metaclass */ CVmMetaclass *mc; switch (info_obj.typ) { case VM_OBJ: /* get the metaclass from the object */ mc = vm_objp(vmg_ info_obj.val.obj) ->get_metaclass_reg(); break; case VM_LIST: /* list constant - use the List metaclass */ mc = CVmObjList::metaclass_reg_; break; case VM_SSTRING: /* string constant - use the String metaclass */ mc = CVmObjString::metaclass_reg_; break; default: /* other types don't have metaclasses */ mc = 0; break; } /* get the registration table entry */ vm_meta_entry_t *me = mc == 0 ? 0 : G_meta_table->get_entry_from_reg(mc->get_reg_idx()); /* get the metaclass and property from the entry */ if (me != 0) { /* set 'obj' to the IntrinsicClass object */ info_obj.set_obj(me->class_obj_); /* get the property ID */ info_prop.set_propid(me->xlat_func(rc->method_idx)); } } } } else if (def_obj == VM_INVALID_OBJ) { /* there's no defining object, so this is a function call */ func_ptr.get_fnptr(vmg_ &info_func); } else { /* it's an object.prop invocation */ info_obj.set_obj(def_obj); // $$$ walk up to base modified obj? info_prop.set_propid( G_interpreter->get_target_prop_from_frame(vmg_ fp)); /* get the 'self' in this frame */ info_self.set_obj(G_interpreter->get_self_from_frame(vmg_ fp)); } /* * build the argument list and source location, except for system * routines */ if (method_ofs != 0 || rc != 0) { /* allocate a list object to store the argument list */ int ac = (rc != 0 ? rc->argc : fr_argc); info_args.set_obj(CVmObjList::create(vmg_ FALSE, ac)); arglst = (CVmObjList *)vm_objp(vmg_ info_args.val.obj); /* push the argument list for gc protection */ G_stk->push(&info_args); ++gc_cnt; /* build the argument list */ for (i = 0 ; i < ac ; ++i) { /* add this element to the argument list */ const vm_val_t *v = (rc != 0 ? rc->argp - i : G_interpreter->get_param_from_frame(vmg_ fp, i)); arglst->cons_set_element(i, v); } /* get the source location */ get_source_info(vmg_ entry_addr, method_ofs, &info_srcloc); /* * if they want locals, and this isn't a recursive native * caller, retrieve them */ if (rc == 0 && (((flags & T3_GST_LOCALS) != 0 && want_locals) || ((flags & T3_GST_FREFS) != 0 && want_frefs))) { /* get the locals */ get_stack_locals(vmg_ fp, entry_addr, method_ofs, (flags & T3_GST_LOCALS) != 0 && want_locals ? &info_locals : 0, (flags & T3_GST_FREFS) != 0 && want_frefs ? &info_frameref : 0); /* * that leaves the LookupTable and StackFrameDesc on the * stack, so note that we need to discard the stack level * when we're done with it */ if (info_locals.typ == VM_OBJ) ++gc_cnt; if (info_frameref.typ == VM_OBJ) ++gc_cnt; } } else { /* * it's a system routine - no argument information is * available, so return nil rather than an empty list to to * indicate the absence */ info_args.set_nil(); /* there's obviously no source location for system code */ info_srcloc.set_nil(); } /* * We have all of the information on this level now, so create the * information object for the level. This is an object of the * exported stack-info class, which is a TadsObject type. */ /* start with the original complement of arguments */ info_argc = 7; /* * if we have a modern T3StackInfo object, push the locals, * named argument elements, and frame reference object */ if (want_frefs) { G_stk->push(&info_frameref); ++info_argc; } if (want_named_args) { /* * the constructor has a slot for named arguments - push either * a table or nil, depending... */ vm_val_t *argp; const uchar *t = 0; /* if the flags request locals, retrieve the named arguments */ if ((flags & T3_GST_LOCALS) != 0) t = CVmRun::get_named_args_from_frame(vmg_ fp, &argp); /* * if we do in fact have named arguments, build a lookup table * copy and push it; otherwise just push nil */ if (t != 0) { /* get the number of table entries */ int n = osrp2(t); t += 2; /* create a lookup table for the arguments */ G_stk->push()->set_obj(CVmObjLookupTable::create( vmg_ FALSE, n <= 8 ? 8 : n <= 32 ? 32 : 64, n)); CVmObjLookupTable *lt = (CVmObjLookupTable *)vm_objp( vmg_ G_stk->get(0)->val.obj); /* * Populate the lookup table with the named arguments. The * compiler builds the table in the order pushed, which is * right to left. Lookup tables preserve the order in * which elements are added, and reflect this order in key * lists, so to that extent the order of building the * lookup table matters. For readability of the generated * list, in case it's presented to the user, build the * table in left-to-right order, which is the reverse of * the table order in the bytecode table. */ argp += n - 1; for (int i = (n-1)*2 ; i >= 0 ; i -= 2, --argp) { /* get the name pointer and length from the index */ uint ofs = osrp2(t + i), nxtofs = osrp2(t + i + 2); const char *name = (const char *)t + ofs; size_t len = nxtofs - ofs; /* create a string from the name */ vm_val_t str; str.set_obj(CVmObjString::create(vmg_ FALSE, name, len)); /* add it to the table */ lt->add_entry(vmg_ &str, argp); } } else { /* there are no named arguments - push nil */ G_stk->push()->set_nil(); } /* count the argument */ ++info_argc; } if (want_locals) { G_stk->push(&info_locals); ++info_argc; } /* push the rest of the arguments */ G_stk->push(&info_srcloc); G_stk->push(&info_args); G_stk->push(&info_self); G_stk->push(&info_prop); G_stk->push(&info_obj); G_stk->push(&info_func); G_stk->push(&stack_info_cls); ele.set_obj(CVmObjTads::create_from_stack(vmg_ 0, info_argc)); /* discard the gc protection items */ G_stk->discard(gc_cnt); /* * if we're fetching a single level, this is it - return the new * stack info object and we're done */ if (single_level >= 0) { /* return the single level object */ retval_obj(vmg_ ele.val.obj); /* we're done */ return; } /* add the new element to our list */ lst->cons_set_element(level, &ele); done_with_level: /* * If this is a system call level, and we're not in debug mode, * this recursive frame contains the entry address for the caller, * but not the calling byte-code address. Stay on the current * level in this case. */ if (method_ofs == 0 && entry_addr != 0) { /* * This is a recursive caller, and we have a valid entry * address for the prior frame. Stay in the current frame, and * retrieve the actual return address from the calling frame. */ if (rc != 0) { /* get the actual return address from the recursive context */ method_ofs = rc->return_addr - entry_addr; /* * we're now in the bytecode part of the frame, so forget * the recursive context */ rc = 0; } else { /* no recursive context - use a fake return address */ method_ofs = G_interpreter->get_funchdr_size(); } } else { /* move up to the enclosing frame */ entry_addr = G_interpreter->get_enclosing_entry_ptr_from_frame(vmg_ fp); method_ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp); rc = G_interpreter->get_rcdesc_from_frame(vmg_ fp); fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp); } } /* return the list */ retval_obj(vmg_ lst_val.val.obj); /* discard our gc protection */ G_stk->discard(); }
/* * Get a list of our properties */ void CVmObjClass::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval) { vm_obj_id_t mod_obj; vm_meta_entry_t *entry; size_t my_prop_cnt; size_t mod_prop_cnt; vm_val_t mod_val; CVmObjList *lst; CVmObjList *mod_lst; /* presume we won't find any static properties of our own */ my_prop_cnt = 0; /* get my metaclass table entry */ entry = get_meta_entry(vmg0_); /* if we have an entry, count the properties */ if (entry != 0) my_prop_cnt = list_class_props(vmg_ self, entry, 0, 0, FALSE); /* if we have a modifier object, get its property list */ if ((mod_obj = get_mod_obj()) != VM_INVALID_OBJ) { /* get the modifier's property list - we'll add it to our own */ vm_objp(vmg_ mod_obj)->build_prop_list(vmg_ self, &mod_val); /* get the result as a list object, properly cast */ mod_lst = (CVmObjList *)vm_objp(vmg_ mod_val.val.obj); /* * As an optimization, if we don't have any properties of our own * to add to the modifier list, just return the modifier list * directly (thus avoiding unnecessarily creating another copy of * the list with no changes). */ if (my_prop_cnt == 0) { /* the modifier list is the entire return value */ *retval = mod_val; return; } /* get the size of the modifier list */ mod_prop_cnt = vmb_get_len(mod_lst->get_as_list()); } else { /* * we have no modifier object - for the result list, we simply * need our own list, so set the modifier list to nil */ mod_val.set_nil(); mod_prop_cnt = 0; mod_lst = 0; } /* for gc protection, push the modifier's list */ G_stk->push(&mod_val); /* * Allocate a list big enough to hold the modifier's list plus our own * list. */ retval->set_obj(CVmObjList:: create(vmg_ FALSE, my_prop_cnt + mod_prop_cnt)); /* push the return value list for gc protection */ G_stk->push(retval); /* get it as a list object, properly cast */ lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); lst->cons_clear(); /* start the list with our own properties */ if (entry != 0) list_class_props(vmg_ self, entry, lst, 0, FALSE); /* copy the modifier list into the results, if there is a modifier list */ if (mod_prop_cnt != 0) lst->cons_copy_elements(my_prop_cnt, mod_lst->get_as_list()); /* done with the gc protection */ G_stk->discard(2); }
/* * Retrieve a list of the named argument names. */ void CVmBifT3::get_named_arg_list(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* create the result list; we'll expand as necessary later */ G_stk->push()->set_obj(CVmObjList::create(vmg_ FALSE, 10)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ G_stk->get(0)->val.obj); /* clear it out, since we're building it incrementally */ lst->cons_clear(); /* we haven't added any elements yet */ int idx = 0; /* scan the stack and populate the name list from the tables we find */ for (vm_val_t *fp = G_interpreter->get_frame_ptr() ; fp != 0 ; fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp)) { /* look for a named argument table in this frame */ vm_val_t *argp; const uchar *t = CVmRun::get_named_args_from_frame(vmg_ fp, &argp); if (t != 0) { /* get the number of table entries */ int n = osrp2(t); t += 2; /* * Build the list. The compiler generates the list in * right-to-left order (the order of pushing the arguments). * For readability, reverse this: generate the list left to * right, so that it appears in the original source code order. */ argp += n - 1; for (int i = (n-1)*2 ; i >= 0 ; i -= 2, --argp) { /* get this string's offset and figure its length */ uint ofs = osrp2(t + i); uint len = osrp2(t + i + 2) - ofs; /* create a string from the name */ vm_val_t str; str.set_obj(CVmObjString::create( vmg_ FALSE, (const char *)t + ofs, len)); /* add it to the list */ lst->cons_ensure_space(vmg_ idx, 10); lst->cons_set_element(idx, &str); ++idx; } } } /* set the final list length */ lst->cons_set_len(idx); /* keep only the unique elements */ lst->cons_uniquify(vmg0_); /* return the results */ retval_pop(vmg0_); }
/* * Show a popup menu */ void CVmBifTIOExt::show_popup_menu(VMG_ uint argc) { int x, y, default_pos; char *txt; os_event_info_t evt; int ret; int elecnt; vm_obj_id_t lst_obj; CVmObjList *lst; vm_val_t val; /* check arguments */ check_argc(vmg_ argc, 3); /* get the x,y coordinates */ if (G_stk->get(0)->typ == VM_NIL) { /* nil x,y - use default position */ default_pos = TRUE; x = y = 0; /* discard the nil x,y values */ G_stk->discard(2); } else { /* pop the x,y positions */ x = pop_int_val(vmg0_); y = pop_int_val(vmg0_); } /* get the HTML text for the contents of the window */ txt = pop_str_val_ui(vmg_ 0, 0); /* flush the console display output */ G_console->flush_all(vmg_ VM_NL_NONE); /* show the window */ ret = os_show_popup_menu(default_pos, x, y, txt, strlen(txt), &evt); /* free the HTML text buffer we allocated */ t3free(txt); /* see what we have */ switch (ret) { case OSPOP_FAIL: case OSPOP_CANCEL: case OSPOP_EOF: default: elecnt = 1; break; case OSPOP_HREF: elecnt = 2; break; } /* allocate the return list */ lst_obj = CVmObjList::create(vmg_ FALSE, elecnt); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); lst->cons_clear(); /* protect the list from garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* set the first element to the main return code */ val.set_int(ret); lst->cons_set_element(0, &val); /* set additional elements according to the return code */ switch (ret) { case OSPOP_HREF: /* add the HREF element */ val.set_obj(str_from_ui_str(vmg_ evt.href)); lst->cons_set_element(1, &val); break; default: /* there aren't any other elements for other return codes */ break; } /* return the list */ retval_obj(vmg_ lst_obj); /* discard the GC protection */ G_stk->discard(); }