第二人生的源码分析(111)脚本的综合分析
2010-09-14 13:09:55 来源:WEB开发网当语法分析一个脚本完成时,那么就会把整个脚本的分析结果保存起来,在第二人生里把脚本的结果保存到那里呢?现在就来分析这个问题,先来看看语法分析的文件里,有如下的代码:#001 case 3:
第4行里就创建一个LLScriptScript脚本程序保存对象,这个对象保存在全局变量gScriptp里。并且创建时就保存脚本的开始状态(yyvsp[(1) - (1)].state),比如脚本里关键字default的开始,就会创建一个开始状态LLScriptState对象。下面就来分析类LLScriptScript是怎么样保存脚本和分析脚本的,它的声明如下:
#002 #line 277 "indra.y"
#003 {
#004 (yyval.script) = new LLScriptScript(NULL, (yyvsp[(1) - (1)].state));
#005 gAllocationManager->addAllocation((yyval.script));
#006 gScriptp = (yyval.script);
#007 }
#008 break;#001 class LLScriptScript : public LLScriptFilePosition
通过上面的代码,看到对脚本代码完整的分析过程,其实它是依照下面的状态来进行不同的阶段处理的,如下:
#002 {
#003 public:
构造函数保存全局储存对象,保存脚本的开始状态。
#004 LLScriptScript(LLScritpGlobalStorage *globals,
#005 LLScriptState *states);
#006
析构函数删除全局对象。
#007 ~LLScriptScript()
#008 {
#009 delete mGlobalScope;
#010 }
#011
递归遍历处理语法分析、输出汇编代码、输出字节码等等。
#012 void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type,
#013 LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
获取生成代码的大小,这里设置返回0。
#014 S32 getSize();
#015
设置脚本目标生成文件。
#016 void setBytecodeDest(const char* dst_filename);
#017
脚本程序开始状态保存成员。
#018 LLScriptState *mStates;
脚本程序的全局作用域。
#019 LLScriptScope *mGlobalScope;
脚本程序的全局变量。
#020 LLScriptGlobalVariable *mGlobals;
脚本程序的全局函数。
#021 LLScriptGlobalFunctions *mGlobalFunctions;
保存函数属性是否代理能执行。
#022 BOOL mGodLike;
#023
#024 private:
保存生成字节码的目标文件。
#025 char mBytecodeDest[MAX_STRING]; /*Flawfinder: ignore*/
#026 };
#027
通过上面分析,了解了脚本保存的结构,下一次再仔细地分析它是怎么进行脚本处理。
前面介绍了分析脚本类的声明,下面来仔细地分析它的实现代码,理解它的实现过程,也就理解了脚本代码的编译过程,如下:
返回生成的代码大小为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] = '';
下面开始分析全局变量和全局函数的保存位置。
#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] = '';
#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, "GLOBALSn");
#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 FUNCTIONSn");
#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, "STATESn");
#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.Objectn");
#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 managedn");
#298 fprintf(fp, "{n");
#299 fprintf(fp, ".entrypointn");
#300 fprintf(fp, ".maxstack 2n");
#301 fprintf(fp, ".locals init (class LSL V_0)n");
#302 fprintf(fp, "newobj instance void class LSL::.ctor()n");
#303 fprintf(fp, "stloc.0n");
#304 fprintf(fp, "ldloc.0n");
#305 fprintf(fp, "callvirt instance void class LSL::defaultstate_entry()n");
#306 fprintf(fp, "retn");
#307 fprintf(fp, "}n");
#308
#309 // Output ctor header.
#310 fprintf(fp, ".method public hidebysig specialname rtspecialname instance default void .ctor () cil managedn");
#311 fprintf(fp, "{n");
#312 fprintf(fp, ".maxstack 500n");
#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.0n");
#325 fprintf(fp, "call instance void valuetype [mscorlib]System.Object::.ctor()n");
#326 fprintf(fp, "retn");
#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
因此一个脚本代码需要经过上面13种的组合分析,才会真正地处理完脚本的编译,这是一个非常复杂漫长的过程。
#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;
更多精彩
赞助商链接