[lua source code] object system

版本號(hào):Lua 5.3

Lua Type

lua 的類型定義在lobject.h這個(gè)文件里蛉威,主要的類型如下:

  • none
  • nil
  • light user data
  • boolean
  • number
    • integer
    • float
  • function type
    • light C function
    • closure (gc object)
      • lua closure
      • C closure
  • string (gc object)
  • user data (gcobject)
  • table (gc object)
  • thread ( gc object)

Lua Value

lua使用一個(gè)union來(lái)統(tǒng)一表示上述類型:

union Value {
  GCObject *gc;    /* collectable objects */
  void *p;         /* light userdata */      
  int b;           /* booleans */            
  lua_CFunction f; /* light C functions */ 
  lua_Integer i;   /* integer numbers */    
  lua_Number n;    /* float numbers */   
};

同時(shí),添加一個(gè)額外的byte來(lái)標(biāo)記具體的類型:

#define TValuefields    Value value_; int tt_  //<值雇锡,類型標(biāo)記>
struct lua_TValue {
  TValuefields; // Value value_; int tt_;
};
typedef struct lua_TValue TValue;

如果展開(kāi)上述代碼箫锤,則為:

typedef struct lua_TValue{
   Value value_;
   int tt_;      
}TValue;

其中贬蛙,tt_是一個(gè)8 bits 的類型標(biāo)記字段,被分成3個(gè)部分:

  • 0-3位谚攒,表示大類型
  • 4-5位阳准,表示子類型
  • 6位,表示是否可以垃圾回收

綜合使用上面三點(diǎn)馏臭,就可以完整標(biāo)記所有的lua類型野蝇,每種類型標(biāo)記的值如下(這些定義在lua.h和lobject.h里,此處把它們合在一起括儒,更直觀):

#define LUA_TNONE            (-1)
#define LUA_TNIL              0
#define LUA_TBOOLEAN          1
#define LUA_TLIGHTUSERDATA  2
#define LUA_TNUMBER        3
  #define LUA_TNUMFLT        (LUA_TNUMBER | (0 << 4))  /* float numbers */
  #define LUA_TNUMINT        (LUA_TNUMBER | (1 << 4))  /* integer numbers */
#define LUA_TSTRING        4
  #define LUA_TSHRSTR       (LUA_TSTRING | (0 << 4))  /* short strings */
  #define LUA_TLNGSTR       (LUA_TSTRING | (1 << 4))  /* long strings */
#define LUA_TTABLE          5
#define LUA_TFUNCTION        6
  #define LUA_TLCL          (LUA_TFUNCTION | (0 << 4))  /* Lua closure */
  #define LUA_TLCF          (LUA_TFUNCTION | (1 << 4))  /* light C function */
  #define LUA_TCCL          (LUA_TFUNCTION | (2 << 4))  /* C closure */
#define LUA_TUSERDATA        7
#define LUA_TTHREAD        8
#define LUA_NUMTAGS        9
#define LUA_TPROTO          LUA_NUMTAGS
#define LUA_TDEADKEY          (LUA_NUMTAGS+1)
#define LUA_TOTALTAGS        (LUA_TPROTO + 2)
#define BIT_ISCOLLECTABLE    (1 << 6)

Value是一個(gè)聯(lián)合體浪耘,第一個(gè)字段是GCObject,包括:closure(lua closure+C closure), string, userdata, table, thread塑崖,其他幾個(gè)則是非垃圾回收類型:light user data, boolean, light C function, number(integer+float).非垃圾回收字段被直接展開(kāi)在聯(lián)合體里,GCObject則是可垃圾回收類型的公共類:

#define CommonHeader GCObject* next;lua_byte tt; lua_byte marked
typedef struct GCObject{
  CommonHeader; // GCObject* next;lua_byte tt; lua_byte marked;
};

GC Object

展開(kāi)上述GCObject代碼痛倚,則為:

typedef struct GCObject{
  GCObject* next;
  lua_byte tt; 
  lua_byte marked;
};

