The above section describes the lua_dofile execution script file or the compiled script binary file.
In this section, I will see how Lua distinguishes these two types of files and what happened in the program before the virtual machine starts to execute bytecode?
Lua_dofile is called in Lua. C to execute the file. Check lua_dofile.
/*** Open file, generate opcode and execute global statement. Return 0 on** success or 1 on error.*/int lua_dofile (char *filename){ int status; int c; FILE *f = lua_openfile(filename); if (f == NULL) return 1; c = fgetc(f); ungetc(c, f); status = (c == ID_CHUNK) ? luaI_undump(f) : do_protectedmain(); lua_closefile(); return status;}
This function is used to open a file, generate bytecode, and execute global statements.
0 is returned for success, and 1 is returned for failure.
Lua_openfile has already been used in the compiler analysis to open the file, so we will not repeat it here. (If you encounter such an analysis later, we will not describe it again, just skip it .).
See the following
c = fgetc(f); ungetc(c, f);
These two statements are the first character to read the file, and then put the character back into the file input stream, keep the input stream at the position of the file header (that is, the status that has not been read ).
The following sentence
status = (c == ID_CHUNK) ? luaI_undump(f) : do_protectedmain();
Check the character you just read. During compiler analysis, we know that the first character at the beginning of the *. Out binary file generated by the compiler is id_chunk and the ASCII code is 27. This character does not appear in Normal script files. Therefore, you can use this label to determine whether the file is a *. Out binary file or a script file.
For compiled binary files, call luai_undump to restore the scenario and execute it.
/*** load and run all chunks in a file*/int luaI_undump(FILE* D){ TFunc* m; while ((m=luaI_undump1(D))) { int status=luaI_dorun(m); luaI_freefunc(m); if (status!=0) return status; } return 0;}
We can see that luai_undump first calls luai_undump1 to restore the scenario. For how to restore the scenario, let's talk about it in the next section.
Call luai_dorun after recovery.
Return to lua_dofile. If it is a script file, call do_protectedmain.
static int do_protectedmain (void){ TFunc tf; int status; jmp_buf myErrorJmp; jmp_buf *oldErr = errorJmp; errorJmp = &myErrorJmp; luaI_initTFunc(&tf); tf.fileName = lua_parsedfile; if (setjmp(myErrorJmp) == 0) { lua_parse(&tf); status = luaI_dorun(&tf); } else { status = 1; adjustC(0); /* erase extra slot */ } errorJmp = oldErr; luaI_free(tf.code); return status;}
Here we can see that after initialization and setting the exception recovery breakpoint, after the syntax analysis lua_parse, it also calls luai_dorun. When it is adjusted to this step, there is no difference between compiled binary files and script files. Setjmp can be considered as a try... catch exception handling in the C language version, although there are differences between them.
The following code is used to handle exceptions, set the status bit, and release related resources.
int luaI_dorun (TFunc *tf){ int status; adjustC(1); /* one slot for the pseudo-function */ stack[CBase].tag = LUA_T_FUNCTION; stack[CBase].value.tf = tf; status = do_protectedrun(0); adjustC(0); return status;}
Set the compiled tfunc to the stack and call do_protectedrun.
/*** Execute a protected call. Assumes that function is at CBase and** parameters are on top of it. Leave nResults on the stack. */static int do_protectedrun (int nResults){ jmp_buf myErrorJmp; int status; StkId oldCBase = CBase; jmp_buf *oldErr = errorJmp; errorJmp = &myErrorJmp; if (setjmp(myErrorJmp) == 0) { do_call(CBase+1, nResults); CnResults = (top-stack) - CBase; /* number of results */ CBase += CnResults; /* incorporate results on the stack */ status = 0; } else { /* an error occurred: restore CBase and top */ CBase = oldCBase; top = stack+CBase; status = 1; } errorJmp = oldErr; return status;}
The annotations are clear and I will not elaborate on them. If the function name contains protected, it is a protected call. Internally, setjmp is used to set the exception recovery point. The function calls do_call to execute the tfunc.
/*** Call a function (C or Lua). The parameters must be on the stack,** between [stack+base,top). The function to be called is at stack+base-1.** When returns, the results are on the stack, between [stack+base-1,top).** The number of results is nResults, unless nResults=MULT_RET.*/static void do_call (StkId base, int nResults){ StkId firstResult; Object *func = stack+base-1; int i; if (tag(func) == LUA_T_CFUNCTION) { tag(func) = LUA_T_CMARK; firstResult = callC(fvalue(func), base); } else if (tag(func) == LUA_T_FUNCTION) { tag(func) = LUA_T_MARK; firstResult = lua_execute(func->value.tf->code, base); } else { /* func is not a function */ /* Call the fallback for invalid functions */ open_stack((top-stack)-(base-1)); stack[base-1] = luaI_fallBacks[FB_FUNCTION].function; do_call(base, nResults); return; } /* adjust the number of results */ if (nResults != MULT_RET && top - (stack+firstResult) != nResults) adjust_top(firstResult+nResults); /* move results to base-1 (to erase parameters and function) */ base--; nResults = top - (stack+firstResult); /* actual number of results */ for (i=0; i<nResults; i++) *(stack+base+i) = *(stack+firstResult+i); top -= firstResult-base;}
Determine whether the C function on the stack is a Lua function.
For C, call callc.
If it is a Lua function, the call lua_execute is executed by the virtual machine.
If it is not a function, call the "function" rollback function.
Then, adjust the return value and stack.
There is a simple rule to adjust the stack: the stack status after the function call should be the same as that before the function call, except for several possible return values on the stack. This sentence may not be easy to understand. Here is a small example. For example, in C, we have a function:
int add(int a, int b){ return a + b;}
If we call int result = add (3, 5), the result is exactly the same as int result = 8 after this sentence is executed. Here, where you call add (3, 5), you can directly put 8 to the stack to achieve the same effect, and there is no impact on subsequent program execution.
Recall the stack response structure returned by the function call in the Assembly. Do you think the function call return here is a bit familiar.
Callc is the function pointer that calls a C.
Lua_execute is a large switch... case to execute commands. This is detailed in previous versions and may be skipped in this version.
Fallback has no effect on the main process of the program.
----------------------------------------
So far:
> How to restore the scenario of luai_undump1
----------------------------------------
Opcode. c Before lua2.4 execution