⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 library.txt

📁 This package consists of the executable (UCW), a default script file, this file, and the library fi
💻 TXT
📖 第 1 页 / 共 4 页
字号:

Programs in Java and the .NET family are also compiled (albeit for a virtual machine) but carry rich metadata about their own classes, etc.  A Java program may list the available fields of a class, which allows almost automatic object persistence, etc.  Reflection (sometimes called introspection) is the ability of a program to query its own metadata.

UnderC exports classes like XClass, XFunction, etc which 'shadow' their internal equivalents (Class, Function, etc).  This external interface provides you with a simplified view of UnderC internals designed for read-only access to metadata.  You don't need to know the intimate details of the UnderC implementation to use UCRI effectively, although there are a few places where such knowledge is useful. This section is a tutorial on UCRI programming, which also will derive convenient functions and classes to do the following useful things:
   - custom tracing of functions and methods
   - looking for all references to a given function
   - implementing a simple profiler for UnderC

When reading the following, it's useful to have /ucw/src/ucri.h at hand.

XNTable

This class represents a named symbol table, which allows you access to namespace and class contexts. There are two functions, uc_global() and uc_std(), which return the important namespaces. (Please note that you _must_ call uc_ucri_init() first to make these available.).  Symbol tables contain _entries_, represented by XEntry.

;> XNTable* pglob = uc_global();
;> int fred = 2;
;> XEntry* pe = pglob->lookup("fred");

