Beispiel #1
0
/*
 *   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;
}
Beispiel #2
0
/*
 *   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);
}
Beispiel #3
0
/*
 *   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);
    }
}
Beispiel #4
0
/*
 *   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;
}
Beispiel #5
0
/*
 *   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);
}
Beispiel #6
0
/*
 *   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;
}
Beispiel #7
0
/*
 *   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;
    }
}
Beispiel #8
0
/* 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);
    }
}
Beispiel #9
0
/*
 *   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;
        }
    }
}
Beispiel #10
0
/*
 *   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);
    }
}
Beispiel #11
0
/*
 *   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();
}
Beispiel #12
0
/*
 *   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_);
}
Beispiel #13
0
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();
}
Beispiel #14
0
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;
}
Beispiel #15
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;
}
Beispiel #16
0
/*
 *   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;
}
Beispiel #17
0
/*
 *   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();
}
Beispiel #18
0
/*
 *   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);
}
Beispiel #19
0
/*
 *   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);
}
Beispiel #20
0
/*
 *   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;
}
Beispiel #21
0
    /* 
     *   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);
            }
        }
    }
Beispiel #22
0
/*
 *   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_);
}