LUA源码分析1—lua的内部的类型分析

本文分析主要是基于Lua5.3.1版本。
lua的源码比较简洁,看起来比较费劲。
先要搞懂一个lua脚本如何被编译成为字节码,然后被放到Lua虚拟机里面执行,整个过程很复杂。首先需要先搞懂lua中如何定义数据。

1. LUA的类型抽象

lua中的类型叫做Value。
Value的定义如下:

/*
** Union of all Lua values
*/
typedef union Value {
  struct GCObject *gc;    /* collectable objects */
  void *p;         /* light userdata */
  lua_CFunction f; /* light C functions */
  lua_Integer i;   /* integer numbers */
  lua_Number n;    /* float numbers */
} Value;

复制代码

可以看出:
Value其实是一个Union类型。
也就是说一个Value,要不是一个GCObject,要不是一个light userdata/ light C functions/integer numbers/float numbers

然后再把这个Union给封装一层, 加上一个type标识这个value是什么类型,这个新的类型就是TValue.

/*
** Tagged Values. This is the basic representation of values in Lua:
** an actual value plus a tag with its type.
*/
#define TValuefields	Value value_; lu_byte tt_

typedef struct TValue {
  TValuefields;
} TValue;

复制代码

用图来解释下如下:

图片.png

对于

 void *p;         /* light userdata */
  lua_CFunction f; /* light C functions */
  lua_Integer i;   /* integer numbers */
  lua_Number n;    /* float numbers */
复制代码

这几种类型从注释上看,还是比较好理解的,但是对于GCObject,这个就是lua的一种抽象类型了,代表这种类型的对象是可以被垃圾回收的。

2. lua可垃圾回收对象类型

从上面的图可以看出,TValue类型中有一种可被垃圾回收的对象类型—GCObject.

2.1 GCObject对象定义

lua本身是一个动态语言,所有的对象其实都是分配在堆内存上的,通过一个GCList链表进行管理。所以Lua在创建对象的时候都是使用GCObject类型做抽象,也就是创建的所有对象其实都是一种GCObject对象。
通过名字不难看出,GCObject就是一种可以被垃圾回收的Object。简单的理解就是在Lua VM中,它们都是被垃圾回收链表管理的对象。

先看下GCObject对象的定义:

/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/

#define CommonHeader	GCObject *next; lu_byte tt; lu_byte marked


/*
** Common type has only the common header
*/
struct GCObject {
  CommonHeader;
};

复制代码

可以看出GCObject都有一个CommnHeader,其中有3个成员:

  • next指针, 证明lua中所有的变量都是使用一个链表串起来的;
  • tt 当前这个Object类型
  • marked 是标记这个用户的回收状态,后续再分析LUA的垃圾回收的时候会介绍到;

那么LUA内部,支持哪些可以垃圾回收的类型呢?
从代码分析,可以看出一共是5种内部类型,分别如下:

  1. TString
  2. Udata
  3. Proto
  4. Closure
  5. Table

如下是几种数据类型的定义说明.

TString的定义:

/*
** Header for string value; string bytes follow the end of this structure
** (aligned according to 'UTString'; see next).
*/
typedef struct TString {
  CommonHeader;
  lu_byte extra;  /* reserved words for short strings; "has hash" for longs */
  lu_byte shrlen;  /* length for short strings */
  unsigned int hash;
  union {
    size_t lnglen;  /* length for long strings */
    struct TString *hnext;  /* linked list for hash table */
  } u;
} TString;

复制代码

Udata定义:

/*
** Header for userdata; memory area follows the end of this structure
** (aligned according to 'UUdata'; see next).
*/
typedef struct Udata {
  CommonHeader;
  lu_byte ttuv_;  /* user value's tag */
  struct Table *metatable;
  size_t len;  /* number of bytes */
  union Value user_;  /* user value */
} Udata;

复制代码

Proto定义:

/*
** Function Prototypes
*/
typedef struct Proto {
  CommonHeader;
  lu_byte numparams;  /* number of fixed parameters */
  lu_byte is_vararg;
  lu_byte maxstacksize;  /* number of registers needed by this function */
  int sizeupvalues;  /* size of 'upvalues' */
  int sizek;  /* size of 'k' */
  int sizecode;
  int sizelineinfo;
  int sizep;  /* size of 'p' */
  int sizelocvars;
  int linedefined;
  int lastlinedefined;
  TValue *k;  /* constants used by the function */
  Instruction *code;  /* opcodes */
  struct Proto **p;  /* functions defined inside the function */
  int *lineinfo;  /* map from opcodes to source lines (debug information) */
  LocVar *locvars;  /* information about local variables (debug information) */
  Upvaldesc *upvalues;  /* upvalue information */
  struct LClosure *cache;  /* last-created closure with this prototype */
  TString  *source;  /* used for debug information */
  GCObject *gclist;
} Proto;
复制代码

