前面介绍了分析脚本类的声明,下面来仔细地分析它的实现代码,理解它的实现过程,也就理解了脚本代码的编译过程,如下:
返回生成的代码大小为
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
#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
种的组合分析,才会真正地处理完脚本的编译,这是一个非常复杂漫长的过程。