Initial commit
This commit is contained in:
559
Torque/SDK/engine/console/codeBlock.cc
Normal file
559
Torque/SDK/engine/console/codeBlock.cc
Normal file
@@ -0,0 +1,559 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "console/console.h"
|
||||
#include "console/compiler.h"
|
||||
#include "console/codeBlock.h"
|
||||
#include "console/telnetDebugger.h"
|
||||
#include "core/resManager.h"
|
||||
|
||||
using namespace Compiler;
|
||||
|
||||
bool CodeBlock::smInFunction = false;
|
||||
U32 CodeBlock::smBreakLineCount = 0;
|
||||
CodeBlock * CodeBlock::smCodeBlockList = NULL;
|
||||
CodeBlock * CodeBlock::smCurrentCodeBlock = NULL;
|
||||
ConsoleParser *CodeBlock::smCurrentParser = NULL;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
CodeBlock::CodeBlock()
|
||||
{
|
||||
globalStrings = NULL;
|
||||
functionStrings = NULL;
|
||||
globalFloats = NULL;
|
||||
functionFloats = NULL;
|
||||
lineBreakPairs = NULL;
|
||||
breakList = NULL;
|
||||
breakListSize = 0;
|
||||
|
||||
refCount = 0;
|
||||
code = NULL;
|
||||
name = NULL;
|
||||
mRoot = StringTable->insert("");
|
||||
}
|
||||
|
||||
CodeBlock::~CodeBlock()
|
||||
{
|
||||
// Make sure we aren't lingering in the current code block...
|
||||
AssertFatal(smCurrentCodeBlock != this, "CodeBlock::~CodeBlock - Caught lingering in smCurrentCodeBlock!")
|
||||
|
||||
if(name)
|
||||
removeFromCodeList();
|
||||
delete[] const_cast<char*>(globalStrings);
|
||||
delete[] const_cast<char*>(functionStrings);
|
||||
delete[] globalFloats;
|
||||
delete[] functionFloats;
|
||||
delete[] code;
|
||||
delete[] breakList;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
StringTableEntry CodeBlock::getCurrentCodeBlockName()
|
||||
{
|
||||
if (CodeBlock::getCurrentBlock())
|
||||
return CodeBlock::getCurrentBlock()->name;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
CodeBlock *CodeBlock::find(StringTableEntry name)
|
||||
{
|
||||
for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile)
|
||||
if(walk->name == name)
|
||||
return walk;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void CodeBlock::addToCodeList()
|
||||
{
|
||||
// remove any code blocks with my name
|
||||
for(CodeBlock **walk = &smCodeBlockList; *walk;walk = &((*walk)->nextFile))
|
||||
{
|
||||
if((*walk)->name == name)
|
||||
{
|
||||
*walk = (*walk)->nextFile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
nextFile = smCodeBlockList;
|
||||
smCodeBlockList = this;
|
||||
}
|
||||
|
||||
void CodeBlock::clearAllBreaks()
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return;
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
code[p[1]] = p[0] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void CodeBlock::clearBreakpoint(U32 lineNumber)
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return;
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
if((p[0] >> 8) == lineNumber)
|
||||
{
|
||||
code[p[1]] = p[0] & 0xFF;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeBlock::setAllBreaks()
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return;
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
code[p[1]] = OP_BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
bool CodeBlock::setBreakpoint(U32 lineNumber)
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return false;
|
||||
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
if((p[0] >> 8) == lineNumber)
|
||||
{
|
||||
code[p[1]] = OP_BREAK;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 CodeBlock::findFirstBreakLine(U32 lineNumber)
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return 0;
|
||||
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
U32 line = (p[0] >> 8);
|
||||
|
||||
if( lineNumber <= line )
|
||||
return line;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct LinePair
|
||||
{
|
||||
U32 instLine;
|
||||
U32 ip;
|
||||
};
|
||||
|
||||
void CodeBlock::findBreakLine(U32 ip, U32 &line, U32 &instruction)
|
||||
{
|
||||
U32 min = 0;
|
||||
U32 max = lineBreakPairCount - 1;
|
||||
LinePair *p = (LinePair *) lineBreakPairs;
|
||||
|
||||
U32 found;
|
||||
if(!lineBreakPairCount || p[min].ip > ip || p[max].ip < ip)
|
||||
{
|
||||
line = 0;
|
||||
instruction = OP_INVALID;
|
||||
return;
|
||||
}
|
||||
else if(p[min].ip == ip)
|
||||
found = min;
|
||||
else if(p[max].ip == ip)
|
||||
found = max;
|
||||
else
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(min == max - 1)
|
||||
{
|
||||
found = min;
|
||||
break;
|
||||
}
|
||||
U32 mid = (min + max) >> 1;
|
||||
if(p[mid].ip == ip)
|
||||
{
|
||||
found = mid;
|
||||
break;
|
||||
}
|
||||
else if(p[mid].ip > ip)
|
||||
max = mid;
|
||||
else
|
||||
min = mid;
|
||||
}
|
||||
}
|
||||
instruction = p[found].instLine & 0xFF;
|
||||
line = p[found].instLine >> 8;
|
||||
}
|
||||
|
||||
const char *CodeBlock::getFileLine(U32 ip)
|
||||
{
|
||||
static char nameBuffer[256];
|
||||
U32 line, inst;
|
||||
findBreakLine(ip, line, inst);
|
||||
|
||||
dSprintf(nameBuffer, sizeof(nameBuffer), "%s (%d)", name ? name : "<input>", line);
|
||||
return nameBuffer;
|
||||
}
|
||||
|
||||
void CodeBlock::removeFromCodeList()
|
||||
{
|
||||
for(CodeBlock **walk = &smCodeBlockList; *walk; walk = &((*walk)->nextFile))
|
||||
{
|
||||
if(*walk == this)
|
||||
{
|
||||
*walk = nextFile;
|
||||
|
||||
// clear out all breakpoints
|
||||
clearAllBreaks();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeBlock::calcBreakList()
|
||||
{
|
||||
U32 size = 0;
|
||||
S32 line = -1;
|
||||
U32 seqCount = 0;
|
||||
U32 i;
|
||||
for(i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 lineNumber = lineBreakPairs[i * 2];
|
||||
if(lineNumber == U32(line + 1))
|
||||
seqCount++;
|
||||
else
|
||||
{
|
||||
if(seqCount)
|
||||
size++;
|
||||
size++;
|
||||
seqCount = 1;
|
||||
}
|
||||
line = lineNumber;
|
||||
}
|
||||
if(seqCount)
|
||||
size++;
|
||||
|
||||
breakList = new U32[size];
|
||||
breakListSize = size;
|
||||
line = -1;
|
||||
seqCount = 0;
|
||||
size = 0;
|
||||
|
||||
for(i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 lineNumber = lineBreakPairs[i * 2];
|
||||
|
||||
if(lineNumber == U32(line + 1))
|
||||
seqCount++;
|
||||
else
|
||||
{
|
||||
if(seqCount)
|
||||
breakList[size++] = seqCount;
|
||||
breakList[size++] = lineNumber - getMax(0, line) - 1;
|
||||
seqCount = 1;
|
||||
}
|
||||
|
||||
line = lineNumber;
|
||||
}
|
||||
|
||||
if(seqCount)
|
||||
breakList[size++] = seqCount;
|
||||
|
||||
for(i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
p[0] = (p[0] << 8) | code[p[1]];
|
||||
}
|
||||
|
||||
// Let the telnet debugger know that this code
|
||||
// block has been loaded and that it can add break
|
||||
// points it has for it.
|
||||
if ( TelDebugger )
|
||||
TelDebugger->addAllBreakpoints( this );
|
||||
}
|
||||
|
||||
bool CodeBlock::read(StringTableEntry fileName, Stream &st)
|
||||
{
|
||||
name = fileName;
|
||||
|
||||
//
|
||||
if (name)
|
||||
{
|
||||
if (const char *slash = dStrchr(this->name, '/'))
|
||||
{
|
||||
char root[512];
|
||||
dStrncpy(root, this->name, slash-this->name);
|
||||
root[slash-this->name] = 0;
|
||||
mRoot = StringTable->insert(root);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
addToCodeList();
|
||||
|
||||
U32 globalSize,size,i;
|
||||
st.read(&size);
|
||||
if(size)
|
||||
{
|
||||
globalSize = size;
|
||||
globalStrings = new char[size];
|
||||
st.read(size, globalStrings);
|
||||
}
|
||||
st.read(&size);
|
||||
if(size)
|
||||
{
|
||||
functionStrings = new char[size];
|
||||
st.read(size, functionStrings);
|
||||
}
|
||||
st.read(&size);
|
||||
if(size)
|
||||
{
|
||||
globalFloats = new F64[size];
|
||||
for(U32 i = 0; i < size; i++)
|
||||
st.read(&globalFloats[i]);
|
||||
}
|
||||
st.read(&size);
|
||||
if(size)
|
||||
{
|
||||
functionFloats = new F64[size];
|
||||
for(U32 i = 0; i < size; i++)
|
||||
st.read(&functionFloats[i]);
|
||||
}
|
||||
U32 codeSize;
|
||||
st.read(&codeSize);
|
||||
st.read(&lineBreakPairCount);
|
||||
|
||||
U32 totSize = codeSize + lineBreakPairCount * 2;
|
||||
code = new U32[totSize];
|
||||
|
||||
for(i = 0; i < codeSize; i++)
|
||||
{
|
||||
U8 b;
|
||||
st.read(&b);
|
||||
if(b == 0xFF)
|
||||
st.read(&code[i]);
|
||||
else
|
||||
code[i] = b;
|
||||
}
|
||||
|
||||
for(i = codeSize; i < totSize; i++)
|
||||
st.read(&code[i]);
|
||||
|
||||
lineBreakPairs = code + codeSize;
|
||||
|
||||
// StringTable-ize our identifiers.
|
||||
U32 identCount;
|
||||
st.read(&identCount);
|
||||
while(identCount--)
|
||||
{
|
||||
U32 offset;
|
||||
st.read(&offset);
|
||||
StringTableEntry ste;
|
||||
if(offset < globalSize)
|
||||
ste = StringTable->insert(globalStrings + offset);
|
||||
else
|
||||
ste = StringTable->insert("");
|
||||
U32 count;
|
||||
st.read(&count);
|
||||
while(count--)
|
||||
{
|
||||
U32 ip;
|
||||
st.read(&ip);
|
||||
code[ip] = *((U32 *) &ste);
|
||||
}
|
||||
}
|
||||
|
||||
if(lineBreakPairCount)
|
||||
calcBreakList();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, const char *script)
|
||||
{
|
||||
gSyntaxError = false;
|
||||
|
||||
consoleAllocReset();
|
||||
|
||||
STEtoU32 = compileSTEtoU32;
|
||||
|
||||
statementList = NULL;
|
||||
|
||||
// Set up the parser.
|
||||
smCurrentParser = getParserForFile(fileName);
|
||||
AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName));
|
||||
|
||||
// Now do some parsing.
|
||||
smCurrentParser->setScanBuffer(script, fileName);
|
||||
smCurrentParser->restart(NULL);
|
||||
smCurrentParser->parse();
|
||||
|
||||
if(gSyntaxError)
|
||||
{
|
||||
consoleAllocReset();
|
||||
return false;
|
||||
}
|
||||
|
||||
FileStream st;
|
||||
if(!ResourceManager->openFileForWrite(st, codeFileName))
|
||||
return false;
|
||||
st.write(U32(Con::DSOVersion));
|
||||
|
||||
// Reset all our value tables...
|
||||
resetTables();
|
||||
|
||||
smInFunction = false;
|
||||
smBreakLineCount = 0;
|
||||
setBreakCodeBlock(this);
|
||||
|
||||
if(statementList)
|
||||
codeSize = precompileBlock(statementList, 0) + 1;
|
||||
else
|
||||
codeSize = 1;
|
||||
|
||||
lineBreakPairCount = smBreakLineCount;
|
||||
code = new U32[codeSize + smBreakLineCount * 2];
|
||||
lineBreakPairs = code + codeSize;
|
||||
|
||||
// Write string table data...
|
||||
getGlobalStringTable().write(st);
|
||||
getFunctionStringTable().write(st);
|
||||
|
||||
// Write float table data...
|
||||
getGlobalFloatTable().write(st);
|
||||
getFunctionFloatTable().write(st);
|
||||
|
||||
smBreakLineCount = 0;
|
||||
U32 lastIp;
|
||||
if(statementList)
|
||||
lastIp = compileBlock(statementList, code, 0, 0, 0);
|
||||
else
|
||||
lastIp = 0;
|
||||
|
||||
if(lastIp != codeSize - 1)
|
||||
Con::errorf(ConsoleLogEntry::General, "CodeBlock::compile - precompile size mismatch, a precompile/compile function pair is probably mismatched.");
|
||||
|
||||
code[lastIp++] = OP_RETURN;
|
||||
U32 totSize = codeSize + smBreakLineCount * 2;
|
||||
st.write(codeSize);
|
||||
st.write(lineBreakPairCount);
|
||||
|
||||
// Write out our bytecode, doing a bit of compression for low numbers.
|
||||
U32 i;
|
||||
for(i = 0; i < codeSize; i++)
|
||||
{
|
||||
if(code[i] < 0xFF)
|
||||
st.write(U8(code[i]));
|
||||
else
|
||||
{
|
||||
st.write(U8(0xFF));
|
||||
st.write(code[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the break info...
|
||||
for(i = codeSize; i < totSize; i++)
|
||||
st.write(code[i]);
|
||||
|
||||
getIdentTable().write(st);
|
||||
|
||||
consoleAllocReset();
|
||||
st.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *CodeBlock::compileExec(StringTableEntry fileName, const char *string, bool noCalls, int setFrame)
|
||||
{
|
||||
STEtoU32 = evalSTEtoU32;
|
||||
consoleAllocReset();
|
||||
|
||||
name = fileName;
|
||||
|
||||
if(name)
|
||||
addToCodeList();
|
||||
|
||||
statementList = NULL;
|
||||
|
||||
// Set up the parser.
|
||||
smCurrentParser = getParserForFile(fileName);
|
||||
AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName));
|
||||
|
||||
// Now do some parsing.
|
||||
smCurrentParser->setScanBuffer(string, fileName);
|
||||
smCurrentParser->restart(NULL);
|
||||
smCurrentParser->parse();
|
||||
|
||||
if(!statementList)
|
||||
{
|
||||
delete this;
|
||||
return "";
|
||||
}
|
||||
|
||||
resetTables();
|
||||
|
||||
smInFunction = false;
|
||||
smBreakLineCount = 0;
|
||||
setBreakCodeBlock(this);
|
||||
|
||||
codeSize = precompileBlock(statementList, 0) + 1;
|
||||
|
||||
lineBreakPairCount = smBreakLineCount;
|
||||
|
||||
globalStrings = getGlobalStringTable().build();
|
||||
functionStrings = getFunctionStringTable().build();
|
||||
globalFloats = getGlobalFloatTable().build();
|
||||
functionFloats = getFunctionFloatTable().build();
|
||||
|
||||
code = new U32[codeSize + lineBreakPairCount * 2];
|
||||
lineBreakPairs = code + codeSize;
|
||||
|
||||
smBreakLineCount = 0;
|
||||
U32 lastIp = compileBlock(statementList, code, 0, 0, 0);
|
||||
code[lastIp++] = OP_RETURN;
|
||||
|
||||
consoleAllocReset();
|
||||
|
||||
if(lineBreakPairCount && fileName)
|
||||
calcBreakList();
|
||||
|
||||
if(lastIp != codeSize)
|
||||
Con::warnf(ConsoleLogEntry::General, "precompile size mismatch");
|
||||
|
||||
return exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void CodeBlock::incRefCount()
|
||||
{
|
||||
refCount++;
|
||||
}
|
||||
|
||||
void CodeBlock::decRefCount()
|
||||
{
|
||||
refCount--;
|
||||
if(!refCount)
|
||||
delete this;
|
||||
}
|
||||
Reference in New Issue
Block a user