Closure分为两种,Lua闭包和C闭包,定义如下:

/*
** Closures
*/

#define ClosureHeader \
	CommonHeader; lu_byte nupvalues; GCObject *gclist

typedef struct CClosure {
  ClosureHeader;
  lua_CFunction f;
  TValue upvalue[1];  /* list of upvalues */
} CClosure;

typedef struct LClosure {
  ClosureHeader;
  struct Proto *p; //lua函数的原型指针
  UpVal *upvals[1];  /* list of upvalues */
} LClosure;
复制代码

Table定义:

typedef struct Table {
  CommonHeader;
  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */
  lu_byte lsizenode;  /* log2 of size of 'node' array */
  unsigned int sizearray;  /* size of 'array' array */
  TValue *array;  /* array part */
  Node *node;
  Node *lastfree;  /* any free position is before this position */
  struct Table *metatable;
  GCObject *gclist;
} Table;
复制代码

从面向对象的角度分析的话,可以认为这5种内部基础类型,都继承自GCObject

image.png

2.2 Lua中的GCObject对象创建过程

2.2.1 创建一个GCObject对象

创建一个内部对象,使用同一个的接口函数luaC_newobj (lua_State *L, int tt, size_t sz)
参数说明:

  • lua_Stata *L 是当前的lua上下文
  • tt 需要创建的对象的类型
  • sz 需要创建的对象的大小

返回值说明:

  • GCObject 创建好的抽象对象的的指针

从入参其实不难理解,这个函数有点像面向对象里面的工厂方法,传入一个需要的对象类型,就返回一个创建好的父类指针,但是这里其实只是做了申请内存的操作。
每个具体的对象都有自己的创建函数,每个创建函数又都会调用luaC_newobj函数来申请内存。
下面是luaC_newobj函数代码实现:

/*
** create a new collectable object (with given type and size) and link
** it to 'allgc' list.
*/
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
  global_State *g = G(L);
  GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz));
  o->marked = luaC_white(g); //标记这个对象是white
  o->tt = tt;  //类型
  o->next = g->allgc; //新创建的对象放到gclist的最前面
  g->allgc = o;
  return o;
}

#define luaM_newobject(L,tag,s)	luaM_realloc_(L, NULL, tag, (s))

/*
** generic allocation routine.
*/
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
  void *newblock;
  global_State *g = G(L);
  size_t realosize = (block) ? osize : 0; //新建的话,block都是NULL
  lua_assert((realosize == 0) == (block == NULL));
#if defined(HARDMEMTESTS)
  if (nsize > realosize && g->gcrunning)
    luaC_fullgc(L, 1);  /* force a GC whenever possible */
#endif
  newblock = (*g->frealloc)(g->ud, block, osize, nsize);  //调用的就是l_alloc
  if (newblock == NULL && nsize > 0) {
    lua_assert(nsize > realosize);  /* cannot fail when shrinking a block */
    if (g->version) {  /* is state fully built? */
      luaC_fullgc(L, 1);  /* try to free some memory... */
      newblock = (*g->frealloc)(g->ud, block, osize, nsize);  /* try again */
    }
    if (newblock == NULL)
      luaD_throw(L, LUA_ERRMEM);
  }
  lua_assert((nsize == 0) == (newblock == NULL));
  g->GCdebt = (g->GCdebt + nsize) - realosize;
  return newblock;
}

复制代码

上面的函数夹杂了很多GC的处理逻辑。本文对其中垃圾回收的原理不做详细介绍。但是可以很清晰的看到,创建一个对象,最终都会使用 (*g->frealloc)(g->ud, block, osize, nsize);这个函数。而这个函数其实就是realloc,申请堆内存。

2.2.2 抽象对象到具体对象的转换

通过统一的接口luaC_newobj函数,创建的内部对象都是GCObjcet类型。如果要正常使用,还需要做一次转换,LUA对于这种抽象结构转换为具体对象结构的行为,封装了一套宏函数:

/* macros to convert a GCObject into a specific value */
#define gco2ts(o)  \
	check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))
#define gco2u(o)  check_exp((o)->tt == LUA_TUSERDATA, &((cast_u(o))->u))
#define gco2lcl(o)  check_exp((o)->tt == LUA_TLCL, &((cast_u(o))->cl.l))
#define gco2ccl(o)  check_exp((o)->tt == LUA_TCCL, &((cast_u(o))->cl.c))
#define gco2cl(o)  \
	check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))
#define gco2t(o)  check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))
#define gco2p(o)  check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p))
#define gco2th(o)  check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th))

复制代码

3 总结

一张图总结下:

图片.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享