/* * Get a property from an intrinsic class modifier object. Look up the * property in my own modifier, and recursively in my superclass modifiers. */ int CVmObjClass::get_prop_from_mod(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { vm_obj_id_t sc; vm_obj_id_t mod_obj; /* if we have a modifier object, look it up in the modifier */ if ((mod_obj = get_mod_obj()) != VM_INVALID_OBJ && vm_objp(vmg_ mod_obj)->get_prop(vmg_ prop, val, mod_obj, source_obj, argc)) { /* got it - return it from the modifier */ return TRUE; } /* * it's not in our modifier(s); check with any intrinsic superclass * modifiers */ if (get_superclass_count(vmg_ self) != 0 && (sc = get_superclass(vmg_ self, 0)) != VM_INVALID_OBJ) { /* we have a superclass - check it recursively */ return ((CVmObjClass *)vm_objp(vmg_ sc)) ->get_prop_from_mod(vmg_ prop, val, sc, source_obj, argc); } /* * we didn't find it, and we have no superclass, so there's nowhere * else to look - return failure */ return FALSE; }
/* * Find the intrinsic class which the given modifier object modifies. This * can only be used with a modifier that modifies me or one of my * superclasses. */ vm_obj_id_t CVmObjClass::find_mod_src_obj(VMG_ vm_obj_id_t self, vm_obj_id_t mod_obj) { vm_obj_id_t my_mod_obj; vm_obj_id_t sc; /* * Is this one of my modifier objects? It is if it's my most * specialized modifier object (i.e., get_mod_obj()), or if my most * specialized modifier object descends from the given object. */ my_mod_obj = get_mod_obj(); if (my_mod_obj != VM_INVALID_OBJ && (mod_obj == my_mod_obj || vm_objp(vmg_ my_mod_obj)->is_instance_of(vmg_ mod_obj))) { /* it's one of mine, so I'm the intrinsic class mod_obj modifies */ return self; } /* * It's not one of mine, so check my superclasses recursively. If we * have no direct superclass, we've failed to find the object. */ if (get_superclass_count(vmg_ self) == 0 || (sc = get_superclass(vmg_ self, 0)) == VM_INVALID_OBJ) return VM_INVALID_OBJ; /* ask the superclass to find the modifier */ return ((CVmObjClass *)vm_objp(vmg_ sc)) ->find_mod_src_obj(vmg_ sc, mod_obj); }
/* * Global symbol enumeration callback - fix up this symbol's compiler * metaclass identifier, if this is an object symbol. */ static void fix_obj_meta_cb(void *ctx0, CTcSymbol *sym) { /* cast our context and set up to access VM globals */ fix_obj_meta_cb_ctx *ctx = (fix_obj_meta_cb_ctx *)ctx0; VMGLOB_PTR(ctx->vmg); /* if this is an object symbol, fix up its metaclass identifier */ if (sym->get_type() == TC_SYM_OBJ) { CVmObject *obj; CVmMetaclass *meta; uint idx; tc_metaclass_t id; /* cast the symbol to an object symbol */ CTcSymObj *obj_sym = (CTcSymObj *)sym; /* get the object */ obj = vm_objp(vmg_ (vm_obj_id_t)obj_sym->get_obj_id()); /* look up its metaclass */ meta = obj->get_metaclass_reg(); /* get the registration table index */ idx = meta->get_reg_idx(); /* that's our index into the translation table - look it up */ id = ctx->xlat[idx]; /* store the compiler metaclass ID back in the symbol */ obj_sym->set_metaclass(id); } }
/* * create from stack arguments */ vm_obj_id_t CVmObjAnonFn::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; vm_val_t funcptr; CVmObjAnonFn *new_obj; uint idx; /* at least one argument is required (the function pointer) */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* retrieve our function pointer argument */ G_stk->pop(&funcptr); if (funcptr.typ == VM_FUNCPTR) { /* it's a regular function pointer - accept it */ } else if (funcptr.typ == VM_OBJ && vm_objp(vmg_ funcptr.val.obj)->get_invoker(vmg_ 0)) { /* it's a pointer to an invokable object - accept it */ } else { /* it's not a valid function pointer */ err_throw(VMERR_FUNCPTR_VAL_REQD); } /* create the new object */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* create the new object, giving it one slot per constructor argument */ new_obj = new (vmg_ id) CVmObjAnonFn(vmg_ argc); /* set the first element to our function pointer */ new_obj->set_element(0, &funcptr); /* set the remaining elements to the context objects */ for (idx = 1 ; idx < argc ; ++idx) { vm_val_t val; /* pop this value */ G_stk->pop(&val); /* set the element */ new_obj->set_element(idx, &val); } /* return the new object ID */ return id; }
/* * Create an object with the given ID and load the object from the image * file. */ void CVmMetaTable::create_from_image(VMG_ uint idx, vm_obj_id_t id, const char *ptr, size_t siz) { /* make sure the entry is defined */ if (idx >= count_) err_throw(VMERR_BAD_METACLASS_INDEX); /* create the object table entry in the memory manager */ G_obj_table->alloc_obj_with_id(id, TRUE); /* invoke the appropriate constructor */ table_[idx].meta_->create_for_image_load(vmg_ id); /* load the object */ vm_objp(vmg_ id)->load_from_image(vmg_ id, ptr, siz); }
/* * Invoke */ int CVmObjAnonFn::get_invoker(VMG_ vm_val_t *val) { /* our first vector element is our function pointer */ if (val != 0) { /* get the function pointer */ get_element(0, val); /* if this is itself an invokable value, get its invoker */ if (val->typ == VM_OBJ) return vm_objp(vmg_ val->val.obj)->get_invoker(vmg_ val); } /* we are indeed invokable */ return TRUE; }
/* * Set the value of a local given the variable descriptor */ void CVmObjFrameRef::set_local_val(VMG_ const CVmDbgFrameSymPtr *sym, const vm_val_t *new_val) { /* * if we have an active stack frame, get the value from the frame; * otherwise get the value from our local snapshot */ vm_frameref_ext *ext = get_ext(); if (ext->fp != 0) { /* we have a frame - get the value from the frame */ G_interpreter->set_local_in_frame(vmg_ new_val, ext->fp, sym); } else { /* * There's no frame, so we must retrieve the value from the * snapshot. First, get the value of the local or parameter. Our * snapshot array consists of all of the locals followed by all of * the parameters, so local N is at vars[N] and parameter N is at * vars[nlocals + N]. */ vm_val_t *v = &ext->vars[sym->get_var_num() + (sym->is_param() ? ext->nlocals : 0)]; /* * If it's a context local, index the local by the context index. * Otherwise the value is simply the value in the snapshot array. */ if (sym->is_ctx_local()) { vm_val_t cont; vm_val_t ival; ival.set_int(sym->get_ctx_arr_idx()); if (v->typ == VM_OBJ) vm_objp(vmg_ v->val.obj)->set_index_val_ov( vmg_ &cont, v->val.obj, &ival, new_val); else err_throw(VMERR_CANNOT_INDEX_TYPE); } else *v = *new_val; } }
/* set a property */ void CVmObjClass::set_prop(VMG_ CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* try treating the request as a static property of the metaclass */ vm_meta_entry_t *entry = get_meta_entry(vmg0_); if (entry != 0 && entry->meta_->set_stat_prop( vmg_ undo, self, &get_ext()->class_state, prop, val)) return; /* the class doesn't handle it, so check for a modifier object */ vm_obj_id_t mod_obj = get_mod_obj(); if (mod_obj != VM_INVALID_OBJ) { /* we have a modifier - set the property in the modifier */ vm_objp(vmg_ mod_obj)->set_prop(vmg_ undo, mod_obj, prop, val); } else { /* if we don't have a modifier, we can't set the property */ err_throw(VMERR_INVALID_SETPROP); } }
/* * Initialize from a vm_val_t */ int CVmFuncPtr::set(VMG_ const vm_val_t *val) { vm_val_t invoker; /* we might need to re-resolve from an invoker, so loop until resolved */ for (;;) { /* check the type */ switch (val->typ) { case VM_OBJ: case VM_OBJX: /* object - check to see if it's invokable */ if (vm_objp(vmg_ val->val.obj)->get_invoker(vmg_ &invoker)) { /* it's invokable - re-resolve using the invoker */ val = &invoker; } else { /* other object types are not function pointers */ set((uchar *)0); return FALSE; } break; case VM_FUNCPTR: case VM_CODEOFS: /* function pointer - get the address from the code pool */ set((const uchar *)G_code_pool->get_ptr(val->val.ofs)); return TRUE; case VM_CODEPTR: /* direct code pointer - the value contains the address */ set((const uchar *)val->val.ptr); return TRUE; case VM_BIFPTR: case VM_BIFPTRX: /* built-in function pointer */ { /* * decode the function set information from the value, and * look up the bif descriptor in the registration table */ const vm_bif_desc *desc = G_bif_table->get_desc( val->val.bifptr.set_idx, val->val.bifptr.func_idx); /* fail if there's no descriptor */ if (desc == 0) return FALSE; /* point to the synthetic header */ set((const uchar *)desc->synth_hdr); } return TRUE; default: /* other types are invalid */ set((uchar *)0); return FALSE; } } }
/* * Get the local variable table for a given stack level. We'll fill in * 'retval' with a LooupTable object containing the local values keyed by * symbol name, or nil if we can't find the locals. We'll also push the * object onto the stack for gc protection, so the caller must pop it when * done with it. */ void CVmBifT3::get_stack_locals(VMG_ vm_val_t *fp, const uchar *entry_addr, ulong method_ofs, vm_val_t *local_tab, vm_val_t *frameref_obj) { /* presume we won't be able to find source information for the location */ if (local_tab != 0) local_tab->set_nil(); if (frameref_obj != 0) frameref_obj->set_nil(); /* we haven't found any relevant frames yet */ int best = 0; /* set up a debug table pointer for the function or method */ CVmDbgTablePtr dp; if (dp.set(entry_addr)) { /* search for the innermost frame containing the method offset */ int frame_cnt = dp.get_frame_count(vmg0_); for (int i = 1 ; i <= frame_cnt ; ++i) { /* get this frame */ CVmDbgFramePtr fp; dp.set_frame_ptr(vmg_ &fp, i); /* check to see if this contains method_ofs */ uint start_ofs = fp.get_start_ofs(vmg0_); uint end_ofs = fp.get_end_ofs(vmg0_); if (method_ofs >= start_ofs && method_ofs <= end_ofs) { /* * This is a relevant frame. If we don't have any frames * yet, or this one is nested within the best we've found * so far, this is the best frame so far. */ if (best == 0 || fp.is_nested_in(vmg_ &dp, best)) best = i; } } } /* get the frame-reference, if the caller wants one */ if (frameref_obj != 0) { /* get the slot */ vm_val_t *fro = G_interpreter->get_frameref_slot(vmg_ fp); /* if there's not already a StackFrameRef object there, create one */ if (fro->typ == VM_NIL) { /* create the StackFrameRef and store it in the stack slot */ fro->set_obj(CVmObjFrameRef::create(vmg_ fp, entry_addr)); } /* create the StackFrameDesc to return to the caller */ frameref_obj->set_obj(CVmObjFrameDesc::create( vmg_ fro->val.obj, best, method_ofs)); /* push it for gc protection */ G_stk->push(frameref_obj); } /* if they didn't want the local variable table, we're done */ if (local_tab == 0) return; /* create a lookup table for the locals */ local_tab->set_obj(CVmObjLookupTable::create(vmg_ FALSE, 32, 64)); CVmObjLookupTable *t = (CVmObjLookupTable *)vm_objp(vmg_ local_tab->val.obj); /* save it on the stack for gc protection */ G_stk->push(local_tab); /* if we didn't find a best frame, there are no locals to retrieve */ if (best == 0) return; /* set up a pointer to our winning frame */ CVmDbgFramePtr dfp; dp.set_frame_ptr(vmg_ &dfp, best); /* walk up the list of frames from the innermost to outermost */ for (;;) { /* set up a pointer to the first symbol */ CVmDbgFrameSymPtr symp; dfp.set_first_sym_ptr(vmg_ &symp); /* scan this frame's local symbol list */ int sym_cnt = dfp.get_sym_count(); for (int i = 0 ; i < sym_cnt ; ++i, symp.inc(vmg0_)) { vm_val_t name, val; /* get the symbol name */ symp.get_str_val(vmg_ &name); /* * if this symbol is already in the table, skip it - we work * from inner to outer frames, and inner frames hide symbols in * outer frames */ if (t->index_check(vmg_ &val, &name)) continue; /* get the value from the frame */ G_interpreter->get_local_from_frame(vmg_ &val, fp, &symp); /* add this to the lookup table */ t->add_entry(vmg_ &name, &val); } /* get the enclosing frame - 0 means no parent */ int parent = dfp.get_enclosing_frame(); if (parent == 0) break; /* set up dfp to point to the parent frame */ dp.set_frame_ptr(vmg_ &dfp, parent); } }
/* * 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(); }
/* * 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_); }
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(); }
int main(int argc, char **argv) { CImageFile *imagefp; vm_val_t val; vm_val_t *stkval; vm_globals *vmg__; CVmHostIfc *hostifc; CVmMainClientConsole clientifc; /* initialize for testing */ test_init(); /* initialize the VM */ hostifc = new CVmHostIfcStdio(argv[0]); vm_initialize(&vmg__, &vm_init_options(hostifc, &clientifc, "us-ascii", "us_ascii")); /* create a fake host file object */ imagefp = new CImageFile(); /* create the constant pool */ G_const_pool->attach_backing_store(imagefp); /* create a couple of objects and push them on the stack */ val.set_obj(CVmObjTads::create(vmg_ FALSE, 0, 4)); G_stk->push(&val); val.set_obj(CVmObjTads::create(vmg_ FALSE, 0, 4)); G_stk->push(&val); /* * create another object, and store it in a property of the first * object */ val.set_obj(CVmObjTads::create(vmg_ FALSE, 0, 4)); stkval = G_stk->get(1); vm_objp(vmg_ stkval->val.obj)-> set_prop(vmg_ G_undo, stkval->val.obj, PROP_A, &val); /* collect garbage - nothing should be deleted at this point */ G_obj_table->gc_full(vmg0_); /* * forget about the second object by popping it off the stack, then * collect garbage again -- the second object should be deleted at * this point, because it's no longer reachable */ G_stk->discard(); G_obj_table->gc_full(vmg0_); /* * Force the first object's property table to expand by filling up * its initial table */ stkval = G_stk->get(0); val.set_nil(); vm_objp(vmg_ stkval->val.obj)-> set_prop(vmg_ G_undo, stkval->val.obj, PROP_A, &val); vm_objp(vmg_ stkval->val.obj)-> set_prop(vmg_ G_undo, stkval->val.obj, PROP_B, &val); vm_objp(vmg_ stkval->val.obj)-> set_prop(vmg_ G_undo, stkval->val.obj, PROP_C, &val); vm_objp(vmg_ stkval->val.obj)-> set_prop(vmg_ G_undo, stkval->val.obj, PROP_D, &val); vm_objp(vmg_ stkval->val.obj)-> set_prop(vmg_ G_undo, stkval->val.obj, PROP_E, &val); /* set an existing property */ vm_objp(vmg_ stkval->val.obj)-> set_prop(vmg_ G_undo, stkval->val.obj, PROP_B, &val); /* we're done, so delete all of our managers */ G_const_pool->detach_backing_store(); delete imagefp; /* shut down the VM */ vm_terminate(vmg__, &clientifc); /* delete the host interface */ delete hostifc; /* terminate */ return 0; }
/* * Open a network file */ CVmNetFile *CVmNetFile::open(VMG_ const vm_val_t *val, const vm_rcdesc *rc, int mode, os_filetype_t typ, const char *mime_type) { vm_val_t filespec; /* check for a TadsObject implementing getFilename */ if (G_predef->filespec_getFilename != VM_INVALID_PROP && val->typ == VM_OBJ && CVmObjTads::is_tadsobj_obj(vmg_ val->val.obj)) { /* call getFilename - the return value is the file spec */ G_interpreter->get_prop( vmg_ 0, val, G_predef->filespec_getFilename, val, 0, rc); /* the result is the real file spec */ filespec = *G_interpreter->get_r0(); } else { /* it's not a TadsObject, so it must directly have the file name */ filespec = *val; } /* check the argument type */ CVmNetFile *nf = 0; if (filespec.typ == VM_OBJ && CVmObjTemporaryFile::is_tmpfil_obj(vmg_ filespec.val.obj)) { /* temporary file object - get the object, properly cast */ CVmObjTemporaryFile *tmp = (CVmObjTemporaryFile *)vm_objp( vmg_ filespec.val.obj); /* if the temporary file object is invalid, it's an error */ if (tmp->get_fname() == 0) err_throw(VMERR_CREATE_FILE); /* create the local file descriptor for the temp file path */ nf = open_local(vmg_ tmp->get_fname(), 0, mode, typ); /* mark it as a temp file */ nf->is_temp = TRUE; } else { /* anything else has to be a string */ char fname[OSFNMAX]; CVmBif::get_str_val_fname(vmg_ fname, sizeof(fname), CVmBif::get_str_val(vmg_ &filespec)); /* * if it's a local file, and it has a relative path, explicitly * apply the image file path as the default working directory */ if (!os_is_file_absolute(fname) && !is_net_mode(vmg0_)) { /* build the full, absolute name based on the image file path */ char fname_abs[OSFNMAX]; os_build_full_path(fname_abs, sizeof(fname_abs), G_image_loader->get_path(), fname); /* replace the relative path with the new absolute path */ lib_strcpy(fname, sizeof(fname), fname_abs); } /* create the regular network file descriptor */ nf = open(vmg_ fname, 0, mode, typ, mime_type); } /* if they gave us an object as our file spec, remember it */ if (nf != 0 && val->typ == VM_OBJ) nf->filespec = val->val.obj; /* return the network file descriptor */ return nf; }
/* * property evaluator - get a lookup table of the local variables, with * their current values, keyed by name */ int CVmObjFrameDesc::getp_get_vars(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* create our lookup table object */ retval->set_obj(CVmObjLookupTable::create(vmg_ FALSE, 32, 64)); CVmObjLookupTable *tab = (CVmObjLookupTable *) vm_objp(vmg_ retval->val.obj); /* get our extension */ vm_framedesc_ext *ext = get_ext(); /* get our underlying frame ref and its extension */ CVmObjFrameRef *fr = get_frame_ref(vmg0_); vm_frameref_ext *fext = fr->get_ext(); /* set up pointer to this method's debug records */ CVmDbgTablePtr dp; if (!dp.set(fext->entryp)) return TRUE; /* set up a pointer to our frame */ CVmDbgFramePtr dfp; dp.set_frame_ptr(vmg_ &dfp, ext->frame_idx); /* push the table for gc protection */ G_stk->push(retval); /* walk up the list of frames from innermost to outermost */ for (;;) { /* set up a pointer to the first symbol */ CVmDbgFrameSymPtr sym; dfp.set_first_sym_ptr(vmg_ &sym); /* scan this frame's local symbol list */ int sym_cnt = dfp.get_sym_count(); for (int i = 0 ; i < sym_cnt ; ++i, sym.inc(vmg0_)) { /* set up a string value for the key */ vm_val_t key; sym.get_str_val(vmg_ &key); G_stk->push(&key); /* * If this entry isn't already in the table, add it. Don't * bother if it already exists: we work from inner to outer * scopes, and inner scopes hide things in outer scopes, so if * we find an entry in the table already it means that it was * an inner-scope entry that hides the one we're processing * now. */ vm_val_t val; if (!tab->index_check(vmg_ &val, &key)) { /* get the value */ fr->get_local_val(vmg_ &val, &sym); /* add the entry to the table */ tab->add_entry(vmg_ &key, &val); } /* done with the key for gc protection */ G_stk->discard(); } /* move up to the enclosing frame */ int parent = dfp.get_enclosing_frame(); if (parent == 0) break; /* set up dfp to point to the parent fraem */ dp.set_frame_ptr(vmg_ &dfp, parent); } /* discard the table ref we pushed for gc protection */ G_stk->discard(); /* handled */ return TRUE; }
/* * 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(); }
/* * 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); }
/* * readText() - read text from the keyboard and return it as a string. */ void CVmBifSample::read_text(VMG_ uint argc) { char buf[128]; size_t len; vm_obj_id_t str_id; CVmObjString *str_obj; /* check to make sure we have the right number of arguments */ check_argc(vmg_ argc, 0); /* * Read a string from the keyboard. Use fgets() rather than plain * gets(), because fgets() lets us limit the buffer size and thus avoid * any chance of a buffer overflow. (Someone should tell the people at * Microsoft about this - it would probably cut out about eighty * percent of those emergency internet security alerts that require * everyone to download an IE patch every couple of weeks. :) */ fgets(buf, sizeof(buf), stdin); /* * One small detail about fgets: if the input ended with a newline, * there will be a newline in the buffer. Remove it if it's there. */ if ((len = strlen(buf)) != 0 && buf[len - 1] == '\n') buf[len-1] = '\0'; /* * As in display_text(), we have to deal with character set mapping * before we can send the string back to the TADS program. This time, * we want to perform the conversion from the local character set to * Unicode. Again, T3 provides a handy conversion object for our * convenience - this time, it's called G_cmap_from_ui. * * In order to return a string to the TADS program, we have to allocate * a new string object. First, let's see how big a string we need to * allocate, by calling the character mapper with no buffer space at * all - the mapper will run through the string and check to see how * big it will be after conversion, but it won't actually store * anything. */ len = G_cmap_from_ui->map_str(0, 0, buf); /* * Allocate a new string to contain the return value. This gives us * back an "object ID" value, which we can convert into an internal C++ * string object pointer using the vm_objp() formula shown. */ str_id = CVmObjString::create(vmg_ FALSE, len); str_obj = (CVmObjString *)vm_objp(vmg_ str_id); /* * The string object has a buffer of the size we requested, which is * the size we already know we need to contain the mapped string. So, * we can call the mapper again to have it perform the actual mapping * into our string buffer. */ G_cmap_from_ui->map_str(str_obj->cons_get_buf(), len, buf); /* * One last step: we must return the string object to the caller. To * do this, use the retval_obj() function to return the ID of the * string object. */ retval_obj(vmg_ str_id); }
/* * Get a network event */ void CVmBifNet::get_event(VMG_ uint oargc) { /* check arguments */ check_argc_range(vmg_ oargc, 0, 1); /* get the timeout, if present */ long timeout = OS_FOREVER; if (oargc >= 1) { if (G_stk->get(0)->typ == VM_NIL) G_stk->discard(); else timeout = pop_int_val(vmg0_); } /* we don't know the NetEvent subclass yet, so presume the base class */ vm_obj_id_t ev_cl = G_predef->net_event; int argc; /* get the next message */ TadsMessage *msg = 0; int wret = G_net_queue->wait(vmg_ timeout, &msg); /* cast the message */ TadsEventMessage *evtmsg = cast_tads_message(TadsEventMessage, msg); /* make sure we release the message before exiting */ err_try { /* check the wait result */ const char *err; switch (wret) { case OSWAIT_EVENT + 0: /* we got a message */ if (evtmsg != 0) { /* ask the message to set up a new NetRequestEvent */ int evt_code; ev_cl = evtmsg->prep_event_obj(vmg_ &argc, &evt_code); /* add the event type argument */ G_interpreter->push_int(vmg_ evt_code); ++argc; } else { /* unrecognized message type */ err = "getNetEvent(): unexpected message type in queue"; goto evt_error; } break; case OSWAIT_EVENT + 1: /* 'quit' event - return an error */ err = "getNetEvent(): queue terminated"; goto evt_error; case OSWAIT_EVENT + 2: /* debug break - return a NetEvent with the interrupt code */ argc = 1; G_interpreter->push_int(vmg_ VMBN_EVT_DBGBRK); break; case OSWAIT_TIMEOUT: /* the timeout expired - return a NetTimeoutEvent */ ev_cl = G_predef->net_timeout_event; argc = 1; G_interpreter->push_int(vmg_ VMBN_EVT_TIMEOUT); break; case OSWAIT_ERROR: default: /* the wait failed */ err = "getNetEvent(): error waiting for request message"; evt_error: /* on error, throw a NetException describing the problem */ G_interpreter->push_string(vmg_ err); G_interpreter->throw_new_class( vmg_ G_predef->net_exception, 1, err); AFTER_ERR_THROW(break;) } /* * if the specific event type subclass isn't defined, fall back on * the base NetEvent class */ if (ev_cl == VM_INVALID_OBJ) ev_cl = G_predef->net_event; /* * construct the NetEvent subclass, or a generic list if we don't * even have the NetEvent class defined */ if (ev_cl != VM_INVALID_OBJ) vm_objp(vmg_ ev_cl)->create_instance(vmg_ ev_cl, 0, argc); else retval_obj(vmg_ CVmObjList::create_from_stack(vmg_ 0, argc)); } err_finally { /* we're done with the message object */ if (msg != 0) msg->release_ref(); } err_end; }
/* * Set up the thread. All string parameters are provided in internal * string format, with VMB_LEN length prefixes. */ HttpReqThread(VMG_ const vm_val_t *id, const char *url, const char *verb, int32 options, const char *hdrs, vm_val_t *body, const char *body_type, const vm_rcdesc *rc) { /* save the VM globals */ vmg = VMGLOB_ADDR; /* add a reference on the net event queue */ if ((queue = G_net_queue) != 0) queue->add_ref(); /* create a global for the ID value */ idg = G_obj_table->create_global_var(); idg->val = *id; /* save the option flags */ this->options = options; /* set up to parse the URL */ size_t urll = vmb_get_len(url); url += VMB_LEN; /* note whether the scheme is plain http or https */ https = (urll >= 8 && memcmp(url, "https://", 8) == 0); /* we don't have any URL pieces yet */ host = 0; resource = 0; port = (https ? 443 : 80); /* skip the scheme prefix */ size_t pfx = (urll >= 7 && memcmp(url, "http://", 7) == 0 ? 7 : urll >= 8 && memcmp(url, "https://", 8) == 0 ? 8 : 0); url += pfx, urll -= pfx; /* the host name is the part up to the ':', '/', or end of string */ const char *start; for (start = url ; urll > 0 && *url != ':' && *url != '/' ; ++url, --urll) ; /* save the host name */ host = lib_copy_str(start, url - start); /* parse the port, if present */ if (urll > 0 && *url == ':') { /* parse the port number string */ for (--urll, start = ++url, port = 0 ; urll > 0 && *url != '/' ; ++url, --urll) { if (is_digit(*url)) port = port*10 + value_of_digit(*url); } } /* * The rest (including the '/') is the resource string. If there's * nothing left, the resource is implicitly '/'. */ if (urll > 0) resource = lib_copy_str(url, urll); else resource = lib_copy_str("/"); /* make null-terminated copies of the various strings */ this->verb = lib_copy_str(verb + VMB_LEN, vmb_get_len(verb)); this->hdrs = (hdrs == 0 ? 0 : lib_copy_str(hdrs + VMB_LEN, vmb_get_len(hdrs))); /* if there's a content body, set up the payload */ this->body = 0; if (body != 0) { /* set up an empty payload */ this->body = new OS_HttpPayload(); /* * Convert the body to a stream payload, if it's a string or * ByteArray. If it's a LookupTable, it's a set of form * fields. */ if (body->typ == VM_OBJ && CVmObjLookupTable::is_lookup_table_obj(vmg_ body->val.obj)) { /* * The body is a LookupTable, so we have a set of form * fields to encode as HTML form data. */ /* cast the object value */ CVmObjLookupTable *tab = (CVmObjLookupTable *)vm_objp(vmg_ body->val.obj); /* add each key in the table as a form field */ iter_body_table_ctx ctx(this, rc); tab->for_each(vmg_ iter_body_table, &ctx); } else { /* * It's not a lookup table, so it must be bytes to upload, * as a string or ByteArray. */ const char *def_mime_type = 0; CVmStream *stream = val_to_stream(vmg_ body, &def_mime_type); /* presume we're going to use the default mime type */ const char *mime_type = def_mime_type; size_t mime_type_len = (mime_type != 0 ? strlen(mime_type) : 0); /* if the caller specified a mime type, use it instead */ if (body_type != 0) { mime_type = body_type + VMB_LEN; mime_type_len = vmb_get_len(body_type); } /* add the stream */ this->body->add( "", 0, "", 0, mime_type, mime_type_len, stream); } } }
/* * Connect to the Web UI */ void CVmBifNet::connect_ui(VMG_ uint oargc) { /* check arguments */ check_argc(vmg_ oargc, 2); /* get the server object */ vm_val_t *srv = G_stk->get(0); if (srv->typ != VM_OBJ || !CVmObjHTTPServer::is_httpsrv_obj(vmg_ srv->val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* get the object pointer properly cast */ CVmObjHTTPServer *srvp = (CVmObjHTTPServer *)vm_objp(vmg_ srv->val.obj); /* get the URL path */ const char *path = G_stk->get(1)->get_as_string(vmg0_); if (path == 0) err_throw(VMERR_BAD_TYPE_BIF); /* make a null-terminated copy of the path */ char *pathb = lib_copy_str(path + VMB_LEN, vmb_get_len(path)); /* get the server network address information */ char *errmsg = 0; char *addr = 0, *ip = 0; int port; int ok = srvp->get_listener_addr(addr, ip, port); /* * If we don't have a network configuration yet, create one. This * notifies other subsystems that we have an active web UI; for * example, the presence of a network UI disables the regular console * UI, since all UI actions have to go through the web UI once it's * established. * * The interpreter startup creates a network configuration if it * receives command-line information telling it that the game was * launched by a Web server in response to an incoming client request. * When the user launches the game in stand-alone mode directly from * the operating system shell, there's no Web server launch * information, so the startup code doesn't create a net config object. * So, if we get here and find we don't have this object, it means that * we're running in local standalone mode. */ if (G_net_config == 0) G_net_config = new TadsNetConfig(); /* connect */ if (ok) { /* connect */ ok = osnet_connect_webui(vmg_ addr, port, pathb, &errmsg); } else { /* couldn't get the server address */ errmsg = lib_copy_str( "No address information available for HTTPServer"); } /* free strings */ lib_free_str(pathb); lib_free_str(addr); lib_free_str(ip); /* if there's an error, throw it */ if (!ok) { G_interpreter->push_string(vmg_ errmsg); lib_free_str(errmsg); G_interpreter->throw_new_class(vmg_ G_predef->net_exception, 1, "Error connecting to Web UI"); } /* no return value */ retval_nil(vmg0_); }