Look at Lua for a while, and to tell the truth, it's going pretty slow. In the final analysis, because "motive is not pure", I really do not hold the mentality of learning Lua to see the information. Originally, Lua was told that Lua's implementation was relatively simple, and that it was possible to use Lua to understand some of the details of explanatory language. Of course Lua is really not simple, and the concept of closures has never been heard before. Not used Lua but to hard to chew the Lua language implementation, I also pretty spell!
My main reference is the cloud wind God's "LUA source appreciation" and the master translator of the "Lua Chinese course." Here I want to record the procedure of the function call. Follow the function call this line to concatenate the various points of knowledge. The first thing to do is start with the bytecode. The LUA virtual machine's instruction set has two byte codes for function calls.
Op_call,/*a b CR (a), ..., R (a+c-2): = R (a) (R (a+1), ..., R (a+b-1)) */op_tailcall,/*a B Creturn R (a) (R (a+1), ..., R (a+b-1)) * /
The latter applies to the case of a tail call, and the first byte code applies to the general case. We know that the implementation of the LUA code is actually a large for loop with a large switch statement. When the virtual machine reads the Op_call bytecode, the corresponding branch is called. The corresponding code snippet is as follows:
Vmcase (op_call, int b = Getarg_b (i); int nresults = Getarg_c (i)-1; if (b! = 0) L->top = ra+b; /* Else previous instruction set Top * /if (Luad_precall (L, RA, nresults)) {/ * C function? * /if (Nresults &G t;= 0) l->top = ci->top; /* Adjust results */ base = ci->u.l.base; } else {/ * Lua function */ CI = l->ci; Ci->callstatus |= cist_reentry; Goto Newframe; /* Restart Luav_execute over new Lua function */ } )
Lua can call either LUA functions or C functions. Let me take a look at the call C function! The focus is Luad_precall (L, RA, nresults)).
int Luad_precall (lua_state *l, stkid func, int nresults) {lua_cfunction F; Callinfo *ci; int n; /* Number of arguments (LUA) or returns (C) */ptrdiff_t FUNCR = Savestack (L, func); Switch (Ttype (func)) {case LUA_TLCF:/* Light C function */F = Fvalue (func); Goto Cfunc; Case LUA_TCCL: {/* C closure */F = clcvalue (func)->f; Cfunc:luad_checkstack (L, lua_minstack); /* Ensure minimum stack size */ci = next_ci (L); /* now ' enter ' new function */ci->nresults = nresults; Ci->func = Restorestack (L, FUNCR); Ci->top = L->top + lua_minstack; Lua_assert (Ci->top <= l->stack_last); Ci->callstatus = 0; LUAC_CHECKGC (L); /* Stack grow uses memory */if (L->hookmask & Lua_maskcall) Luad_hook (L, Lua_hookcall,-1); Lua_unlock (L); n = (*f) (L); /* Do the actual call * */Lua_lock (L); Api_checknelems (L, N); Luad_poscall (L, l->top-n); return 1; }......... }}
ptrdiff_t funcr = Savestack (L, func); Ci->func = Restorestack (L, FUNCR); When these two sentences are executed, the result is Ci->func = func. n = (*f) (L); Through this sentence, the actual C function is called. The C function of a typical LUA call is as follows:
static int L_sin (Lua_state *l) {Double d = lua_tonumber (l, 1);/* Get Argument */lua_pushnumber (L, sin (d));/* Push Resul T */return 1; /* Number of results */}
The first line of the function is to get the parameter, Lua_tonumber (L, 1) is a macro. It actually calls the Lua_tonumberx (). In this function, the TValue *o = Ci->func + idx is called indirectly through the method;
The parameter variables of the first IDX are obtained. The call directive explicitly stipulates that the function parameters are stored in the B-1 stack after R (A). And Ci->func is pointing to R (a). So how does lua ensure that the number of parameters is correct? The mystery is in the Index2addr () called by Lua_tonumberx ().
Static TValue *index2addr (lua_state *l, int idx) { Callinfo *ci = l->ci; if (idx > 0) { TValue *o = ci->func + idx; Api_check (L, idx <= ci->top-(Ci->func + 1), "unacceptable index"); if (o >= l->top) return nonvalidvalue; else return o; /* ..... */ }
This function compares the position of O and L->top and returns an error if it is greater than or equal to. And L->top is in the Op_call branch of the sentence is rewritten as ra+b, that is, the next position of the last parameter. This mechanism ensures that the C function does not mistake the number of arguments. Then the parameter returns the problem, the virtual machine directive specifies that the return value should be in R (A), ..., R (a+c-2). How does lua implement this rule? First look at the function Lua_pushnumber (), which is defined as follows:
Lua_api void Lua_pushnumber (lua_state *l, Lua_number N) { lua_lock (L); Setnvalue (L->top, n); Luai_checknum (L, L->top, luag_runerror (L, "C api-attempt to push a signaling NaN")); Api_incr_top (L); Lua_unlock (L);}
The job of this function is to write n to the stack space pointed to by L->top, and then move the l->top to a high position. It is clear that this storage location is not compliant with LUA virtual machines. So Luad_poscall (L, l->top-n) is required to complete this task. This n is the number of return values for the C function, so l->top actually points to the first return value of the C function.
int luaD_ Poscall (lua_state *l, Stkid firstresult) {stkid res; int wanted, I; Callinfo *ci = l->ci; if (L->hookmask & (Lua_maskret | Lua_maskline) {if (L->hookmask & Lua_maskret) {ptrdiff_t fr = Savestack (L, Firstresult); /* Hook Change stack */Luad_hook (L, Lua_hookret,-1); Firstresult = Restorestack (L, FR); } L->OLDPC = ci->previous->u.l.savedpc; /* ' OLDPC ' for caller function */} res = ci->func; /* Res = = Final position of 1st result */wanted = ci->nresults; L->ci = CI = ci->previous; /* Back to Caller */* Move results to correct place */for (i = wanted; I! = 0 && Firstresult < l->top; i--) setobjs2s (L, res++, firstresult++); while (i--> 0) setnilvalue (res++); L->top = res; return (Wanted-lua_multret); /* 0 iff wanted = = Lua_multret *}
In fact, Luad_poscall is also relatively simple, it is actually a return value of the "Porter." Res is assigned a value of Ci->func, which actually points to the previous R (A). Then a for loop implements the work of the copy. The CI structure corresponding to the C function is also retire. In this way, a call procedure for a C function is completed.
Implementation of Lua calling C function