可見(jiàn)规婆,GCObject是以鏈表的形式串在一起。其中,tt字段是類型標(biāo)記字段抒蚜,既然TValue里已經(jīng)標(biāo)記了類型掘鄙,此處為什么重復(fù)標(biāo)記呢?我的理解是因?yàn)樵谑褂玫倪^(guò)程中嗡髓,GCObject未必是作為一個(gè)TValue傳入操漠,如果只有GCObject指針的時(shí)候,重復(fù)的tt即可使用上饿这。而marked則是在垃圾回收過(guò)程中用以標(biāo)記對(duì)象存活狀態(tài)的浊伙。

所有的GC類型,都有公共的CommonHeader頭部长捧,這是在C這種語(yǔ)言里的一種“繼承”用法嚣鄙。

TString

typedef struct TString{
  CommonHeader; // GCObject* next;lua_byte tt; lua_byte marked;
  lua_byte extra;
  unsigned int hash;
  size_t len;
  struct TString* hnext;
}TString;

由于lua的string有兩個(gè)子類型:short stringlong string。其中串结,extra字段用來(lái)標(biāo)記是否是long string哑子,hash字段則是用存儲(chǔ)在全局字符串池里的哈希值;len表示長(zhǎng)度肌割,lua的字符串并不以\0結(jié)尾卧蜓,所以需要存儲(chǔ)長(zhǎng)度信息。hnext是用來(lái)把全局TString串起來(lái)把敞,整個(gè)鏈表就是字符串池弥奸。而真正的字符串的內(nèi)容,直接存儲(chǔ)在結(jié)構(gòu)體后面的內(nèi)存里先巴,為了保證內(nèi)存的對(duì)齊其爵,對(duì)上述TString和基本類型合并做一個(gè)字節(jié)對(duì)齊:

typedef union { double u; void *s; lua_Integer i; long l; } L_Umaxalign;
typedef union UTString{
  L_Umaxalign dummy;
  TString tsv; 
}UTString;

從而,真正的字符串內(nèi)容的內(nèi)存地址獲取如下:

/*
** Get the actual string (array of bytes) from a 'TString'.
** (Access to 'extra' ensures that value is really a 'TString'.)
*/
#define getaddrstr(ts)  (cast(char *, (ts)) + sizeof(UTString))
#define getstr(ts)  \
  check_exp(sizeof((ts)->extra), cast(const char*, getaddrstr(ts)))

/* get the actual string (array of bytes) from a Lua value */
#define svalue(o)       getstr(tsvalue(o))

UData

typedef struct Udata{
  CommonHeader;
  lua_byte ttuv_;// user value's tag
  struct Table* metatable;
  size_t len;
  union Value user_; //user value
}Udata;

User Data和String的布局基本一樣伸蚯。首先是共同的CommonHeader摩渺,然后是一個(gè)類型標(biāo)記字段: ttuv_,此處標(biāo)記的是該UserData里實(shí)際存儲(chǔ)的值(user_字段)的類型剂邮;其次最明顯的區(qū)別是有一個(gè)Table類型的metatable摇幻,所有對(duì)User Data的操作都會(huì)去這個(gè)metatable里查找是否有對(duì)應(yīng)的屬性或者方法定義,這也是lua的所有魔法所在挥萌。len字段則定義了實(shí)際的數(shù)據(jù)長(zhǎng)度绰姻,同時(shí)還有一個(gè)附加的用戶定義值字段:user_

User Data和String一樣把額外的數(shù)據(jù)塊存在結(jié)構(gòu)體后面的內(nèi)存里引瀑,同樣地對(duì)起始地址做了對(duì)齊:

typedef union UUdata{
  L_Umaxalign dummy;
  Udata uv;
}UUdata;

從而狂芋,User Data的額外數(shù)據(jù)塊的地址如下,注意:

/*
**  Get the address of memory block inside 'Udata'.
** (Access to 'ttuv_' ensures that value is really a 'Udata'.)
*/
#define getudatamem(u)  \
  check_exp(sizeof((u)->ttuv_), (cast(char*, (u)) + sizeof(UUdata)))

另外憨栽,對(duì)于User Data來(lái)說(shuō)帜矾,metatable是每個(gè)實(shí)例一個(gè)翼虫,user_ttuv_兩個(gè)字段則是值部分。所以設(shè)置和獲取UserData的接口如下:

#define setuservalue(L,u,o) \
    { const TValue *io=(o); Udata *iu = (u); \
      iu->user_ = io->value_; iu->ttuv_ = io->tt_; \
      checkliveness(G(L),io); }