(It can get tedious typing these explicit typenames out, especially in an interactive session, so from now I'll freely use 'let' as a shortcut for __declare)

;> #define let __declare
;> let p2 = pglob->lookup("uc_global");

The #v command is useful if you've forgotten the type of a variable; it will also list the class members, if it's a class type.

;> #v p2
VAR XEntry* p2 size 4 offset 32770
CLASS(XEntry) __C__ addr_mode base_entry clone
data entry function m_entry
name nfun ptr set_data
set_ptr size str_to_val type
val_as_str

Obviously, all entries have a name:
;> pe->name();
(char*) "fred"

Global variables like 'fred' have an associated address, which you can use to access the value:

;> pe->ptr();    // i.e. &fred
(void*) 957FF7
;> *(int *)pe->ptr();
(int&) 2

All entries have a type, represented by XType:

;> XType* t = pe->type();
;> t->as_str();
(char*) "int"
;> t->size();
(int) 4

XType provides a number of methods (see the class definition in ucri.h) for querying the type:

;> float** pf;
;; let tpf = pglob->lookup("pf")->type();
;> tpf->as_str();
(char*) "float**"
;> tpf->is_float();
(bool) true
;> tpf->is_pointer();
(bool) true
;> tpf->pointer_depth();
(int) 2
;> tpf->is_class();
(bool) false

Here's an example of the useful #alias command, which works very much like #define, except that it's specifically designed for constructing new commands, so the macro arguments are assumed to simply separated by spaces. Here I'm defining a new command 'type' which tells you what the current type of a variable is, without all the extra baggage of #v:

;> #alias type(x) uc_global()->lookup(#x)->type()->as_str();
;> type pf
(char*) "float**"

* XFunction

XEntry has a method nfun() which will tell you whether this particular entry is a function or not;  if it returns a value greater than zero, then this is the number of functions in this overloaded set.

;> pe->nfun();   // not a function!
(int) 0
;> let pfe = pglob->lookup("sin");
;> pfe->nfun();
(int) 1
;> pfe->function(1);
(XFunction*) 1279700
;> let po = pglob->lookup("<<");
;> po->nfun();
(int) 13

(Please note that 'operator<<' is internally kept as '<<', etc; class constructors are '__C__', and class destructors are '__D__'.)

XFunction allows you to access all the properties of a function; its full name, its return type, the names and types of its arguments, and its context.

;> string s;
;> let fsin = pfe->function(1);
;> fsin->as_str(s);
;> s;
(string) s = 'double sin(double )'
;> fsin->ret_type()->as_str();
(char*) "double"

To ask about argument types, use the XTList container (it behaves just like list<XType*>,
but because of certain UC limitations, I'm forced to use an identical template with a different name). It's useful to overload operator<< for XTList:

ostream& operator<< (ostream& os, XTList& tl)
{
    XTList::iterator xtli;
    for(xtli = tl.begin(); xtli != tl.end(); ++xtli)
      os << (*xtli)->as_str() << ' ';
     return os;
}

and to define a shorthand for looking up functions:

XFunction* lookup_fun(char* name)
{ 
  return pglob->lookup(name)->function(1);
}

;> double add(double x, int y, short z) { return x+y+z; }
;> let fadd = lookup_fun("add");
;> XTList tl = fadd->args();
;> tl;
double int short
 
(Please note the usual default behaviour of UnderC in interactive mode; when trying to dump the value of an object, it will look for any overloaded operator<<)

To extract both argument types and names, use XFunction::get_args(). Somewhat inconsistently, this function takes pointers rather than references; the reason is that the second argument is optional and can be NULL. Here I've overloaded operator<< for XStringList as well:

;> XStringList sl;
;> fadd->get_args(&tl,&sl);
;> sl;
x y z

XFunction::where() can be used to discover where a particular function has been defined. It will return 0 if no line number information can be found (for instance, builtin functions):
;> string s;
;> fadd->where(s);
(int) 54
;> s;
(string) s = 'defs.h'

What is a function in UnderC? A function's address does not directly refer to executable pcode, but to a FBlock.  This carries information like size of arguments, etc, and the actual pointer to code.  Making function references indirect like this means that UC functions can be recompiled without the function address changing. XFunction::fblock() returns the function's address, and XFunction::from_fb() returns the function object corresponding to a function.

;> &add;
(double (*)(double x,int y,short z)) A98045
;> fadd->fblock();
(void*) A98045
;> fadd;
(XFunction*) fadd = 1571230
;> XFunction::from_fb(&add);
(XFunction*) 1571230
;>

You can examine function block internals of a function very easily:
;> FBlock* fb = &cos;
;> #d fb
(Class*) class_ptr = 0
(Table*) context = A3FD80
(void*) data = 4BE794
(Entry*) entry = A3E170
(Function*) function = A3FE40
(int) nargs = 2
(int) nlocal = 0
(int) ntotal = 2
(Instruction*) pstart = A3E1D0
(XTrace*) trace = 0

Yes, you can get an XFunction using new XFunction(fb->function), but I don't recommend it because XFunction::from_fb() will guarantee that each XFunction object matches exactly one Function object.


XClass

XClass represents class contexts;  it's derived from XNTable and so supports lookup(). Here we find the entry of the member function string::substr():  XNTable::lookup_class() will return NULL both if the symbol isn't found _or_ the symbol isn't a class.

;> XClass* pc = pglob->lookup_class("string");
;> pc->name();
(char*) "string"
;> pc->lookup("substr");
(XEntry*) 1579720

XNTable::lookup_class() is a shorthand for the following operation, where we check the type  of the entry before extracting the class pointer from that type.  

XClass* glob_class(char* name)
{
 XEntry* pe = uc_global()->lookup(name);
 if (pe != NULL && pe->type()->is_class()) {
    return pe->type()->as_class();
 } else return NULL;
}

To demonstrate the kind of information that XClass makes available to you, here are some simple classes with virtual methods:

class A {
  int a;
public:
  A() : a(10) { }
  void set(int _a) { a = _a; }
  int  get()       { return a; }
  virtual void show() 
  { cout << a << endl; }
};

class B: public A {
public:
  void show()
  { cout << "I'm B! " << get() << endl; }
};

It's possible to find out the base class of a given class, and generally whether it inherits from another. Do note that UCRI guarantees that objects like XEntry,XFunction
and XClass are unique, so that (for instance) repeated calls to lookup_class("A") will return the same object. (Don't assume this is true for XType! Use XType::match() to compare types)

;> let pa = pglob->lookup_class("A");
;> let pb = pglob->lookup_class("B");
;> pa; pb;
(XClass*) pa = 157FD20
(XClass*) pb = 157F6D0
;> pb->base_class();
(XClass*) 157FD20
;> pb->inherits_from(pa);
(int) 1
 1

Once you have an object, you can ask what the dynamic type of the object is. The object's type must be polymorphic, that is, has virtual methods.  The reason is that such classes carry a 'hidden pointer' to a Virtual Method Table (VMT) which handles the magic of selecting the appropriate behaviour at runtime. Generally, the VMT pointer sits immediately before the object in memory in UnderC, but not always; imported objects may use a different strategy.  So use XClass::get_class_of() as a relatively safe method to find the type, but remember that the result is undefined if the object doesn't have a VMT.

;> A* p1 = new A();
;> A* p2 = new B();
;> dynamic_cast<B*>(p1);
(B*) 0
;> dynamic_cast<B*>(p2);
(B*) 1581774
;> p2->show();
I'm B! 20
;> p1->show();
10
;> XClass::get_class_of(p2);
(XClass*) 157F6D0
;> A* p3 = new B();
;> XClass::get_class_of(p2) == XClass::get_class_of(p3);
(bool) true

Here is a more convenient way to look functions up (#include <ucri/utils.h). It will detect qualified references of the form "context::name" and separate out that context to use for lookup:

XNTable* context_from_pattern(string& pat, XNTable* context = NULL)
{
 int i = pat.find("::");
 if (context==NULL) context=uc_global();
 if (i != string::npos) {
    string class_name = pat.substr(0,i);
    pat = pat.substr(i+2,999);
    context = context->lookup_class(class_name.c_str());
 } 
 return context;
}

XFunction* lookup_fun(string name, int k = 1, XNTable* context = NULL)
{
  context = context_from_pattern(name,context);
  XEntry* xe = context->lookup(name.c_str());
  if (xe->nfun() >= k) return xe->function(k);
  else return NULL;
}

These functions, as with all the <ucri/...> headers, are in namespace UCRI, so from now I'm assuming 'using namespace UCRI;'

Examing the contents of Symbol Tables

<ucri/utils.h> defines a simple function for printing out all entries in a entry list.

void dump_entries(XEntries& xl)
{
  XEntry* xe;
  int k = 0;
  FOR_EACH(xe,xl) {
   cout << xe->name();
   if (++k % 5 != 0) cout << '\t';
   else cout << endl;
  }
  cout << endl;
}

;> dump_entries(pglob->variables());
cerr    cin     cout    endl    ends

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -