前面介绍了分析脚本类的声明,下面来仔细地分析它的实现代码,理解它的实现过程,也就理解了脚本代码的编译过程,如下:
返回生成的代码大小为0.
#001 S32 LLScriptScript::getSize()
#002 {
#003 return 0;
#004 }
#005
脚本类的构造函数,主要进行初始化的工作。
#006 LLScriptScript::LLScriptScript(LLScritpGlobalStorage *globals,
#007 LLScriptState *states) :
#008 LLScriptFilePosition(0, 0),
#009 mStates(states), mGlobalScope(NULL), mGlobals(NULL), mGlobalFunctions(NULL), mGodLike(FALSE)
#010 {
设置缺省生成字节码的文件名称。
#011 const char DEFAULT_BYTECODE_FILENAME[] = "lscript.lso";
#012 strncpy(mBytecodeDest, DEFAULT_BYTECODE_FILENAME, sizeof(mBytecodeDest) -1); /*Flawfinder: ignore*/
#013 mBytecodeDest[MAX_STRING-1] = '/0';
下面开始分析全局变量和全局函数的保存位置。
#014 LLScriptGlobalVariable *tvar;
#015 LLScriptGlobalFunctions *tfunc;
#016 LLScritpGlobalStorage *temp;
#017
#018 temp = globals;
#019 while(temp)
#020 {
#021 if (temp->mbGlobalFunction)
#022 {
#023 if (!mGlobalFunctions)
#024 {
#025 mGlobalFunctions = (LLScriptGlobalFunctions *)temp->mGlobal;
#026 }
#027 else
#028 {
#029 tfunc = mGlobalFunctions;
#030 while(tfunc->mNextp)
#031 {
#032 tfunc = tfunc->mNextp;
#033 }
#034 tfunc->mNextp = (LLScriptGlobalFunctions *)temp->mGlobal;
#035 }
#036 }
#037 else
#038 {
#039 if (!mGlobals)
#040 {
#041 mGlobals = (LLScriptGlobalVariable *)temp->mGlobal;
#042 }
#043 else
#044 {
#045 tvar = mGlobals;
#046 while(tvar->mNextp)
#047 {
#048 tvar = tvar->mNextp;
#049 }
#050 tvar->mNextp = (LLScriptGlobalVariable *)temp->mGlobal;
#051 }
#052 }
#053 temp = temp->mNextp;
#054 }
#055 }
#056
这个函数主要实现设置字节码保存的文件名称。
#057 void LLScriptScript::setBytecodeDest(const char* dst_filename)
#058 {
#059 strncpy(mBytecodeDest, dst_filename, MAX_STRING); /*Flawfinder: ignore*/
#060 mBytecodeDest[MAX_STRING-1] = '/0';
#061 }
#062
#063 void print_cil_globals(FILE* fp, LLScriptGlobalVariable* global)
#064 {
#065 fprintf(fp, ".field private ");
#066 print_cil_type(fp, global->mType->mType);
#067 fprintf(fp, " ");
#068 fprintf(fp, global->mIdentifier->mName); /*Flawfinder: ignore*/
#069 fprintf(fp, "/n");
#070 if(NULL != global->mNextp)
#071 {
#072 print_cil_globals(fp, global->mNextp);
#073 }
#074 }
#075
开始递归处理所有脚本树。
#076 void LLScriptScript::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type,
#077 LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
#078 {
如果分析有出错,就不再处理。
#079 if (gErrorToText.getErrors())
#080 {
#081 return;
#082 }
根据不同的编译遍来作不同的树遍历处理。
#083 switch(pass)
#084 {
输出合适的说明文字。
#085 case LSCP_PRETTY_PRINT:
#086 if (mGlobals)
#087 {
#088 fdotabs(fp, tabs, tabsize);
#089 mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount,
#090 NULL);
#091 }
#092
#093 if (mGlobalFunctions)
#094 {
#095 fdotabs(fp, tabs, tabsize);
#096 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry,
#097 entrycount, NULL);
#098 }
#099
#100 fdotabs(fp, tabs, tabsize);
#101 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
#102 break;
进行代码优化,主要删除不需要的代码。
#103 case LSCP_PRUNE:
#104 if (mGlobalFunctions)
#105 {
#106 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry,
#107 entrycount, NULL);
#108 }
#109 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
#110 break;
全局的作用域检查。
#111 case LSCP_SCOPE_PASS1:
#112 {
#113 mGlobalScope = new LLScriptScope(gScopeStringTable);
#114 // zeroth, add library functions to global scope
#115 S32 i;
#116 char *arg;
#117 LLScriptScopeEntry *sentry;
#118 for (i = 0; i < gScriptLibrary.mNextNumber; i++)
#119 {
#120 // First, check to make sure this isn't a god only function, or that the viewer's agent is a god.
#121 if (!gScriptLibrary.mFunctions[i]->mGodOnly || mGodLike)
#122 {
#123 if (gScriptLibrary.mFunctions[i]->mReturnType)
#124 sentry = mGlobalScope->addEntry(gScriptLibrary.mFunctions[i]->mName,
#125 LIT_LIBRARY_FUNCTION, char2type(*gScriptLibrary.mFunctions[i]->mReturnType));
#126 else
#127 sentry = mGlobalScope->addEntry(gScriptLibrary.mFunctions[i]->mName,
#128 LIT_LIBRARY_FUNCTION, LST_NULL);
#129 sentry->mLibraryNumber = i;
#130 arg = gScriptLibrary.mFunctions[i]->mArgs;
#131 if (arg)
#132 {
#133 while (*arg)
#134 {
#135 sentry->mFunctionArgs.addType(char2type(*arg));
#136 sentry->mSize += LSCRIPTDataSize[char2type(*arg)];
#137 sentry->mOffset += LSCRIPTDataSize[char2type(*arg)];
#138 arg++;
#139 }
#140 }
#141 }
#142 }
#143 // first go and collect all the global variables
#144 if (mGlobals)
#145 mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap,
#146 stacksize, entry, entrycount, NULL);
#147 // second, do the global functions
#148 if (mGlobalFunctions)
#149 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap,
#150 stacksize, entry, entrycount, NULL);
#151 // now do states
#152 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap, stacksize, entry,
#153 entrycount, NULL);
#154 break;
#155 }
第二次作用域检查,主要检查跳转、函数调用和状态转换。
#156 case LSCP_SCOPE_PASS2:
#157 // now we're checking jumps, function calls, and state transitions
#158 if (mGlobalFunctions)
#159 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap, stacksize, entry,
#160 entrycount, NULL);
#161 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
#162 break;
对脚本的类型进行检查。
#163 case LSCP_TYPE:
#164 // first we need to check global variables
#165 if (mGlobals)
#166 mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount,
#167 NULL);
#168 // now do global functions and states
#169 if (mGlobalFunctions)
#170 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry,
#171 entrycount, NULL);
#172 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
#173 break;
确定所有脚本需的资源大小。
#174 case LSCP_RESOURCE:
#175 // first determine resource counts for globals
#176 count = 0;
#177 if (mGlobals)
#178 mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount,
#179 NULL);
#180 // now do locals
#181 if (mGlobalFunctions)
#182 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry,
#183 entrycount, NULL);
#184 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
#185 break;
输出汇编代码。
#186 case LSCP_EMIT_ASSEMBLY:
#187
#188 if (mGlobals)
#189 {
#190 fprintf(fp, "GLOBALS/n");
#191 fdotabs(fp, tabs, tabsize);
#192 mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount,
#193 NULL);
#194 fprintf(fp, "/n");
#195 }
#196
#197 if (mGlobalFunctions)
#198 {
#199 fprintf(fp, "GLOBAL FUNCTIONS/n");
#200 fdotabs(fp, tabs, tabsize);
#201 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry,
#202 entrycount, NULL);
#203 fprintf(fp, "/n");
#204 }
#205
#206 fprintf(fp, "STATES/n");
#207 fdotabs(fp, tabs, tabsize);
#208 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
#209 fprintf(fp, "/n");
#210 break;
输出虚拟机可以运行的字节码。
#211 case LSCP_EMIT_BYTE_CODE:
#212 {
#213 // first, create data structure to hold the whole shebang
#214 LLScriptScriptCodeChunk *code = new LLScriptScriptCodeChunk(TOP_OF_MEMORY);
#215
#216 // ok, let's add the registers, all zeroes for now
#217 S32 i;
#218 S32 nooffset = 0;
#219
#220 for (i = LREG_IP; i < LREG_EOF; i++)
#221 {
#222 if (i < LREG_NCE)
#223 code->mRegisters->addBytes(4);
#224 else if (LSL2_CURRENT_MAJOR_VERSION == LSL2_MAJOR_VERSION_TWO)
#225 code->mRegisters->addBytes(8);
#226 }
#227 // global variables
#228 if (mGlobals)
#229 mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, code->mGlobalVariables,
#230 code->mHeap, stacksize, entry, entrycount, NULL);
#231
#232 // put the ending heap block onto the heap
#233 U8 *temp;
#234 S32 size = lsa_create_data_block(&temp, NULL, 0);
#235 code->mHeap->addBytes(temp, size);
#236 delete [] temp;
#237
#238 // global functions
#239 // make space for global function jump table
#240 if (mGlobalFunctions)
#241 {
#242 code->mGlobalFunctions->addBytes(LSCRIPTDataSize[LST_INTEGER]*mGlobalScope->mFunctionCount +
#243 LSCRIPTDataSize[LST_INTEGER]);
#244 integer2bytestream(code->mGlobalFunctions->mCodeChunk, nooffset, mGlobalScope->mFunctionCount);
#245 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, code-
#246 >mGlobalFunctions, NULL, stacksize, entry, entrycount, NULL);
#247 }
#248
#249
#250 nooffset = 0;
#251 // states
#252 // make space for state jump/info table
#253 if (LSL2_CURRENT_MAJOR_VERSION == LSL2_MAJOR_VERSION_TWO)
#254 {
#255 code->mStates->addBytes(LSCRIPTDataSize[LST_INTEGER]*3*mGlobalScope->mStateCount + LSCRIPTDataSize
#256 [LST_INTEGER]);
#257 }
#258 else
#259 {
#260 code->mStates->addBytes(LSCRIPTDataSize[LST_INTEGER]*2*mGlobalScope->mStateCount + LSCRIPTDataSize
#261 [LST_INTEGER]);
#262 }
#263 integer2bytestream(code->mStates->mCodeChunk, nooffset, mGlobalScope->mStateCount);
#264 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, code->mStates, NULL, stacksize, entry,
#265 entrycount, NULL);
#266
#267 // now, put it all together and spit it out
#268 // we need
#269 FILE* bcfp = LLFile::fopen(mBytecodeDest, "wb"); /*Flawfinder: ignore*/
#270
#271 code->build(fp, bcfp);
#272 fclose(bcfp);
#273
#274 delete code;
#275 }
#276 break;
输出CIL的汇编代码。
#277 case LSCP_EMIT_CIL_ASSEMBLY:
#278
#279 // Output dependencies.
#280 fprintf(fp, ".assembly extern mscorlib {.ver 1:0:5000:0}/n");
#281 fprintf(fp, ".assembly extern LScriptLibrary {.ver 0:0:0:0}/n");
#282
#283 // Output assembly name.
#284 fprintf(fp, ".assembly 'lsl' {.ver 0:0:0:0}/n");
#285
#286 // Output class header.
#287 fprintf(fp, ".class public auto ansi beforefieldinit LSL extends [mscorlib]System.Object/n");
#288 fprintf(fp, "{/n");
#289
#290 // Output globals as members.
#291 if(NULL != mGlobals)
#292 {
#293 print_cil_globals(fp, mGlobals);
#294 }
#295
#296 // Output "runtime". Only needed to allow stand alone execution. Not needed when compiling to DLL and using embedded runtime.
#297 fprintf(fp, ".method public static hidebysig default void Main () cil managed/n");
#298 fprintf(fp, "{/n");
#299 fprintf(fp, ".entrypoint/n");
#300 fprintf(fp, ".maxstack 2/n");
#301 fprintf(fp, ".locals init (class LSL V_0)/n");
#302 fprintf(fp, "newobj instance void class LSL::.ctor()/n");
#303 fprintf(fp, "stloc.0/n");
#304 fprintf(fp, "ldloc.0/n");
#305 fprintf(fp, "callvirt instance void class LSL::defaultstate_entry()/n");
#306 fprintf(fp, "ret/n");
#307 fprintf(fp, "}/n");
#308
#309 // Output ctor header.
#310 fprintf(fp, ".method public hidebysig specialname rtspecialname instance default void .ctor () cil managed/n");
#311 fprintf(fp, "{/n");
#312 fprintf(fp, ".maxstack 500/n");
#313
#314 // Initialise globals as members in ctor.
#315 if (mGlobals)
#316 {
#317 fdotabs(fp, tabs, tabsize);
#318 mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount,
#319 NULL);
#320 fprintf(fp, "/n");
#321 }
#322
#323 // Output ctor footer.
#324 fprintf(fp, "ldarg.0/n");
#325 fprintf(fp, "call instance void valuetype [mscorlib]System.Object::.ctor()/n");
#326 fprintf(fp, "ret/n");
#327 fprintf(fp, "}/n");
#328
#329 // Output functions as methods.
#330 if (mGlobalFunctions)
#331 {
#332 fdotabs(fp, tabs, tabsize);
#333 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry,
#334 entrycount, NULL);
#335 fprintf(fp, "/n");
#336 }
#337
#338 // Output states as name mangled methods.
#339 fdotabs(fp, tabs, tabsize);
#340 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
#341 fprintf(fp, "/n");
#342
#343 // Output class footer.
#344 fprintf(fp, "}/n");
#345
#346 break;
下面进行缺省的处理。
#347 default:
#348 if (mGlobals)
#349 mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount,
#350 NULL);
#351 if (mGlobalFunctions)
#352 mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry,
#353 entrycount, NULL);
#354 mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
#355 break;
#356 }
#357 }
#358
通过上面的代码,看到对脚本代码完整的分析过程,其实它是依照下面的状态来进行不同的阶段处理的,如下:
#001 typedef enum e_lscript_compile_pass
#002 {
非法编译状态。
#003 LSCP_INVALID,
输出合适的说明文字
#004 LSCP_PRETTY_PRINT,
进行代码化减和优化。
#005 LSCP_PRUNE,
对脚本代码进行全局的作用域检查。
#006 LSCP_SCOPE_PASS1,
对脚本代码进行跳转等作用域检查。
#007 LSCP_SCOPE_PASS2,
对脚本代码进行类型检查。
#008 LSCP_TYPE,
对脚本代码进行需要的资源分配。
#009 LSCP_RESOURCE,
对脚本代码进行汇编输出处理。
#010 LSCP_EMIT_ASSEMBLY,
对脚本代码进行字节码编译输出。
#011 LSCP_EMIT_BYTE_CODE,
对脚本代码进行事件处理计数。
#012 LSCP_DETERMINE_HANDLERS,
输出LIB数据。
#013 LSCP_LIST_BUILD_SIMPLE,
对于栈进行处理。
#014 LSCP_TO_STACK,
函数声明参数处理。
#015 LSCP_BUILD_FUNCTION_ARGS,
对脚本代码进行CIL汇编输出。
#016 LSCP_EMIT_CIL_ASSEMBLY,
脚本处理结束状态。
#017 LSCP_EOF
#018 } LSCRIPTCompilePass;
因此一个脚本代码需要经过上面13种的组合分析,才会真正地处理完脚本的编译,这是一个非常复杂漫长的过程。