#define getuservalue(L,u,o) \
    { TValue *io=(o); const Udata *iu = (u); \
      io->value_ = iu->user_; io->tt_ = iu->ttuv_; \
      checkliveness(G(L),io); }

總之屡萤,UserData=tag+value+metatable=data+metatable珍剑;

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;

首先,類似User Data死陆,Table也包括data和metatable招拙,其中metatable的構(gòu)成如下:

  lu_byte flags; // 1<<p means tagmethod(p) is not present
  struct Table* metatable;

如果要判斷某個(gè)預(yù)定義下標(biāo)的元方法是否存在,可以通過(guò)1<<p來(lái)判斷措译,如果有别凤,則從metatable里獲取。

其次瞳遍,Table包括array部分和hash table部分闻妓,array部分如下:

// array 
  unsigned int sizearray;
  TValue* array;

而hash table部分如下:

// hash table
  lu_byte lsizenode;
  Node* node;
  Node* lastfree;

Node就是一個(gè)key-value,通過(guò)key部分的鏈表串在一起:

typedef struct Node {
  TValue i_val;
  TKey i_key;
} Node;

typedef union TKey {
  struct {
    TValuefields;
    int next;  /* for chaining (offset for next node) */
  } nk;
  TValue tvk;
} TKey;

最后掠械,gclist是用以垃圾回收的由缆,按下不表。從而猾蒂,我們可以重新調(diào)整下Table的聲明順序均唉,使得更利于閱讀:

typedef struct Table{
  CommonHeader;
  
  lu_byte flags; // 1<<p means tagmethod(p) is not present
  struct Table* metatable;

  lu_byte lsizenode;
  Node* node;
  Node* lastfree;

  unsigned int sizearray;
  TValue* array;

  GCObject* gclist;
}Table;

Closure

到了最復(fù)雜的Closure部分。根據(jù)前面的鋪墊肚菠,我們知道Lua的函數(shù)包括Lua Closure, light C function以及 C Closure三種小類型舔箭,其中l(wèi)ight C function就是純c函數(shù),在Value的定義里直接用一個(gè)lua_CFunction函數(shù)指針指向蚊逢,從而剩下兩個(gè)Closure類型层扶。

lua的源碼里把Lua Closure和 C Closure作為一個(gè)聯(lián)合體,構(gòu)成了Closure類型:

typedef union Closure{
  CClosure c;
  LClosure l;
}Closure;

Closure作為一個(gè)GC Object烙荷,自然需要包含CommonHeader镜会,由于是一個(gè)聯(lián)合體,所以這個(gè)CommonHeader就分別拆到了CClosure和LClosure里去了:

#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;
  UpVal *upvals[1];  /* list of upvalues */
} LClosure;

注意终抽,這里CommonHeader+nupvalues+gclist共同構(gòu)成了ClosureHeader戳表,這是因?yàn)閮煞NClosure都有公共的部分:nupvalues說(shuō)明閉包變量的個(gè)數(shù),gclist則用以垃圾回收昼伴。

我們先看比較簡(jiǎn)單的CClosure匾旭,就是直接把lua_CFunction加上被閉包的c變量upvalue[1]數(shù)組,此處利用數(shù)組在結(jié)構(gòu)的末尾圃郊,則只需聲明為一個(gè)元素的數(shù)組即可价涝。

比較復(fù)雜的是LClosure,中間的關(guān)鍵結(jié)構(gòu)是Proto* p; 這個(gè)字段代表了一個(gè)Lua 閉包持舆。我們一步步展開(kāi):

