1. lua函数
1.1 函数定义
typedef int (*lua_CFunction) (lua_State *L);
typedef struct CClosure {
ClosureHeader;
lua_CFunction f;
TValue upvalue[1]; /* list of upvalues */
} CClosure;
typedef struct Proto { //语法解析的输出
CommonHeader;
lu_byte numparams; /* number of fixed (named) parameters */
lu_byte is_vararg;
lu_byte maxstacksize; /* number of registers needed by this function */
int sizeupvalues; /* size of 'upvalues' */
int sizek; //数组 成员k的大小
int sizecode; //数组 成员code的大小
int sizelineinfo; //数组 成员lineinfo的大小
int sizep; //数组 成员p的大小
int sizelocvars; //数组 成员locvars的大小
int sizeabslineinfo; /* size of 'abslineinfo' */
int linedefined; /* debug information */
int lastlinedefined; /* debug information */
TValue *k; //常量
Instruction *code; //编译成的字节码
struct Proto **p; //子函数
Upvaldesc *upvalues; /* upvalue information */
ls_byte *lineinfo; //行号信息 于code字节码的下标一一对应 (debug information)
AbsLineInfo *abslineinfo; /* idem */
LocVar *locvars; //局部变量数组 (debug information)
TString *source; /* used for debug information */
GCObject *gclist;
} Proto;
typedef struct LClosure {
ClosureHeader;
struct Proto *p; //lua解析完的结构,和c函数对应
UpVal *upvals[1]; /* list of upvalues */
} LClosure;
typedef union Closure {
CClosure c; //c函数
LClosure l; //lua函数
} Closure;
typedef struct CallInfo {
StkId func; //栈基地址
StkId top; //栈顶
struct CallInfo *previous, *next; //双向链表指针
union {
struct { /* only for Lua functions */
const Instruction *savedpc;
volatile l_signalT trap;
int nextraargs; /* # of extra arguments in vararg functions */
} l;
struct { /* only for C functions */
lua_KFunction k; /* continuation in case of yields */
ptrdiff_t old_errfunc;
lua_KContext ctx; /* context info. in case of yields */
} c;
} u;
union {
int funcidx; /* called-function index */
int nyield; /* number of values yielded */
int nres; /* number of values returned */
struct { /* info about transferred values (for call/return hooks) */
unsigned short ftransfer; /* offset of first value transferred */
unsigned short ntransfer; /* number of values transferred */
} transferinfo;
} u2;
short nresults; //返回值个数
unsigned short callstatus; //函数调用的状态
} CallInfo;
1.2 函数调用栈
CallInfo 的初始化,用Closure初始化CallInfo
CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
lua_CFunction f;
retry:
switch (ttypetag(s2v(func))) {
case LUA_VCCL: /* C closure */
f = clCvalue(s2v(func))->f;
goto Cfunc;
case LUA_VLCF: /* light C function */
f = fvalue(s2v(func));
Cfunc: {
int n; /* number of returns */
CallInfo *ci;
checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
L->ci = ci = next_ci(L); //如果cl->next为空,会申请内存并且加入双向链表
ci->nresults = nresults; //返回值数量
ci->callstatus = CIST_C;
ci->top = L->top + LUA_MINSTACK; //栈顶
ci->func = func; //栈基地址
lua_assert(ci->top <= L->stack_last);
if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
int narg = cast_int(L->top - func) - 1;
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
}
lua_unlock(L);
n = (*f)(L); //调用lua c函数 返回的是返回值的个数
lua_lock(L);
api_checknelems(L, n);
luaD_poscall(L, ci, n); //返回值移动
return NULL;
}
case LUA_VLCL: { //lua 函数
CallInfo *ci;
Proto *p = clLvalue(s2v(func))->p; //函数信息结构体 (指令、常量、子函数等)
int narg = cast_int(L->top - func) - 1; //传入的参数个数 (-1是因为包含自身)
int nfixparams = p->numparams; //函数的参数个数
int fsize = p->maxstacksize; //函数需要的栈大小
checkstackGCp(L, fsize, func);
L->ci = ci = next_ci(L); //如果cl->next为空,会申请内存并且加入双向链表
ci->nresults = nresults; //返回值个数
ci->u.l.savedpc = p->code; //pc指向指令的起始位置
ci->top = func + 1 + fsize; //栈顶
ci->func = func; //栈底
L->ci = ci;
for (; narg < nfixparams; narg++) //如果传入的参数少于函数的参数,没穿入的参数设置为nil
setnilvalue(s2v(L->top++)); /* complete missing arguments */
lua_assert(ci->top <= L->stack_last);
return ci;
}
default: { /* not a function */
checkstackGCp(L, 1, func); /* space for metamethod */
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
goto retry; /* try again with metamethod */
}
}
}
由这个函数可以看到 L->ci 保存了整个函数调用的栈。这个结构是一个双向链表,next向下调用,调用完previous返回。
函数调用的时候没有申请一个全新的栈给新的函数,使用的还是状态机中的L->stack栈。
每次函数调用的时候会以func为栈底(Closure函数对象)在ci->func保存,ci->top是栈顶。
调用栈
1.3 函数返回值
//nres是函数实际的返回值个数,wanted是函数解析完后预订的返回值个数
static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
StkId firstresult;
int i;
switch (wanted) { /* handle typical cases separately */
case 0: //没有返回值
L->top = res;
return;
case 1: //一个返回值
if (nres == 0) //如果实际没有返回,返回nil
setnilvalue(s2v(res));
else //将返回值拷贝
setobjs2s(L, res, L->top - nres); //拷贝
L->top = res + 1; //重新设置栈(返回值的上方)
return;
case LUA_MULTRET:
wanted = nres; /* we want all results */
break;
default: /* two/more results and/or to-be-closed variables */
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
ptrdiff_t savedres = savestack(L, res);
L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
L->ci->u2.nres = nres;
luaF_close(L, res, CLOSEKTOP, 1);
L->ci->callstatus &= ~CIST_CLSRET;
if (L->hookmask) /* if needed, call hook after '__close's */
rethook(L, L->ci, nres);
res = restorestack(L, savedres); /* close and hook can move stack */
wanted = decodeNresults(wanted);
if (wanted == LUA_MULTRET)
wanted = nres; /* we want all results */
}
break;
}
/* generic case */
firstresult = L->top - nres; //返回值起始的栈位置
if (nres > wanted) //如果实际返回值个数大于预定义的个数
nres = wanted; /* don't need them */
for (i = 0; i < nres; i++) //整体栈拷贝
setobjs2s(L, res + i, firstresult + i);
for (; i < wanted; i++) //如果实际返回值个数小于预定义的个数,多余的设置为nil
setnilvalue(s2v(res + i));
L->top = res + wanted; //重新设置栈(返回值的上方)
}
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
int wanted = ci->nresults;
if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
rethook(L, ci, nres);
//返回值拷贝到函数栈初始的位置
moveresults(L, ci->func, nres, wanted);
/* function cannot be in any of these cases when returning */
lua_assert(!(ci->callstatus &
(CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
L->ci = ci->previous; /* back to caller (after closing variables) */
}
2. 指令集合
2.1 指令格式
这段定义位于lopcodes.h头文件中
/*===========================================================================
We assume that instructions are unsigned 32-bit integers.
All instructions have an opcode in the first 7 bits.
Instructions can have the following formats:
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 ==========================================
iABC C(8) | B(8) |k| A(8) | Op(7) |
iABx Bx(17) | A(8) | Op(7) |
iAsBx sBx (signed)(17) | A(8) | Op(7) |
iAx Ax(25) | Op(7) |
isJ sJ(25) | Op(7) |
A signed argument is represented in excess K: the represented value is
the written unsigned value minus K, where K is half the maximum for the
corresponding unsigned argument.
===========================================================================*/
可以看到lua5.4.3有5种指令格式 iABC ,iABx,iAsBx,iAx,isJ
- 可以看到指令是uint32类型
- 前7个位代表的操作,目前这种指令格式代表可以有2^7次方个(128)指令
- A,B,C是lua的三个索引,其中A一定是是A寄存器的索引用来获取栈中的元素。B,C可以是B,C寄存器的索引,也可以是常量的索引
- 带x结尾的,如Bx,sBx,Ax,代表的是立即数Bx,Ax都是无符号数,s开头的代表有符号数(sBx)
- isJ只有一条指令是这种格式就是OP_JMP指令,sj是一个有符号数
//指令各部分的大小
#define SIZE_C 8
#define SIZE_B 8
#define SIZE_Bx (SIZE_C + SIZE_B + 1)
#define SIZE_A 8
#define SIZE_Ax (SIZE_Bx + SIZE_A)
#define SIZE_sJ (SIZE_Bx + SIZE_A)
#define SIZE_OP 7
//指令部分的起始位置
#define POS_OP 0
#define POS_A (POS_OP + SIZE_OP)
#define POS_k (POS_A + SIZE_A)
#define POS_B (POS_k + 1)
#define POS_C (POS_B + SIZE_B)
#define POS_Bx POS_k
#define POS_Ax POS_A
#define POS_sJ POS_A
- 上面是各个指令部分的大小定义的宏和指令各部分的起始位置宏
//创建一个全为1的掩码,n代表掩码个数,p是偏移的位置
#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p))
//创建一个全为0的掩码
#define MASK0(n,p) (~MASK1(n,p))
//指令格式判断
enum OpMode {iABC, iABx, iAsBx, iAx, isJ};
//检测指令的格式m是这个枚举值
#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7))
#define checkopm(i,m) (getOpMode(GET_OPCODE(i)) == m)
//opcode的设置和获取
#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) //与上一个前7位的掩码
#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ //把opcode位 置为0,然后或上新的opcode
((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
//这两个宏和上面两个类似,只是偏移和大小变成了变量
#define getarg(i,pos,size) (cast_int(((i)>>(pos)) & MASK1(size,0)))
#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \
((cast(Instruction, v)<<pos)&MASK1(size,pos))))
//获取和设置指令各个部分的宏
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
#define GETARG_B(i) check_exp(checkopm(i, iABC), getarg(i, POS_B, SIZE_B))
#define GETARG_sB(i) sC2int(GETARG_B(i))
#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
#define GETARG_C(i) check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C))
#define GETARG_sC(i) sC2int(GETARG_C(i))
#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
#define TESTARG_k(i) check_exp(checkopm(i, iABC), (cast_int(((i) & (1u << POS_k)))))
#define GETARG_k(i) check_exp(checkopm(i, iABC), getarg(i, POS_k, 1))
#define SETARG_k(i,v) setarg(i, v, POS_k, 1)
#define GETARG_Bx(i) check_exp(checkopm(i, iABx), getarg(i, POS_Bx, SIZE_Bx))
#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx)
#define GETARG_Ax(i) check_exp(checkopm(i, iAx), getarg(i, POS_Ax, SIZE_Ax))
#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax)
#define GETARG_sBx(i) \
check_exp(checkopm(i, iAsBx), getarg(i, POS_Bx, SIZE_Bx) - OFFSET_sBx)
#define SETARG_sBx(i,b) SETARG_Bx((i),cast_uint((b)+OFFSET_sBx))
#define GETARG_sJ(i) \
check_exp(checkopm(i, isJ), getarg(i, POS_sJ, SIZE_sJ) - OFFSET_sJ)
#define SETARG_sJ(i,j) \
setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ)
//指令创建宏
#define CREATE_ABCk(o,a,b,c,k) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_A) \
| (cast(Instruction, b)<<POS_B) \
| (cast(Instruction, c)<<POS_C) \
| (cast(Instruction, k)<<POS_k))
#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_A) \
| (cast(Instruction, bc)<<POS_Bx))
#define CREATE_Ax(o,a) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_Ax))
#define CREATE_sJ(o,j,k) ((cast(Instruction, o) << POS_OP) \
| (cast(Instruction, j) << POS_sJ) \
| (cast(Instruction, k) << POS_k))
- 上面是指令获取设置相关的宏
2.2 指令表
typedef enum {
/*----------------------------------------------------------------------
name args description
------------------------------------------------------------------------*/
OP_MOVE,/* A B R[A] := R[B] */
OP_LOADI,/* A sBx R[A] := sBx */
OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */
OP_LOADK,/* A Bx R[A] := K[Bx] */
OP_LOADKX,/* A R[A] := K[extra arg] */
OP_LOADFALSE,/* A R[A] := false */
OP_LFALSESKIP,/*A R[A] := false; pc++ */
OP_LOADTRUE,/* A R[A] := true */
OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
OP_GETUPVAL,/* A B R[A] := UpValue[B] */
OP_SETUPVAL,/* A B UpValue[B] := R[A] */
OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:string] */
OP_GETTABLE,/* A B C R[A] := R[B][R[C]] */
OP_GETI,/* A B C R[A] := R[B][C] */
OP_GETFIELD,/* A B C R[A] := R[B][K[C]:string] */
OP_SETTABUP,/* A B C UpValue[A][K[B]:string] := RK(C) */
OP_SETTABLE,/* A B C R[A][R[B]] := RK(C) */
OP_SETI,/* A B C R[A][B] := RK(C) */
OP_SETFIELD,/* A B C R[A][K[B]:string] := RK(C) */
OP_NEWTABLE,/* A B C k R[A] := {} */
OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][RK(C):string] */
OP_ADDI,/* A B sC R[A] := R[B] + sC */
OP_ADDK,/* A B C R[A] := R[B] + K[C]:number */
OP_SUBK,/* A B C R[A] := R[B] - K[C]:number */
OP_MULK,/* A B C R[A] := R[B] * K[C]:number */
OP_MODK,/* A B C R[A] := R[B] % K[C]:number */
OP_POWK,/* A B C R[A] := R[B] ^ K[C]:number */
OP_DIVK,/* A B C R[A] := R[B] / K[C]:number */
OP_IDIVK,/* A B C R[A] := R[B] // K[C]:number */
OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */
OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */
OP_BXORK,/* A B C R[A] := R[B] ~ K[C]:integer */
OP_SHRI,/* A B sC R[A] := R[B] >> sC */
OP_SHLI,/* A B sC R[A] := sC << R[B] */
OP_ADD,/* A B C R[A] := R[B] + R[C] */
OP_SUB,/* A B C R[A] := R[B] - R[C] */
OP_MUL,/* A B C R[A] := R[B] * R[C] */
OP_MOD,/* A B C R[A] := R[B] % R[C] */
OP_POW,/* A B C R[A] := R[B] ^ R[C] */
OP_DIV,/* A B C R[A] := R[B] / R[C] */
OP_IDIV,/* A B C R[A] := R[B] // R[C] */
OP_BAND,/* A B C R[A] := R[B] & R[C] */
OP_BOR,/* A B C R[A] := R[B] | R[C] */
OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */
OP_SHL,/* A B C R[ALO_>)?] := R[B] << R[C] */
OP_SHR,/* A B C R[A] := R[B] >> R[C] */
OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */
OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */
OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */
OP_UNM,/* A B R[A] := -R[B] */
OP_BNOT,/* A B R[A] := ~R[B] */
OP_NOT,/* A B R[A] := not R[B] */
OP_LEN,/* A B R[A] := #R[B] (length operator) */
OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */
OP_CLOSE,/* A close all upvalues >= R[A] */
OP_TBC,/* A mark variable A "to be closed" */
OP_JMP,/* sJ pc += sJ */
OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */
OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */
OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */
OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */
OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */
OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */
OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */
OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */
OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */
OP_TEST,/* A k if (not R[A] == k) then pc++ */
OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */
OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */
OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */
OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */
OP_RETURN0,/* return */
OP_RETURN1,/* A return R[A] */
OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */
OP_FORPREP,/* A Bx <check values and prepare counters>;
if not to run then pc+=Bx+1; */
OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */
OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */
OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */
OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */
OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */
OP_VARARGPREP,/*A (adjust vararg parameters) */
OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
} OpCode;
上面的枚举是指令表也是opcode的取值,这段代码的注释是最有意义的它代表着指令的运行规则,这个注释需要结合虚拟机的代码才能看懂,暂时不做解释
3. 虚拟机
3.1 寄存器、栈基址、常量
#define RA(i) (base+GETARG_A(i))
#define RB(i) (base+GETARG_B(i))
#define vRB(i) s2v(RB(i))
#define KB(i) (k+GETARG_B(i))
#define RC(i) (base+GETARG_C(i))
#define vRC(i) s2v(RC(i))
#define KC(i) (k+GETARG_C(i))
#define RKC(i) ((TESTARG_k(i)) ? k + GETARG_C(i) : s2v(base + GETARG_C(i)))
void luaV_execute (lua_State *L, CallInfo *ci) {
LClosure *cl;
TValue *k;
StkId base;
const Instruction *pc;
int trap;
...
returning: /* trap already set */
cl = clLvalue(s2v(ci->func));
k = cl->p->k; //常量集合
pc = ci->u.l.savedpc; //pc寄存器 (指令集)
...
base = ci->func + 1; //栈基址
...
- 寄存器ra、rb、rc访问有RA、RB、RC的宏,这些寄存器本质就是lua栈加偏移。也就是说lua是通过这些寄存器去访问lua栈元素的
- 常量集合有KB、KC这些去访问。
3.2 指令运行
指令执行的逻辑
for(){
switch(opcode(pc)) {
case opcode1:
break;
case opcode2:
break;
...
}
pc++;
}
3.2.1 动态goto
lua5.4.3采用的是动态goto去实现for-switch的逻辑
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
void *addr = &&end; //获取end位置的地址
goto *addr; //goto解引用跳转
printf("aaaaaaa\n");
end:
printf("bbbbbbb\n");
printf("aaaaaaa\n");
printf("aaaaaaa\n");
return 0;
}
这是c的语法可以使用双取值去得到标签的地址,可以用指针去存储这个地址。
如何使用goto实现for-switch的逻辑
#include <stdio.h>
#include <string.h>
enum {
ADD,
SUB,
MUL,
DIV,
END
};
int main(int argc,char *argv[])
{
static const void *const disptab[] = {
&&L_ADD,
&&L_SUB,
&&L_MUL,
&&L_DIV,
&&L_END
};
int code[] = {ADD,ADD,MUL,MUL,END};
int i = 0;
int sum = 0;
goto *disptab[code[i++]];
L_ADD:
sum += 2;
goto *disptab[code[i++]];
L_SUB:
sum -= 2;
goto *disptab[code[i++]];
L_MUL:
sum *= 2;
goto *disptab[code[i++]];
L_DIV:
sum /= 2;
goto *disptab[code[i++]];
L_END:
printf("sum = %d\n",sum);
return 0;
}
- 先在goto之前获取所有lable的地址,组成一个地址表
- 定义一个code指令数组,指令操作的值和lable数组的值一致,剩下的直接goto code数组就行了
lua5.4.3 goto的使用
void luaV_execute (lua_State *L, CallInfo *ci) {
LClosure *cl;
TValue *k;
StkId base;
const Instruction *pc;
int trap;
#if LUA_USE_JUMPTABLE
#include "ljumptab.h"
#endif
...
可以看到虚拟机开始前include了"ljumptab.h"头文件
#undef vmdispatch
#undef vmcase
#undef vmbreak
//动态goto
#define vmdispatch(x) goto *disptab[x];
#define vmcase(l) L_##l:
#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i));
//获取标签的所有地址
static const void *const disptab[NUM_OPCODES] = {
#if 0
** you can update the following list with this command:
**
** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h
**
#endif
&&L_OP_MOVE,
&&L_OP_LOADI,
&&L_OP_LOADF,
&&L_OP_LOADK,
&&L_OP_LOADKX,
这里获取了整个lable的地址保存在了disptab数组中。
#define vmfetch() { \
if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \
trap = luaG_traceexec(L, pc); /* handle hooks */ \
updatebase(ci); /* correct stack */ \
} \
i = *(pc++); \
ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \
}
for (;;) { //指令循环
Instruction i; //当前的指令
StkId ra; //'A'寄存器 (RA、RB、RC 访问栈资源)
vmfetch(); //i ra 赋值
// low-level line tracing for debugging Lua
// printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
lua_assert(base == ci->func + 1);
lua_assert(base <= L->top && L->top < L->stack_last);
/* invalidate top for instructions not expecting it */
lua_assert(isIT(i) || (cast_void(L->top = base), 1));
vmdispatch (GET_OPCODE(i)) {
vmcase(OP_MOVE) { //R[A] := R[B]
setobjs2s(L, ra, RB(i));
vmbreak;
}
vmcase(OP_LOADI) { //R[A] := sBx (sBX 是有符号的立即数)
lua_Integer b = GETARG_sBx(i);
setivalue(s2v(ra), b);
vmbreak;
}
vmcase(OP_LOADF) { //R[A] := (lua_Number)sBx (to double)
int b = GETARG_sBx(i);
setfltvalue(s2v(ra), cast_num(b));
vmbreak;
}
...
- vmcase是一个拼接label的宏
- vmfetch是指令循环,走向下一个指令
- vmdispatch是goto语句跳转到指定的lable
- 当一个指令执行完会跑vmbreak,vmbreak是vmfetch、vmdispatch两步,指向下一个指令并且跳转到指定的指令
- 这段代码是完全等价for-switch的