mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
1513 lines
35 KiB
C++
1513 lines
35 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2012 GarageGames, LLC
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "console/console.h"
|
|
#include "console/compiler.h"
|
|
#include "console/codeBlock.h"
|
|
#include "console/telnetDebugger.h"
|
|
#include "console/ast.h"
|
|
#include "core/strings/unicode.h"
|
|
#include "core/strings/stringFunctions.h"
|
|
#include "core/stringTable.h"
|
|
#include "core/stream/fileStream.h"
|
|
|
|
using namespace Compiler;
|
|
|
|
bool CodeBlock::smInFunction = false;
|
|
CodeBlock * CodeBlock::smCodeBlockList = NULL;
|
|
CodeBlock * CodeBlock::smCurrentCodeBlock = NULL;
|
|
ConsoleParser *CodeBlock::smCurrentParser = NULL;
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
CodeBlock::CodeBlock()
|
|
{
|
|
globalStrings = NULL;
|
|
functionStrings = NULL;
|
|
functionStringsMaxLen = 0;
|
|
globalStringsMaxLen = 0;
|
|
globalFloats = NULL;
|
|
functionFloats = NULL;
|
|
lineBreakPairs = NULL;
|
|
breakList = NULL;
|
|
breakListSize = 0;
|
|
|
|
refCount = 0;
|
|
code = NULL;
|
|
name = NULL;
|
|
fullPath = NULL;
|
|
modPath = NULL;
|
|
}
|
|
|
|
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);
|
|
|
|
functionStringsMaxLen = 0;
|
|
globalStringsMaxLen = 0;
|
|
|
|
delete[] globalFloats;
|
|
delete[] functionFloats;
|
|
delete[] code;
|
|
delete[] breakList;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
StringTableEntry CodeBlock::getCurrentCodeBlockName()
|
|
{
|
|
if (CodeBlock::getCurrentBlock())
|
|
return CodeBlock::getCurrentBlock()->name;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
StringTableEntry CodeBlock::getCurrentCodeBlockFullPath()
|
|
{
|
|
if (CodeBlock::getCurrentBlock())
|
|
return CodeBlock::getCurrentBlock()->fullPath;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
StringTableEntry CodeBlock::getCurrentCodeBlockModName()
|
|
{
|
|
if (CodeBlock::getCurrentBlock())
|
|
return CodeBlock::getCurrentBlock()->modPath;
|
|
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();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Let the telnet debugger know that this code
|
|
// block has been unloaded and that it needs to
|
|
// remove references to it.
|
|
if (TelDebugger)
|
|
TelDebugger->clearCodeBlockPointers(this);
|
|
}
|
|
|
|
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)
|
|
{
|
|
const StringTableEntry exePath = Platform::getMainDotCsDir();
|
|
const StringTableEntry cwd = Platform::getCurrentDirectory();
|
|
|
|
name = fileName;
|
|
|
|
if (fileName)
|
|
{
|
|
fullPath = NULL;
|
|
|
|
if (Platform::isFullPath(fileName))
|
|
fullPath = fileName;
|
|
|
|
if (dStrnicmp(exePath, fileName, dStrlen(exePath)) == 0)
|
|
name = StringTable->insert(fileName + dStrlen(exePath) + 1, true);
|
|
else if (dStrnicmp(cwd, fileName, dStrlen(cwd)) == 0)
|
|
name = StringTable->insert(fileName + dStrlen(cwd) + 1, true);
|
|
|
|
if (fullPath == NULL)
|
|
{
|
|
char buf[1024];
|
|
fullPath = StringTable->insert(Platform::makeFullPathName(fileName, buf, sizeof(buf)), true);
|
|
}
|
|
|
|
modPath = Con::getModNameFromPath(fileName);
|
|
}
|
|
|
|
//
|
|
addToCodeList();
|
|
|
|
U32 globalSize, size, i;
|
|
st.read(&size);
|
|
if (size)
|
|
{
|
|
globalStrings = new char[size];
|
|
globalStringsMaxLen = size;
|
|
st.read(size, globalStrings);
|
|
}
|
|
globalSize = size;
|
|
st.read(&size);
|
|
if (size)
|
|
{
|
|
functionStrings = new char[size];
|
|
functionStringsMaxLen = 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 codeLength;
|
|
st.read(&codeLength);
|
|
st.read(&lineBreakPairCount);
|
|
|
|
U32 totSize = codeLength + lineBreakPairCount * 2;
|
|
code = new U32[totSize];
|
|
|
|
// 0xFF is used as a flag to help compress the bytecode.
|
|
// If detected, the bytecode is only a U8.
|
|
for (i = 0; i < codeLength; i++)
|
|
{
|
|
U8 b;
|
|
st.read(&b);
|
|
if (b == 0xFF)
|
|
st.read(&code[i]);
|
|
else
|
|
code[i] = b;
|
|
}
|
|
|
|
for (i = codeLength; i < totSize; i++)
|
|
st.read(&code[i]);
|
|
|
|
lineBreakPairs = code + codeLength;
|
|
|
|
// 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->EmptyString();
|
|
U32 count;
|
|
st.read(&count);
|
|
while (count--)
|
|
{
|
|
U32 ip;
|
|
st.read(&ip);
|
|
#ifdef TORQUE_CPU_X64
|
|
*(U64*)(code + ip) = (U64)ste;
|
|
#else
|
|
code[ip] = *((U32 *)&ste);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (lineBreakPairCount)
|
|
calcBreakList();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, const char *inScript, bool overrideNoDso)
|
|
{
|
|
AssertFatal(Con::isMainThread(), "Compiling code on a secondary thread");
|
|
|
|
// This will return true, but return value is ignored
|
|
char *script;
|
|
chompUTF8BOM(inScript, &script);
|
|
|
|
gSyntaxError = false;
|
|
|
|
consoleAllocReset();
|
|
|
|
STEtoCode = compileSTEtoCode;
|
|
|
|
gStatementList = NULL;
|
|
gAnonFunctionList = 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 (gStatementList)
|
|
{
|
|
if (gAnonFunctionList)
|
|
{
|
|
// Prepend anonymous functions to statement list, so they're defined already when
|
|
// the statements run.
|
|
gAnonFunctionList->append(gStatementList);
|
|
gStatementList = gAnonFunctionList;
|
|
}
|
|
}
|
|
|
|
|
|
if (gSyntaxError)
|
|
{
|
|
consoleAllocReset();
|
|
return false;
|
|
}
|
|
|
|
#ifdef TORQUE_NO_DSO_GENERATION
|
|
if (!overrideNoDso)
|
|
return false;
|
|
#endif // !TORQUE_NO_DSO_GENERATION
|
|
|
|
FileStream st;
|
|
if (!st.open(codeFileName, Torque::FS::File::Write))
|
|
return false;
|
|
st.write(U32(Con::DSOVersion));
|
|
|
|
// Reset all our value tables...
|
|
resetTables();
|
|
|
|
smInFunction = false;
|
|
|
|
CodeStream codeStream;
|
|
U32 lastIp;
|
|
if (gStatementList)
|
|
{
|
|
lastIp = compileBlock(gStatementList, codeStream, 0) + 1;
|
|
}
|
|
else
|
|
{
|
|
codeSize = 1;
|
|
lastIp = 0;
|
|
}
|
|
|
|
codeStream.emit(OP_RETURN);
|
|
codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
|
|
|
|
lineBreakPairCount = codeStream.getNumLineBreaks();
|
|
|
|
// Write string table data...
|
|
getGlobalStringTable().write(st);
|
|
getFunctionStringTable().write(st);
|
|
|
|
// Write float table data...
|
|
getGlobalFloatTable().write(st);
|
|
getFunctionFloatTable().write(st);
|
|
|
|
if (lastIp != codeSize)
|
|
Con::errorf(ConsoleLogEntry::General, "CodeBlock::compile - precompile size mismatch, a precompile/compile function pair is probably mismatched.");
|
|
|
|
U32 totSize = codeSize + codeStream.getNumLineBreaks() * 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;
|
|
|
|
|
|
}
|
|
|
|
ConsoleValueRef CodeBlock::compileExec(StringTableEntry fileName, const char *inString, bool noCalls, S32 setFrame)
|
|
{
|
|
AssertFatal(Con::isMainThread(), "Compiling code on a secondary thread");
|
|
|
|
// Check for a UTF8 script file
|
|
char *string;
|
|
chompUTF8BOM(inString, &string);
|
|
|
|
STEtoCode = evalSTEtoCode;
|
|
consoleAllocReset();
|
|
|
|
name = fileName;
|
|
|
|
if (fileName)
|
|
{
|
|
const StringTableEntry exePath = Platform::getMainDotCsDir();
|
|
const StringTableEntry cwd = Platform::getCurrentDirectory();
|
|
|
|
fullPath = NULL;
|
|
|
|
if (Platform::isFullPath(fileName))
|
|
fullPath = fileName;
|
|
|
|
if (dStrnicmp(exePath, fileName, dStrlen(exePath)) == 0)
|
|
name = StringTable->insert(fileName + dStrlen(exePath) + 1, true);
|
|
else if (dStrnicmp(cwd, fileName, dStrlen(cwd)) == 0)
|
|
name = StringTable->insert(fileName + dStrlen(cwd) + 1, true);
|
|
|
|
if (fullPath == NULL)
|
|
{
|
|
char buf[1024];
|
|
fullPath = StringTable->insert(Platform::makeFullPathName(fileName, buf, sizeof(buf)), true);
|
|
}
|
|
|
|
modPath = Con::getModNameFromPath(fileName);
|
|
}
|
|
|
|
if (name)
|
|
addToCodeList();
|
|
|
|
gStatementList = NULL;
|
|
gAnonFunctionList = 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 (gStatementList)
|
|
{
|
|
if (gAnonFunctionList)
|
|
{
|
|
// Prepend anonymous functions to statement list, so they're defined already when
|
|
// the statements run.
|
|
gAnonFunctionList->append(gStatementList);
|
|
gStatementList = gAnonFunctionList;
|
|
}
|
|
}
|
|
|
|
if (!gStatementList)
|
|
{
|
|
delete this;
|
|
return ConsoleValueRef();
|
|
}
|
|
|
|
resetTables();
|
|
|
|
smInFunction = false;
|
|
|
|
CodeStream codeStream;
|
|
U32 lastIp = compileBlock(gStatementList, codeStream, 0);
|
|
|
|
lineBreakPairCount = codeStream.getNumLineBreaks();
|
|
|
|
globalStrings = getGlobalStringTable().build();
|
|
globalStringsMaxLen = getGlobalStringTable().totalLen;
|
|
|
|
functionStrings = getFunctionStringTable().build();
|
|
functionStringsMaxLen = getFunctionStringTable().totalLen;
|
|
|
|
globalFloats = getGlobalFloatTable().build();
|
|
functionFloats = getFunctionFloatTable().build();
|
|
|
|
codeStream.emit(OP_RETURN);
|
|
codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
|
|
|
|
//dumpInstructions(0, false);
|
|
|
|
consoleAllocReset();
|
|
|
|
if (lineBreakPairCount && fileName)
|
|
calcBreakList();
|
|
|
|
if (lastIp + 1 != codeSize)
|
|
Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp);
|
|
|
|
return exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
void CodeBlock::incRefCount()
|
|
{
|
|
refCount++;
|
|
}
|
|
|
|
void CodeBlock::decRefCount()
|
|
{
|
|
refCount--;
|
|
if (!refCount)
|
|
delete this;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
String CodeBlock::getFunctionArgs(U32 ip)
|
|
{
|
|
StringBuilder str;
|
|
|
|
U32 fnArgc = code[ip + 5];
|
|
for (U32 i = 0; i < fnArgc; ++i)
|
|
{
|
|
StringTableEntry var = CodeToSTE(code, ip + (i * 2) + 6);
|
|
|
|
if (i != 0)
|
|
str.append(", ");
|
|
|
|
str.append("string ");
|
|
|
|
// Try to capture junked parameters
|
|
if (var[0])
|
|
str.append(var + 1);
|
|
else
|
|
str.append("JUNK");
|
|
}
|
|
|
|
return str.end();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
void CodeBlock::dumpInstructions(U32 startIp, bool upToReturn)
|
|
{
|
|
U32 ip = startIp;
|
|
smInFunction = false;
|
|
U32 endFuncIp = 0;
|
|
|
|
while (ip < codeSize)
|
|
{
|
|
if (ip > endFuncIp)
|
|
{
|
|
smInFunction = false;
|
|
}
|
|
|
|
switch (code[ip++])
|
|
{
|
|
|
|
case OP_FUNC_DECL:
|
|
{
|
|
StringTableEntry fnName = CodeToSTE(code, ip);
|
|
StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
|
|
StringTableEntry fnPackage = CodeToSTE(code, ip + 4);
|
|
bool hasBody = bool(code[ip + 6]);
|
|
U32 newIp = code[ip + 7];
|
|
U32 argc = code[ip + 8];
|
|
endFuncIp = newIp;
|
|
|
|
Con::printf("%i: OP_FUNC_DECL name=%s nspace=%s package=%s hasbody=%i newip=%i argc=%i",
|
|
ip - 1, fnName, fnNamespace, fnPackage, hasBody, newIp, argc);
|
|
|
|
// Skip args.
|
|
|
|
ip += 9 + (argc * 2);
|
|
smInFunction = true;
|
|
break;
|
|
}
|
|
|
|
case OP_CREATE_OBJECT:
|
|
{
|
|
StringTableEntry objParent = CodeToSTE(code, ip);
|
|
bool isDataBlock = code[ip + 2];
|
|
bool isInternal = code[ip + 3];
|
|
bool isSingleton = code[ip + 4];
|
|
U32 lineNumber = code[ip + 5];
|
|
U32 failJump = code[ip + 6];
|
|
|
|
Con::printf("%i: OP_CREATE_OBJECT objParent=%s isDataBlock=%i isInternal=%i isSingleton=%i lineNumber=%i failJump=%i",
|
|
ip - 1, objParent, isDataBlock, isInternal, isSingleton, lineNumber, failJump);
|
|
|
|
ip += 7;
|
|
break;
|
|
}
|
|
|
|
case OP_ADD_OBJECT:
|
|
{
|
|
bool placeAtRoot = code[ip++];
|
|
Con::printf("%i: OP_ADD_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot);
|
|
break;
|
|
}
|
|
|
|
case OP_END_OBJECT:
|
|
{
|
|
bool placeAtRoot = code[ip++];
|
|
Con::printf("%i: OP_END_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot);
|
|
break;
|
|
}
|
|
|
|
case OP_FINISH_OBJECT:
|
|
{
|
|
Con::printf("%i: OP_FINISH_OBJECT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_JMPIFFNOT:
|
|
{
|
|
Con::printf("%i: OP_JMPIFFNOT ip=%i", ip - 1, code[ip]);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_JMPIFNOT:
|
|
{
|
|
Con::printf("%i: OP_JMPIFNOT ip=%i", ip - 1, code[ip]);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_JMPIFF:
|
|
{
|
|
Con::printf("%i: OP_JMPIFF ip=%i", ip - 1, code[ip]);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_JMPIF:
|
|
{
|
|
Con::printf("%i: OP_JMPIF ip=%i", ip - 1, code[ip]);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_JMPIFNOT_NP:
|
|
{
|
|
Con::printf("%i: OP_JMPIFNOT_NP ip=%i", ip - 1, code[ip]);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_JMPIF_NP:
|
|
{
|
|
Con::printf("%i: OP_JMPIF_NP ip=%i", ip - 1, code[ip]);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_JMP:
|
|
{
|
|
Con::printf("%i: OP_JMP ip=%i", ip - 1, code[ip]);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_RETURN:
|
|
{
|
|
Con::printf("%i: OP_RETURN", ip - 1);
|
|
|
|
if (upToReturn)
|
|
return;
|
|
|
|
break;
|
|
}
|
|
|
|
case OP_RETURN_VOID:
|
|
{
|
|
Con::printf("%i: OP_RETURNVOID", ip - 1);
|
|
|
|
if (upToReturn)
|
|
return;
|
|
|
|
break;
|
|
}
|
|
|
|
case OP_RETURN_UINT:
|
|
{
|
|
Con::printf("%i: OP_RETURNUINT", ip - 1);
|
|
|
|
if (upToReturn)
|
|
return;
|
|
|
|
break;
|
|
}
|
|
|
|
case OP_RETURN_FLT:
|
|
{
|
|
Con::printf("%i: OP_RETURNFLT", ip - 1);
|
|
|
|
if (upToReturn)
|
|
return;
|
|
|
|
break;
|
|
}
|
|
|
|
case OP_CMPEQ:
|
|
{
|
|
Con::printf("%i: OP_CMPEQ", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_CMPGR:
|
|
{
|
|
Con::printf("%i: OP_CMPGR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_CMPGE:
|
|
{
|
|
Con::printf("%i: OP_CMPGE", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_CMPLT:
|
|
{
|
|
Con::printf("%i: OP_CMPLT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_CMPLE:
|
|
{
|
|
Con::printf("%i: OP_CMPLE", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_CMPNE:
|
|
{
|
|
Con::printf("%i: OP_CMPNE", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_XOR:
|
|
{
|
|
Con::printf("%i: OP_XOR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_MOD:
|
|
{
|
|
Con::printf("%i: OP_MOD", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_BITAND:
|
|
{
|
|
Con::printf("%i: OP_BITAND", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_BITOR:
|
|
{
|
|
Con::printf("%i: OP_BITOR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_NOT:
|
|
{
|
|
Con::printf("%i: OP_NOT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_NOTF:
|
|
{
|
|
Con::printf("%i: OP_NOTF", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_ONESCOMPLEMENT:
|
|
{
|
|
Con::printf("%i: OP_ONESCOMPLEMENT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SHR:
|
|
{
|
|
Con::printf("%i: OP_SHR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SHL:
|
|
{
|
|
Con::printf("%i: OP_SHL", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_AND:
|
|
{
|
|
Con::printf("%i: OP_AND", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_OR:
|
|
{
|
|
Con::printf("%i: OP_OR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_ADD:
|
|
{
|
|
Con::printf("%i: OP_ADD", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SUB:
|
|
{
|
|
Con::printf("%i: OP_SUB", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_MUL:
|
|
{
|
|
Con::printf("%i: OP_MUL", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_DIV:
|
|
{
|
|
Con::printf("%i: OP_DIV", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_NEG:
|
|
{
|
|
Con::printf("%i: OP_NEG", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_INC:
|
|
{
|
|
Con::printf("%i: OP_INC varName=%s", ip - 1, CodeToSTE(code, ip));
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_DEC:
|
|
{
|
|
Con::printf("%i: OP_DEC varName=%s", ip - 1, CodeToSTE(code, ip));
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURVAR:
|
|
{
|
|
StringTableEntry var = CodeToSTE(code, ip);
|
|
|
|
Con::printf("%i: OP_SETCURVAR var=%s", ip - 1, var);
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURVAR_CREATE:
|
|
{
|
|
StringTableEntry var = CodeToSTE(code, ip);
|
|
|
|
Con::printf("%i: OP_SETCURVAR_CREATE var=%s", ip - 1, var);
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURVAR_ARRAY:
|
|
{
|
|
Con::printf("%i: OP_SETCURVAR_ARRAY", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURVAR_ARRAY_VARLOOKUP:
|
|
{
|
|
StringTableEntry arrayName = CodeToSTE(code, ip);
|
|
StringTableEntry arrayLookup = CodeToSTE(code, ip + 2);
|
|
|
|
Con::printf("%i: OP_SETCURVAR_ARRAY_VARLOOKUP arrayName=%s arrayLookup=%s", ip - 1, arrayName, arrayLookup);
|
|
ip += 4;
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURVAR_ARRAY_CREATE:
|
|
{
|
|
Con::printf("%i: OP_SETCURVAR_ARRAY_CREATE", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP:
|
|
{
|
|
StringTableEntry arrayName = CodeToSTE(code, ip);
|
|
StringTableEntry arrayLookup = CodeToSTE(code, ip + 2);
|
|
|
|
Con::printf("%i: OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP arrayName=%s arrayLookup=%s", ip - 1, arrayName, arrayLookup);
|
|
ip += 4;
|
|
break;
|
|
}
|
|
|
|
case OP_LOADVAR_UINT:
|
|
{
|
|
Con::printf("%i: OP_LOADVAR_UINT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_LOADVAR_FLT:
|
|
{
|
|
Con::printf("%i: OP_LOADVAR_FLT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_LOADVAR_STR:
|
|
{
|
|
Con::printf("%i: OP_LOADVAR_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_LOADVAR_VAR:
|
|
{
|
|
Con::printf("%i: OP_LOADVAR_VAR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SAVEVAR_UINT:
|
|
{
|
|
Con::printf("%i: OP_SAVEVAR_UINT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SAVEVAR_FLT:
|
|
{
|
|
Con::printf("%i: OP_SAVEVAR_FLT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SAVEVAR_STR:
|
|
{
|
|
Con::printf("%i: OP_SAVEVAR_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SAVEVAR_VAR:
|
|
{
|
|
Con::printf("%i: OP_SAVEVAR_VAR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SETCUROBJECT:
|
|
{
|
|
Con::printf("%i: OP_SETCUROBJECT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SETCUROBJECT_NEW:
|
|
{
|
|
Con::printf("%i: OP_SETCUROBJECT_NEW", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SETCUROBJECT_INTERNAL:
|
|
{
|
|
Con::printf("%i: OP_SETCUROBJECT_INTERNAL", ip - 1);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURFIELD:
|
|
{
|
|
StringTableEntry curField = CodeToSTE(code, ip);
|
|
Con::printf("%i: OP_SETCURFIELD field=%s", ip - 1, curField);
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURFIELD_ARRAY:
|
|
{
|
|
Con::printf("%i: OP_SETCURFIELD_ARRAY", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURFIELD_ARRAY_VAR:
|
|
{
|
|
StringTableEntry var = CodeToSTE(code, ip);
|
|
Con::printf("%i: OP_SETCURFIELD_ARRAY_VAR var=%s", ip - 1, var);
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURFIELD_THIS:
|
|
{
|
|
StringTableEntry curField = CodeToSTE(code, ip);
|
|
Con::printf("%i: OP_SETCURFIELD_THIS field=%s", ip - 1, curField);
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_SETCURFIELD_TYPE:
|
|
{
|
|
U32 type = code[ip];
|
|
Con::printf("%i: OP_SETCURFIELD_TYPE type=%i", ip - 1, type);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_LOADFIELD_UINT:
|
|
{
|
|
Con::printf("%i: OP_LOADFIELD_UINT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_LOADFIELD_FLT:
|
|
{
|
|
Con::printf("%i: OP_LOADFIELD_FLT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_LOADFIELD_STR:
|
|
{
|
|
Con::printf("%i: OP_LOADFIELD_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SAVEFIELD_UINT:
|
|
{
|
|
Con::printf("%i: OP_SAVEFIELD_UINT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SAVEFIELD_FLT:
|
|
{
|
|
Con::printf("%i: OP_SAVEFIELD_FLT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_SAVEFIELD_STR:
|
|
{
|
|
Con::printf("%i: OP_SAVEFIELD_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_STR_TO_UINT:
|
|
{
|
|
Con::printf("%i: OP_STR_TO_UINT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_STR_TO_FLT:
|
|
{
|
|
Con::printf("%i: OP_STR_TO_FLT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_STR_TO_NONE:
|
|
{
|
|
Con::printf("%i: OP_STR_TO_NONE", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_FLT_TO_UINT:
|
|
{
|
|
Con::printf("%i: OP_FLT_TO_UINT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_FLT_TO_STR:
|
|
{
|
|
Con::printf("%i: OP_FLT_TO_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_FLT_TO_NONE:
|
|
{
|
|
Con::printf("%i: OP_FLT_TO_NONE", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_UINT_TO_FLT:
|
|
{
|
|
Con::printf("%i: OP_SAVEFIELD_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_UINT_TO_STR:
|
|
{
|
|
Con::printf("%i: OP_UINT_TO_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_UINT_TO_NONE:
|
|
{
|
|
Con::printf("%i: OP_UINT_TO_NONE", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_COPYVAR_TO_NONE:
|
|
{
|
|
Con::printf("%i: OP_COPYVAR_TO_NONE", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_LOADIMMED_UINT:
|
|
{
|
|
U32 val = code[ip];
|
|
Con::printf("%i: OP_LOADIMMED_UINT val=%i", ip - 1, val);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_LOADIMMED_FLT:
|
|
{
|
|
F64 val = (smInFunction ? functionFloats : globalFloats)[code[ip]];
|
|
Con::printf("%i: OP_LOADIMMED_FLT val=%f", ip - 1, val);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_TAG_TO_STR:
|
|
{
|
|
const char* str = (smInFunction ? functionStrings : globalStrings) + code[ip];
|
|
Con::printf("%i: OP_TAG_TO_STR str=%s", ip - 1, str);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_LOADIMMED_STR:
|
|
{
|
|
const char* str = (smInFunction ? functionStrings : globalStrings) + code[ip];
|
|
Con::printf("%i: OP_LOADIMMED_STR str=%s", ip - 1, str);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_DOCBLOCK_STR:
|
|
{
|
|
const char* str = (smInFunction ? functionStrings : globalStrings) + code[ip];
|
|
Con::printf("%i: OP_DOCBLOCK_STR str=%s", ip - 1, str);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_LOADIMMED_IDENT:
|
|
{
|
|
StringTableEntry str = CodeToSTE(code, ip);
|
|
Con::printf("%i: OP_LOADIMMED_IDENT str=%s", ip - 1, str);
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_CALLFUNC_RESOLVE:
|
|
{
|
|
StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
|
|
StringTableEntry fnName = CodeToSTE(code, ip);
|
|
U32 callType = code[ip + 2];
|
|
|
|
Con::printf("%i: OP_CALLFUNC_RESOLVE name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace,
|
|
callType == FuncCallExprNode::FunctionCall ? "FunctionCall"
|
|
: callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall");
|
|
|
|
ip += 5;
|
|
break;
|
|
}
|
|
|
|
case OP_CALLFUNC:
|
|
{
|
|
StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
|
|
StringTableEntry fnName = CodeToSTE(code, ip);
|
|
U32 callType = code[ip + 4];
|
|
|
|
Con::printf("%i: OP_CALLFUNC name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace,
|
|
callType == FuncCallExprNode::FunctionCall ? "FunctionCall"
|
|
: callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall");
|
|
|
|
ip += 5;
|
|
break;
|
|
}
|
|
|
|
case OP_CALLFUNC_POINTER:
|
|
{
|
|
Con::printf("%i: OP_CALLFUNC_POINTER", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_CALLFUNC_THIS:
|
|
{
|
|
StringTableEntry fnName = CodeToSTE(code, ip);
|
|
Con::printf("%i: OP_CALLFUNC_THIS name=%s ", ip - 1, fnName);
|
|
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_ADVANCE_STR:
|
|
{
|
|
Con::printf("%i: OP_ADVANCE_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_ADVANCE_STR_APPENDCHAR:
|
|
{
|
|
char ch = code[ip];
|
|
Con::printf("%i: OP_ADVANCE_STR_APPENDCHAR char=%c", ip - 1, ch);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_ADVANCE_STR_COMMA:
|
|
{
|
|
Con::printf("%i: OP_ADVANCE_STR_COMMA", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_ADVANCE_STR_NUL:
|
|
{
|
|
Con::printf("%i: OP_ADVANCE_STR_NUL", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_REWIND_STR:
|
|
{
|
|
Con::printf("%i: OP_REWIND_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_TERMINATE_REWIND_STR:
|
|
{
|
|
Con::printf("%i: OP_TERMINATE_REWIND_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_COMPARE_STR:
|
|
{
|
|
Con::printf("%i: OP_COMPARE_STR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_PUSH:
|
|
{
|
|
Con::printf("%i: OP_PUSH", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_PUSH_UINT:
|
|
{
|
|
Con::printf("%i: OP_PUSH_UINT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_PUSH_FLT:
|
|
{
|
|
Con::printf("%i: OP_PUSH_FLT", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_PUSH_VAR:
|
|
{
|
|
Con::printf("%i: OP_PUSH_VAR", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_PUSH_THIS:
|
|
{
|
|
Con::printf("%i: OP_PUSH_THIS varName=%s", ip - 1, CodeToSTE(code, ip));
|
|
ip += 2;
|
|
break;
|
|
}
|
|
|
|
case OP_PUSH_FRAME:
|
|
{
|
|
Con::printf("%i: OP_PUSH_FRAME", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_ASSERT:
|
|
{
|
|
const char* message = (smInFunction ? functionStrings : globalStrings) + code[ip];
|
|
Con::printf("%i: OP_ASSERT message=%s", ip - 1, message);
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_BREAK:
|
|
{
|
|
Con::printf("%i: OP_BREAK", ip - 1);
|
|
break;
|
|
}
|
|
|
|
case OP_ITER_BEGIN:
|
|
{
|
|
StringTableEntry varName = CodeToSTE(code, ip);
|
|
U32 failIp = code[ip + 2];
|
|
|
|
Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp);
|
|
|
|
ip += 3;
|
|
break;
|
|
}
|
|
|
|
case OP_ITER_BEGIN_STR:
|
|
{
|
|
StringTableEntry varName = CodeToSTE(code, ip);
|
|
U32 failIp = code[ip + 2];
|
|
|
|
Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp);
|
|
|
|
ip += 3;
|
|
break;
|
|
}
|
|
|
|
case OP_ITER:
|
|
{
|
|
U32 breakIp = code[ip];
|
|
|
|
Con::printf("%i: OP_ITER breakIp=%i", ip - 1, breakIp);
|
|
|
|
++ip;
|
|
break;
|
|
}
|
|
|
|
case OP_ITER_END:
|
|
{
|
|
Con::printf("%i: OP_ITER_END", ip - 1);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
Con::printf("%i: !!INVALID!!", ip - 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
smInFunction = false;
|
|
}
|