/*
** Function Prototypes
*/
typedef struct Proto {
  CommonHeader;
  lu_byte numparams;  /* number of fixed parameters */
  lu_byte is_vararg;
  lu_byte maxstacksize;  /* maximum stack used 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;
  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;

調(diào)整字節(jié)對(duì)齊后的結(jié)構(gòu)體并不利于閱讀飒泻,我們不妨重新排版下:

typedef struct Proto{
  CommonHeader;
  
  // 1
  lu_byte numparams;
  lu_byte is_vararg;
  lu_byte maxstacksize;

  // 2
  int sizek;
  TValue* k;
  
  // 3
  int sizelocalvars;
  LocVar* locvars;

  // 4
  int sizeupvalues;
  Upvaldesc* upvalues;

  // 5
  int sizep;
  struct Proto** p;
  struct LClosure* cache;

  // 6
  int sizecode;
  Instruction* code;

  // 7
  int sizelineinfo;
  int* lineinfo;

  // 8
  int linedefined;
  int lastlinedefined;
  TString* source;

  // 9
  GCObject* gclist;
}Proto;
  1. 函數(shù)原型信息
  • num params: 函數(shù)參數(shù)個(gè)數(shù)
  • is_vararg: 是否是有變長(zhǎng)參數(shù)
  • maxstacksize: 最大的函數(shù)棧長(zhǎng)度
  1. 常量
  • sizek: 常量數(shù)組長(zhǎng)度
  • k: 常量數(shù)組
  1. 局部變量
  • sizelocalvars:局部變量數(shù)組長(zhǎng)度
  • localvars: 局部變量數(shù)組
  1. 閉包變量
  • sizeupvalues: 閉包變量數(shù)組長(zhǎng)度
  • upvalus: 閉包變量數(shù)組
  1. 嵌套的Proto:
  • sizep:嵌套的Proto數(shù)組長(zhǎng)度
  • p:嵌套的Proto數(shù)組
  • cache: 緩存嵌套的Proto的閉包鞭光。
  1. Proto代表一個(gè)可執(zhí)行函數(shù),前面的信息都是數(shù)據(jù)部分(參數(shù)泞遗、常量、局部變量席覆、閉包變量)史辙,此處是指令:
  • sizecode:指令數(shù)組的長(zhǎng)度
  • code:三地址指令數(shù)組,后面單獨(dú)講解佩伤。
  1. 行信息聊倔,用以debug,每行指令都有對(duì)應(yīng)的行信息生巡。
  • sizelineinfo:行信息數(shù)組長(zhǎng)度
  • lineinfo:行信息數(shù)組
  1. 源碼
  • linedefinedlastlinedefined:函數(shù)的起始定義行號(hào)
  • source:源碼字符串耙蔑。
  1. gclist,垃圾回收專用孤荣,后面講解甸陌。

到此為止,我們把Proto的字段分拆一個(gè)閉包函數(shù)所需要的每個(gè)部分盐股,更易于理解钱豁。但還有幾個(gè)小模塊。

LocVar

/*
** Description of a local variable for function prototypes
** (used for debug information)
*/
typedef struct LocVar {
  TString *varname;
  int startpc;  /* first point where variable is active */
  int endpc;    /* first point where variable is dead */
} LocVar;

LocVar的定義疯汁,包括變量名+作用域牲尺。

Upvaldesc

/*
** Description of an upvalue for function prototypes
*/
typedef struct Upvaldesc {
  TString *name;  /* upvalue name (for debug information) */
  lu_byte instack;  /* whether it is in stack */
  lu_byte idx;  /* index of upvalue (in stack or in outer function's list) */
} Upvaldesc;

Upvaldesc只是描述了閉包變量的信息:是否在棧上+在棧上的Index。這里只有描述信息幌蚊,那么閉包變量的值存儲(chǔ)在哪里呢谤碳?

我們回頭看下LClosure的定義:

typedef struct LClosure {
  ClosureHeader;
  struct Proto *p;
  UpVal *upvals[1];  /* list of upvalues */
} LClosure;

注意看,這里和CClosure不同的是溢豆,CClosure直接用TValue數(shù)組存儲(chǔ)閉包變量蜒简,但LClosure則是用UpVal數(shù)組。我們看下UpVal沫换。

UpVal

/*
** Upvalues for Lua closures
*/
struct UpVal {
  TValue *v;  /* points to stack or to its own value */
  lu_mem refcount;  /* reference counter */
  union {
    struct {  /* (when open) */
      UpVal *next;  /* linked list */
      int touched;  /* mark to avoid cycles with dead threads */
    } open;
    TValue value;  /* the value (when closed) */
  } u;
};

UpVal定義在lfunc.h文件里臭蚁,這里第一個(gè)字段v就是指向了閉包變量的真正的值的指針。refcount是被閉包的引用計(jì)數(shù)讯赏,按下不談垮兑。單說(shuō)后面的聯(lián)合體:

  union {
    struct {  /* (when open) */
      UpVal *next;  /* linked list */
      int touched;  /* mark to avoid cycles with dead threads */
    } open;
    TValue value;  /* the value (when closed) */
  } u;

注意看,聯(lián)合體內(nèi)部有一個(gè)open結(jié)構(gòu)和一個(gè)value字段漱挎。一個(gè)Proto在外層函數(shù)沒(méi)有返回之前系枪,處于open狀態(tài),閉包的變量磕谅,直接通過(guò)UpVal ->v這個(gè)指針引用私爷。此時(shí)open結(jié)構(gòu)用來(lái)把當(dāng)前作用域內(nèi)的所有閉包變量都串起來(lái)做成一個(gè)鏈表雾棺,方便查找。此時(shí)u->value并沒(méi)有用到衬浑。

但是捌浩,如果外層函數(shù)返回,則Proto需要把閉包變量的值拷貝出來(lái)工秩,保證對(duì)象安全尸饺。這個(gè)拷貝就放在u->value里。此時(shí)助币,UpVal ->v也直接指向內(nèi)部的u->value浪听。

從而,我們也可以通過(guò)判斷UpVal ->v和u->value是否相等來(lái)判斷UpVal處于open還是clsoed狀態(tài):

#define upisopen(up)    ((up)->v != &(up)->u.value)

待續(xù)

對(duì)象系統(tǒng)的定義部分就到這里眉菱,下次分解下對(duì)象系統(tǒng)基本屬性讀寫(xiě)的util迹栓。
......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市俭缓,隨后出現(xiàn)的幾起案子克伊,更是在濱河造成了極大的恐慌,老刑警劉巖尔崔,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件答毫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡季春,警方通過(guò)查閱死者的電腦和手機(jī)洗搂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)载弄,“玉大人耘拇,你說(shuō)我怎么就攤上這事∮罟ィ” “怎么了惫叛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)逞刷。 經(jīng)常有香客問(wèn)我嘉涌,道長(zhǎng),這世上最難降的妖魔是什么夸浅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任仑最,我火速辦了婚禮,結(jié)果婚禮上帆喇,老公的妹妹穿的比我還像新娘警医。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布预皇。 她就那樣靜靜地躺著侈玄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吟温。 梳的紋絲不亂的頭發(fā)上序仙,一...
    開(kāi)封第一講書(shū)人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音溯街,去河邊找鬼诱桂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛呈昔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播友绝,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼堤尾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了迁客?” 一聲冷哼從身側(cè)響起郭宝,我...
    開(kāi)封第一講書(shū)人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掷漱,沒(méi)想到半個(gè)月后粘室,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡卜范,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年衔统,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片海雪。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锦爵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奥裸,到底是詐尸還是另有隱情险掀,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布湾宙,位于F島的核電站樟氢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏侠鳄。R本人自食惡果不足惜埠啃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望畦攘。 院中可真熱鬧霸妹,春花似錦、人聲如沸知押。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至罢绽,卻和暖如春畏线,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背良价。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工寝殴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人明垢。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓蚣常,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親痊银。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抵蚊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Lua 是單線程的,但是Lua卻有thread類型溯革,顯然直覺(jué)上Lua的thread并非通常意義上的線程贞绳,實(shí)際上它是...
    ffl閱讀 4,685評(píng)論 2 5
  • 1.1程序塊:Lua執(zhí)行的每段代碼,例如一個(gè)源代碼文件或者交互模式中輸入的一行代碼致稀,都稱為一個(gè)程序塊 1.2注釋:...
    c_xiaoqiang閱讀 2,587評(píng)論 0 9
  • Nginx API for Lua Introduction ngx.arg ngx.var.VARIABLE C...
    吃瓜的東閱讀 5,773評(píng)論 0 5
  • 1. 寫(xiě)在前面 很多時(shí)候我們都需要借助一些腳本語(yǔ)言來(lái)為我們實(shí)現(xiàn)一些動(dòng)態(tài)的配置冈闭,那么就會(huì)涉及到如何讓腳本語(yǔ)言跟原生語(yǔ)...
    杰嗒嗒的阿杰閱讀 3,431評(píng)論 9 31
  • 鹿 過(guò)草地 海 在翻騰 鯨 遨游
    斐狐閱讀 193評(píng)論 0 0