5853 lines
155 KiB
C++
5853 lines
155 KiB
C++
/*
|
|
AngelCode Scripting Library
|
|
Copyright (c) 2003-2021 Andreas Jonsson
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any
|
|
damages arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any
|
|
purpose, including commercial applications, and to alter it and
|
|
redistribute it freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you
|
|
must not claim that you wrote the original software. If you use
|
|
this software in a product, an acknowledgment in the product
|
|
documentation would be appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and
|
|
must not be misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source
|
|
distribution.
|
|
|
|
The original version of this library can be located at:
|
|
http://www.angelcode.com/angelscript/
|
|
|
|
Andreas Jonsson
|
|
andreas@angelcode.com
|
|
*/
|
|
|
|
|
|
//
|
|
// as_restore.cpp
|
|
//
|
|
// Functions for saving and restoring module bytecode
|
|
// asCRestore was originally written by Dennis Bollyn, dennis@gyrbo.be
|
|
|
|
#include "as_config.h"
|
|
#include "as_restore.h"
|
|
#include "as_bytecode.h"
|
|
#include "as_scriptobject.h"
|
|
#include "as_texts.h"
|
|
#include "as_debug.h"
|
|
|
|
BEGIN_AS_NAMESPACE
|
|
|
|
// Macros for doing endianess agnostic bitmask serialization
|
|
#define SAVE_TO_BIT(dst, val, bit) ((dst) |= ((val) << (bit)))
|
|
#define LOAD_FROM_BIT(dst, val, bit) ((dst) = ((val) >> (bit)) & 1)
|
|
|
|
asCReader::asCReader(asCModule* _module, asIBinaryStream* _stream, asCScriptEngine* _engine)
|
|
: module(_module), stream(_stream), engine(_engine)
|
|
{
|
|
error = false;
|
|
bytesRead = 0;
|
|
}
|
|
|
|
int asCReader::ReadData(void *data, asUINT size)
|
|
{
|
|
asASSERT(size == 1 || size == 2 || size == 4 || size == 8);
|
|
int ret = 0;
|
|
#if defined(AS_BIG_ENDIAN)
|
|
for( asUINT n = 0; ret >= 0 && n < size; n++ )
|
|
ret = stream->Read(((asBYTE*)data)+n, 1);
|
|
#else
|
|
for( int n = size-1; ret >= 0 && n >= 0; n-- )
|
|
ret = stream->Read(((asBYTE*)data)+n, 1);
|
|
#endif
|
|
if (ret < 0)
|
|
Error(TXT_UNEXPECTED_END_OF_FILE);
|
|
bytesRead += size;
|
|
return ret;
|
|
}
|
|
|
|
int asCReader::Read(bool *wasDebugInfoStripped)
|
|
{
|
|
TimeIt("asCReader::Read");
|
|
|
|
// Before starting the load, make sure that
|
|
// any existing resources have been freed
|
|
module->InternalReset();
|
|
|
|
// Call the inner method to do the actual loading
|
|
int r = ReadInner();
|
|
if( r < 0 )
|
|
{
|
|
// Something went wrong while loading the bytecode, so we need
|
|
// to clean-up whatever has been created during the process.
|
|
|
|
// Make sure none of the loaded functions attempt to release
|
|
// references that have not yet been increased
|
|
asUINT i;
|
|
for( i = 0; i < module->m_scriptFunctions.GetLength(); i++ )
|
|
if( !dontTranslate.MoveTo(0, module->m_scriptFunctions[i]) )
|
|
if( module->m_scriptFunctions[i]->scriptData )
|
|
module->m_scriptFunctions[i]->scriptData->byteCode.SetLength(0);
|
|
|
|
asCSymbolTable<asCGlobalProperty>::iterator it = module->m_scriptGlobals.List();
|
|
for( ; it; it++ )
|
|
if( (*it)->GetInitFunc() )
|
|
if( (*it)->GetInitFunc()->scriptData )
|
|
(*it)->GetInitFunc()->scriptData->byteCode.SetLength(0);
|
|
|
|
module->InternalReset();
|
|
}
|
|
else
|
|
{
|
|
// Init system functions properly
|
|
engine->PrepareEngine();
|
|
|
|
// Initialize the global variables (unless requested not to)
|
|
if( engine->ep.initGlobalVarsAfterBuild )
|
|
r = module->ResetGlobalVars(0);
|
|
|
|
if( wasDebugInfoStripped )
|
|
*wasDebugInfoStripped = noDebugInfo;
|
|
}
|
|
|
|
// Clean up the loaded string constants
|
|
for (asUINT n = 0; n < usedStringConstants.GetLength(); n++)
|
|
engine->stringFactory->ReleaseStringConstant(usedStringConstants[n]);
|
|
usedStringConstants.SetLength(0);
|
|
|
|
return r;
|
|
}
|
|
|
|
int asCReader::Error(const char *msg)
|
|
{
|
|
// Don't write if it has already been reported an error earlier
|
|
if( !error )
|
|
{
|
|
asCString str;
|
|
str.Format(msg, bytesRead);
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
error = true;
|
|
}
|
|
|
|
return asERROR;
|
|
}
|
|
|
|
int asCReader::ReadInner()
|
|
{
|
|
TimeIt("asCReader::ReadInner");
|
|
|
|
// This function will load each entity one by one from the stream.
|
|
// If any error occurs, it will return to the caller who is
|
|
// responsible for cleaning up the partially loaded entities.
|
|
|
|
engine->deferValidationOfTemplateTypes = true;
|
|
|
|
unsigned long i, count;
|
|
asCScriptFunction* func;
|
|
|
|
// Read the flag as 1 byte even on platforms with 4byte booleans
|
|
noDebugInfo = ReadEncodedUInt() ? VALUE_OF_BOOLEAN_TRUE : 0;
|
|
|
|
// Read enums
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
module->m_enumTypes.Allocate(count, false);
|
|
for( i = 0; i < count && !error; i++ )
|
|
{
|
|
asCEnumType *et = asNEW(asCEnumType)(engine);
|
|
if( et == 0 )
|
|
{
|
|
error = true;
|
|
return asOUT_OF_MEMORY;
|
|
}
|
|
|
|
bool isExternal = false;
|
|
ReadTypeDeclaration(et, 1, &isExternal);
|
|
|
|
// If the type is shared then we should use the original if it exists
|
|
bool sharedExists = false;
|
|
if( et->IsShared() )
|
|
{
|
|
for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ )
|
|
{
|
|
asCTypeInfo *t = engine->sharedScriptTypes[n];
|
|
if( t &&
|
|
t->IsShared() &&
|
|
t->name == et->name &&
|
|
t->nameSpace == et->nameSpace &&
|
|
(t->flags & asOBJ_ENUM) )
|
|
{
|
|
asDELETE(et, asCEnumType);
|
|
et = CastToEnumType(t);
|
|
sharedExists = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isExternal && !sharedExists)
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_EXTERNAL_SHARED_s_NOT_FOUND, et->name.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());
|
|
asDELETE(et, asCEnumType);
|
|
error = true;
|
|
return asERROR;
|
|
}
|
|
|
|
if( sharedExists )
|
|
{
|
|
existingShared.Insert(et, true);
|
|
et->AddRefInternal();
|
|
}
|
|
else
|
|
{
|
|
if( et->IsShared() )
|
|
{
|
|
engine->sharedScriptTypes.PushLast(et);
|
|
et->AddRefInternal();
|
|
}
|
|
|
|
// Set this module as the owner
|
|
et->module = module;
|
|
}
|
|
module->AddEnumType(et);
|
|
|
|
if (isExternal)
|
|
module->m_externalTypes.PushLast(et);
|
|
|
|
ReadTypeDeclaration(et, 2);
|
|
}
|
|
|
|
if( error ) return asERROR;
|
|
|
|
// classTypes[]
|
|
// First restore the structure names, then the properties
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
module->m_classTypes.Allocate(count, false);
|
|
for( i = 0; i < count && !error; ++i )
|
|
{
|
|
asCObjectType *ot = asNEW(asCObjectType)(engine);
|
|
if( ot == 0 )
|
|
{
|
|
error = true;
|
|
return asOUT_OF_MEMORY;
|
|
}
|
|
|
|
bool isExternal = false;
|
|
ReadTypeDeclaration(ot, 1, &isExternal);
|
|
|
|
// If the type is shared, then we should use the original if it exists
|
|
bool sharedExists = false;
|
|
if( ot->IsShared() )
|
|
{
|
|
for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ )
|
|
{
|
|
asCTypeInfo *ti = engine->sharedScriptTypes[n];
|
|
asCObjectType *t = CastToObjectType(ti);
|
|
if( t &&
|
|
t->IsShared() &&
|
|
t->name == ot->name &&
|
|
t->nameSpace == ot->nameSpace &&
|
|
t->IsInterface() == ot->IsInterface() )
|
|
{
|
|
asDELETE(ot, asCObjectType);
|
|
ot = CastToObjectType(t);
|
|
sharedExists = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isExternal && !sharedExists)
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_EXTERNAL_SHARED_s_NOT_FOUND, ot->name.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());
|
|
asDELETE(ot, asCObjectType);
|
|
error = true;
|
|
return asERROR;
|
|
}
|
|
|
|
if( sharedExists )
|
|
{
|
|
existingShared.Insert(ot, true);
|
|
ot->AddRefInternal();
|
|
}
|
|
else
|
|
{
|
|
if( ot->IsShared() )
|
|
{
|
|
engine->sharedScriptTypes.PushLast(ot);
|
|
ot->AddRefInternal();
|
|
}
|
|
|
|
// Set this module as the owner
|
|
ot->module = module;
|
|
}
|
|
module->AddClassType(ot);
|
|
|
|
if (isExternal)
|
|
module->m_externalTypes.PushLast(ot);
|
|
}
|
|
|
|
if( error ) return asERROR;
|
|
|
|
// Read func defs
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
module->m_funcDefs.Allocate(count, false);
|
|
for( i = 0; i < count && !error; i++ )
|
|
{
|
|
bool isNew, isExternal;
|
|
asCScriptFunction *funcDef = ReadFunction(isNew, false, true, true, &isExternal);
|
|
if(funcDef)
|
|
{
|
|
funcDef->module = module;
|
|
|
|
asCFuncdefType *fdt = funcDef->funcdefType;
|
|
fdt->module = module;
|
|
|
|
module->AddFuncDef(fdt);
|
|
engine->funcDefs.PushLast(fdt);
|
|
|
|
// TODO: clean up: This is also done by the builder. It should probably be moved to a method in the module
|
|
// Check if there is another identical funcdef from another module and if so reuse that instead
|
|
if(funcDef->IsShared())
|
|
{
|
|
for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ )
|
|
{
|
|
asCFuncdefType *f2 = engine->funcDefs[n];
|
|
if( f2 == 0 || fdt == f2 )
|
|
continue;
|
|
|
|
if( !f2->funcdef->IsShared() )
|
|
continue;
|
|
|
|
if( f2->name == fdt->name &&
|
|
f2->nameSpace == fdt->nameSpace &&
|
|
f2->parentClass == fdt->parentClass &&
|
|
f2->funcdef->IsSignatureExceptNameEqual(funcDef) )
|
|
{
|
|
// Replace our funcdef for the existing one
|
|
module->ReplaceFuncDef(fdt, f2);
|
|
f2->AddRefInternal();
|
|
|
|
if (isExternal)
|
|
module->m_externalTypes.PushLast(f2);
|
|
|
|
engine->funcDefs.RemoveValue(fdt);
|
|
|
|
savedFunctions[savedFunctions.IndexOf(funcDef)] = f2->funcdef;
|
|
|
|
if (fdt->parentClass)
|
|
{
|
|
// The real funcdef should already be in the object
|
|
asASSERT(fdt->parentClass->childFuncDefs.IndexOf(f2) >= 0);
|
|
|
|
fdt->parentClass = 0;
|
|
}
|
|
|
|
fdt->ReleaseInternal();
|
|
funcDef = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the funcdef to the parentClass if this is a child funcdef
|
|
if (funcDef && fdt->parentClass)
|
|
fdt->parentClass->childFuncDefs.PushLast(fdt);
|
|
|
|
// Check if an external shared funcdef was really found
|
|
if (isExternal && funcDef)
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_EXTERNAL_SHARED_s_NOT_FOUND, funcDef->name.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());
|
|
error = true;
|
|
return asERROR;
|
|
}
|
|
}
|
|
else
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
|
|
// Read interface methods
|
|
for( i = 0; i < module->m_classTypes.GetLength() && !error; i++ )
|
|
{
|
|
if( module->m_classTypes[i]->IsInterface() )
|
|
ReadTypeDeclaration(module->m_classTypes[i], 2);
|
|
}
|
|
|
|
// Read class methods and behaviours
|
|
for( i = 0; i < module->m_classTypes.GetLength() && !error; ++i )
|
|
{
|
|
if( !module->m_classTypes[i]->IsInterface() )
|
|
ReadTypeDeclaration(module->m_classTypes[i], 2);
|
|
}
|
|
|
|
// Read class properties
|
|
for( i = 0; i < module->m_classTypes.GetLength() && !error; ++i )
|
|
{
|
|
if( !module->m_classTypes[i]->IsInterface() )
|
|
ReadTypeDeclaration(module->m_classTypes[i], 3);
|
|
}
|
|
|
|
if( error ) return asERROR;
|
|
|
|
// Read typedefs
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
module->m_typeDefs.Allocate(count, false);
|
|
for( i = 0; i < count && !error; i++ )
|
|
{
|
|
asCTypedefType *td = asNEW(asCTypedefType)(engine);
|
|
if( td == 0 )
|
|
{
|
|
error = true;
|
|
return asOUT_OF_MEMORY;
|
|
}
|
|
|
|
bool isExternal = false;
|
|
ReadTypeDeclaration(td, 1, &isExternal);
|
|
td->module = module;
|
|
module->AddTypeDef(td);
|
|
ReadTypeDeclaration(td, 2);
|
|
}
|
|
|
|
if( error ) return asERROR;
|
|
|
|
// scriptGlobals[]
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
if( count && engine->ep.disallowGlobalVars )
|
|
{
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_GLOBAL_VARS_NOT_ALLOWED);
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
module->m_scriptGlobals.Allocate(count, false);
|
|
for( i = 0; i < count && !error; ++i )
|
|
{
|
|
ReadGlobalProperty();
|
|
}
|
|
|
|
// scriptFunctions[]
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
for( i = 0; i < count && !error; ++i )
|
|
{
|
|
size_t len = module->m_scriptFunctions.GetLength();
|
|
bool isNew, isExternal;
|
|
func = ReadFunction(isNew, true, true, true, &isExternal);
|
|
if( func == 0 )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
break;
|
|
}
|
|
|
|
// Is the function shared and was it created now?
|
|
if( func->IsShared() && len != module->m_scriptFunctions.GetLength() )
|
|
{
|
|
// If the function already existed in another module, then
|
|
// we need to replace it with previously existing one
|
|
for( asUINT n = 0; n < engine->scriptFunctions.GetLength() && !error; n++ )
|
|
{
|
|
asCScriptFunction *realFunc = engine->scriptFunctions[n];
|
|
if( realFunc &&
|
|
realFunc != func &&
|
|
realFunc->IsShared() &&
|
|
realFunc->nameSpace == func->nameSpace &&
|
|
realFunc->IsSignatureEqual(func) )
|
|
{
|
|
// Replace the recently created function with the pre-existing function
|
|
module->m_scriptFunctions[module->m_scriptFunctions.GetLength()-1] = realFunc;
|
|
realFunc->AddRefInternal();
|
|
savedFunctions[savedFunctions.GetLength()-1] = realFunc;
|
|
engine->RemoveScriptFunction(func);
|
|
|
|
// Insert the function in the dontTranslate array
|
|
dontTranslate.Insert(realFunc, true);
|
|
|
|
if (isExternal)
|
|
module->m_externalFunctions.PushLast(realFunc);
|
|
|
|
// Release the function, but make sure nothing else is released
|
|
func->id = 0;
|
|
if( func->scriptData )
|
|
func->scriptData->byteCode.SetLength(0);
|
|
func->ReleaseInternal();
|
|
func = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if an external shared func was really found
|
|
if (isExternal && func)
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_EXTERNAL_SHARED_s_NOT_FOUND, func->name.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());
|
|
error = true;
|
|
return asERROR;
|
|
}
|
|
}
|
|
|
|
// globalFunctions[]
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
for( i = 0; i < count && !error; ++i )
|
|
{
|
|
bool isNew;
|
|
func = ReadFunction(isNew, false, false);
|
|
if( func )
|
|
{
|
|
// All the global functions were already loaded while loading the scriptFunctions, here
|
|
// we're just re-reading the references to know which goes into the globalFunctions array
|
|
asASSERT( !isNew );
|
|
|
|
module->m_globalFunctions.Put(func);
|
|
}
|
|
else
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
|
|
if( error ) return asERROR;
|
|
|
|
// bindInformations[]
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
module->m_bindInformations.Allocate(count, false);
|
|
for( i = 0; i < count && !error; ++i )
|
|
{
|
|
sBindInfo *info = asNEW(sBindInfo);
|
|
if( info == 0 )
|
|
{
|
|
error = true;
|
|
return asOUT_OF_MEMORY;
|
|
}
|
|
|
|
bool isNew;
|
|
info->importedFunctionSignature = ReadFunction(isNew, false, false);
|
|
if( info->importedFunctionSignature == 0 )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
break;
|
|
}
|
|
|
|
if( engine->freeImportedFunctionIdxs.GetLength() )
|
|
{
|
|
int id = engine->freeImportedFunctionIdxs.PopLast();
|
|
info->importedFunctionSignature->id = int(FUNC_IMPORTED + id);
|
|
engine->importedFunctions[id] = info;
|
|
}
|
|
else
|
|
{
|
|
info->importedFunctionSignature->id = int(FUNC_IMPORTED + engine->importedFunctions.GetLength());
|
|
engine->importedFunctions.PushLast(info);
|
|
}
|
|
ReadString(&info->importFromModule);
|
|
info->boundFunctionId = -1;
|
|
module->m_bindInformations.PushLast(info);
|
|
}
|
|
|
|
if( error ) return asERROR;
|
|
|
|
// usedTypes[]
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
usedTypes.Allocate(count, false);
|
|
for( i = 0; i < count && !error; ++i )
|
|
{
|
|
asCTypeInfo *ti = ReadTypeInfo();
|
|
usedTypes.PushLast(ti);
|
|
}
|
|
|
|
// usedTypeIds[]
|
|
if( !error )
|
|
ReadUsedTypeIds();
|
|
|
|
// usedFunctions[]
|
|
if( !error )
|
|
ReadUsedFunctions();
|
|
|
|
// usedGlobalProperties[]
|
|
if( !error )
|
|
ReadUsedGlobalProps();
|
|
|
|
// usedStringConstants[]
|
|
if( !error )
|
|
ReadUsedStringConstants();
|
|
|
|
// usedObjectProperties
|
|
if( !error )
|
|
ReadUsedObjectProps();
|
|
|
|
// Validate the template types
|
|
if( !error )
|
|
{
|
|
for( i = 0; i < usedTypes.GetLength() && !error; i++ )
|
|
{
|
|
asCObjectType *ot = CastToObjectType(usedTypes[i]);
|
|
if( !ot ||
|
|
!(ot->flags & asOBJ_TEMPLATE) ||
|
|
!ot->beh.templateCallback )
|
|
continue;
|
|
|
|
bool dontGarbageCollect = false;
|
|
asCScriptFunction *callback = engine->scriptFunctions[ot->beh.templateCallback];
|
|
if( !engine->CallGlobalFunctionRetBool(ot, &dontGarbageCollect, callback->sysFuncIntf, callback) )
|
|
{
|
|
asCString sub = ot->templateSubTypes[0].Format(ot->nameSpace);
|
|
for( asUINT n = 1; n < ot->templateSubTypes.GetLength(); n++ )
|
|
{
|
|
sub += ",";
|
|
sub += ot->templateSubTypes[n].Format(ot->nameSpace);
|
|
}
|
|
asCString str;
|
|
str.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, ot->name.AddressOf(), sub.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
else
|
|
{
|
|
// If the callback said this template instance won't be garbage collected then remove the flag
|
|
if( dontGarbageCollect )
|
|
ot->flags &= ~asOBJ_GC;
|
|
}
|
|
}
|
|
}
|
|
engine->deferValidationOfTemplateTypes = false;
|
|
|
|
if( error ) return asERROR;
|
|
|
|
// Update the loaded bytecode to point to the correct types, property offsets,
|
|
// function ids, etc. This is basically a linking stage.
|
|
for( i = 0; i < module->m_scriptFunctions.GetLength() && !error; i++ )
|
|
if( module->m_scriptFunctions[i]->funcType == asFUNC_SCRIPT )
|
|
TranslateFunction(module->m_scriptFunctions[i]);
|
|
|
|
asCSymbolTable<asCGlobalProperty>::iterator globIt = module->m_scriptGlobals.List();
|
|
while( globIt && !error )
|
|
{
|
|
asCScriptFunction *initFunc = (*globIt)->GetInitFunc();
|
|
if( initFunc )
|
|
TranslateFunction(initFunc);
|
|
globIt++;
|
|
}
|
|
|
|
if( error ) return asERROR;
|
|
|
|
// Add references for all functions (except for the pre-existing shared code)
|
|
for( i = 0; i < module->m_scriptFunctions.GetLength(); i++ )
|
|
if( !dontTranslate.MoveTo(0, module->m_scriptFunctions[i]) )
|
|
module->m_scriptFunctions[i]->AddReferences();
|
|
|
|
globIt = module->m_scriptGlobals.List();
|
|
while( globIt )
|
|
{
|
|
asCScriptFunction *initFunc = (*globIt)->GetInitFunc();
|
|
if( initFunc )
|
|
initFunc->AddReferences();
|
|
globIt++;
|
|
}
|
|
return error ? asERROR : asSUCCESS;
|
|
}
|
|
|
|
void asCReader::ReadUsedStringConstants()
|
|
{
|
|
TimeIt("asCReader::ReadUsedStringConstants");
|
|
|
|
asCString str;
|
|
|
|
asUINT count;
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
|
|
if (count > 0 && engine->stringFactory == 0)
|
|
{
|
|
Error(TXT_STRINGS_NOT_RECOGNIZED);
|
|
return;
|
|
}
|
|
|
|
usedStringConstants.Allocate(count, false);
|
|
for( asUINT i = 0; i < count; ++i )
|
|
{
|
|
ReadString(&str);
|
|
usedStringConstants.PushLast(const_cast<void*>(engine->stringFactory->GetStringConstant(str.AddressOf(), (asUINT)str.GetLength())));
|
|
}
|
|
}
|
|
|
|
void asCReader::ReadUsedFunctions()
|
|
{
|
|
TimeIt("asCReader::ReadUsedFunctions");
|
|
|
|
asUINT count;
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
usedFunctions.SetLength(count);
|
|
if( usedFunctions.GetLength() != count )
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
return;
|
|
}
|
|
memset(usedFunctions.AddressOf(), 0, sizeof(asCScriptFunction *)*count);
|
|
|
|
for( asUINT n = 0; n < usedFunctions.GetLength(); n++ )
|
|
{
|
|
char c;
|
|
|
|
// Read the data to be able to uniquely identify the function
|
|
|
|
// Is the function from the module or the application?
|
|
ReadData(&c, 1);
|
|
|
|
if( c == 'n' )
|
|
{
|
|
// Null function pointer
|
|
usedFunctions[n] = 0;
|
|
}
|
|
else
|
|
{
|
|
asCScriptFunction func(engine, c == 'm' ? module : 0, asFUNC_DUMMY);
|
|
asCObjectType *parentClass = 0;
|
|
ReadFunctionSignature(&func, &parentClass);
|
|
if( error )
|
|
{
|
|
func.funcType = asFUNC_DUMMY;
|
|
return;
|
|
}
|
|
|
|
// Find the correct function
|
|
if( c == 'm' )
|
|
{
|
|
if( func.funcType == asFUNC_IMPORTED )
|
|
{
|
|
for( asUINT i = 0; i < module->m_bindInformations.GetLength(); i++ )
|
|
{
|
|
asCScriptFunction *f = module->m_bindInformations[i]->importedFunctionSignature;
|
|
if( func.objectType != f->objectType ||
|
|
func.funcType != f->funcType ||
|
|
func.nameSpace != f->nameSpace ||
|
|
!func.IsSignatureEqual(f) )
|
|
continue;
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
else if( func.funcType == asFUNC_FUNCDEF )
|
|
{
|
|
const asCArray<asCFuncdefType *> &funcs = module->m_funcDefs;
|
|
for( asUINT i = 0; i < funcs.GetLength(); i++ )
|
|
{
|
|
asCScriptFunction *f = funcs[i]->funcdef;
|
|
if( f == 0 ||
|
|
func.name != f->name ||
|
|
!func.IsSignatureExceptNameAndObjectTypeEqual(f) ||
|
|
funcs[i]->parentClass != parentClass )
|
|
continue;
|
|
|
|
asASSERT( f->objectType == 0 );
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: optimize: Global functions should be searched for in module->globalFunctions
|
|
// TODO: optimize: funcdefs should be searched for in module->funcDefs
|
|
// TODO: optimize: object methods should be searched for directly in the object type
|
|
for( asUINT i = 0; i < module->m_scriptFunctions.GetLength(); i++ )
|
|
{
|
|
asCScriptFunction *f = module->m_scriptFunctions[i];
|
|
if( func.objectType != f->objectType ||
|
|
func.funcType != f->funcType ||
|
|
func.nameSpace != f->nameSpace ||
|
|
!func.IsSignatureEqual(f) )
|
|
continue;
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (c == 's')
|
|
{
|
|
// Look for shared entities in the engine, as they may not necessarily be part
|
|
// of the scope of the module if they have been inhereted from other modules.
|
|
if (func.funcType == asFUNC_FUNCDEF)
|
|
{
|
|
const asCArray<asCFuncdefType *> &funcs = engine->funcDefs;
|
|
for (asUINT i = 0; i < funcs.GetLength(); i++)
|
|
{
|
|
asCScriptFunction *f = funcs[i]->funcdef;
|
|
if (f == 0 ||
|
|
func.name != f->name ||
|
|
!func.IsSignatureExceptNameAndObjectTypeEqual(f) ||
|
|
funcs[i]->parentClass != parentClass)
|
|
continue;
|
|
|
|
asASSERT(f->objectType == 0);
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++)
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[i];
|
|
if (f == 0 || !f->IsShared() ||
|
|
func.objectType != f->objectType ||
|
|
func.funcType != f->funcType ||
|
|
func.nameSpace != f->nameSpace ||
|
|
!func.IsSignatureEqual(f))
|
|
continue;
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asASSERT(c == 'a');
|
|
|
|
if( func.funcType == asFUNC_FUNCDEF )
|
|
{
|
|
// This is a funcdef (registered or shared)
|
|
const asCArray<asCFuncdefType *> &funcs = engine->funcDefs;
|
|
for( asUINT i = 0; i < funcs.GetLength(); i++ )
|
|
{
|
|
asCScriptFunction *f = funcs[i]->funcdef;
|
|
if( f == 0 || func.name != f->name || !func.IsSignatureExceptNameAndObjectTypeEqual(f) || funcs[i]->parentClass != parentClass )
|
|
continue;
|
|
|
|
asASSERT( f->objectType == 0 );
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
else if( func.name[0] == '$' )
|
|
{
|
|
// This is a special function
|
|
|
|
if( func.name == "$beh0" && func.objectType )
|
|
{
|
|
if (func.objectType->flags & asOBJ_TEMPLATE)
|
|
{
|
|
// Look for the matching constructor inside the factory stubs generated for the template instance
|
|
// See asCCompiler::PerformFunctionCall
|
|
for (asUINT i = 0; i < func.objectType->beh.constructors.GetLength(); i++)
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[func.objectType->beh.constructors[i]];
|
|
|
|
// Find the id of the real constructor and not the generated stub
|
|
asUINT id = 0;
|
|
asDWORD *bc = f->scriptData->byteCode.AddressOf();
|
|
while (bc)
|
|
{
|
|
if ((*(asBYTE*)bc) == asBC_CALLSYS)
|
|
{
|
|
id = asBC_INTARG(bc);
|
|
break;
|
|
}
|
|
bc += asBCTypeSize[asBCInfo[*(asBYTE*)bc].type];
|
|
}
|
|
|
|
f = engine->scriptFunctions[id];
|
|
if (f == 0 ||
|
|
!func.IsSignatureExceptNameAndObjectTypeEqual(f))
|
|
continue;
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( usedFunctions[n] == 0 )
|
|
{
|
|
// This is a class constructor, so we can search directly in the object type's constructors
|
|
for (asUINT i = 0; i < func.objectType->beh.constructors.GetLength(); i++)
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[func.objectType->beh.constructors[i]];
|
|
if (f == 0 ||
|
|
!func.IsSignatureExceptNameAndObjectTypeEqual(f))
|
|
continue;
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if( func.name == "$fact" || func.name == "$beh3" )
|
|
{
|
|
// This is a factory (or stub), so look for the function in the return type's factories
|
|
asCObjectType *objType = CastToObjectType(func.returnType.GetTypeInfo());
|
|
if( objType )
|
|
{
|
|
for( asUINT i = 0; i < objType->beh.factories.GetLength(); i++ )
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[objType->beh.factories[i]];
|
|
if( f == 0 ||
|
|
!func.IsSignatureExceptNameAndObjectTypeEqual(f) )
|
|
continue;
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if( func.name == "$list" )
|
|
{
|
|
// listFactory is used for both factory is global and returns a handle and constructor that is a method
|
|
asCObjectType *objType = func.objectType ? func.objectType : CastToObjectType(func.returnType.GetTypeInfo());
|
|
if( objType )
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[objType->beh.listFactory];
|
|
if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) )
|
|
usedFunctions[n] = f;
|
|
}
|
|
}
|
|
else if( func.name == "$beh2" )
|
|
{
|
|
// This is a destructor, so check the object type's destructor
|
|
asCObjectType *objType = func.objectType;
|
|
if( objType )
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[objType->beh.destruct];
|
|
if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) )
|
|
usedFunctions[n] = f;
|
|
}
|
|
}
|
|
else if( func.name == "$dlgte" )
|
|
{
|
|
// This is the delegate factory
|
|
asCScriptFunction *f = engine->registeredGlobalFuncs.GetFirst(engine->nameSpaces[0], DELEGATE_FACTORY);
|
|
asASSERT( f && func.IsSignatureEqual(f) );
|
|
usedFunctions[n] = f;
|
|
}
|
|
else
|
|
{
|
|
// Must match one of the above cases
|
|
asASSERT(false);
|
|
}
|
|
}
|
|
else if( func.objectType == 0 )
|
|
{
|
|
// This is a global function
|
|
const asCArray<asUINT> &funcs = engine->registeredGlobalFuncs.GetIndexes(func.nameSpace, func.name);
|
|
for( asUINT i = 0; i < funcs.GetLength(); i++ )
|
|
{
|
|
asCScriptFunction *f = engine->registeredGlobalFuncs.Get(funcs[i]);
|
|
if( f == 0 ||
|
|
!func.IsSignatureExceptNameAndObjectTypeEqual(f) )
|
|
continue;
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
else if( func.objectType )
|
|
{
|
|
// It is a class member, so we can search directly in the object type's members
|
|
// TODO: virtual function is different that implemented method
|
|
for( asUINT i = 0; i < func.objectType->methods.GetLength(); i++ )
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[func.objectType->methods[i]];
|
|
if( f == 0 ||
|
|
!func.IsSignatureEqual(f) )
|
|
continue;
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( usedFunctions[n] == 0 )
|
|
{
|
|
// TODO: clean up: This part of the code should never happen. All functions should
|
|
// be found in the above logic. The only valid reason to come here
|
|
// is if the bytecode is wrong and the function doesn't exist anyway.
|
|
// This loop is kept temporarily until we can be certain all scenarios
|
|
// are covered.
|
|
for( asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++ )
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[i];
|
|
if( f == 0 ||
|
|
func.objectType != f->objectType ||
|
|
func.nameSpace != f->nameSpace ||
|
|
!func.IsSignatureEqual(f) )
|
|
continue;
|
|
|
|
usedFunctions[n] = f;
|
|
break;
|
|
}
|
|
|
|
// No function is expected to be found
|
|
asASSERT(usedFunctions[n] == 0);
|
|
}
|
|
}
|
|
|
|
// Set the type to dummy so it won't try to release the id
|
|
func.funcType = asFUNC_DUMMY;
|
|
|
|
if( usedFunctions[n] == 0 )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCReader::ReadFunctionSignature(asCScriptFunction *func, asCObjectType **parentClass)
|
|
{
|
|
asUINT i, count;
|
|
asCDataType dt;
|
|
int num;
|
|
|
|
ReadString(&func->name);
|
|
if( func->name == DELEGATE_FACTORY )
|
|
{
|
|
// It's not necessary to read anymore, everything is known
|
|
asCScriptFunction *f = engine->registeredGlobalFuncs.GetFirst(engine->nameSpaces[0], DELEGATE_FACTORY);
|
|
asASSERT( f );
|
|
func->returnType = f->returnType;
|
|
func->parameterTypes = f->parameterTypes;
|
|
func->inOutFlags = f->inOutFlags;
|
|
func->funcType = f->funcType;
|
|
func->defaultArgs = f->defaultArgs;
|
|
func->nameSpace = f->nameSpace;
|
|
return;
|
|
}
|
|
|
|
ReadDataType(&func->returnType);
|
|
|
|
count = SanityCheck(ReadEncodedUInt(), 256);
|
|
func->parameterTypes.Allocate(count, false);
|
|
for( i = 0; i < count; ++i )
|
|
{
|
|
ReadDataType(&dt);
|
|
func->parameterTypes.PushLast(dt);
|
|
}
|
|
|
|
func->inOutFlags.SetLength(func->parameterTypes.GetLength());
|
|
if( func->inOutFlags.GetLength() != func->parameterTypes.GetLength() )
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
return;
|
|
}
|
|
memset(func->inOutFlags.AddressOf(), 0, sizeof(asETypeModifiers)*func->inOutFlags.GetLength());
|
|
if (func->parameterTypes.GetLength() > 0)
|
|
{
|
|
count = ReadEncodedUInt();
|
|
if (count > func->parameterTypes.GetLength())
|
|
{
|
|
// Cannot be more than the number of arguments
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
num = ReadEncodedUInt();
|
|
func->inOutFlags[i] = static_cast<asETypeModifiers>(num);
|
|
}
|
|
}
|
|
|
|
func->funcType = (asEFuncType)ReadEncodedUInt();
|
|
|
|
// Read the default args, from last to first
|
|
if (func->parameterTypes.GetLength() > 0)
|
|
{
|
|
count = ReadEncodedUInt();
|
|
if (count > func->parameterTypes.GetLength())
|
|
{
|
|
// Cannot be more than the number of arguments
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
if (count)
|
|
{
|
|
func->defaultArgs.SetLength(func->parameterTypes.GetLength());
|
|
if (func->defaultArgs.GetLength() != func->parameterTypes.GetLength())
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
return;
|
|
}
|
|
memset(func->defaultArgs.AddressOf(), 0, sizeof(asCString*)*func->defaultArgs.GetLength());
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
asCString *str = asNEW(asCString);
|
|
if (str == 0)
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
return;
|
|
}
|
|
func->defaultArgs[func->defaultArgs.GetLength() - 1 - i] = str;
|
|
ReadString(str);
|
|
}
|
|
}
|
|
}
|
|
|
|
func->objectType = CastToObjectType(ReadTypeInfo());
|
|
if( func->objectType )
|
|
{
|
|
func->objectType->AddRefInternal();
|
|
|
|
asBYTE b;
|
|
ReadData(&b, 1);
|
|
func->SetReadOnly((b & 1) ? true : false);
|
|
func->SetPrivate((b & 2) ? true : false);
|
|
func->SetProtected((b & 4) ? true : false);
|
|
func->nameSpace = func->objectType->nameSpace;
|
|
}
|
|
else
|
|
{
|
|
if (func->funcType == asFUNC_FUNCDEF)
|
|
{
|
|
asBYTE b;
|
|
ReadData(&b, 1);
|
|
if (b == 'n')
|
|
{
|
|
asCString ns;
|
|
ReadString(&ns);
|
|
func->nameSpace = engine->AddNameSpace(ns.AddressOf());
|
|
}
|
|
else if (b == 'o')
|
|
{
|
|
func->nameSpace = 0;
|
|
if (parentClass)
|
|
*parentClass = CastToObjectType(ReadTypeInfo());
|
|
else
|
|
error = true;
|
|
}
|
|
else
|
|
error = true;
|
|
}
|
|
else
|
|
{
|
|
asCString ns;
|
|
ReadString(&ns);
|
|
func->nameSpace = engine->AddNameSpace(ns.AddressOf());
|
|
}
|
|
}
|
|
}
|
|
|
|
asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool addToEngine, bool addToGC, bool *isExternal)
|
|
{
|
|
isNew = false;
|
|
if (isExternal) *isExternal = false;
|
|
if( error ) return 0;
|
|
|
|
char c;
|
|
ReadData(&c, 1);
|
|
|
|
if( c == '\0' )
|
|
{
|
|
// There is no function, so return a null pointer
|
|
return 0;
|
|
}
|
|
|
|
if( c == 'r' )
|
|
{
|
|
// This is a reference to a previously saved function
|
|
asUINT index = ReadEncodedUInt();
|
|
if( index < savedFunctions.GetLength() )
|
|
return savedFunctions[index];
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Load the new function
|
|
isNew = true;
|
|
asCScriptFunction *func = asNEW(asCScriptFunction)(engine,0,asFUNC_DUMMY);
|
|
if( func == 0 )
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
return 0;
|
|
}
|
|
savedFunctions.PushLast(func);
|
|
|
|
int i, count;
|
|
asCDataType dt;
|
|
int num;
|
|
|
|
asCObjectType *parentClass = 0;
|
|
ReadFunctionSignature(func, &parentClass);
|
|
if( error )
|
|
{
|
|
func->DestroyHalfCreated();
|
|
return 0;
|
|
}
|
|
|
|
if( func->funcType == asFUNC_SCRIPT )
|
|
{
|
|
// Skip this for external shared entities
|
|
if (module->m_externalTypes.IndexOf(func->objectType) >= 0)
|
|
{
|
|
// Replace with the real function from the existing entity
|
|
isNew = false;
|
|
|
|
asCObjectType *ot = func->objectType;
|
|
for (asUINT n = 0; n < ot->methods.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *func2 = engine->scriptFunctions[ot->methods[n]];
|
|
if (func2->funcType == asFUNC_VIRTUAL)
|
|
func2 = ot->virtualFunctionTable[func2->vfTableIdx];
|
|
|
|
if (func->IsSignatureEqual(func2))
|
|
{
|
|
func->DestroyHalfCreated();
|
|
|
|
// as this is an existing function it shouldn't be translated as if just loaded
|
|
dontTranslate.Insert(func2, true);
|
|
|
|
// update the saved functions for future references
|
|
savedFunctions[savedFunctions.GetLength() - 1] = func2;
|
|
|
|
// As it is an existing function it shouldn't be added to the module or the engine
|
|
return func2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char bits;
|
|
ReadData(&bits, 1);
|
|
func->SetShared((bits & 1) ? true : false);
|
|
func->SetExplicit((bits & 32) ? true : false);
|
|
func->dontCleanUpOnException = (bits & 2) ? true : false;
|
|
if ((bits & 4) && isExternal)
|
|
*isExternal = true;
|
|
|
|
// for external shared functions the rest is not needed
|
|
if (!(bits & 4))
|
|
{
|
|
func->AllocateScriptFunctionData();
|
|
if (func->scriptData == 0)
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
func->DestroyHalfCreated();
|
|
return 0;
|
|
}
|
|
|
|
if (addToGC && !addToModule)
|
|
engine->gc.AddScriptObjectToGC(func, &engine->functionBehaviours);
|
|
|
|
ReadByteCode(func);
|
|
|
|
func->scriptData->variableSpace = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
|
|
func->scriptData->objVariablesOnHeap = 0;
|
|
if (bits & 8)
|
|
{
|
|
count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
func->scriptData->objVariablePos.Allocate(count, false);
|
|
func->scriptData->objVariableTypes.Allocate(count, false);
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
func->scriptData->objVariableTypes.PushLast(ReadTypeInfo());
|
|
num = ReadEncodedUInt();
|
|
func->scriptData->objVariablePos.PushLast(num);
|
|
|
|
if (error)
|
|
{
|
|
// No need to continue (the error has already been reported before)
|
|
func->DestroyHalfCreated();
|
|
return 0;
|
|
}
|
|
}
|
|
if (count > 0)
|
|
func->scriptData->objVariablesOnHeap = SanityCheck(ReadEncodedUInt(), 10000);
|
|
|
|
int length = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
func->scriptData->objVariableInfo.SetLength(length);
|
|
for (i = 0; i < length; ++i)
|
|
{
|
|
func->scriptData->objVariableInfo[i].programPos = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
func->scriptData->objVariableInfo[i].variableOffset = SanityCheck(ReadEncodedInt(), 10000);
|
|
asEObjVarInfoOption option = (asEObjVarInfoOption)ReadEncodedUInt();
|
|
func->scriptData->objVariableInfo[i].option = option;
|
|
if (option != asOBJ_INIT &&
|
|
option != asOBJ_UNINIT &&
|
|
option != asBLOCK_BEGIN &&
|
|
option != asBLOCK_END &&
|
|
option != asOBJ_VARDECL)
|
|
{
|
|
error = true;
|
|
func->DestroyHalfCreated();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bits & 16)
|
|
{
|
|
// Read info on try/catch blocks
|
|
int length = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
func->scriptData->tryCatchInfo.SetLength(length);
|
|
for (i = 0; i < length; ++i)
|
|
{
|
|
// The program position must be adjusted to be in number of instructions
|
|
func->scriptData->tryCatchInfo[i].tryPos = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
func->scriptData->tryCatchInfo[i].catchPos = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
}
|
|
}
|
|
|
|
if (!noDebugInfo)
|
|
{
|
|
int length = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
func->scriptData->lineNumbers.SetLength(length);
|
|
if (int(func->scriptData->lineNumbers.GetLength()) != length)
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
func->DestroyHalfCreated();
|
|
return 0;
|
|
}
|
|
for (i = 0; i < length; ++i)
|
|
func->scriptData->lineNumbers[i] = ReadEncodedUInt();
|
|
|
|
// Read the array of script sections
|
|
length = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
func->scriptData->sectionIdxs.SetLength(length);
|
|
if (int(func->scriptData->sectionIdxs.GetLength()) != length)
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
func->DestroyHalfCreated();
|
|
return 0;
|
|
}
|
|
for (i = 0; i < length; ++i)
|
|
{
|
|
if ((i & 1) == 0)
|
|
func->scriptData->sectionIdxs[i] = ReadEncodedUInt();
|
|
else
|
|
{
|
|
asCString str;
|
|
ReadString(&str);
|
|
func->scriptData->sectionIdxs[i] = engine->GetScriptSectionNameIndex(str.AddressOf());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read the variable information
|
|
if (!noDebugInfo)
|
|
{
|
|
int length = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
func->scriptData->variables.Allocate(length, false);
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
asSScriptVariable *var = asNEW(asSScriptVariable);
|
|
if (var == 0)
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
func->DestroyHalfCreated();
|
|
return 0;
|
|
}
|
|
func->scriptData->variables.PushLast(var);
|
|
|
|
var->declaredAtProgramPos = ReadEncodedUInt();
|
|
var->stackOffset = SanityCheck(ReadEncodedInt(),10000);
|
|
ReadString(&var->name);
|
|
ReadDataType(&var->type);
|
|
|
|
if (error)
|
|
{
|
|
// No need to continue (the error has already been reported before)
|
|
func->DestroyHalfCreated();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read script section name
|
|
if (!noDebugInfo)
|
|
{
|
|
asCString name;
|
|
ReadString(&name);
|
|
func->scriptData->scriptSectionIdx = engine->GetScriptSectionNameIndex(name.AddressOf());
|
|
func->scriptData->declaredAt = ReadEncodedUInt();
|
|
}
|
|
|
|
// Read parameter names
|
|
if (!noDebugInfo)
|
|
{
|
|
asUINT countParam = asUINT(ReadEncodedUInt64());
|
|
if (countParam > func->parameterTypes.GetLength())
|
|
{
|
|
error = true;
|
|
func->DestroyHalfCreated();
|
|
return 0;
|
|
}
|
|
func->parameterNames.SetLength(countParam);
|
|
for (asUINT n = 0; n < countParam; n++)
|
|
ReadString(&func->parameterNames[n]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( func->funcType == asFUNC_VIRTUAL || func->funcType == asFUNC_INTERFACE )
|
|
{
|
|
func->vfTableIdx = ReadEncodedUInt();
|
|
}
|
|
else if( func->funcType == asFUNC_FUNCDEF )
|
|
{
|
|
asBYTE bits;
|
|
ReadData(&bits, 1);
|
|
if( bits & 1 )
|
|
func->SetShared(true);
|
|
if ((bits & 2) && isExternal)
|
|
*isExternal = true;
|
|
|
|
// The asCFuncdefType constructor adds itself to the func->funcdefType member
|
|
asCFuncdefType *fdt = asNEW(asCFuncdefType)(engine, func);
|
|
fdt->parentClass = parentClass;
|
|
}
|
|
|
|
// Methods loaded for shared objects, owned by other modules should not be created as new functions
|
|
if( func->objectType && func->objectType->module != module )
|
|
{
|
|
// Return the real function from the object
|
|
asCScriptFunction *realFunc = 0;
|
|
bool found = false;
|
|
if( func->funcType == asFUNC_SCRIPT )
|
|
{
|
|
realFunc = engine->scriptFunctions[func->objectType->beh.destruct];
|
|
if( realFunc && realFunc->funcType != asFUNC_VIRTUAL && func->IsSignatureEqual(realFunc) )
|
|
{
|
|
found = true;
|
|
}
|
|
for( asUINT n = 0; !found && n < func->objectType->beh.constructors.GetLength(); n++ )
|
|
{
|
|
realFunc = engine->scriptFunctions[func->objectType->beh.constructors[n]];
|
|
if( realFunc && realFunc->funcType != asFUNC_VIRTUAL && func->IsSignatureEqual(realFunc) )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
for( asUINT n = 0; !found && n < func->objectType->beh.factories.GetLength(); n++ )
|
|
{
|
|
realFunc = engine->scriptFunctions[func->objectType->beh.factories[n]];
|
|
if( realFunc && realFunc->funcType != asFUNC_VIRTUAL && func->IsSignatureEqual(realFunc) )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
for( asUINT n = 0; !found && n < func->objectType->methods.GetLength(); n++ )
|
|
{
|
|
realFunc = engine->scriptFunctions[func->objectType->methods[n]];
|
|
if( realFunc && realFunc->funcType == func->funcType && func->IsSignatureEqual(realFunc) )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
for( asUINT n = 0; !found && n < func->objectType->virtualFunctionTable.GetLength(); n++ )
|
|
{
|
|
realFunc = func->objectType->virtualFunctionTable[n];
|
|
if( realFunc && realFunc->funcType == func->funcType && func->IsSignatureEqual(realFunc) )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if( func->funcType == asFUNC_VIRTUAL || func->funcType == asFUNC_INTERFACE )
|
|
{
|
|
// If the loaded function is a virtual function, then look for the identical virtual function in the methods array
|
|
for( asUINT n = 0; n < func->objectType->methods.GetLength(); n++ )
|
|
{
|
|
realFunc = engine->scriptFunctions[func->objectType->methods[n]];
|
|
if( realFunc && realFunc->funcType == func->funcType && func->IsSignatureEqual(realFunc) )
|
|
{
|
|
asASSERT( func->vfTableIdx == realFunc->vfTableIdx );
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( found )
|
|
{
|
|
// as this is an existing function it shouldn't be translated as if just loaded
|
|
dontTranslate.Insert(realFunc, true);
|
|
|
|
// update the saved functions for future references
|
|
savedFunctions[savedFunctions.GetLength() - 1] = realFunc;
|
|
|
|
if( realFunc->funcType == asFUNC_VIRTUAL && addToModule )
|
|
{
|
|
// Virtual methods must be added to the module's script functions array,
|
|
// even if they are not owned by the module
|
|
module->m_scriptFunctions.PushLast(realFunc);
|
|
realFunc->AddRefInternal();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, func->objectType->GetName());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
savedFunctions.PopLast();
|
|
realFunc = 0;
|
|
}
|
|
|
|
// Destroy the newly created function instance since it has been replaced by an existing function
|
|
isNew = false;
|
|
func->DestroyHalfCreated();
|
|
|
|
// As it is an existing function it shouldn't be added to the module or the engine
|
|
return realFunc;
|
|
}
|
|
|
|
if( addToModule )
|
|
{
|
|
// The refCount is already 1
|
|
module->m_scriptFunctions.PushLast(func);
|
|
func->module = module;
|
|
}
|
|
if( addToEngine )
|
|
{
|
|
func->id = engine->GetNextScriptFunctionId();
|
|
engine->AddScriptFunction(func);
|
|
}
|
|
if( func->objectType )
|
|
func->ComputeSignatureId();
|
|
|
|
return func;
|
|
}
|
|
|
|
void asCReader::ReadTypeDeclaration(asCTypeInfo *type, int phase, bool *isExternal)
|
|
{
|
|
if( phase == 1 )
|
|
{
|
|
asASSERT(isExternal);
|
|
if (isExternal)
|
|
*isExternal = false;
|
|
|
|
// Read the initial attributes
|
|
ReadString(&type->name);
|
|
ReadData(&type->flags, 4);
|
|
type->size = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
asCString ns;
|
|
ReadString(&ns);
|
|
type->nameSpace = engine->AddNameSpace(ns.AddressOf());
|
|
|
|
// Verify that the flags match the asCTypeInfo
|
|
if ((CastToEnumType(type) && !(type->flags & asOBJ_ENUM)) ||
|
|
(CastToFuncdefType(type) && !(type->flags & asOBJ_FUNCDEF)) ||
|
|
(CastToObjectType(type) && !(type->flags & (asOBJ_REF | asOBJ_VALUE))))
|
|
{
|
|
error = true;
|
|
return;
|
|
}
|
|
|
|
// Reset the size of script classes, since it will be recalculated as properties are added
|
|
if( (type->flags & asOBJ_SCRIPT_OBJECT) && type->size != 0 )
|
|
type->size = sizeof(asCScriptObject);
|
|
|
|
asCObjectType *ot = CastToObjectType(type);
|
|
if (ot)
|
|
{
|
|
// Use the default script class behaviours
|
|
ot->beh = engine->scriptTypeBehaviours.beh;
|
|
ot->beh.construct = 0;
|
|
ot->beh.factory = 0;
|
|
ot->beh.constructors.PopLast(); // These will be read from the file
|
|
ot->beh.factories.PopLast(); // These will be read from the file
|
|
engine->scriptFunctions[ot->beh.addref]->AddRefInternal();
|
|
engine->scriptFunctions[ot->beh.release]->AddRefInternal();
|
|
engine->scriptFunctions[ot->beh.gcEnumReferences]->AddRefInternal();
|
|
engine->scriptFunctions[ot->beh.gcGetFlag]->AddRefInternal();
|
|
engine->scriptFunctions[ot->beh.gcGetRefCount]->AddRefInternal();
|
|
engine->scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRefInternal();
|
|
engine->scriptFunctions[ot->beh.gcSetFlag]->AddRefInternal();
|
|
engine->scriptFunctions[ot->beh.copy]->AddRefInternal();
|
|
// TODO: weak: Should not do this if the class has been declared with 'noweak'
|
|
engine->scriptFunctions[ot->beh.getWeakRefFlag]->AddRefInternal();
|
|
}
|
|
|
|
// external shared flag
|
|
if (type->flags & asOBJ_SHARED)
|
|
{
|
|
char c;
|
|
ReadData(&c, 1);
|
|
if (c == 'e')
|
|
*isExternal = true;
|
|
else if (c != ' ')
|
|
{
|
|
error = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if( phase == 2 )
|
|
{
|
|
// external shared types doesn't store this
|
|
if ((type->flags & asOBJ_SHARED) && module->m_externalTypes.IndexOf(type) >= 0)
|
|
return;
|
|
|
|
if( type->flags & asOBJ_ENUM )
|
|
{
|
|
asCEnumType *t = CastToEnumType(type);
|
|
int count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
bool sharedExists = existingShared.MoveTo(0, type);
|
|
if( !sharedExists )
|
|
{
|
|
t->enumValues.Allocate(count, false);
|
|
for( int n = 0; n < count; n++ )
|
|
{
|
|
asSEnumValue *e = asNEW(asSEnumValue);
|
|
if( e == 0 )
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
return;
|
|
}
|
|
ReadString(&e->name);
|
|
ReadData(&e->value, 4); // TODO: Should be encoded
|
|
t->enumValues.PushLast(e);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Verify that the enum values exists in the original
|
|
asCString name;
|
|
int value;
|
|
for( int n = 0; n < count; n++ )
|
|
{
|
|
ReadString(&name);
|
|
ReadData(&value, 4); // TODO: Should be encoded
|
|
bool found = false;
|
|
for( asUINT e = 0; e < t->enumValues.GetLength(); e++ )
|
|
{
|
|
if( t->enumValues[e]->name == name &&
|
|
t->enumValues[e]->value == value )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if( !found )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( type->flags & asOBJ_TYPEDEF )
|
|
{
|
|
asCTypedefType *td = CastToTypedefType(type);
|
|
asASSERT(td);
|
|
eTokenType t = (eTokenType)ReadEncodedUInt();
|
|
td->aliasForType = asCDataType::CreatePrimitive(t, false);
|
|
}
|
|
else
|
|
{
|
|
asCObjectType *ot = CastToObjectType(type);
|
|
asASSERT(ot);
|
|
|
|
// If the type is shared and pre-existing, we should just
|
|
// validate that the loaded methods match the original
|
|
bool sharedExists = existingShared.MoveTo(0, type);
|
|
if( sharedExists )
|
|
{
|
|
asCObjectType *dt = CastToObjectType(ReadTypeInfo());
|
|
if( ot->derivedFrom != dt )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ot->derivedFrom = CastToObjectType(ReadTypeInfo());
|
|
if( ot->derivedFrom )
|
|
ot->derivedFrom->AddRefInternal();
|
|
}
|
|
|
|
// interfaces[] / interfaceVFTOffsets[]
|
|
int size = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
if( sharedExists )
|
|
{
|
|
for( int n = 0; n < size; n++ )
|
|
{
|
|
asCObjectType *intf = CastToObjectType(ReadTypeInfo());
|
|
if (!ot->IsInterface())
|
|
ReadEncodedUInt();
|
|
|
|
if( !type->Implements(intf) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ot->interfaces.Allocate(size, false);
|
|
if( !ot->IsInterface() )
|
|
ot->interfaceVFTOffsets.Allocate(size, false);
|
|
for( int n = 0; n < size; n++ )
|
|
{
|
|
asCObjectType *intf = CastToObjectType(ReadTypeInfo());
|
|
ot->interfaces.PushLast(intf);
|
|
|
|
if (!ot->IsInterface())
|
|
{
|
|
asUINT offset = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
ot->interfaceVFTOffsets.PushLast(offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
// behaviours
|
|
if( !ot->IsInterface() && type->flags != asOBJ_TYPEDEF && type->flags != asOBJ_ENUM )
|
|
{
|
|
bool isNew;
|
|
asCScriptFunction *func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists);
|
|
if( sharedExists )
|
|
{
|
|
// Find the real function in the object, and update the savedFunctions array
|
|
asCScriptFunction *realFunc = engine->GetScriptFunction(ot->beh.destruct);
|
|
if( (realFunc == 0 && func == 0) || realFunc->IsSignatureEqual(func) )
|
|
{
|
|
// If the function is not the last, then the substitution has already occurred before
|
|
if( func && savedFunctions[savedFunctions.GetLength()-1] == func )
|
|
savedFunctions[savedFunctions.GetLength()-1] = realFunc;
|
|
}
|
|
else
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
if( func )
|
|
{
|
|
if( isNew )
|
|
{
|
|
// Destroy the function without releasing any references
|
|
func->id = 0;
|
|
func->scriptData->byteCode.SetLength(0);
|
|
func->ReleaseInternal();
|
|
}
|
|
dontTranslate.Insert(realFunc, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( func )
|
|
{
|
|
ot->beh.destruct = func->id;
|
|
func->AddRefInternal();
|
|
}
|
|
else
|
|
ot->beh.destruct = 0;
|
|
}
|
|
|
|
size = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
for( int n = 0; n < size; n++ )
|
|
{
|
|
func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists);
|
|
if( func )
|
|
{
|
|
if( sharedExists )
|
|
{
|
|
// Find the real function in the object, and update the savedFunctions array
|
|
bool found = false;
|
|
for( asUINT f = 0; f < ot->beh.constructors.GetLength(); f++ )
|
|
{
|
|
asCScriptFunction *realFunc = engine->GetScriptFunction(ot->beh.constructors[f]);
|
|
if( realFunc->IsSignatureEqual(func) )
|
|
{
|
|
// If the function is not the last, then the substitution has already occurred before
|
|
if( savedFunctions[savedFunctions.GetLength()-1] == func )
|
|
savedFunctions[savedFunctions.GetLength()-1] = realFunc;
|
|
found = true;
|
|
dontTranslate.Insert(realFunc, true);
|
|
break;
|
|
}
|
|
}
|
|
if( !found )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
if( isNew )
|
|
{
|
|
// Destroy the function without releasing any references
|
|
func->id = 0;
|
|
func->scriptData->byteCode.SetLength(0);
|
|
func->ReleaseInternal();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ot->beh.constructors.PushLast(func->id);
|
|
func->AddRefInternal();
|
|
|
|
if( func->parameterTypes.GetLength() == 0 )
|
|
ot->beh.construct = func->id;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
|
|
func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists);
|
|
if( func )
|
|
{
|
|
if( sharedExists )
|
|
{
|
|
// Find the real function in the object, and update the savedFunctions array
|
|
bool found = false;
|
|
for( asUINT f = 0; f < ot->beh.factories.GetLength(); f++ )
|
|
{
|
|
asCScriptFunction *realFunc = engine->GetScriptFunction(ot->beh.factories[f]);
|
|
if( realFunc->IsSignatureEqual(func) )
|
|
{
|
|
// If the function is not the last, then the substitution has already occurred before
|
|
if( savedFunctions[savedFunctions.GetLength()-1] == func )
|
|
savedFunctions[savedFunctions.GetLength()-1] = realFunc;
|
|
found = true;
|
|
dontTranslate.Insert(realFunc, true);
|
|
break;
|
|
}
|
|
}
|
|
if( !found )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
if( isNew )
|
|
{
|
|
// Destroy the function without releasing any references
|
|
func->id = 0;
|
|
func->scriptData->byteCode.SetLength(0);
|
|
func->ReleaseInternal();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ot->beh.factories.PushLast(func->id);
|
|
func->AddRefInternal();
|
|
|
|
if( func->parameterTypes.GetLength() == 0 )
|
|
ot->beh.factory = func->id;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
}
|
|
}
|
|
|
|
// methods[]
|
|
size = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
int n;
|
|
for( n = 0; n < size; n++ )
|
|
{
|
|
bool isNew;
|
|
asCScriptFunction *func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists);
|
|
if( func )
|
|
{
|
|
if( sharedExists )
|
|
{
|
|
// Find the real function in the object, and update the savedFunctions array
|
|
bool found = false;
|
|
for( asUINT f = 0; f < ot->methods.GetLength(); f++ )
|
|
{
|
|
asCScriptFunction *realFunc = engine->GetScriptFunction(ot->methods[f]);
|
|
if( realFunc->IsSignatureEqual(func) )
|
|
{
|
|
// If the function is not the last, then the substitution has already occurred before
|
|
if( savedFunctions[savedFunctions.GetLength()-1] == func )
|
|
savedFunctions[savedFunctions.GetLength()-1] = realFunc;
|
|
found = true;
|
|
dontTranslate.Insert(realFunc, true);
|
|
break;
|
|
}
|
|
}
|
|
if( !found )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
if( isNew )
|
|
{
|
|
// Destroy the function without releasing any references
|
|
if( func->id == func->signatureId )
|
|
engine->signatureIds.RemoveValue(func);
|
|
func->id = 0;
|
|
if( func->scriptData )
|
|
func->scriptData->byteCode.SetLength(0);
|
|
func->ReleaseInternal();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the method is the assignment operator we need to replace the default implementation
|
|
if( func->name == "opAssign" && func->parameterTypes.GetLength() == 1 &&
|
|
func->parameterTypes[0].GetTypeInfo() == func->objectType &&
|
|
(func->inOutFlags[0] & asTM_INREF) )
|
|
{
|
|
engine->scriptFunctions[ot->beh.copy]->ReleaseInternal();
|
|
ot->beh.copy = func->id;
|
|
func->AddRefInternal();
|
|
}
|
|
|
|
ot->methods.PushLast(func->id);
|
|
func->AddRefInternal();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
}
|
|
|
|
// virtualFunctionTable[]
|
|
size = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
for( n = 0; n < size; n++ )
|
|
{
|
|
bool isNew;
|
|
asCScriptFunction *func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists);
|
|
if( func )
|
|
{
|
|
if( sharedExists )
|
|
{
|
|
// Find the real function in the object, and update the savedFunctions array
|
|
bool found = false;
|
|
for( asUINT f = 0; f < ot->virtualFunctionTable.GetLength(); f++ )
|
|
{
|
|
asCScriptFunction *realFunc = ot->virtualFunctionTable[f];
|
|
if( realFunc->IsSignatureEqual(func) )
|
|
{
|
|
// If the function is not the last, then the substitution has already occurred before
|
|
if( savedFunctions[savedFunctions.GetLength()-1] == func )
|
|
savedFunctions[savedFunctions.GetLength()-1] = realFunc;
|
|
found = true;
|
|
dontTranslate.Insert(realFunc, true);
|
|
break;
|
|
}
|
|
}
|
|
if( !found )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
if( isNew )
|
|
{
|
|
// Destroy the function without releasing any references
|
|
func->id = 0;
|
|
if( func->scriptData )
|
|
func->scriptData->byteCode.SetLength(0);
|
|
func->ReleaseInternal();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ot->virtualFunctionTable.PushLast(func);
|
|
func->AddRefInternal();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( phase == 3 )
|
|
{
|
|
// external shared types doesn't store this
|
|
if ((type->flags & asOBJ_SHARED) && module->m_externalTypes.IndexOf(type) >= 0)
|
|
return;
|
|
|
|
asCObjectType *ot = CastToObjectType(type);
|
|
|
|
// This is only done for object types
|
|
asASSERT(ot);
|
|
|
|
// properties[]
|
|
asUINT size = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
for( asUINT n = 0; n < size; n++ )
|
|
ReadObjectProperty(ot);
|
|
}
|
|
}
|
|
|
|
asWORD asCReader::ReadEncodedUInt16()
|
|
{
|
|
asDWORD dw = ReadEncodedUInt();
|
|
if( (dw>>16) != 0 && (dw>>16) != 0xFFFF )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
|
|
return asWORD(dw & 0xFFFF);
|
|
}
|
|
|
|
asUINT asCReader::ReadEncodedUInt()
|
|
{
|
|
asQWORD qw = ReadEncodedUInt64();
|
|
if( (qw>>32) != 0 && (qw>>32) != 0xFFFFFFFF )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
|
|
return asUINT(qw & 0xFFFFFFFFu);
|
|
}
|
|
|
|
int asCReader::ReadEncodedInt()
|
|
{
|
|
return int(ReadEncodedUInt());
|
|
}
|
|
|
|
asQWORD asCReader::ReadEncodedUInt64()
|
|
{
|
|
asQWORD i = 0;
|
|
asBYTE b = 0xFF; // set to 0xFF to better catch if the stream doesn't update the value
|
|
ReadData(&b, 1);
|
|
bool isNegative = ( b & 0x80 ) ? true : false;
|
|
b &= 0x7F;
|
|
|
|
if( (b & 0x7F) == 0x7F )
|
|
{
|
|
ReadData(&b, 1); i = asQWORD(b) << 56;
|
|
ReadData(&b, 1); i += asQWORD(b) << 48;
|
|
ReadData(&b, 1); i += asQWORD(b) << 40;
|
|
ReadData(&b, 1); i += asQWORD(b) << 32;
|
|
ReadData(&b, 1); i += asUINT(b) << 24;
|
|
ReadData(&b, 1); i += asUINT(b) << 16;
|
|
ReadData(&b, 1); i += asUINT(b) << 8;
|
|
ReadData(&b, 1); i += b;
|
|
}
|
|
else if( (b & 0x7E) == 0x7E )
|
|
{
|
|
i = asQWORD(b & 0x01) << 48;
|
|
ReadData(&b, 1); i += asQWORD(b) << 40;
|
|
ReadData(&b, 1); i += asQWORD(b) << 32;
|
|
ReadData(&b, 1); i += asUINT(b) << 24;
|
|
ReadData(&b, 1); i += asUINT(b) << 16;
|
|
ReadData(&b, 1); i += asUINT(b) << 8;
|
|
ReadData(&b, 1); i += b;
|
|
}
|
|
else if( (b & 0x7C) == 0x7C )
|
|
{
|
|
i = asQWORD(b & 0x03) << 40;
|
|
ReadData(&b, 1); i += asQWORD(b) << 32;
|
|
ReadData(&b, 1); i += asUINT(b) << 24;
|
|
ReadData(&b, 1); i += asUINT(b) << 16;
|
|
ReadData(&b, 1); i += asUINT(b) << 8;
|
|
ReadData(&b, 1); i += b;
|
|
}
|
|
else if( (b & 0x78) == 0x78 )
|
|
{
|
|
i = asQWORD(b & 0x07) << 32;
|
|
ReadData(&b, 1); i += asUINT(b) << 24;
|
|
ReadData(&b, 1); i += asUINT(b) << 16;
|
|
ReadData(&b, 1); i += asUINT(b) << 8;
|
|
ReadData(&b, 1); i += b;
|
|
}
|
|
else if( (b & 0x70) == 0x70 )
|
|
{
|
|
i = asUINT(b & 0x0F) << 24;
|
|
ReadData(&b, 1); i += asUINT(b) << 16;
|
|
ReadData(&b, 1); i += asUINT(b) << 8;
|
|
ReadData(&b, 1); i += b;
|
|
}
|
|
else if( (b & 0x60) == 0x60 )
|
|
{
|
|
i = asUINT(b & 0x1F) << 16;
|
|
ReadData(&b, 1); i += asUINT(b) << 8;
|
|
ReadData(&b, 1); i += b;
|
|
}
|
|
else if( (b & 0x40) == 0x40 )
|
|
{
|
|
i = asUINT(b & 0x3F) << 8;
|
|
ReadData(&b, 1); i += b;
|
|
}
|
|
else
|
|
{
|
|
i = b;
|
|
}
|
|
if( isNegative )
|
|
i = (asQWORD)(-asINT64(i));
|
|
|
|
return i;
|
|
}
|
|
|
|
asUINT asCReader::SanityCheck(asUINT val, asUINT max)
|
|
{
|
|
if (val > max)
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
|
|
// Return 0 as default value
|
|
return 0;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
int asCReader::SanityCheck(int val, asUINT max)
|
|
{
|
|
if (val > int(max) || val < -int(max))
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
|
|
// Return 0 as default value
|
|
return 0;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
void asCReader::ReadString(asCString* str)
|
|
{
|
|
asUINT len = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
if( len & 1 )
|
|
{
|
|
asUINT idx = len/2;
|
|
if( idx < savedStrings.GetLength() )
|
|
*str = savedStrings[idx];
|
|
else
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
else if( len > 0 )
|
|
{
|
|
len /= 2;
|
|
str->SetLength(len);
|
|
int r = stream->Read(str->AddressOf(), len);
|
|
if (r < 0)
|
|
Error(TXT_UNEXPECTED_END_OF_FILE);
|
|
|
|
savedStrings.PushLast(*str);
|
|
}
|
|
else
|
|
str->SetLength(0);
|
|
}
|
|
|
|
void asCReader::ReadGlobalProperty()
|
|
{
|
|
asCString name;
|
|
asCDataType type;
|
|
|
|
ReadString(&name);
|
|
|
|
asCString ns;
|
|
ReadString(&ns);
|
|
asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf());
|
|
|
|
ReadDataType(&type);
|
|
|
|
asCGlobalProperty *prop = module->AllocateGlobalProperty(name.AddressOf(), type, nameSpace);
|
|
|
|
// Read the initialization function
|
|
bool isNew;
|
|
// Do not add the function to the GC at this time. It will
|
|
// only be added to the GC when the module releases the property
|
|
asCScriptFunction *func = ReadFunction(isNew, false, true, false);
|
|
if( func )
|
|
{
|
|
// Make sure the function knows it is owned by the module
|
|
func->module = module;
|
|
|
|
prop->SetInitFunc(func);
|
|
func->ReleaseInternal();
|
|
}
|
|
}
|
|
|
|
void asCReader::ReadObjectProperty(asCObjectType *ot)
|
|
{
|
|
asCString name;
|
|
ReadString(&name);
|
|
asCDataType dt;
|
|
ReadDataType(&dt);
|
|
int flags = ReadEncodedUInt();
|
|
bool isPrivate = (flags & 1) ? true : false;
|
|
bool isProtected = (flags & 2) ? true : false;
|
|
bool isInherited = (flags & 4) ? true : false;
|
|
|
|
// TODO: shared: If the type is shared and pre-existing, we should just
|
|
// validate that the loaded methods match the original
|
|
if( !existingShared.MoveTo(0, ot) )
|
|
ot->AddPropertyToClass(name, dt, isPrivate, isProtected, isInherited);
|
|
}
|
|
|
|
void asCReader::ReadDataType(asCDataType *dt)
|
|
{
|
|
// Check if this is a previously used type
|
|
asUINT idx = ReadEncodedUInt();
|
|
if( idx != 0 )
|
|
{
|
|
// Get the datatype from the cache
|
|
*dt = savedDataTypes[idx-1];
|
|
return;
|
|
}
|
|
|
|
// Read the type definition
|
|
eTokenType tokenType = (eTokenType)ReadEncodedUInt();
|
|
|
|
// Reserve a spot in the savedDataTypes
|
|
asUINT saveSlot = savedDataTypes.GetLength();
|
|
savedDataTypes.PushLast(asCDataType());
|
|
|
|
// Read the datatype for the first time
|
|
asCTypeInfo *ti = 0;
|
|
if( tokenType == ttIdentifier )
|
|
ti = ReadTypeInfo();
|
|
|
|
// Read type flags as a bitmask
|
|
// Endian-safe code
|
|
bool isObjectHandle, isHandleToConst, isReference, isReadOnly;
|
|
char b = 0;
|
|
ReadData(&b, 1);
|
|
LOAD_FROM_BIT(isObjectHandle, b, 0);
|
|
LOAD_FROM_BIT(isHandleToConst, b, 1);
|
|
LOAD_FROM_BIT(isReference, b, 2);
|
|
LOAD_FROM_BIT(isReadOnly, b, 3);
|
|
|
|
if( tokenType == ttIdentifier )
|
|
*dt = asCDataType::CreateType(ti, false);
|
|
else
|
|
*dt = asCDataType::CreatePrimitive(tokenType, false);
|
|
if( isObjectHandle )
|
|
{
|
|
dt->MakeReadOnly(isHandleToConst ? true : false);
|
|
|
|
// Here we must allow a scoped type to be a handle
|
|
// e.g. if the datatype is for a system function
|
|
dt->MakeHandle(true, true);
|
|
}
|
|
dt->MakeReadOnly(isReadOnly ? true : false);
|
|
dt->MakeReference(isReference ? true : false);
|
|
|
|
// Update the previously saved slot
|
|
savedDataTypes[saveSlot] = *dt;
|
|
}
|
|
|
|
asCTypeInfo* asCReader::ReadTypeInfo()
|
|
{
|
|
asCTypeInfo *ot = 0;
|
|
char ch;
|
|
ReadData(&ch, 1);
|
|
if( ch == 'a' )
|
|
{
|
|
// Read the name of the template type
|
|
asCString typeName, ns;
|
|
ReadString(&typeName);
|
|
ReadString(&ns);
|
|
asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf());
|
|
|
|
asCTypeInfo *tmp = engine->GetRegisteredType(typeName.AddressOf(), nameSpace);
|
|
asCObjectType *tmpl = CastToObjectType(tmp);
|
|
if( tmpl == 0 )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_TEMPLATE_TYPE_s_DOESNT_EXIST, typeName.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
|
|
asUINT numSubTypes = SanityCheck(ReadEncodedUInt(), 100);
|
|
asCArray<asCDataType> subTypes;
|
|
for( asUINT n = 0; n < numSubTypes; n++ )
|
|
{
|
|
ReadData(&ch, 1);
|
|
if( ch == 's' )
|
|
{
|
|
asCDataType dt;
|
|
ReadDataType(&dt);
|
|
subTypes.PushLast(dt);
|
|
}
|
|
else
|
|
{
|
|
eTokenType tokenType = (eTokenType)ReadEncodedUInt();
|
|
asCDataType dt = asCDataType::CreatePrimitive(tokenType, false);
|
|
subTypes.PushLast(dt);
|
|
}
|
|
}
|
|
|
|
// Return the actual template if the subtypes are the template's dummy types
|
|
if( tmpl->templateSubTypes == subTypes )
|
|
ot = tmpl;
|
|
else
|
|
{
|
|
// Get the template instance type based on the loaded subtypes
|
|
ot = engine->GetTemplateInstanceType(tmpl, subTypes, module);
|
|
}
|
|
|
|
if( ot == 0 )
|
|
{
|
|
// Show all subtypes in error message
|
|
asCString sub = subTypes[0].Format(nameSpace);
|
|
for( asUINT n = 1; n < subTypes.GetLength(); n++ )
|
|
{
|
|
sub += ",";
|
|
sub += subTypes[n].Format(nameSpace);
|
|
}
|
|
asCString str;
|
|
str.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, typeName.AddressOf(), sub.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
}
|
|
else if( ch == 'l' )
|
|
{
|
|
asCObjectType *st = CastToObjectType(ReadTypeInfo());
|
|
if( st == 0 || st->beh.listFactory == 0 )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
ot = engine->GetListPatternType(st->beh.listFactory);
|
|
}
|
|
else if( ch == 's' )
|
|
{
|
|
// Read the name of the template subtype
|
|
asCString typeName;
|
|
ReadString(&typeName);
|
|
|
|
// Find the template subtype
|
|
ot = 0;
|
|
for( asUINT n = 0; n < engine->templateSubTypes.GetLength(); n++ )
|
|
{
|
|
if( engine->templateSubTypes[n] && engine->templateSubTypes[n]->name == typeName )
|
|
{
|
|
ot = engine->templateSubTypes[n];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( ot == 0 )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_TEMPLATE_SUBTYPE_s_DOESNT_EXIST, typeName.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
}
|
|
else if( ch == 'o' )
|
|
{
|
|
// Read the object type name
|
|
asCString typeName, ns;
|
|
ReadString(&typeName);
|
|
ReadString(&ns);
|
|
asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf());
|
|
|
|
if( typeName.GetLength() && typeName != "$obj" && typeName != "$func" )
|
|
{
|
|
// Find the object type
|
|
ot = module->GetType(typeName.AddressOf(), nameSpace);
|
|
if (!ot)
|
|
ot = engine->GetRegisteredType(typeName.AddressOf(), nameSpace);
|
|
|
|
if( ot == 0 )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_OBJECT_TYPE_s_DOESNT_EXIST, typeName.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
}
|
|
else if( typeName == "$obj" )
|
|
{
|
|
ot = &engine->scriptTypeBehaviours;
|
|
}
|
|
else if( typeName == "$func" )
|
|
{
|
|
ot = &engine->functionBehaviours;
|
|
}
|
|
else
|
|
asASSERT( false );
|
|
}
|
|
else if (ch == 'c')
|
|
{
|
|
// Read the object type name
|
|
asCString typeName, ns;
|
|
ReadString(&typeName);
|
|
|
|
// Read the parent class
|
|
asCObjectType *parentClass = CastToObjectType(ReadTypeInfo());
|
|
if (parentClass == 0)
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
|
|
// Find the child type in the parentClass
|
|
for (asUINT n = 0; n < parentClass->childFuncDefs.GetLength(); n++)
|
|
{
|
|
if (parentClass->childFuncDefs[n]->name == typeName)
|
|
ot = parentClass->childFuncDefs[n];
|
|
}
|
|
|
|
if (ot == 0)
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_OBJECT_TYPE_s_DOESNT_EXIST, typeName.AddressOf());
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No object type
|
|
asASSERT( ch == '\0' || error );
|
|
ot = 0;
|
|
}
|
|
|
|
return ot;
|
|
}
|
|
|
|
void asCReader::ReadByteCode(asCScriptFunction *func)
|
|
{
|
|
asASSERT( func->scriptData );
|
|
|
|
// Read number of instructions
|
|
asUINT total, numInstructions;
|
|
total = numInstructions = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
|
|
// Reserve some space for the instructions
|
|
func->scriptData->byteCode.AllocateNoConstruct(numInstructions, false);
|
|
|
|
asUINT pos = 0;
|
|
while( numInstructions )
|
|
{
|
|
asBYTE b;
|
|
ReadData(&b, 1);
|
|
|
|
// Allocate the space for the instruction
|
|
asUINT len = asBCTypeSize[asBCInfo[b].type];
|
|
asUINT newSize = asUINT(func->scriptData->byteCode.GetLength()) + len;
|
|
if( func->scriptData->byteCode.GetCapacity() < newSize )
|
|
{
|
|
// Determine the average size of the loaded instructions and re-estimate the final size
|
|
asUINT size = asUINT(float(newSize) / (total - numInstructions) * total) + 1;
|
|
func->scriptData->byteCode.AllocateNoConstruct(size, true);
|
|
}
|
|
if( !func->scriptData->byteCode.SetLengthNoConstruct(newSize) )
|
|
{
|
|
// Out of memory
|
|
error = true;
|
|
return;
|
|
}
|
|
|
|
asDWORD *bc = func->scriptData->byteCode.AddressOf() + pos;
|
|
pos += len;
|
|
|
|
switch( asBCInfo[b].type )
|
|
{
|
|
case asBCTYPE_NO_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
bc++;
|
|
}
|
|
break;
|
|
case asBCTYPE_W_ARG:
|
|
case asBCTYPE_wW_ARG:
|
|
case asBCTYPE_rW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
|
|
// Read the argument
|
|
asWORD w = ReadEncodedUInt16();
|
|
*(((asWORD*)bc)+1) = w;
|
|
|
|
bc++;
|
|
}
|
|
break;
|
|
case asBCTYPE_rW_DW_ARG:
|
|
case asBCTYPE_wW_DW_ARG:
|
|
case asBCTYPE_W_DW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
|
|
// Read the word argument
|
|
asWORD w = ReadEncodedUInt16();
|
|
*(((asWORD*)bc)+1) = w;
|
|
bc++;
|
|
|
|
// Read the dword argument
|
|
*bc++ = ReadEncodedUInt();
|
|
}
|
|
break;
|
|
case asBCTYPE_DW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
bc++;
|
|
|
|
// Read the argument
|
|
*bc++ = ReadEncodedUInt();
|
|
}
|
|
break;
|
|
case asBCTYPE_DW_DW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
bc++;
|
|
|
|
// Read the first argument
|
|
*bc++ = ReadEncodedUInt();
|
|
|
|
// Read the second argument
|
|
*bc++ = ReadEncodedUInt();
|
|
}
|
|
break;
|
|
case asBCTYPE_wW_rW_rW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
|
|
// Read the first argument
|
|
asWORD w = ReadEncodedUInt16();
|
|
*(((asWORD*)bc)+1) = w;
|
|
bc++;
|
|
|
|
// Read the second argument
|
|
w = ReadEncodedUInt16();
|
|
*(asWORD*)bc = w;
|
|
|
|
// Read the third argument
|
|
w = ReadEncodedUInt16();
|
|
*(((asWORD*)bc)+1) = w;
|
|
|
|
bc++;
|
|
}
|
|
break;
|
|
case asBCTYPE_wW_rW_ARG:
|
|
case asBCTYPE_rW_rW_ARG:
|
|
case asBCTYPE_wW_W_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
|
|
// Read the first argument
|
|
asWORD w = ReadEncodedUInt16();
|
|
*(((asWORD*)bc)+1) = w;
|
|
bc++;
|
|
|
|
// Read the second argument
|
|
w = ReadEncodedUInt16();
|
|
*(asWORD*)bc = w;
|
|
|
|
bc++;
|
|
}
|
|
break;
|
|
case asBCTYPE_wW_rW_DW_ARG:
|
|
case asBCTYPE_rW_W_DW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
|
|
// Read the first argument
|
|
asWORD w = ReadEncodedUInt16();
|
|
*(((asWORD*)bc)+1) = w;
|
|
bc++;
|
|
|
|
// Read the second argument
|
|
w = ReadEncodedUInt16();
|
|
*(asWORD*)bc = w;
|
|
bc++;
|
|
|
|
// Read the third argument
|
|
asDWORD dw = ReadEncodedUInt();
|
|
*bc++ = dw;
|
|
}
|
|
break;
|
|
case asBCTYPE_QW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
bc++;
|
|
|
|
// Read the argument
|
|
asQWORD qw = ReadEncodedUInt64();
|
|
*(asQWORD*)bc = qw;
|
|
bc += 2;
|
|
}
|
|
break;
|
|
case asBCTYPE_QW_DW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
bc++;
|
|
|
|
// Read the first argument
|
|
asQWORD qw = ReadEncodedUInt64();
|
|
*(asQWORD*)bc = qw;
|
|
bc += 2;
|
|
|
|
// Read the second argument
|
|
asDWORD dw = ReadEncodedUInt();
|
|
*bc++ = dw;
|
|
}
|
|
break;
|
|
case asBCTYPE_rW_QW_ARG:
|
|
case asBCTYPE_wW_QW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
|
|
// Read the first argument
|
|
asWORD w = ReadEncodedUInt16();
|
|
*(((asWORD*)bc)+1) = w;
|
|
bc++;
|
|
|
|
// Read the argument
|
|
asQWORD qw = ReadEncodedUInt64();
|
|
*(asQWORD*)bc = qw;
|
|
bc += 2;
|
|
}
|
|
break;
|
|
case asBCTYPE_rW_DW_DW_ARG:
|
|
{
|
|
*(asBYTE*)(bc) = b;
|
|
|
|
// Read the 1st argument
|
|
asWORD w = ReadEncodedUInt16();
|
|
*(((asWORD*)bc)+1) = w;
|
|
bc++;
|
|
|
|
// Read the 2nd argument
|
|
*bc++ = ReadEncodedUInt();
|
|
|
|
// Read the 3rd argument
|
|
*bc++ = ReadEncodedUInt();
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// This should never happen
|
|
asASSERT(false);
|
|
|
|
// Read the next 3 bytes
|
|
asDWORD c; asBYTE t;
|
|
#if defined(AS_BIG_ENDIAN)
|
|
c = b << 24;
|
|
ReadData(&t, 1); c += t << 16;
|
|
ReadData(&t, 1); c += t << 8;
|
|
ReadData(&t, 1); c += t;
|
|
#else
|
|
c = b;
|
|
ReadData(&t, 1); c += t << 8;
|
|
ReadData(&t, 1); c += t << 16;
|
|
ReadData(&t, 1); c += t << 24;
|
|
#endif
|
|
|
|
*bc++ = c;
|
|
c = *(asBYTE*)&c;
|
|
|
|
// Read the bc as is
|
|
for( int n = 1; n < asBCTypeSize[asBCInfo[c].type]; n++ )
|
|
ReadData(&*bc++, 4);
|
|
}
|
|
}
|
|
|
|
numInstructions--;
|
|
}
|
|
|
|
// Correct the final size in case we over-estimated it
|
|
func->scriptData->byteCode.SetLengthNoConstruct(pos);
|
|
}
|
|
|
|
void asCReader::ReadUsedTypeIds()
|
|
{
|
|
TimeIt("asCReader::ReadUsedTypeIds");
|
|
|
|
asUINT count = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
usedTypeIds.Allocate(count, false);
|
|
for( asUINT n = 0; n < count; n++ )
|
|
{
|
|
asCDataType dt;
|
|
ReadDataType(&dt);
|
|
usedTypeIds.PushLast(engine->GetTypeIdFromDataType(dt));
|
|
}
|
|
}
|
|
|
|
void asCReader::ReadUsedGlobalProps()
|
|
{
|
|
TimeIt("asCReader::ReadUsedGlobalProps");
|
|
|
|
int c = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
|
|
usedGlobalProperties.Allocate(c, false);
|
|
|
|
for( int n = 0; n < c; n++ )
|
|
{
|
|
asCString name, ns;
|
|
asCDataType type;
|
|
char moduleProp;
|
|
|
|
ReadString(&name);
|
|
ReadString(&ns);
|
|
ReadDataType(&type);
|
|
ReadData(&moduleProp, 1);
|
|
|
|
asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf());
|
|
|
|
// Find the real property
|
|
asCGlobalProperty *globProp = 0;
|
|
if( moduleProp )
|
|
globProp = module->m_scriptGlobals.GetFirst(nameSpace, name);
|
|
else
|
|
globProp = engine->registeredGlobalProps.GetFirst(nameSpace, name);
|
|
|
|
void *prop = 0;
|
|
if( globProp && globProp->type == type )
|
|
prop = globProp->GetAddressOfValue();
|
|
|
|
usedGlobalProperties.PushLast(prop);
|
|
|
|
if( prop == 0 )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCReader::ReadUsedObjectProps()
|
|
{
|
|
TimeIt("asCReader::ReadUsedObjectProps");
|
|
|
|
asUINT c = SanityCheck(ReadEncodedUInt(), 1000000);
|
|
|
|
usedObjectProperties.SetLength(c);
|
|
for( asUINT n = 0; n < c; n++ )
|
|
{
|
|
asCObjectType *objType = CastToObjectType(ReadTypeInfo());
|
|
if( objType == 0 )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
break;
|
|
}
|
|
|
|
asCString name;
|
|
ReadString(&name);
|
|
|
|
// Find the property
|
|
bool found = false;
|
|
for( asUINT p = 0; p < objType->properties.GetLength(); p++ )
|
|
{
|
|
if( objType->properties[p]->name == name )
|
|
{
|
|
usedObjectProperties[n].objType = objType;
|
|
usedObjectProperties[n].prop = objType->properties[p];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !found )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
short asCReader::FindObjectPropOffset(asWORD index)
|
|
{
|
|
static asCObjectProperty *lastCompositeProp = 0;
|
|
if (lastCompositeProp)
|
|
{
|
|
if (index != 0)
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
|
|
short offset = (short)lastCompositeProp->byteOffset;
|
|
lastCompositeProp = 0;
|
|
return offset;
|
|
}
|
|
|
|
if( index >= usedObjectProperties.GetLength() )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
|
|
if (usedObjectProperties[index].prop->compositeOffset || usedObjectProperties[index].prop->isCompositeIndirect)
|
|
{
|
|
lastCompositeProp = usedObjectProperties[index].prop;
|
|
return (short)lastCompositeProp->compositeOffset;
|
|
}
|
|
return (short)usedObjectProperties[index].prop->byteOffset;
|
|
}
|
|
|
|
asCScriptFunction *asCReader::FindFunction(int idx)
|
|
{
|
|
if( idx >= 0 && idx < (int)usedFunctions.GetLength() )
|
|
return usedFunctions[idx];
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void asCReader::TranslateFunction(asCScriptFunction *func)
|
|
{
|
|
// Skip this if the function is part of an pre-existing shared object
|
|
if( dontTranslate.MoveTo(0, func) ) return;
|
|
|
|
asASSERT( func->scriptData );
|
|
|
|
// Pre-compute the size of each instruction in order to translate jump offsets
|
|
asUINT n;
|
|
asDWORD *bc = func->scriptData->byteCode.AddressOf();
|
|
asUINT bcLength = (asUINT)func->scriptData->byteCode.GetLength();
|
|
asCArray<asUINT> bcSizes(bcLength);
|
|
asCArray<asUINT> instructionNbrToPos(bcLength);
|
|
for( n = 0; n < bcLength; )
|
|
{
|
|
int c = *(asBYTE*)&bc[n];
|
|
asUINT size = asBCTypeSize[asBCInfo[c].type];
|
|
if( size == 0 )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
bcSizes.PushLast(size);
|
|
instructionNbrToPos.PushLast(n);
|
|
n += size;
|
|
}
|
|
|
|
asUINT bcNum = 0;
|
|
for( n = 0; n < bcLength; bcNum++ )
|
|
{
|
|
int c = *(asBYTE*)&bc[n];
|
|
if( c == asBC_REFCPY ||
|
|
c == asBC_RefCpyV ||
|
|
c == asBC_OBJTYPE )
|
|
{
|
|
// Translate the index to the true object type
|
|
asPWORD *ot = (asPWORD*)&bc[n+1];
|
|
*(asCObjectType**)ot = CastToObjectType(FindType(int(*ot)));
|
|
}
|
|
else if( c == asBC_TYPEID ||
|
|
c == asBC_Cast )
|
|
{
|
|
// Translate the index to the type id
|
|
int *tid = (int*)&bc[n+1];
|
|
*tid = FindTypeId(*tid);
|
|
}
|
|
else if( c == asBC_ADDSi ||
|
|
c == asBC_LoadThisR )
|
|
{
|
|
// Translate the index to the type id
|
|
int *tid = (int*)&bc[n+1];
|
|
*tid = FindTypeId(*tid);
|
|
|
|
// Translate the prop index into the property offset
|
|
*(((short*)&bc[n])+1) = FindObjectPropOffset(*(((short*)&bc[n])+1));
|
|
}
|
|
else if( c == asBC_LoadRObjR ||
|
|
c == asBC_LoadVObjR )
|
|
{
|
|
// Translate the index to the type id
|
|
int *tid = (int*)&bc[n+2];
|
|
*tid = FindTypeId(*tid);
|
|
|
|
asCObjectType *ot = engine->GetObjectTypeFromTypeId(*tid);
|
|
if( ot && (ot->flags & asOBJ_LIST_PATTERN) )
|
|
{
|
|
// List patterns have a different way of adjusting the offsets
|
|
SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1];
|
|
*(((short*)&bc[n])+2) = (short)listAdj->AdjustOffset(*(((short*)&bc[n])+2));
|
|
}
|
|
else
|
|
{
|
|
// Translate the prop index into the property offset
|
|
*(((short*)&bc[n])+2) = FindObjectPropOffset(*(((short*)&bc[n])+2));
|
|
}
|
|
}
|
|
else if( c == asBC_COPY )
|
|
{
|
|
// Translate the index to the type id
|
|
int *tid = (int*)&bc[n+1];
|
|
*tid = FindTypeId(*tid);
|
|
|
|
// COPY is used to copy POD types that don't have the opAssign method. It is
|
|
// also used to copy references to scoped types during variable initializations.
|
|
// Update the number of dwords to copy as it may be different on the target platform
|
|
if( (*tid) & asTYPEID_OBJHANDLE )
|
|
{
|
|
// It is the actual reference that is being copied, not the object itself
|
|
asBC_SWORDARG0(&bc[n]) = AS_PTR_SIZE;
|
|
}
|
|
else
|
|
{
|
|
asCDataType dt = engine->GetDataTypeFromTypeId(*tid);
|
|
if( !dt.IsValid() )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
}
|
|
else
|
|
asBC_SWORDARG0(&bc[n]) = (short)dt.GetSizeInMemoryDWords();
|
|
}
|
|
}
|
|
else if( c == asBC_RET )
|
|
{
|
|
// Determine the correct amount of DWORDs to pop
|
|
asWORD dw = (asWORD)func->GetSpaceNeededForArguments();
|
|
if( func->DoesReturnOnStack() ) dw += AS_PTR_SIZE;
|
|
if( func->objectType ) dw += AS_PTR_SIZE;
|
|
asBC_WORDARG0(&bc[n]) = dw;
|
|
}
|
|
else if( c == asBC_CALL ||
|
|
c == asBC_CALLINTF ||
|
|
c == asBC_CALLSYS ||
|
|
c == asBC_Thiscall1 )
|
|
{
|
|
// Translate the index to the func id
|
|
int *fid = (int*)&bc[n+1];
|
|
asCScriptFunction *f = FindFunction(*fid);
|
|
if( f )
|
|
*fid = f->id;
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
}
|
|
else if( c == asBC_FuncPtr )
|
|
{
|
|
// Translate the index to the func pointer
|
|
asPWORD *fid = (asPWORD*)&bc[n+1];
|
|
*fid = (asPWORD)FindFunction(int(*fid));
|
|
}
|
|
else if( c == asBC_ALLOC )
|
|
{
|
|
// Translate the index to the true object type
|
|
asPWORD *arg = (asPWORD*)&bc[n+1];
|
|
*(asCObjectType**)arg = CastToObjectType(FindType(int(*arg)));
|
|
|
|
// The constructor function id must be translated, unless it is zero
|
|
int *fid = (int*)&bc[n+1+AS_PTR_SIZE];
|
|
if( *fid != 0 )
|
|
{
|
|
// Subtract 1 from the id, as it was incremented during the writing
|
|
asCScriptFunction *f = FindFunction(*fid-1);
|
|
if( f )
|
|
*fid = f->id;
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if( c == asBC_STR )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
else if( c == asBC_CALLBND )
|
|
{
|
|
// Translate the function id
|
|
asUINT *fid = (asUINT*)&bc[n+1];
|
|
if( *fid < module->m_bindInformations.GetLength() )
|
|
{
|
|
sBindInfo *bi = module->m_bindInformations[*fid];
|
|
if( bi )
|
|
*fid = bi->importedFunctionSignature->id;
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
}
|
|
else if( c == asBC_PGA ||
|
|
c == asBC_PshGPtr ||
|
|
c == asBC_LDG ||
|
|
c == asBC_PshG4 ||
|
|
c == asBC_LdGRdR4 ||
|
|
c == asBC_CpyGtoV4 ||
|
|
c == asBC_CpyVtoG4 ||
|
|
c == asBC_SetG4 )
|
|
{
|
|
// Translate the index to pointer
|
|
asPWORD *index = (asPWORD*)&bc[n + 1];
|
|
if ((*index & 1))
|
|
{
|
|
if ((asUINT(*index)>>1) < usedGlobalProperties.GetLength())
|
|
*(void**)index = usedGlobalProperties[asUINT(*index)>>1];
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only PGA and PshGPtr can hold string constants
|
|
asASSERT(c == asBC_PGA || c == asBC_PshGPtr);
|
|
|
|
if ((asUINT(*index)>>1) < usedStringConstants.GetLength())
|
|
*(void**)index = usedStringConstants[asUINT(*index)>>1];
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if( c == asBC_JMP ||
|
|
c == asBC_JZ ||
|
|
c == asBC_JNZ ||
|
|
c == asBC_JLowZ ||
|
|
c == asBC_JLowNZ ||
|
|
c == asBC_JS ||
|
|
c == asBC_JNS ||
|
|
c == asBC_JP ||
|
|
c == asBC_JNP ) // The JMPP instruction doesn't need modification
|
|
{
|
|
// Get the offset
|
|
int offset = int(bc[n+1]);
|
|
|
|
// Count the instruction sizes to the destination instruction
|
|
int size = 0;
|
|
if( offset >= 0 )
|
|
// If moving ahead, then start from next instruction
|
|
for( asUINT num = bcNum+1; offset-- > 0; num++ )
|
|
size += bcSizes[num];
|
|
else
|
|
// If moving backwards, then start at current instruction
|
|
for( asUINT num = bcNum; offset++ < 0; num-- )
|
|
size -= bcSizes[num];
|
|
|
|
// The size is dword offset
|
|
bc[n+1] = size;
|
|
}
|
|
else if( c == asBC_AllocMem )
|
|
{
|
|
// The size of the allocated memory is only known after all the elements has been seen.
|
|
// This helper class will collect this information and adjust the size when the
|
|
// corresponding asBC_FREE is encountered
|
|
|
|
// The adjuster also needs to know the list type so it can know the type of the elements
|
|
asCObjectType *ot = CastToObjectType(func->GetTypeInfoOfLocalVar(asBC_SWORDARG0(&bc[n])));
|
|
listAdjusters.PushLast(asNEW(SListAdjuster)(this, &bc[n], ot));
|
|
}
|
|
else if( c == asBC_FREE )
|
|
{
|
|
// Translate the index to the true object type
|
|
asPWORD *pot = (asPWORD*)&bc[n+1];
|
|
*(asCObjectType**)pot = CastToObjectType(FindType(int(*pot)));
|
|
|
|
asCObjectType *ot = *(asCObjectType**)pot;
|
|
if( ot && (ot->flags & asOBJ_LIST_PATTERN) )
|
|
{
|
|
if( listAdjusters.GetLength() == 0 )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return;
|
|
}
|
|
|
|
// Finalize the adjustment of the list buffer that was initiated with asBC_AllocMem
|
|
SListAdjuster *list = listAdjusters.PopLast();
|
|
list->AdjustAllocMem();
|
|
asDELETE(list, SListAdjuster);
|
|
}
|
|
}
|
|
else if( c == asBC_SetListSize )
|
|
{
|
|
// Adjust the offset in the list where the size is informed
|
|
SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1];
|
|
bc[n+1] = listAdj->AdjustOffset(bc[n+1]);
|
|
|
|
// Inform the list adjuster how many values will be repeated
|
|
listAdj->SetRepeatCount(bc[n+2]);
|
|
}
|
|
else if( c == asBC_PshListElmnt )
|
|
{
|
|
// Adjust the offset in the list where the size is informed
|
|
SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1];
|
|
bc[n+1] = listAdj->AdjustOffset(bc[n+1]);
|
|
}
|
|
else if( c == asBC_SetListType )
|
|
{
|
|
// Adjust the offset in the list where the typeid is informed
|
|
SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1];
|
|
bc[n+1] = listAdj->AdjustOffset(bc[n+1]);
|
|
|
|
// Translate the type id
|
|
bc[n+2] = FindTypeId(bc[n+2]);
|
|
|
|
// Inform the list adjuster the type id of the next element
|
|
listAdj->SetNextType(bc[n+2]);
|
|
}
|
|
|
|
n += asBCTypeSize[asBCInfo[c].type];
|
|
}
|
|
|
|
// Calculate the stack adjustments
|
|
CalculateAdjustmentByPos(func);
|
|
|
|
// Adjust all variable positions in the bytecode
|
|
bc = func->scriptData->byteCode.AddressOf();
|
|
for( n = 0; n < bcLength; )
|
|
{
|
|
int c = *(asBYTE*)&bc[n];
|
|
switch( asBCInfo[c].type )
|
|
{
|
|
case asBCTYPE_wW_ARG:
|
|
case asBCTYPE_rW_DW_ARG:
|
|
case asBCTYPE_wW_QW_ARG:
|
|
case asBCTYPE_rW_ARG:
|
|
case asBCTYPE_wW_DW_ARG:
|
|
case asBCTYPE_wW_W_ARG:
|
|
case asBCTYPE_rW_QW_ARG:
|
|
case asBCTYPE_rW_W_DW_ARG:
|
|
case asBCTYPE_rW_DW_DW_ARG:
|
|
{
|
|
asBC_SWORDARG0(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG0(&bc[n]));
|
|
}
|
|
break;
|
|
|
|
case asBCTYPE_wW_rW_ARG:
|
|
case asBCTYPE_wW_rW_DW_ARG:
|
|
case asBCTYPE_rW_rW_ARG:
|
|
{
|
|
asBC_SWORDARG0(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG0(&bc[n]));
|
|
asBC_SWORDARG1(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG1(&bc[n]));
|
|
}
|
|
break;
|
|
|
|
case asBCTYPE_wW_rW_rW_ARG:
|
|
{
|
|
asBC_SWORDARG0(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG0(&bc[n]));
|
|
asBC_SWORDARG1(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG1(&bc[n]));
|
|
asBC_SWORDARG2(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG2(&bc[n]));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// The other types don't treat variables so won't be modified
|
|
break;
|
|
}
|
|
|
|
n += asBCTypeSize[asBCInfo[c].type];
|
|
}
|
|
|
|
// Adjust the space needed for local variables
|
|
func->scriptData->variableSpace = AdjustStackPosition(func->scriptData->variableSpace);
|
|
|
|
// Adjust the variable information. This will be used during the adjustment below
|
|
for( n = 0; n < func->scriptData->variables.GetLength(); n++ )
|
|
{
|
|
func->scriptData->variables[n]->declaredAtProgramPos = instructionNbrToPos[func->scriptData->variables[n]->declaredAtProgramPos];
|
|
func->scriptData->variables[n]->stackOffset = AdjustStackPosition(func->scriptData->variables[n]->stackOffset);
|
|
}
|
|
|
|
// objVariablePos
|
|
for( n = 0; n < func->scriptData->objVariablePos.GetLength(); n++ )
|
|
func->scriptData->objVariablePos[n] = AdjustStackPosition(func->scriptData->objVariablePos[n]);
|
|
|
|
// Adjust the get offsets. This must be done in the second iteration because
|
|
// it relies on the function ids and variable position already being correct in the
|
|
// bytecodes that come after the GET instructions.
|
|
// TODO: optimize: Instead of doing a full extra loop. We can push the GET instructions
|
|
// on a stack, and then when a call instruction is found update all of them.
|
|
// This will also make the AdjustGetOffset() function quicker as it can
|
|
// receive the called function directly instead of having to search for it.
|
|
bc = func->scriptData->byteCode.AddressOf();
|
|
for( n = 0; n < bcLength; )
|
|
{
|
|
int c = *(asBYTE*)&bc[n];
|
|
|
|
if( c == asBC_GETREF ||
|
|
c == asBC_GETOBJ ||
|
|
c == asBC_GETOBJREF ||
|
|
c == asBC_ChkNullS )
|
|
{
|
|
asBC_WORDARG0(&bc[n]) = (asWORD)AdjustGetOffset(asBC_WORDARG0(&bc[n]), func, n);
|
|
}
|
|
|
|
n += asBCTypeSize[asBCInfo[c].type];
|
|
}
|
|
|
|
for( n = 0; n < func->scriptData->objVariableInfo.GetLength(); n++ )
|
|
{
|
|
// The program position must be adjusted as it is stored in number of instructions
|
|
func->scriptData->objVariableInfo[n].programPos = instructionNbrToPos[func->scriptData->objVariableInfo[n].programPos];
|
|
func->scriptData->objVariableInfo[n].variableOffset = AdjustStackPosition(func->scriptData->objVariableInfo[n].variableOffset);
|
|
}
|
|
|
|
for (n = 0; n < func->scriptData->tryCatchInfo.GetLength(); n++)
|
|
{
|
|
func->scriptData->tryCatchInfo[n].tryPos = instructionNbrToPos[func->scriptData->tryCatchInfo[n].tryPos];
|
|
func->scriptData->tryCatchInfo[n].catchPos = instructionNbrToPos[func->scriptData->tryCatchInfo[n].catchPos];
|
|
}
|
|
|
|
// The program position (every even number) needs to be adjusted
|
|
// for the line numbers to be in number of dwords instead of number of instructions
|
|
for( n = 0; n < func->scriptData->lineNumbers.GetLength(); n += 2 )
|
|
func->scriptData->lineNumbers[n] = instructionNbrToPos[func->scriptData->lineNumbers[n]];
|
|
for( n = 0; n < func->scriptData->sectionIdxs.GetLength(); n += 2 )
|
|
func->scriptData->sectionIdxs[n] = instructionNbrToPos[func->scriptData->sectionIdxs[n]];
|
|
|
|
CalculateStackNeeded(func);
|
|
}
|
|
|
|
asCReader::SListAdjuster::SListAdjuster(asCReader *rd, asDWORD *bc, asCObjectType *listType) :
|
|
reader(rd), allocMemBC(bc), maxOffset(0), patternType(listType), repeatCount(0), lastOffset(-1), nextOffset(0), nextTypeId(-1)
|
|
{
|
|
asASSERT( patternType && (patternType->flags & asOBJ_LIST_PATTERN) );
|
|
|
|
// Find the first expected value in the list
|
|
asSListPatternNode *node = patternType->engine->scriptFunctions[patternType->templateSubTypes[0].GetBehaviour()->listFactory]->listPattern;
|
|
asASSERT( node && node->type == asLPT_START );
|
|
patternNode = node->next;
|
|
}
|
|
|
|
int asCReader::SListAdjuster::AdjustOffset(int offset)
|
|
{
|
|
if( offset < lastOffset )
|
|
{
|
|
reader->Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
|
|
// If it is the same offset being accessed again, just return the same adjusted value
|
|
if( lastOffset == offset )
|
|
return lastAdjustedOffset;
|
|
|
|
lastOffset = offset;
|
|
lastAdjustedOffset = maxOffset;
|
|
|
|
// What is being expected at this position?
|
|
if( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME )
|
|
{
|
|
// Align the offset to 4 bytes boundary
|
|
if( maxOffset & 0x3 )
|
|
{
|
|
maxOffset += 4 - (maxOffset & 0x3);
|
|
lastAdjustedOffset = maxOffset;
|
|
}
|
|
|
|
// Don't move the patternNode yet because the caller must make a call to SetRepeatCount too
|
|
maxOffset += 4;
|
|
nextOffset = offset+1;
|
|
return lastAdjustedOffset;
|
|
}
|
|
else if( patternNode->type == asLPT_TYPE )
|
|
{
|
|
const asCDataType &dt = reinterpret_cast<asSListPatternDataTypeNode*>(patternNode)->dataType;
|
|
if( dt.GetTokenType() == ttQuestion )
|
|
{
|
|
if( nextTypeId != -1 )
|
|
{
|
|
if( repeatCount > 0 )
|
|
repeatCount--;
|
|
|
|
asCDataType nextdt = patternType->engine->GetDataTypeFromTypeId(nextTypeId);
|
|
asUINT size;
|
|
if(nextdt.IsObjectHandle() || (nextdt.GetTypeInfo() && (nextdt.GetTypeInfo()->flags & asOBJ_REF)) )
|
|
size = AS_PTR_SIZE*4;
|
|
else
|
|
size = nextdt.GetSizeInMemoryBytes();
|
|
|
|
// Align the offset to 4 bytes boundary
|
|
if( size >= 4 && (maxOffset & 0x3) )
|
|
{
|
|
maxOffset += 4 - (maxOffset & 0x3);
|
|
lastAdjustedOffset = maxOffset;
|
|
}
|
|
|
|
// Only move the patternNode if we're not expecting any more repeated entries
|
|
if( repeatCount == 0 )
|
|
patternNode = patternNode->next;
|
|
|
|
nextTypeId = -1;
|
|
|
|
maxOffset += size;
|
|
nextOffset = offset+1;
|
|
return lastAdjustedOffset;
|
|
}
|
|
else
|
|
{
|
|
// Align the offset to 4 bytes boundary
|
|
if( maxOffset & 0x3 )
|
|
{
|
|
maxOffset += 4 - (maxOffset & 0x3);
|
|
lastAdjustedOffset = maxOffset;
|
|
}
|
|
|
|
// The first adjustment is for the typeId
|
|
maxOffset += 4;
|
|
nextOffset = offset+1;
|
|
return lastAdjustedOffset;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Determine the size of the element
|
|
asUINT size;
|
|
if( dt.IsObjectHandle() || (dt.GetTypeInfo() && (dt.GetTypeInfo()->flags & asOBJ_REF)) )
|
|
size = AS_PTR_SIZE*4;
|
|
else
|
|
size = dt.GetSizeInMemoryBytes();
|
|
|
|
// If values are skipped, the offset needs to be incremented
|
|
while( nextOffset <= offset )
|
|
{
|
|
if( repeatCount > 0 )
|
|
repeatCount--;
|
|
|
|
// Align the offset to 4 bytes boundary
|
|
if( size >= 4 && (maxOffset & 0x3) )
|
|
maxOffset += 4 - (maxOffset & 0x3);
|
|
|
|
lastAdjustedOffset = maxOffset;
|
|
nextOffset += 1;
|
|
maxOffset += size;
|
|
}
|
|
|
|
// Only move the patternNode if we're not expecting any more repeated entries
|
|
if( repeatCount == 0 )
|
|
patternNode = patternNode->next;
|
|
|
|
nextOffset = offset+1;
|
|
return lastAdjustedOffset;
|
|
}
|
|
}
|
|
else if( patternNode->type == asLPT_START )
|
|
{
|
|
if( repeatCount > 0 )
|
|
repeatCount--;
|
|
SInfo info = {repeatCount, patternNode};
|
|
stack.PushLast(info);
|
|
|
|
repeatCount = 0;
|
|
patternNode = patternNode->next;
|
|
|
|
lastOffset--;
|
|
return AdjustOffset(offset);
|
|
}
|
|
else if( patternNode->type == asLPT_END )
|
|
{
|
|
if( stack.GetLength() == 0 )
|
|
{
|
|
reader->Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
|
|
SInfo info = stack.PopLast();
|
|
repeatCount = info.repeatCount;
|
|
if( repeatCount )
|
|
patternNode = info.startNode;
|
|
else
|
|
patternNode = patternNode->next;
|
|
|
|
lastOffset--;
|
|
return AdjustOffset(offset);
|
|
}
|
|
else
|
|
{
|
|
// Something is wrong with the pattern list declaration
|
|
reader->Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
|
|
UNREACHABLE_RETURN;
|
|
}
|
|
|
|
void asCReader::SListAdjuster::SetRepeatCount(asUINT rc)
|
|
{
|
|
// Make sure the list is expecting a repeat at this location
|
|
asASSERT( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME );
|
|
|
|
// Now move to the next patternNode
|
|
patternNode = patternNode->next;
|
|
|
|
repeatCount = rc;
|
|
}
|
|
|
|
void asCReader::SListAdjuster::AdjustAllocMem()
|
|
{
|
|
allocMemBC[1] = maxOffset;
|
|
}
|
|
|
|
void asCReader::SListAdjuster::SetNextType(int typeId)
|
|
{
|
|
asASSERT( nextTypeId == -1 );
|
|
|
|
nextTypeId = typeId;
|
|
}
|
|
|
|
void asCReader::CalculateStackNeeded(asCScriptFunction *func)
|
|
{
|
|
asASSERT( func->scriptData );
|
|
|
|
int largestStackUsed = 0;
|
|
|
|
// Clear the known stack size for each bytecode
|
|
asCArray<int> stackSize;
|
|
stackSize.SetLength(func->scriptData->byteCode.GetLength());
|
|
memset(&stackSize[0], -1, stackSize.GetLength()*4);
|
|
|
|
// Add the first instruction to the list of unchecked code
|
|
// paths and set the stack size at that instruction to variableSpace
|
|
asCArray<asUINT> paths;
|
|
paths.PushLast(0);
|
|
stackSize[0] = func->scriptData->variableSpace;
|
|
|
|
// Go through each of the code paths
|
|
for( asUINT p = 0; p < paths.GetLength(); ++p )
|
|
{
|
|
asUINT pos = paths[p];
|
|
int currStackSize = stackSize[pos];
|
|
|
|
asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[pos];
|
|
if( bc == asBC_RET )
|
|
continue;
|
|
|
|
// Determine the change in stack size for this instruction
|
|
int stackInc = asBCInfo[bc].stackInc;
|
|
if( stackInc == 0xFFFF )
|
|
{
|
|
// Determine the true delta from the instruction arguments
|
|
if( bc == asBC_CALL ||
|
|
bc == asBC_CALLSYS ||
|
|
bc == asBC_Thiscall1 ||
|
|
bc == asBC_CALLBND ||
|
|
bc == asBC_ALLOC ||
|
|
bc == asBC_CALLINTF ||
|
|
bc == asBC_CallPtr )
|
|
{
|
|
asCScriptFunction *called = GetCalledFunction(func, pos);
|
|
if( called )
|
|
{
|
|
stackInc = -called->GetSpaceNeededForArguments();
|
|
if( called->objectType )
|
|
stackInc -= AS_PTR_SIZE;
|
|
if( called->DoesReturnOnStack() )
|
|
stackInc -= AS_PTR_SIZE;
|
|
}
|
|
else
|
|
{
|
|
// It is an allocation for an object without a constructor
|
|
asASSERT( bc == asBC_ALLOC );
|
|
stackInc = -AS_PTR_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
currStackSize += stackInc;
|
|
asASSERT( currStackSize >= 0 );
|
|
|
|
if( currStackSize > largestStackUsed )
|
|
largestStackUsed = currStackSize;
|
|
|
|
if( bc == asBC_JMP )
|
|
{
|
|
// Find the label that we should jump to
|
|
int offset = asBC_INTARG(&func->scriptData->byteCode[pos]);
|
|
pos += 2 + offset;
|
|
|
|
// Add the destination as a new path
|
|
if( stackSize[pos] == -1 )
|
|
{
|
|
stackSize[pos] = currStackSize;
|
|
paths.PushLast(pos);
|
|
}
|
|
else
|
|
asASSERT(stackSize[pos] == currStackSize);
|
|
continue;
|
|
}
|
|
else if( bc == asBC_JZ || bc == asBC_JNZ ||
|
|
bc == asBC_JLowZ || bc == asBC_JLowNZ ||
|
|
bc == asBC_JS || bc == asBC_JNS ||
|
|
bc == asBC_JP || bc == asBC_JNP )
|
|
{
|
|
// Find the label that is being jumped to
|
|
int offset = asBC_INTARG(&func->scriptData->byteCode[pos]);
|
|
|
|
// Add both paths to the code paths
|
|
pos += 2;
|
|
if( stackSize[pos] == -1 )
|
|
{
|
|
stackSize[pos] = currStackSize;
|
|
paths.PushLast(pos);
|
|
}
|
|
else
|
|
asASSERT(stackSize[pos] == currStackSize);
|
|
|
|
pos += offset;
|
|
if( stackSize[pos] == -1 )
|
|
{
|
|
stackSize[pos] = currStackSize;
|
|
paths.PushLast(pos);
|
|
}
|
|
else
|
|
asASSERT(stackSize[pos] == currStackSize);
|
|
|
|
continue;
|
|
}
|
|
else if( bc == asBC_JMPP )
|
|
{
|
|
pos++;
|
|
|
|
// Add all subsequent JMP instructions to the path
|
|
while( *(asBYTE*)&func->scriptData->byteCode[pos] == asBC_JMP )
|
|
{
|
|
if( stackSize[pos] == -1 )
|
|
{
|
|
stackSize[pos] = currStackSize;
|
|
paths.PushLast(pos);
|
|
}
|
|
else
|
|
asASSERT(stackSize[pos] == currStackSize);
|
|
pos += 2;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Add next instruction to the paths
|
|
pos += asBCTypeSize[asBCInfo[bc].type];
|
|
if( stackSize[pos] == -1 )
|
|
{
|
|
stackSize[pos] = currStackSize;
|
|
paths.PushLast(pos);
|
|
}
|
|
else
|
|
asASSERT(stackSize[pos] == currStackSize);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
func->scriptData->stackNeeded = largestStackUsed;
|
|
}
|
|
|
|
void asCReader::CalculateAdjustmentByPos(asCScriptFunction *func)
|
|
{
|
|
// Adjust the offset of all negative variables (parameters) as
|
|
// all pointers have been stored as having a size of 1 dword
|
|
asUINT n;
|
|
asCArray<int> adjustments;
|
|
asUINT offset = 0;
|
|
if( func->objectType )
|
|
{
|
|
adjustments.PushLast(offset);
|
|
adjustments.PushLast(1-AS_PTR_SIZE);
|
|
offset += 1;
|
|
}
|
|
if( func->DoesReturnOnStack() )
|
|
{
|
|
adjustments.PushLast(offset);
|
|
adjustments.PushLast(1-AS_PTR_SIZE);
|
|
offset += 1;
|
|
}
|
|
for( n = 0; n < func->parameterTypes.GetLength(); n++ )
|
|
{
|
|
if( !func->parameterTypes[n].IsPrimitive() ||
|
|
func->parameterTypes[n].IsReference() )
|
|
{
|
|
adjustments.PushLast(offset);
|
|
adjustments.PushLast(1-AS_PTR_SIZE);
|
|
offset += 1;
|
|
}
|
|
else
|
|
{
|
|
asASSERT( func->parameterTypes[n].IsPrimitive() );
|
|
offset += func->parameterTypes[n].GetSizeOnStackDWords();
|
|
}
|
|
}
|
|
|
|
// Build look-up table with the adjustments for each stack position
|
|
adjustNegativeStackByPos.SetLength(offset);
|
|
memset(adjustNegativeStackByPos.AddressOf(), 0, adjustNegativeStackByPos.GetLength()*sizeof(int));
|
|
for( n = 0; n < adjustments.GetLength(); n+=2 )
|
|
{
|
|
int pos = adjustments[n];
|
|
int adjust = adjustments[n+1];
|
|
|
|
for( asUINT i = pos+1; i < adjustNegativeStackByPos.GetLength(); i++ )
|
|
adjustNegativeStackByPos[i] += adjust;
|
|
}
|
|
|
|
// The bytecode has been stored as if all object variables take up only 1 dword.
|
|
// It is necessary to adjust to the size according to the current platform.
|
|
adjustments.SetLength(0);
|
|
int highestPos = 0;
|
|
for( n = 0; n < func->scriptData->objVariableTypes.GetLength(); n++ )
|
|
{
|
|
// Determine the size the variable currently occupies on the stack
|
|
int size = AS_PTR_SIZE;
|
|
|
|
// objVariableTypes is null if the type is a null pointer
|
|
if( func->scriptData->objVariableTypes[n] &&
|
|
(func->scriptData->objVariableTypes[n]->GetFlags() & asOBJ_VALUE) &&
|
|
n >= func->scriptData->objVariablesOnHeap )
|
|
{
|
|
size = func->scriptData->objVariableTypes[n]->GetSize();
|
|
if( size < 4 )
|
|
size = 1;
|
|
else
|
|
size /= 4;
|
|
}
|
|
|
|
// Check if type has a different size than stored
|
|
if( size > 1 )
|
|
{
|
|
if( func->scriptData->objVariablePos[n] > highestPos )
|
|
highestPos = func->scriptData->objVariablePos[n];
|
|
|
|
adjustments.PushLast(func->scriptData->objVariablePos[n]);
|
|
adjustments.PushLast(size-1);
|
|
}
|
|
}
|
|
|
|
// Count position 0 too
|
|
adjustByPos.SetLength(highestPos+1);
|
|
memset(adjustByPos.AddressOf(), 0, adjustByPos.GetLength()*sizeof(int));
|
|
|
|
// Build look-up table with the adjustments for each stack position
|
|
for( n = 0; n < adjustments.GetLength(); n+=2 )
|
|
{
|
|
int pos = adjustments[n];
|
|
int adjust = adjustments[n+1];
|
|
|
|
for( asUINT i = pos; i < adjustByPos.GetLength(); i++ )
|
|
adjustByPos[i] += adjust;
|
|
}
|
|
}
|
|
|
|
int asCReader::AdjustStackPosition(int pos)
|
|
{
|
|
if( pos >= (int)adjustByPos.GetLength() )
|
|
{
|
|
// It can be higher for primitives allocated on top of highest object variable
|
|
if( adjustByPos.GetLength() )
|
|
pos += (short)adjustByPos[adjustByPos.GetLength()-1];
|
|
}
|
|
else if( pos >= 0 )
|
|
pos += (short)adjustByPos[pos];
|
|
else if( -pos >= (int)adjustNegativeStackByPos.GetLength() )
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
else
|
|
pos += (short)adjustNegativeStackByPos[-pos];
|
|
|
|
return pos;
|
|
}
|
|
|
|
asCScriptFunction *asCReader::GetCalledFunction(asCScriptFunction *func, asDWORD programPos)
|
|
{
|
|
asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[programPos];
|
|
|
|
if( bc == asBC_CALL ||
|
|
bc == asBC_CALLSYS ||
|
|
bc == asBC_Thiscall1 ||
|
|
bc == asBC_CALLINTF )
|
|
{
|
|
// Find the function from the function id in bytecode
|
|
int funcId = asBC_INTARG(&func->scriptData->byteCode[programPos]);
|
|
return engine->scriptFunctions[funcId];
|
|
}
|
|
else if( bc == asBC_ALLOC )
|
|
{
|
|
// Find the function from the function id in the bytecode
|
|
int funcId = asBC_INTARG(&func->scriptData->byteCode[programPos+AS_PTR_SIZE]);
|
|
return engine->scriptFunctions[funcId];
|
|
}
|
|
else if( bc == asBC_CALLBND )
|
|
{
|
|
// Find the function from the engine's bind array
|
|
int funcId = asBC_INTARG(&func->scriptData->byteCode[programPos]);
|
|
return engine->importedFunctions[funcId & ~FUNC_IMPORTED]->importedFunctionSignature;
|
|
}
|
|
else if( bc == asBC_CallPtr )
|
|
{
|
|
asUINT v;
|
|
int var = asBC_SWORDARG0(&func->scriptData->byteCode[programPos]);
|
|
|
|
// Find the funcdef from the local variable
|
|
for( v = 0; v < func->scriptData->objVariablePos.GetLength(); v++ )
|
|
if( func->scriptData->objVariablePos[v] == var )
|
|
return CastToFuncdefType(func->scriptData->objVariableTypes[v])->funcdef;
|
|
|
|
// Look in parameters
|
|
int paramPos = 0;
|
|
if( func->objectType )
|
|
paramPos -= AS_PTR_SIZE;
|
|
if( func->DoesReturnOnStack() )
|
|
paramPos -= AS_PTR_SIZE;
|
|
for( v = 0; v < func->parameterTypes.GetLength(); v++ )
|
|
{
|
|
if (var == paramPos)
|
|
{
|
|
if (func->parameterTypes[v].IsFuncdef())
|
|
return CastToFuncdefType(func->parameterTypes[v].GetTypeInfo())->funcdef;
|
|
else
|
|
{
|
|
error = true;
|
|
return 0;
|
|
}
|
|
}
|
|
paramPos -= func->parameterTypes[v].GetSizeOnStackDWords();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD programPos)
|
|
{
|
|
// TODO: optimize: multiple instructions for the same function doesn't need to look for the function everytime
|
|
// the function can remember where it found the function and check if the programPos is still valid
|
|
|
|
// Get offset 0 doesn't need adjustment
|
|
if( offset == 0 ) return 0;
|
|
|
|
bool bcAlloc = false;
|
|
|
|
// Find out which function that will be called
|
|
asCScriptFunction *calledFunc = 0;
|
|
int stackDelta = 0;
|
|
for( asUINT n = programPos; func->scriptData->byteCode.GetLength(); )
|
|
{
|
|
asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n];
|
|
if( bc == asBC_CALL ||
|
|
bc == asBC_CALLSYS ||
|
|
bc == asBC_Thiscall1 ||
|
|
bc == asBC_CALLINTF ||
|
|
bc == asBC_ALLOC ||
|
|
bc == asBC_CALLBND ||
|
|
bc == asBC_CallPtr )
|
|
{
|
|
// The alloc instruction allocates the object memory
|
|
// so it doesn't take the this pointer as input
|
|
if (bc == asBC_ALLOC)
|
|
bcAlloc = true;
|
|
|
|
calledFunc = GetCalledFunction(func, n);
|
|
break;
|
|
}
|
|
else if( bc == asBC_REFCPY ||
|
|
bc == asBC_COPY )
|
|
{
|
|
// In this case we know there is only 1 pointer on the stack above
|
|
asASSERT( offset == 1 );
|
|
return offset - (1 - AS_PTR_SIZE);
|
|
}
|
|
|
|
// Keep track of the stack size between the
|
|
// instruction that needs to be adjusted and the call
|
|
stackDelta += asBCInfo[bc].stackInc;
|
|
|
|
n += asBCTypeSize[asBCInfo[bc].type];
|
|
}
|
|
|
|
if( calledFunc == 0 )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return offset;
|
|
}
|
|
|
|
// Count the number of pointers pushed on the stack above the
|
|
// current offset, and then adjust the offset accordingly
|
|
asUINT numPtrs = 0;
|
|
int currOffset = -stackDelta;
|
|
if( offset > currOffset && calledFunc->GetObjectType() && !bcAlloc )
|
|
{
|
|
currOffset++;
|
|
if( currOffset > 0 )
|
|
numPtrs++;
|
|
#if AS_PTR_SIZE == 2
|
|
// For 64bit platforms it is necessary to increment the currOffset by one more
|
|
// DWORD since the stackDelta was counting the full 64bit size of the pointer
|
|
else if( stackDelta )
|
|
currOffset++;
|
|
#endif
|
|
}
|
|
if( offset > currOffset && calledFunc->DoesReturnOnStack() )
|
|
{
|
|
currOffset++;
|
|
if( currOffset > 0 )
|
|
numPtrs++;
|
|
#if AS_PTR_SIZE == 2
|
|
// For 64bit platforms it is necessary to increment the currOffset by one more
|
|
// DWORD since the stackDelta was counting the full 64bit size of the pointer
|
|
else if( stackDelta )
|
|
currOffset++;
|
|
#endif
|
|
}
|
|
for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ )
|
|
{
|
|
if( offset <= currOffset ) break;
|
|
|
|
if( !calledFunc->parameterTypes[p].IsPrimitive() ||
|
|
calledFunc->parameterTypes[p].IsReference() )
|
|
{
|
|
currOffset++;
|
|
if( currOffset > 0 )
|
|
numPtrs++;
|
|
#if AS_PTR_SIZE == 2
|
|
// For 64bit platforms it is necessary to increment the currOffset by one more
|
|
// DWORD since the stackDelta was counting the full 64bit size of the pointer
|
|
else if( stackDelta )
|
|
currOffset++;
|
|
#endif
|
|
|
|
// The variable arg ? has an additiona 32bit integer with the typeid
|
|
if( calledFunc->parameterTypes[p].IsAnyType() )
|
|
currOffset += 1;
|
|
}
|
|
else
|
|
{
|
|
// Enums or built-in primitives are passed by value
|
|
asASSERT( calledFunc->parameterTypes[p].IsPrimitive() );
|
|
currOffset += calledFunc->parameterTypes[p].GetSizeOnStackDWords();
|
|
}
|
|
}
|
|
|
|
return offset - numPtrs * (1 - AS_PTR_SIZE);
|
|
}
|
|
|
|
int asCReader::FindTypeId(int idx)
|
|
{
|
|
if( idx >= 0 && idx < (int)usedTypeIds.GetLength() )
|
|
return usedTypeIds[idx];
|
|
else
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
asCTypeInfo *asCReader::FindType(int idx)
|
|
{
|
|
if( idx < 0 || idx >= (int)usedTypes.GetLength() )
|
|
{
|
|
Error(TXT_INVALID_BYTECODE_d);
|
|
return 0;
|
|
}
|
|
|
|
return usedTypes[idx];
|
|
}
|
|
|
|
#ifndef AS_NO_COMPILER
|
|
|
|
asCWriter::asCWriter(asCModule* _module, asIBinaryStream* _stream, asCScriptEngine* _engine, bool _stripDebug)
|
|
: module(_module), stream(_stream), engine(_engine), stripDebugInfo(_stripDebug), error(false), bytesWritten(0)
|
|
{
|
|
}
|
|
|
|
int asCWriter::Error(const char *msg)
|
|
{
|
|
// Don't write if it has already been reported an error earlier
|
|
if (!error)
|
|
{
|
|
asCString str;
|
|
str.Format(msg, bytesWritten);
|
|
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
|
|
error = true;
|
|
}
|
|
|
|
return asERROR;
|
|
}
|
|
|
|
int asCWriter::WriteData(const void *data, asUINT size)
|
|
{
|
|
asASSERT(size == 1 || size == 2 || size == 4 || size == 8);
|
|
int ret = 0;
|
|
#if defined(AS_BIG_ENDIAN)
|
|
for( asUINT n = 0; ret >= 0 && n < size; n++ )
|
|
ret = stream->Write(((asBYTE*)data)+n, 1);
|
|
#else
|
|
for( int n = size-1; ret >= 0 && n >= 0; n-- )
|
|
ret = stream->Write(((asBYTE*)data)+n, 1);
|
|
#endif
|
|
if (ret < 0)
|
|
Error(TXT_UNEXPECTED_END_OF_FILE);
|
|
bytesWritten += size;
|
|
return ret;
|
|
}
|
|
|
|
int asCWriter::Write()
|
|
{
|
|
TimeIt("asCWriter::Write");
|
|
|
|
unsigned long i, count;
|
|
|
|
// Store everything in the same order that the builder parses scripts
|
|
|
|
// TODO: Should be possible to skip saving the enum values. They are usually not needed after the script is compiled anyway
|
|
// TODO: Should be possible to skip saving the typedefs. They are usually not needed after the script is compiled anyway
|
|
// TODO: Should be possible to skip saving constants. They are usually not needed after the script is compiled anyway
|
|
|
|
// Write the flag as 1byte even on platforms with 4byte booleans
|
|
WriteEncodedInt64(stripDebugInfo ? 1 : 0);
|
|
|
|
// Store enums
|
|
{
|
|
TimeIt("store enums");
|
|
|
|
count = (asUINT)module->m_enumTypes.GetLength();
|
|
WriteEncodedInt64(count);
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
WriteTypeDeclaration(module->m_enumTypes[i], 1);
|
|
WriteTypeDeclaration(module->m_enumTypes[i], 2);
|
|
}
|
|
}
|
|
|
|
// Store type declarations first
|
|
{
|
|
TimeIt("type declarations");
|
|
|
|
count = (asUINT)module->m_classTypes.GetLength();
|
|
WriteEncodedInt64(count);
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
// Store only the name of the class/interface types
|
|
WriteTypeDeclaration(module->m_classTypes[i], 1);
|
|
}
|
|
}
|
|
|
|
// Store func defs
|
|
{
|
|
TimeIt("func defs");
|
|
|
|
count = (asUINT)module->m_funcDefs.GetLength();
|
|
WriteEncodedInt64(count);
|
|
for( i = 0; i < count; i++ )
|
|
WriteFunction(module->m_funcDefs[i]->funcdef);
|
|
}
|
|
|
|
// Now store all interface methods
|
|
{
|
|
TimeIt("interface methods");
|
|
|
|
count = (asUINT)module->m_classTypes.GetLength();
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
if( module->m_classTypes[i]->IsInterface() )
|
|
WriteTypeDeclaration(module->m_classTypes[i], 2);
|
|
}
|
|
}
|
|
|
|
// Then store the class methods and behaviours
|
|
{
|
|
TimeIt("class methods and behaviours");
|
|
|
|
for( i = 0; i < count; ++i )
|
|
{
|
|
if( !module->m_classTypes[i]->IsInterface() )
|
|
WriteTypeDeclaration(module->m_classTypes[i], 2);
|
|
}
|
|
}
|
|
|
|
// Then store the class properties
|
|
{
|
|
TimeIt("class properties");
|
|
|
|
for( i = 0; i < count; ++i )
|
|
{
|
|
if( !module->m_classTypes[i]->IsInterface() )
|
|
WriteTypeDeclaration(module->m_classTypes[i], 3);
|
|
}
|
|
}
|
|
|
|
// Store typedefs
|
|
{
|
|
TimeIt("type defs");
|
|
|
|
count = (asUINT)module->m_typeDefs.GetLength();
|
|
WriteEncodedInt64(count);
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
WriteTypeDeclaration(module->m_typeDefs[i], 1);
|
|
WriteTypeDeclaration(module->m_typeDefs[i], 2);
|
|
}
|
|
}
|
|
|
|
// scriptGlobals[]
|
|
{
|
|
TimeIt("script globals");
|
|
|
|
count = (asUINT)module->m_scriptGlobals.GetSize();
|
|
WriteEncodedInt64(count);
|
|
asCSymbolTable<asCGlobalProperty>::iterator it = module->m_scriptGlobals.List();
|
|
for( ; it; it++ )
|
|
WriteGlobalProperty(*it);
|
|
}
|
|
|
|
// scriptFunctions[]
|
|
{
|
|
TimeIt("scriptFunctions");
|
|
|
|
count = 0;
|
|
for( i = 0; i < module->m_scriptFunctions.GetLength(); i++ )
|
|
if( module->m_scriptFunctions[i]->objectType == 0 )
|
|
count++;
|
|
WriteEncodedInt64(count);
|
|
for( i = 0; i < module->m_scriptFunctions.GetLength(); ++i )
|
|
if( module->m_scriptFunctions[i]->objectType == 0 )
|
|
WriteFunction(module->m_scriptFunctions[i]);
|
|
}
|
|
|
|
// globalFunctions[]
|
|
{
|
|
TimeIt("globalFunctions");
|
|
|
|
count = (int)module->m_globalFunctions.GetSize();
|
|
asCSymbolTable<asCScriptFunction>::iterator funcIt = module->m_globalFunctions.List();
|
|
WriteEncodedInt64(count);
|
|
while( funcIt )
|
|
{
|
|
WriteFunction(*funcIt);
|
|
funcIt++;
|
|
}
|
|
}
|
|
|
|
// bindInformations[]
|
|
{
|
|
TimeIt("bindInformations");
|
|
|
|
count = (asUINT)module->m_bindInformations.GetLength();
|
|
WriteEncodedInt64(count);
|
|
for( i = 0; i < count; ++i )
|
|
{
|
|
WriteFunction(module->m_bindInformations[i]->importedFunctionSignature);
|
|
WriteString(&module->m_bindInformations[i]->importFromModule);
|
|
}
|
|
}
|
|
|
|
// usedTypes[]
|
|
{
|
|
TimeIt("usedTypes");
|
|
|
|
count = (asUINT)usedTypes.GetLength();
|
|
WriteEncodedInt64(count);
|
|
for( i = 0; i < count; ++i )
|
|
WriteTypeInfo(usedTypes[i]);
|
|
}
|
|
|
|
// usedTypeIds[]
|
|
WriteUsedTypeIds();
|
|
|
|
// usedFunctions[]
|
|
WriteUsedFunctions();
|
|
|
|
// usedGlobalProperties[]
|
|
WriteUsedGlobalProps();
|
|
|
|
// usedStringConstants[]
|
|
WriteUsedStringConstants();
|
|
|
|
// usedObjectProperties[]
|
|
WriteUsedObjectProps();
|
|
|
|
return error ? asERROR : asSUCCESS;
|
|
}
|
|
|
|
int asCWriter::FindStringConstantIndex(void *str)
|
|
{
|
|
asSMapNode<void*, int> *cursor = 0;
|
|
if (stringToIndexMap.MoveTo(&cursor, str))
|
|
return cursor->value;
|
|
|
|
usedStringConstants.PushLast(str);
|
|
int index = int(usedStringConstants.GetLength() - 1);
|
|
stringToIndexMap.Insert(str, index);
|
|
return index;
|
|
}
|
|
|
|
void asCWriter::WriteUsedStringConstants()
|
|
{
|
|
TimeIt("asCWriter::WriteUsedStringConstants");
|
|
|
|
asUINT count = (asUINT)usedStringConstants.GetLength();
|
|
WriteEncodedInt64(count);
|
|
|
|
asCString str;
|
|
for (asUINT i = 0; i < count; ++i)
|
|
{
|
|
asUINT length;
|
|
engine->stringFactory->GetRawStringData(usedStringConstants[i], 0, &length);
|
|
str.SetLength(length);
|
|
engine->stringFactory->GetRawStringData(usedStringConstants[i], str.AddressOf(), &length);
|
|
WriteString(&str);
|
|
}
|
|
}
|
|
|
|
void asCWriter::WriteUsedFunctions()
|
|
{
|
|
TimeIt("asCWriter::WriteUsedFunctions");
|
|
|
|
asUINT count = (asUINT)usedFunctions.GetLength();
|
|
WriteEncodedInt64(count);
|
|
|
|
for( asUINT n = 0; n < usedFunctions.GetLength(); n++ )
|
|
{
|
|
char c;
|
|
|
|
// Write enough data to be able to uniquely identify the function upon load
|
|
asCScriptFunction *func = usedFunctions[n];
|
|
if(func)
|
|
{
|
|
// Is the function from the module or the application?
|
|
c = func->module ? 'm' : 'a';
|
|
|
|
// Functions and methods that are shared should be stored as 's' as the bytecode
|
|
// may be imported from other modules (even if the current module have received ownership)
|
|
if (c == 'm' && func->IsShared() )
|
|
c = 's';
|
|
|
|
WriteData(&c, 1);
|
|
WriteFunctionSignature(func);
|
|
}
|
|
else
|
|
{
|
|
// null function pointer
|
|
c = 'n';
|
|
WriteData(&c, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCWriter::WriteFunctionSignature(asCScriptFunction *func)
|
|
{
|
|
asUINT i, count;
|
|
|
|
WriteString(&func->name);
|
|
if( func->name == DELEGATE_FACTORY )
|
|
{
|
|
// It's not necessary to write anything else
|
|
return;
|
|
}
|
|
|
|
WriteDataType(&func->returnType);
|
|
|
|
count = (asUINT)func->parameterTypes.GetLength();
|
|
WriteEncodedInt64(count);
|
|
for( i = 0; i < count; ++i )
|
|
WriteDataType(&func->parameterTypes[i]);
|
|
|
|
// Only write the inout flags if any of them are set
|
|
// If the number of parameters is 0, then no need to save this
|
|
if (func->parameterTypes.GetLength() > 0)
|
|
{
|
|
count = 0;
|
|
for (i = asUINT(func->inOutFlags.GetLength()); i > 0; i--)
|
|
if (func->inOutFlags[i - 1] != asTM_NONE)
|
|
{
|
|
count = i;
|
|
break;
|
|
}
|
|
WriteEncodedInt64(count);
|
|
for (i = 0; i < count; ++i)
|
|
WriteEncodedInt64(func->inOutFlags[i]);
|
|
}
|
|
|
|
WriteEncodedInt64(func->funcType);
|
|
|
|
// Write the default args, from last to first
|
|
// If the number of parameters is 0, then no need to save this
|
|
if (func->parameterTypes.GetLength() > 0)
|
|
{
|
|
count = 0;
|
|
for (i = (asUINT)func->defaultArgs.GetLength(); i-- > 0; )
|
|
if (func->defaultArgs[i])
|
|
count++;
|
|
WriteEncodedInt64(count);
|
|
for (i = (asUINT)func->defaultArgs.GetLength(); i-- > 0; )
|
|
if (func->defaultArgs[i])
|
|
WriteString(func->defaultArgs[i]);
|
|
}
|
|
|
|
WriteTypeInfo(func->objectType);
|
|
|
|
if( func->objectType )
|
|
{
|
|
asBYTE b = 0;
|
|
b += func->IsReadOnly() ? 1 : 0;
|
|
b += func->IsPrivate() ? 2 : 0;
|
|
b += func->IsProtected() ? 4 : 0;
|
|
WriteData(&b, 1);
|
|
}
|
|
else
|
|
{
|
|
if (func->funcType == asFUNC_FUNCDEF)
|
|
{
|
|
if (func->nameSpace)
|
|
{
|
|
// This funcdef was declared as global entity
|
|
asBYTE b = 'n';
|
|
WriteData(&b, 1);
|
|
WriteString(&func->nameSpace->name);
|
|
}
|
|
else
|
|
{
|
|
// This funcdef was declared as class member
|
|
asBYTE b = 'o';
|
|
WriteData(&b, 1);
|
|
WriteTypeInfo(func->funcdefType->parentClass);
|
|
}
|
|
}
|
|
else
|
|
WriteString(&func->nameSpace->name);
|
|
}
|
|
}
|
|
|
|
void asCWriter::WriteFunction(asCScriptFunction* func)
|
|
{
|
|
char c;
|
|
|
|
// If there is no function, then store a null char
|
|
if( func == 0 )
|
|
{
|
|
c = '\0';
|
|
WriteData(&c, 1);
|
|
return;
|
|
}
|
|
|
|
// First check if the function has been saved already
|
|
for( asUINT f = 0; f < savedFunctions.GetLength(); f++ )
|
|
{
|
|
if( savedFunctions[f] == func )
|
|
{
|
|
c = 'r';
|
|
WriteData(&c, 1);
|
|
WriteEncodedInt64(f);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Keep a reference to the function in the list
|
|
savedFunctions.PushLast(func);
|
|
|
|
c = 'f';
|
|
WriteData(&c, 1);
|
|
|
|
asUINT i, count;
|
|
|
|
WriteFunctionSignature(func);
|
|
|
|
if( func->funcType == asFUNC_SCRIPT )
|
|
{
|
|
// Skip this for external shared entities
|
|
if (module->m_externalTypes.IndexOf(func->objectType) >= 0)
|
|
return;
|
|
|
|
char bits = 0;
|
|
bits += func->IsShared() ? 1 : 0;
|
|
bits += func->dontCleanUpOnException ? 2 : 0;
|
|
if (module->m_externalFunctions.IndexOf(func) >= 0)
|
|
bits += 4;
|
|
if (func->scriptData->objVariablePos.GetLength() || func->scriptData->objVariableInfo.GetLength())
|
|
bits += 8;
|
|
if (func->scriptData->tryCatchInfo.GetLength())
|
|
bits += 16;
|
|
bits += func->IsExplicit() ? 32 : 0;
|
|
WriteData(&bits, 1);
|
|
|
|
// For external shared functions the rest is not needed
|
|
if (bits & 4)
|
|
return;
|
|
|
|
// Calculate the adjustment by position lookup table
|
|
CalculateAdjustmentByPos(func);
|
|
|
|
WriteByteCode(func);
|
|
|
|
asDWORD varSpace = AdjustStackPosition(func->scriptData->variableSpace);
|
|
WriteEncodedInt64(varSpace);
|
|
|
|
if (bits & 8)
|
|
{
|
|
count = (asUINT)func->scriptData->objVariablePos.GetLength();
|
|
WriteEncodedInt64(count);
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
WriteTypeInfo(func->scriptData->objVariableTypes[i]);
|
|
WriteEncodedInt64(AdjustStackPosition(func->scriptData->objVariablePos[i]));
|
|
}
|
|
if (count > 0)
|
|
WriteEncodedInt64(func->scriptData->objVariablesOnHeap);
|
|
|
|
WriteEncodedInt64((asUINT)func->scriptData->objVariableInfo.GetLength());
|
|
for (i = 0; i < func->scriptData->objVariableInfo.GetLength(); ++i)
|
|
{
|
|
// The program position must be adjusted to be in number of instructions
|
|
WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->objVariableInfo[i].programPos]);
|
|
WriteEncodedInt64(AdjustStackPosition(func->scriptData->objVariableInfo[i].variableOffset));
|
|
WriteEncodedInt64(func->scriptData->objVariableInfo[i].option);
|
|
}
|
|
}
|
|
|
|
if (bits & 16)
|
|
{
|
|
// Write info on try/catch blocks
|
|
WriteEncodedInt64((asUINT)func->scriptData->tryCatchInfo.GetLength());
|
|
for (i = 0; i < func->scriptData->tryCatchInfo.GetLength(); ++i)
|
|
{
|
|
// The program position must be adjusted to be in number of instructions
|
|
WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->tryCatchInfo[i].tryPos]);
|
|
WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->tryCatchInfo[i].catchPos]);
|
|
}
|
|
}
|
|
|
|
// The program position (every even number) needs to be adjusted
|
|
// to be in number of instructions instead of DWORD offset
|
|
if( !stripDebugInfo )
|
|
{
|
|
asUINT length = (asUINT)func->scriptData->lineNumbers.GetLength();
|
|
WriteEncodedInt64(length);
|
|
for( i = 0; i < length; ++i )
|
|
{
|
|
if( (i & 1) == 0 )
|
|
WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->lineNumbers[i]]);
|
|
else
|
|
WriteEncodedInt64(func->scriptData->lineNumbers[i]);
|
|
}
|
|
|
|
// Write the array of script sections
|
|
length = (asUINT)func->scriptData->sectionIdxs.GetLength();
|
|
WriteEncodedInt64(length);
|
|
for( i = 0; i < length; ++i )
|
|
{
|
|
if( (i & 1) == 0 )
|
|
WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->sectionIdxs[i]]);
|
|
else
|
|
{
|
|
if( func->scriptData->sectionIdxs[i] >= 0 )
|
|
WriteString(engine->scriptSectionNames[func->scriptData->sectionIdxs[i]]);
|
|
else
|
|
{
|
|
c = 0;
|
|
WriteData(&c, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write the variable information
|
|
if( !stripDebugInfo )
|
|
{
|
|
WriteEncodedInt64((asUINT)func->scriptData->variables.GetLength());
|
|
for( i = 0; i < func->scriptData->variables.GetLength(); i++ )
|
|
{
|
|
// The program position must be adjusted to be in number of instructions
|
|
WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->variables[i]->declaredAtProgramPos]);
|
|
// The stack position must be adjusted according to the pointer sizes
|
|
WriteEncodedInt64(AdjustStackPosition(func->scriptData->variables[i]->stackOffset));
|
|
WriteString(&func->scriptData->variables[i]->name);
|
|
WriteDataType(&func->scriptData->variables[i]->type);
|
|
}
|
|
}
|
|
|
|
// Store script section name
|
|
if( !stripDebugInfo )
|
|
{
|
|
if( func->scriptData->scriptSectionIdx >= 0 )
|
|
WriteString(engine->scriptSectionNames[func->scriptData->scriptSectionIdx]);
|
|
else
|
|
{
|
|
c = 0;
|
|
WriteData(&c, 1);
|
|
}
|
|
WriteEncodedInt64(func->scriptData->declaredAt);
|
|
}
|
|
|
|
// Store the parameter names
|
|
if( !stripDebugInfo )
|
|
{
|
|
count = asUINT(func->parameterNames.GetLength());
|
|
WriteEncodedInt64(count);
|
|
for( asUINT n = 0; n < count; n++ )
|
|
WriteString(&func->parameterNames[n]);
|
|
}
|
|
}
|
|
else if( func->funcType == asFUNC_VIRTUAL || func->funcType == asFUNC_INTERFACE )
|
|
{
|
|
// TODO: Do we really need to store this? It can probably be reconstructed by the reader
|
|
WriteEncodedInt64(func->vfTableIdx);
|
|
}
|
|
else if( func->funcType == asFUNC_FUNCDEF )
|
|
{
|
|
char bits = 0;
|
|
bits += func->IsShared() ? 1 : 0;
|
|
if (module->m_externalTypes.IndexOf(func->funcdefType) >= 0)
|
|
bits += 2;
|
|
WriteData(&bits,1);
|
|
}
|
|
}
|
|
|
|
void asCWriter::WriteTypeDeclaration(asCTypeInfo *type, int phase)
|
|
{
|
|
if( phase == 1 )
|
|
{
|
|
// name
|
|
WriteString(&type->name);
|
|
// flags
|
|
WriteData(&type->flags, 4);
|
|
|
|
// size
|
|
// TODO: Do we really need to store this? The reader should be able to
|
|
// determine the correct size from the object type's flags
|
|
if( (type->flags & asOBJ_SCRIPT_OBJECT) && type->size > 0 )
|
|
{
|
|
// The size for script objects may vary from platform to platform so
|
|
// only store 1 to diferentiate from interfaces that have size 0.
|
|
WriteEncodedInt64(1);
|
|
}
|
|
else
|
|
{
|
|
// Enums, typedefs, and interfaces have fixed sizes independently
|
|
// of platform so it is safe to serialize the size directly.
|
|
WriteEncodedInt64(type->size);
|
|
}
|
|
|
|
// namespace
|
|
WriteString(&type->nameSpace->name);
|
|
|
|
// external shared flag
|
|
if ((type->flags & asOBJ_SHARED))
|
|
{
|
|
char c = ' ';
|
|
if (module->m_externalTypes.IndexOf(type) >= 0)
|
|
c = 'e';
|
|
WriteData(&c, 1);
|
|
}
|
|
}
|
|
else if( phase == 2 )
|
|
{
|
|
// external shared types doesn't need to save this
|
|
if ((type->flags & asOBJ_SHARED) && module->m_externalTypes.IndexOf(type) >= 0)
|
|
return;
|
|
|
|
if(type->flags & asOBJ_ENUM )
|
|
{
|
|
// enumValues[]
|
|
asCEnumType *t = CastToEnumType(type);
|
|
int size = (int)t->enumValues.GetLength();
|
|
WriteEncodedInt64(size);
|
|
|
|
for( int n = 0; n < size; n++ )
|
|
{
|
|
WriteString(&t->enumValues[n]->name);
|
|
WriteData(&t->enumValues[n]->value, 4);
|
|
}
|
|
}
|
|
else if(type->flags & asOBJ_TYPEDEF )
|
|
{
|
|
asCTypedefType *td = CastToTypedefType(type);
|
|
eTokenType t = td->aliasForType.GetTokenType();
|
|
WriteEncodedInt64(t);
|
|
}
|
|
else
|
|
{
|
|
asCObjectType *t = CastToObjectType(type);
|
|
WriteTypeInfo(t->derivedFrom);
|
|
|
|
// interfaces[] / interfaceVFTOffsets[]
|
|
// TOOD: Is it really necessary to store the VFTOffsets? Can't the reader calculate those?
|
|
int size = (asUINT)t->interfaces.GetLength();
|
|
WriteEncodedInt64(size);
|
|
asUINT n;
|
|
asASSERT( t->IsInterface() || t->interfaces.GetLength() == t->interfaceVFTOffsets.GetLength() );
|
|
for( n = 0; n < t->interfaces.GetLength(); n++ )
|
|
{
|
|
WriteTypeInfo(t->interfaces[n]);
|
|
if( !t->IsInterface() )
|
|
WriteEncodedInt64(t->interfaceVFTOffsets[n]);
|
|
}
|
|
|
|
// behaviours
|
|
// TODO: Default behaviours should just be stored as a indicator
|
|
// to avoid storing the actual function object
|
|
if( !t->IsInterface() && type->flags != asOBJ_TYPEDEF && type->flags != asOBJ_ENUM )
|
|
{
|
|
WriteFunction(engine->scriptFunctions[t->beh.destruct]);
|
|
size = (int)t->beh.constructors.GetLength();
|
|
WriteEncodedInt64(size);
|
|
for( n = 0; n < t->beh.constructors.GetLength(); n++ )
|
|
{
|
|
WriteFunction(engine->scriptFunctions[t->beh.constructors[n]]);
|
|
WriteFunction(engine->scriptFunctions[t->beh.factories[n]]);
|
|
}
|
|
}
|
|
|
|
// methods[]
|
|
// TODO: Avoid storing inherited methods in interfaces, as the reader
|
|
// can add those directly from the base interface
|
|
size = (int)t->methods.GetLength();
|
|
WriteEncodedInt64(size);
|
|
for( n = 0; n < t->methods.GetLength(); n++ )
|
|
{
|
|
WriteFunction(engine->scriptFunctions[t->methods[n]]);
|
|
}
|
|
|
|
// virtualFunctionTable[]
|
|
// TODO: Is it really necessary to store this? Can't it be easily rebuilt by the reader
|
|
size = (int)t->virtualFunctionTable.GetLength();
|
|
WriteEncodedInt64(size);
|
|
for( n = 0; n < (asUINT)size; n++ )
|
|
{
|
|
WriteFunction(t->virtualFunctionTable[n]);
|
|
}
|
|
}
|
|
}
|
|
else if( phase == 3 )
|
|
{
|
|
// external shared types doesn't need to save this
|
|
if ((type->flags & asOBJ_SHARED) && module->m_externalTypes.IndexOf(type) >= 0)
|
|
return;
|
|
|
|
// properties[]
|
|
asCObjectType *t = CastToObjectType(type);
|
|
|
|
// This is only done for object types
|
|
asASSERT(t);
|
|
|
|
asUINT size = (asUINT)t->properties.GetLength();
|
|
WriteEncodedInt64(size);
|
|
for (asUINT n = 0; n < t->properties.GetLength(); n++)
|
|
{
|
|
WriteObjectProperty(t->properties[n]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCWriter::WriteEncodedInt64(asINT64 i)
|
|
{
|
|
asBYTE signBit = ( i & asINT64(1)<<63 ) ? 0x80 : 0;
|
|
if( signBit ) i = -i;
|
|
|
|
asBYTE b;
|
|
if( i < (1<<6) )
|
|
{
|
|
b = (asBYTE)(signBit + i); WriteData(&b, 1);
|
|
}
|
|
else if( i < (1<<13) )
|
|
{
|
|
b = asBYTE(0x40 + signBit + (i >> 8)); WriteData(&b, 1);
|
|
b = asBYTE(i & 0xFF); WriteData(&b, 1);
|
|
}
|
|
else if( i < (1<<20) )
|
|
{
|
|
b = asBYTE(0x60 + signBit + (i >> 16)); WriteData(&b, 1);
|
|
b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE(i & 0xFF); WriteData(&b, 1);
|
|
}
|
|
else if( i < (1<<27) )
|
|
{
|
|
b = asBYTE(0x70 + signBit + (i >> 24)); WriteData(&b, 1);
|
|
b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE(i & 0xFF); WriteData(&b, 1);
|
|
}
|
|
else if( i < (asINT64(1)<<34) )
|
|
{
|
|
b = asBYTE(0x78 + signBit + (i >> 32)); WriteData(&b, 1);
|
|
b = asBYTE((i >> 24) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE(i & 0xFF); WriteData(&b, 1);
|
|
}
|
|
else if( i < (asINT64(1)<<41) )
|
|
{
|
|
b = asBYTE(0x7C + signBit + (i >> 40)); WriteData(&b, 1);
|
|
b = asBYTE((i >> 32) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 24) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE(i & 0xFF); WriteData(&b, 1);
|
|
}
|
|
else if( i < (asINT64(1)<<48) )
|
|
{
|
|
b = asBYTE(0x7E + signBit + (i >> 48)); WriteData(&b, 1);
|
|
b = asBYTE((i >> 40) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 32) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 24) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE(i & 0xFF); WriteData(&b, 1);
|
|
}
|
|
else
|
|
{
|
|
b = asBYTE(0x7F + signBit); WriteData(&b, 1);
|
|
b = asBYTE((i >> 56) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 48) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 40) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 32) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 24) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1);
|
|
b = asBYTE(i & 0xFF); WriteData(&b, 1);
|
|
}
|
|
}
|
|
|
|
void asCWriter::WriteString(asCString* str)
|
|
{
|
|
// First check if the string hasn't been saved already
|
|
asSMapNode<asCString, int> *cursor = 0;
|
|
if (stringToIdMap.MoveTo(&cursor, *str))
|
|
{
|
|
// Save a reference to the existing string
|
|
// The lowest bit is set to 1 to indicate a reference
|
|
WriteEncodedInt64(cursor->value*2+1);
|
|
return;
|
|
}
|
|
|
|
// Save a new string
|
|
// The lowest bit is set to 0 to indicate a new string
|
|
asUINT len = (asUINT)str->GetLength();
|
|
WriteEncodedInt64(len*2);
|
|
|
|
if( len > 0 )
|
|
{
|
|
stream->Write(str->AddressOf(), (asUINT)len);
|
|
bytesWritten += len;
|
|
|
|
savedStrings.PushLast(*str);
|
|
stringToIdMap.Insert(*str, int(savedStrings.GetLength()) - 1);
|
|
}
|
|
}
|
|
|
|
void asCWriter::WriteGlobalProperty(asCGlobalProperty* prop)
|
|
{
|
|
// TODO: We might be able to avoid storing the name and type of the global
|
|
// properties twice if we merge this with the WriteUsedGlobalProperties.
|
|
WriteString(&prop->name);
|
|
WriteString(&prop->nameSpace->name);
|
|
WriteDataType(&prop->type);
|
|
|
|
// Store the initialization function
|
|
WriteFunction(prop->GetInitFunc());
|
|
}
|
|
|
|
void asCWriter::WriteObjectProperty(asCObjectProperty* prop)
|
|
{
|
|
WriteString(&prop->name);
|
|
WriteDataType(&prop->type);
|
|
int flags = 0;
|
|
if( prop->isPrivate ) flags |= 1;
|
|
if( prop->isProtected ) flags |= 2;
|
|
if( prop->isInherited ) flags |= 4;
|
|
WriteEncodedInt64(flags);
|
|
}
|
|
|
|
void asCWriter::WriteDataType(const asCDataType *dt)
|
|
{
|
|
// First check if the datatype has already been saved
|
|
for( asUINT n = 0; n < savedDataTypes.GetLength(); n++ )
|
|
{
|
|
if( *dt == savedDataTypes[n] )
|
|
{
|
|
WriteEncodedInt64(n+1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Indicate a new type with a null byte
|
|
asUINT c = 0;
|
|
WriteEncodedInt64(c);
|
|
|
|
// Save the new datatype
|
|
savedDataTypes.PushLast(*dt);
|
|
|
|
int t = dt->GetTokenType();
|
|
WriteEncodedInt64(t);
|
|
if( t == ttIdentifier )
|
|
WriteTypeInfo(dt->GetTypeInfo());
|
|
|
|
// Endianess safe bitmask
|
|
char bits = 0;
|
|
SAVE_TO_BIT(bits, dt->IsObjectHandle(), 0);
|
|
SAVE_TO_BIT(bits, dt->IsHandleToConst(), 1);
|
|
SAVE_TO_BIT(bits, dt->IsReference(), 2);
|
|
SAVE_TO_BIT(bits, dt->IsReadOnly(), 3);
|
|
WriteData(&bits, 1);
|
|
}
|
|
|
|
void asCWriter::WriteTypeInfo(asCTypeInfo* ti)
|
|
{
|
|
char ch;
|
|
|
|
if( ti )
|
|
{
|
|
// Check for template instances/specializations
|
|
asCObjectType *ot = CastToObjectType(ti);
|
|
if( ot && ot->templateSubTypes.GetLength() )
|
|
{
|
|
// Check for list pattern type or template type
|
|
if( ot->flags & asOBJ_LIST_PATTERN )
|
|
{
|
|
ch = 'l'; // list
|
|
WriteData(&ch, 1);
|
|
WriteTypeInfo(ot->templateSubTypes[0].GetTypeInfo());
|
|
}
|
|
else
|
|
{
|
|
ch = 'a'; // array
|
|
WriteData(&ch, 1);
|
|
WriteString(&ot->name);
|
|
WriteString(&ot->nameSpace->name);
|
|
|
|
WriteEncodedInt64(ot->templateSubTypes.GetLength());
|
|
for( asUINT n = 0; n < ot->templateSubTypes.GetLength(); n++ )
|
|
{
|
|
if( !ot->templateSubTypes[n].IsPrimitive() || ot->templateSubTypes[n].IsEnumType() )
|
|
{
|
|
ch = 's'; // sub type
|
|
WriteData(&ch, 1);
|
|
WriteDataType(&ot->templateSubTypes[n]);
|
|
}
|
|
else
|
|
{
|
|
ch = 't'; // token
|
|
WriteData(&ch, 1);
|
|
eTokenType t = ot->templateSubTypes[n].GetTokenType();
|
|
WriteEncodedInt64(t);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( ti->flags & asOBJ_TEMPLATE_SUBTYPE )
|
|
{
|
|
ch = 's'; // sub type
|
|
WriteData(&ch, 1);
|
|
WriteString(&ti->name);
|
|
}
|
|
else if( !ti->GetParentType() )
|
|
{
|
|
ch = 'o'; // object
|
|
WriteData(&ch, 1);
|
|
WriteString(&ti->name);
|
|
WriteString(&ti->nameSpace->name);
|
|
}
|
|
else
|
|
{
|
|
asASSERT(ti->flags & asOBJ_FUNCDEF);
|
|
|
|
ch = 'c'; // child type
|
|
WriteData(&ch, 1);
|
|
WriteString(&ti->name);
|
|
WriteTypeInfo(CastToFuncdefType(ti)->parentClass);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ch = '\0';
|
|
WriteData(&ch, 1);
|
|
}
|
|
}
|
|
|
|
void asCWriter::CalculateAdjustmentByPos(asCScriptFunction *func)
|
|
{
|
|
// Adjust the offset of all negative variables (parameters) so all pointers will have a size of 1 dword
|
|
asUINT n;
|
|
asCArray<int> adjustments;
|
|
asUINT offset = 0;
|
|
if( func->objectType )
|
|
{
|
|
adjustments.PushLast(offset);
|
|
adjustments.PushLast(1-AS_PTR_SIZE);
|
|
offset += AS_PTR_SIZE;
|
|
}
|
|
if( func->DoesReturnOnStack() )
|
|
{
|
|
adjustments.PushLast(offset);
|
|
adjustments.PushLast(1-AS_PTR_SIZE);
|
|
offset += AS_PTR_SIZE;
|
|
}
|
|
for( n = 0; n < func->parameterTypes.GetLength(); n++ )
|
|
{
|
|
if( !func->parameterTypes[n].IsPrimitive() ||
|
|
func->parameterTypes[n].IsReference() )
|
|
{
|
|
adjustments.PushLast(offset);
|
|
adjustments.PushLast(1-AS_PTR_SIZE);
|
|
offset += AS_PTR_SIZE;
|
|
}
|
|
else
|
|
{
|
|
asASSERT( func->parameterTypes[n].IsPrimitive() );
|
|
offset += func->parameterTypes[n].GetSizeOnStackDWords();
|
|
}
|
|
}
|
|
|
|
// Build look-up table with the adjustments for each stack position
|
|
adjustNegativeStackByPos.SetLength(offset);
|
|
memset(adjustNegativeStackByPos.AddressOf(), 0, adjustNegativeStackByPos.GetLength()*sizeof(int));
|
|
for( n = 0; n < adjustments.GetLength(); n+=2 )
|
|
{
|
|
int pos = adjustments[n];
|
|
int adjust = adjustments[n+1];
|
|
|
|
for( asUINT i = pos+1; i < adjustNegativeStackByPos.GetLength(); i++ )
|
|
adjustNegativeStackByPos[i] += adjust;
|
|
}
|
|
|
|
// Adjust the offset of all positive variables so that all object types and handles have a size of 1 dword
|
|
// This is similar to how the adjustment is done in the asCReader::TranslateFunction, only the reverse
|
|
adjustments.SetLength(0);
|
|
for( n = 0; n < func->scriptData->objVariableTypes.GetLength(); n++ )
|
|
{
|
|
// Determine the size the variable currently occupies on the stack
|
|
int size = AS_PTR_SIZE;
|
|
|
|
// objVariableTypes is null if the variable type is a null pointer
|
|
if( func->scriptData->objVariableTypes[n] &&
|
|
(func->scriptData->objVariableTypes[n]->GetFlags() & asOBJ_VALUE) &&
|
|
n >= func->scriptData->objVariablesOnHeap )
|
|
{
|
|
size = func->scriptData->objVariableTypes[n]->GetSize();
|
|
if( size < 4 )
|
|
size = 1;
|
|
else
|
|
size /= 4;
|
|
}
|
|
|
|
// If larger than 1 dword, adjust the offsets accordingly
|
|
if (size > 1)
|
|
{
|
|
// How much needs to be adjusted?
|
|
adjustments.PushLast(func->scriptData->objVariablePos[n]);
|
|
adjustments.PushLast(-(size - 1));
|
|
}
|
|
}
|
|
|
|
// Build look-up table with the adjustments for each stack position
|
|
adjustStackByPos.SetLength(func->scriptData->stackNeeded);
|
|
memset(adjustStackByPos.AddressOf(), 0, adjustStackByPos.GetLength()*sizeof(int));
|
|
for( n = 0; n < adjustments.GetLength(); n+=2 )
|
|
{
|
|
int pos = adjustments[n];
|
|
int adjust = adjustments[n+1];
|
|
|
|
for( asUINT i = pos; i < adjustStackByPos.GetLength(); i++ )
|
|
adjustStackByPos[i] += adjust;
|
|
}
|
|
|
|
// Compute the sequence number of each bytecode instruction in order to update the jump offsets
|
|
asUINT length = func->scriptData->byteCode.GetLength();
|
|
asDWORD *bc = func->scriptData->byteCode.AddressOf();
|
|
bytecodeNbrByPos.SetLength(length);
|
|
asUINT num;
|
|
for( offset = 0, num = 0; offset < length; )
|
|
{
|
|
bytecodeNbrByPos[offset] = num;
|
|
offset += asBCTypeSize[asBCInfo[*(asBYTE*)(bc+offset)].type];
|
|
num++;
|
|
}
|
|
|
|
// Store the number of instructions in the last position of bytecodeNbrByPos,
|
|
// so this can be easily queried in SaveBytecode. Normally this is already done
|
|
// as most functions end with BC_RET, but in some cases the last instruction in
|
|
// the function is not a BC_RET, e.g. when a function has a never ending loop.
|
|
bytecodeNbrByPos[length - 1] = num - 1;
|
|
}
|
|
|
|
int asCWriter::AdjustStackPosition(int pos)
|
|
{
|
|
if( pos >= (int)adjustStackByPos.GetLength() )
|
|
{
|
|
// This happens for example if the function only have temporary variables
|
|
// The adjustByPos can also be empty if the function doesn't have any variables at all, but receive a handle by parameter
|
|
if( adjustStackByPos.GetLength() > 0 )
|
|
pos += adjustStackByPos[adjustStackByPos.GetLength()-1];
|
|
}
|
|
else if( pos >= 0 )
|
|
pos += adjustStackByPos[pos];
|
|
else
|
|
{
|
|
asASSERT( -pos < (int)adjustNegativeStackByPos.GetLength() );
|
|
pos -= (short)adjustNegativeStackByPos[-pos];
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD programPos)
|
|
{
|
|
// TODO: optimize: multiple instructions for the same function doesn't need to look for the function everytime
|
|
// the function can remember where it found the function and check if the programPos is still valid
|
|
|
|
// Get offset 0 doesn't need adjustment
|
|
if( offset == 0 ) return 0;
|
|
|
|
bool bcAlloc = false;
|
|
|
|
// Find out which function that will be called
|
|
asCScriptFunction *calledFunc = 0;
|
|
int stackDelta = 0;
|
|
for( asUINT n = programPos; n < func->scriptData->byteCode.GetLength(); )
|
|
{
|
|
asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n];
|
|
if( bc == asBC_CALL ||
|
|
bc == asBC_CALLSYS ||
|
|
bc == asBC_Thiscall1 ||
|
|
bc == asBC_CALLINTF )
|
|
{
|
|
// Find the function from the function id in bytecode
|
|
int funcId = asBC_INTARG(&func->scriptData->byteCode[n]);
|
|
calledFunc = engine->scriptFunctions[funcId];
|
|
break;
|
|
}
|
|
else if( bc == asBC_ALLOC )
|
|
{
|
|
// The alloc instruction doesn't take the object pointer on the stack,
|
|
// as the memory will be allocated by the instruction itself
|
|
bcAlloc = true;
|
|
|
|
// Find the function from the function id in the bytecode
|
|
int funcId = asBC_INTARG(&func->scriptData->byteCode[n+AS_PTR_SIZE]);
|
|
calledFunc = engine->scriptFunctions[funcId];
|
|
break;
|
|
}
|
|
else if( bc == asBC_CALLBND )
|
|
{
|
|
// Find the function from the engine's bind array
|
|
int funcId = asBC_INTARG(&func->scriptData->byteCode[n]);
|
|
calledFunc = engine->importedFunctions[funcId & ~FUNC_IMPORTED]->importedFunctionSignature;
|
|
break;
|
|
}
|
|
else if( bc == asBC_CallPtr )
|
|
{
|
|
int var = asBC_SWORDARG0(&func->scriptData->byteCode[n]);
|
|
asUINT v;
|
|
// Find the funcdef from the local variable
|
|
for( v = 0; v < func->scriptData->objVariablePos.GetLength(); v++ )
|
|
{
|
|
if( func->scriptData->objVariablePos[v] == var )
|
|
{
|
|
calledFunc = CastToFuncdefType(func->scriptData->objVariableTypes[v])->funcdef;
|
|
break;
|
|
}
|
|
}
|
|
if( !calledFunc )
|
|
{
|
|
// Look in parameters
|
|
int paramPos = 0;
|
|
if( func->objectType )
|
|
paramPos -= AS_PTR_SIZE;
|
|
if( func->DoesReturnOnStack() )
|
|
paramPos -= AS_PTR_SIZE;
|
|
for( v = 0; v < func->parameterTypes.GetLength(); v++ )
|
|
{
|
|
if( var == paramPos )
|
|
{
|
|
calledFunc = CastToFuncdefType(func->parameterTypes[v].GetTypeInfo())->funcdef;
|
|
break;
|
|
}
|
|
paramPos -= func->parameterTypes[v].GetSizeOnStackDWords();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else if( bc == asBC_REFCPY ||
|
|
bc == asBC_COPY )
|
|
{
|
|
// In this case we know there is only 1 pointer on the stack above
|
|
asASSERT( offset == AS_PTR_SIZE );
|
|
return offset + (1 - AS_PTR_SIZE);
|
|
}
|
|
|
|
// Keep track of the stack size between the
|
|
// instruction that needs to be adjusted and the call
|
|
stackDelta += asBCInfo[bc].stackInc;
|
|
|
|
n += asBCTypeSize[asBCInfo[bc].type];
|
|
}
|
|
|
|
asASSERT( calledFunc );
|
|
|
|
// Count the number of pointers pushed on the stack above the
|
|
// current offset, and then adjust the offset accordingly
|
|
asUINT numPtrs = 0;
|
|
int currOffset = -stackDelta;
|
|
if( offset > currOffset && calledFunc->GetObjectType() && !bcAlloc )
|
|
{
|
|
currOffset += AS_PTR_SIZE;
|
|
if( currOffset > 0 )
|
|
numPtrs++;
|
|
}
|
|
if( offset > currOffset && calledFunc->DoesReturnOnStack() )
|
|
{
|
|
currOffset += AS_PTR_SIZE;
|
|
if( currOffset > 0 )
|
|
numPtrs++;
|
|
}
|
|
for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ )
|
|
{
|
|
if( offset <= currOffset ) break;
|
|
|
|
if( !calledFunc->parameterTypes[p].IsPrimitive() ||
|
|
calledFunc->parameterTypes[p].IsReference() )
|
|
{
|
|
// objects and references are passed by pointer
|
|
currOffset += AS_PTR_SIZE;
|
|
if( currOffset > 0 )
|
|
numPtrs++;
|
|
|
|
// The variable arg ? has an additional 32bit int with the typeid
|
|
if( calledFunc->parameterTypes[p].IsAnyType() )
|
|
currOffset += 1;
|
|
}
|
|
else
|
|
{
|
|
// built-in primitives or enums are passed by value
|
|
asASSERT( calledFunc->parameterTypes[p].IsPrimitive() );
|
|
currOffset += calledFunc->parameterTypes[p].GetSizeOnStackDWords();
|
|
}
|
|
}
|
|
|
|
// The get offset must match one of the parameter offsets
|
|
asASSERT( offset == currOffset );
|
|
|
|
return offset + numPtrs * (1 - AS_PTR_SIZE);
|
|
}
|
|
|
|
void asCWriter::WriteByteCode(asCScriptFunction *func)
|
|
{
|
|
asDWORD *bc = func->scriptData->byteCode.AddressOf();
|
|
size_t length = func->scriptData->byteCode.GetLength();
|
|
|
|
// The length cannot be stored, because it is platform dependent,
|
|
// instead we store the number of instructions
|
|
asUINT count = bytecodeNbrByPos[bytecodeNbrByPos.GetLength()-1] + 1;
|
|
WriteEncodedInt64(count);
|
|
|
|
asDWORD *startBC = bc;
|
|
while( length )
|
|
{
|
|
asDWORD tmpBC[4]; // The biggest instructions take up 4 DWORDs
|
|
asDWORD c = *(asBYTE*)bc;
|
|
|
|
// Copy the instruction to a temp buffer so we can work on it before saving
|
|
memcpy(tmpBC, bc, asBCTypeSize[asBCInfo[c].type]*sizeof(asDWORD));
|
|
|
|
if( c == asBC_ALLOC ) // PTR_DW_ARG
|
|
{
|
|
// Translate the object type
|
|
asCObjectType *ot = *(asCObjectType**)(tmpBC+1);
|
|
*(asPWORD*)(tmpBC+1) = FindTypeInfoIdx(ot);
|
|
|
|
// Translate the constructor func id, unless it is 0
|
|
if( *(int*)&tmpBC[1+AS_PTR_SIZE] != 0 )
|
|
{
|
|
// Increment 1 to the translated function id, as 0 will be reserved for no function
|
|
*(int*)&tmpBC[1+AS_PTR_SIZE] = 1+FindFunctionIndex(engine->scriptFunctions[*(int*)&tmpBC[1+AS_PTR_SIZE]]);
|
|
}
|
|
}
|
|
else if( c == asBC_REFCPY || // PTR_ARG
|
|
c == asBC_RefCpyV || // wW_PTR_ARG
|
|
c == asBC_OBJTYPE ) // PTR_ARG
|
|
{
|
|
// Translate object type pointers into indices
|
|
*(asPWORD*)(tmpBC+1) = FindTypeInfoIdx(*(asCObjectType**)(tmpBC+1));
|
|
}
|
|
else if( c == asBC_JitEntry ) // PTR_ARG
|
|
{
|
|
// We don't store the JIT argument
|
|
*(asPWORD*)(tmpBC+1) = 0;
|
|
}
|
|
else if( c == asBC_TYPEID || // DW_ARG
|
|
c == asBC_Cast ) // DW_ARG
|
|
{
|
|
// Translate type ids into indices
|
|
*(int*)(tmpBC+1) = FindTypeIdIdx(*(int*)(tmpBC+1));
|
|
}
|
|
else if( c == asBC_ADDSi || // W_DW_ARG
|
|
c == asBC_LoadThisR ) // W_DW_ARG
|
|
{
|
|
// Translate property offsets into indices
|
|
*(((short*)tmpBC)+1) = (short)FindObjectPropIndex(*(((short*)tmpBC)+1), *(int*)(tmpBC+1), bc);
|
|
|
|
// Translate type ids into indices
|
|
*(int*)(tmpBC+1) = FindTypeIdIdx(*(int*)(tmpBC+1));
|
|
}
|
|
else if( c == asBC_LoadRObjR || // rW_W_DW_ARG
|
|
c == asBC_LoadVObjR ) // rW_W_DW_ARG
|
|
{
|
|
asCObjectType *ot = engine->GetObjectTypeFromTypeId(*(int*)(tmpBC+2));
|
|
if( ot->flags & asOBJ_LIST_PATTERN )
|
|
{
|
|
// List patterns have a different way of translating the offsets
|
|
SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1];
|
|
*(((short*)tmpBC)+2) = (short)listAdj->AdjustOffset(*(((short*)tmpBC)+2), ot);
|
|
}
|
|
else
|
|
{
|
|
// Translate property offsets into indices
|
|
*(((short*)tmpBC)+2) = (short)FindObjectPropIndex(*(((short*)tmpBC)+2), *(int*)(tmpBC+2), bc);
|
|
}
|
|
|
|
// Translate type ids into indices
|
|
*(int*)(tmpBC+2) = FindTypeIdIdx(*(int*)(tmpBC+2));
|
|
}
|
|
else if( c == asBC_COPY ) // W_DW_ARG
|
|
{
|
|
// Translate type ids into indices
|
|
*(int*)(tmpBC+1) = FindTypeIdIdx(*(int*)(tmpBC+1));
|
|
|
|
// Update the WORDARG0 to 0, as this will be recalculated on the target platform
|
|
asBC_WORDARG0(tmpBC) = 0;
|
|
}
|
|
else if( c == asBC_RET ) // W_ARG
|
|
{
|
|
// Save with arg 0, as this will be recalculated on the target platform
|
|
asBC_WORDARG0(tmpBC) = 0;
|
|
}
|
|
else if( c == asBC_CALL || // DW_ARG
|
|
c == asBC_CALLINTF || // DW_ARG
|
|
c == asBC_CALLSYS || // DW_ARG
|
|
c == asBC_Thiscall1 ) // DW_ARG
|
|
{
|
|
// Translate the function id
|
|
*(int*)(tmpBC+1) = FindFunctionIndex(engine->scriptFunctions[*(int*)(tmpBC+1)]);
|
|
}
|
|
else if( c == asBC_FuncPtr ) // PTR_ARG
|
|
{
|
|
// Translate the function pointer
|
|
*(asPWORD*)(tmpBC+1) = FindFunctionIndex(*(asCScriptFunction**)(tmpBC+1));
|
|
}
|
|
else if( c == asBC_CALLBND ) // DW_ARG
|
|
{
|
|
// Translate the function id
|
|
int funcId = tmpBC[1];
|
|
for( asUINT n = 0; n < module->m_bindInformations.GetLength(); n++ )
|
|
if( module->m_bindInformations[n]->importedFunctionSignature->id == funcId )
|
|
{
|
|
funcId = n;
|
|
break;
|
|
}
|
|
|
|
tmpBC[1] = funcId;
|
|
}
|
|
else if( c == asBC_PGA || // PTR_ARG
|
|
c == asBC_PshGPtr || // PTR_ARG
|
|
c == asBC_LDG || // PTR_ARG
|
|
c == asBC_PshG4 || // PTR_ARG
|
|
c == asBC_LdGRdR4 || // wW_PTR_ARG
|
|
c == asBC_CpyGtoV4 || // wW_PTR_ARG
|
|
c == asBC_CpyVtoG4 || // rW_PTR_ARG
|
|
c == asBC_SetG4 ) // PTR_DW_ARG
|
|
{
|
|
// Check if the address is a global property or a string constant
|
|
void *ptr = *(void**)(tmpBC + 1);
|
|
if (engine->varAddressMap.MoveTo(0, ptr))
|
|
{
|
|
// Translate global variable pointers into indices
|
|
// Flag the first bit to signal global property
|
|
*(asPWORD*)(tmpBC + 1) = (FindGlobalPropPtrIndex(*(void**)(tmpBC + 1)) << 1) + 1;
|
|
}
|
|
else
|
|
{
|
|
// Only PGA and PshGPtr can hold string constants
|
|
asASSERT(c == asBC_PGA || c == asBC_PshGPtr);
|
|
|
|
// Translate string constants into indices
|
|
// Leave the first bit clear to signal string constant
|
|
*(asPWORD*)(tmpBC + 1) = FindStringConstantIndex(*(void**)(tmpBC + 1)) << 1;
|
|
}
|
|
}
|
|
else if( c == asBC_JMP || // DW_ARG
|
|
c == asBC_JZ ||
|
|
c == asBC_JNZ ||
|
|
c == asBC_JLowZ ||
|
|
c == asBC_JLowNZ ||
|
|
c == asBC_JS ||
|
|
c == asBC_JNS ||
|
|
c == asBC_JP ||
|
|
c == asBC_JNP ) // The JMPP instruction doesn't need modification
|
|
{
|
|
// Get the DWORD offset from arg
|
|
int offset = *(int*)(tmpBC+1);
|
|
|
|
// Determine instruction number for next instruction and destination
|
|
int bcSeqNum = bytecodeNbrByPos[asUINT(bc - startBC)] + 1;
|
|
asDWORD *targetBC = bc + 2 + offset;
|
|
int targetBcSeqNum = bytecodeNbrByPos[asUINT(targetBC - startBC)];
|
|
|
|
// Set the offset in number of instructions
|
|
*(int*)(tmpBC+1) = targetBcSeqNum - bcSeqNum;
|
|
}
|
|
else if( c == asBC_GETOBJ || // W_ARG
|
|
c == asBC_GETOBJREF ||
|
|
c == asBC_GETREF ||
|
|
c == asBC_ChkNullS )
|
|
{
|
|
// Adjust the offset according to the function call that comes after
|
|
asBC_WORDARG0(tmpBC) = (asWORD)AdjustGetOffset(asBC_WORDARG0(tmpBC), func, asDWORD(bc - startBC));
|
|
}
|
|
else if( c == asBC_AllocMem )
|
|
{
|
|
// It's not necessary to store the size of the list buffer, as it will be recalculated in the reader
|
|
asBC_DWORDARG(tmpBC) = 0;
|
|
|
|
// Determine the type of the list pattern from the variable
|
|
short var = asBC_WORDARG0(tmpBC);
|
|
asCObjectType *ot = CastToObjectType(func->GetTypeInfoOfLocalVar(var));
|
|
|
|
// Create this helper object to adjust the offset of the elements accessed in the buffer
|
|
listAdjusters.PushLast(asNEW(SListAdjuster)(ot));
|
|
}
|
|
else if( c == asBC_FREE ) // wW_PTR_ARG
|
|
{
|
|
// Translate object type pointers into indices
|
|
asCObjectType *ot = *(asCObjectType**)(tmpBC+1);
|
|
*(asPWORD*)(tmpBC+1) = FindTypeInfoIdx(ot);
|
|
|
|
// Pop and destroy the list adjuster helper that was created with asBC_AllocMem
|
|
if( ot && (ot->flags & asOBJ_LIST_PATTERN) )
|
|
{
|
|
SListAdjuster *list = listAdjusters.PopLast();
|
|
asDELETE(list, SListAdjuster);
|
|
}
|
|
}
|
|
else if( c == asBC_SetListSize )
|
|
{
|
|
// Adjust the offset in the initialization list
|
|
SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1];
|
|
tmpBC[1] = listAdj->AdjustOffset(tmpBC[1], listAdj->patternType);
|
|
|
|
// Tell the adjuster how many repeated values there are
|
|
listAdj->SetRepeatCount(tmpBC[2]);
|
|
}
|
|
else if( c == asBC_PshListElmnt ) // W_DW_ARG
|
|
{
|
|
// Adjust the offset in the initialization list
|
|
SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1];
|
|
tmpBC[1] = listAdj->AdjustOffset(tmpBC[1], listAdj->patternType);
|
|
}
|
|
else if( c == asBC_SetListType )
|
|
{
|
|
// Adjust the offset in the initialization list
|
|
SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1];
|
|
tmpBC[1] = listAdj->AdjustOffset(tmpBC[1], listAdj->patternType);
|
|
|
|
// Inform the adjuster of the type id of the next element
|
|
listAdj->SetNextType(tmpBC[2]);
|
|
|
|
// Translate the type id
|
|
tmpBC[2] = FindTypeIdIdx(tmpBC[2]);
|
|
}
|
|
// Adjust the variable offsets
|
|
switch( asBCInfo[c].type )
|
|
{
|
|
case asBCTYPE_wW_ARG:
|
|
case asBCTYPE_rW_DW_ARG:
|
|
case asBCTYPE_wW_QW_ARG:
|
|
case asBCTYPE_rW_ARG:
|
|
case asBCTYPE_wW_DW_ARG:
|
|
case asBCTYPE_wW_W_ARG:
|
|
case asBCTYPE_rW_QW_ARG:
|
|
case asBCTYPE_rW_W_DW_ARG:
|
|
case asBCTYPE_rW_DW_DW_ARG:
|
|
{
|
|
asBC_SWORDARG0(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG0(tmpBC));
|
|
}
|
|
break;
|
|
|
|
case asBCTYPE_wW_rW_ARG:
|
|
case asBCTYPE_wW_rW_DW_ARG:
|
|
case asBCTYPE_rW_rW_ARG:
|
|
{
|
|
asBC_SWORDARG0(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG0(tmpBC));
|
|
asBC_SWORDARG1(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG1(tmpBC));
|
|
}
|
|
break;
|
|
|
|
case asBCTYPE_wW_rW_rW_ARG:
|
|
{
|
|
asBC_SWORDARG0(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG0(tmpBC));
|
|
asBC_SWORDARG1(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG1(tmpBC));
|
|
asBC_SWORDARG2(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG2(tmpBC));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// The other types don't treat variables so won't be modified
|
|
break;
|
|
}
|
|
|
|
// TODO: bytecode: Must make sure that floats and doubles are always stored the same way regardless of platform.
|
|
// Some platforms may not use the IEEE 754 standard, in which case it is necessary to encode the values
|
|
|
|
// Now store the instruction in the smallest possible way
|
|
switch( asBCInfo[c].type )
|
|
{
|
|
case asBCTYPE_NO_ARG:
|
|
{
|
|
// Just write 1 byte
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
}
|
|
break;
|
|
case asBCTYPE_W_ARG:
|
|
case asBCTYPE_wW_ARG:
|
|
case asBCTYPE_rW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the argument
|
|
short w = *(((short*)tmpBC)+1);
|
|
WriteEncodedInt64(w);
|
|
}
|
|
break;
|
|
case asBCTYPE_rW_DW_ARG:
|
|
case asBCTYPE_wW_DW_ARG:
|
|
case asBCTYPE_W_DW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the word argument
|
|
short w = *(((short*)tmpBC)+1);
|
|
WriteEncodedInt64(w);
|
|
|
|
// Write the dword argument
|
|
WriteEncodedInt64((int)tmpBC[1]);
|
|
}
|
|
break;
|
|
case asBCTYPE_DW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the argument
|
|
WriteEncodedInt64((int)tmpBC[1]);
|
|
}
|
|
break;
|
|
case asBCTYPE_DW_DW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the dword argument
|
|
WriteEncodedInt64((int)tmpBC[1]);
|
|
|
|
// Write the dword argument
|
|
WriteEncodedInt64((int)tmpBC[2]);
|
|
}
|
|
break;
|
|
case asBCTYPE_wW_rW_rW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the first argument
|
|
short w = *(((short*)tmpBC)+1);
|
|
WriteEncodedInt64(w);
|
|
|
|
// Write the second argument
|
|
w = *(((short*)tmpBC)+2);
|
|
WriteEncodedInt64(w);
|
|
|
|
// Write the third argument
|
|
w = *(((short*)tmpBC)+3);
|
|
WriteEncodedInt64(w);
|
|
}
|
|
break;
|
|
case asBCTYPE_wW_rW_ARG:
|
|
case asBCTYPE_rW_rW_ARG:
|
|
case asBCTYPE_wW_W_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the first argument
|
|
short w = *(((short*)tmpBC)+1);
|
|
WriteEncodedInt64(w);
|
|
|
|
// Write the second argument
|
|
w = *(((short*)tmpBC)+2);
|
|
WriteEncodedInt64(w);
|
|
}
|
|
break;
|
|
case asBCTYPE_wW_rW_DW_ARG:
|
|
case asBCTYPE_rW_W_DW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the first argument
|
|
short w = *(((short*)tmpBC)+1);
|
|
WriteEncodedInt64(w);
|
|
|
|
// Write the second argument
|
|
w = *(((short*)tmpBC)+2);
|
|
WriteEncodedInt64(w);
|
|
|
|
// Write the third argument
|
|
int dw = tmpBC[2];
|
|
WriteEncodedInt64(dw);
|
|
}
|
|
break;
|
|
case asBCTYPE_QW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the argument
|
|
asQWORD qw = *(asQWORD*)&tmpBC[1];
|
|
WriteEncodedInt64(qw);
|
|
}
|
|
break;
|
|
case asBCTYPE_QW_DW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the argument
|
|
asQWORD qw = *(asQWORD*)&tmpBC[1];
|
|
WriteEncodedInt64(qw);
|
|
|
|
// Write the second argument
|
|
int dw = tmpBC[3];
|
|
WriteEncodedInt64(dw);
|
|
}
|
|
break;
|
|
case asBCTYPE_rW_QW_ARG:
|
|
case asBCTYPE_wW_QW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the first argument
|
|
short w = *(((short*)tmpBC)+1);
|
|
WriteEncodedInt64(w);
|
|
|
|
// Write the argument
|
|
asQWORD qw = *(asQWORD*)&tmpBC[1];
|
|
WriteEncodedInt64(qw);
|
|
}
|
|
break;
|
|
case asBCTYPE_rW_DW_DW_ARG:
|
|
{
|
|
// Write the instruction code
|
|
asBYTE b = (asBYTE)c;
|
|
WriteData(&b, 1);
|
|
|
|
// Write the short argument
|
|
short w = *(((short*)tmpBC)+1);
|
|
WriteEncodedInt64(w);
|
|
|
|
// Write the dword argument
|
|
WriteEncodedInt64((int)tmpBC[1]);
|
|
|
|
// Write the dword argument
|
|
WriteEncodedInt64((int)tmpBC[2]);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// This should never happen
|
|
asASSERT(false);
|
|
|
|
// Store the bc as is
|
|
for( int n = 0; n < asBCTypeSize[asBCInfo[c].type]; n++ )
|
|
WriteData(&tmpBC[n], 4);
|
|
}
|
|
}
|
|
|
|
// Move to the next instruction
|
|
bc += asBCTypeSize[asBCInfo[c].type];
|
|
length -= asBCTypeSize[asBCInfo[c].type];
|
|
}
|
|
}
|
|
|
|
asCWriter::SListAdjuster::SListAdjuster(asCObjectType *ot) : patternType(ot), repeatCount(0), entries(0), lastOffset(-1), nextOffset(0), nextTypeId(-1)
|
|
{
|
|
asASSERT( ot && (ot->flags & asOBJ_LIST_PATTERN) );
|
|
|
|
// Find the first expected value in the list
|
|
asSListPatternNode *node = ot->engine->scriptFunctions[patternType->templateSubTypes[0].GetBehaviour()->listFactory]->listPattern;
|
|
asASSERT( node && node->type == asLPT_START );
|
|
patternNode = node->next;
|
|
}
|
|
|
|
int asCWriter::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatternType)
|
|
{
|
|
// TODO: cleanup: The listPatternType parameter is not needed
|
|
asASSERT( patternType == listPatternType );
|
|
UNUSED_VAR(listPatternType);
|
|
|
|
asASSERT( offset >= lastOffset );
|
|
|
|
// If it is the same offset being accessed again, just return the same adjusted value
|
|
if( offset == lastOffset )
|
|
return entries-1;
|
|
|
|
asASSERT( offset >= nextOffset );
|
|
|
|
// Update last offset for next call
|
|
lastOffset = offset;
|
|
|
|
// What is being expected at this position?
|
|
if( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME )
|
|
{
|
|
// Don't move the patternNode yet because the caller must make a call to SetRepeatCount too
|
|
nextOffset = offset + 4;
|
|
return entries++;
|
|
}
|
|
else if( patternNode->type == asLPT_TYPE )
|
|
{
|
|
const asCDataType &dt = reinterpret_cast<asSListPatternDataTypeNode*>(patternNode)->dataType;
|
|
if( dt.GetTokenType() == ttQuestion )
|
|
{
|
|
// The bytecode need to inform the type that will
|
|
// come next and then adjust that position too before
|
|
// we can move to the next node
|
|
if( nextTypeId != -1 )
|
|
{
|
|
nextOffset = offset + 4;
|
|
|
|
if( repeatCount > 0 )
|
|
repeatCount--;
|
|
|
|
// Only move the patternNode if we're not expecting any more repeated entries
|
|
if( repeatCount == 0 )
|
|
patternNode = patternNode->next;
|
|
|
|
nextTypeId = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( repeatCount > 0 )
|
|
{
|
|
// Was any value skipped?
|
|
asUINT size;
|
|
if( dt.IsObjectHandle() || (dt.GetTypeInfo() && (dt.GetTypeInfo()->flags & asOBJ_REF)) )
|
|
size = AS_PTR_SIZE*4;
|
|
else
|
|
size = dt.GetSizeInMemoryBytes();
|
|
|
|
int count = 0;
|
|
while( nextOffset <= offset )
|
|
{
|
|
count++;
|
|
nextOffset += size;
|
|
|
|
// Align the offset on 4 byte boundaries
|
|
if( size >= 4 && (nextOffset & 0x3) )
|
|
nextOffset += 4 - (nextOffset & 0x3);
|
|
}
|
|
|
|
if( --count > 0 )
|
|
{
|
|
// Skip these values
|
|
repeatCount -= count;
|
|
entries += count;
|
|
}
|
|
|
|
nextOffset = offset + size;
|
|
repeatCount--;
|
|
}
|
|
|
|
// Only move the patternNode if we're not expecting any more repeated entries
|
|
if( repeatCount == 0 )
|
|
patternNode = patternNode->next;
|
|
}
|
|
|
|
return entries++;
|
|
}
|
|
else if( patternNode->type == asLPT_START )
|
|
{
|
|
if( repeatCount > 0 )
|
|
repeatCount--;
|
|
SInfo info = {repeatCount, patternNode};
|
|
stack.PushLast(info);
|
|
|
|
repeatCount = 0;
|
|
patternNode = patternNode->next;
|
|
|
|
lastOffset--;
|
|
return AdjustOffset(offset, listPatternType);
|
|
}
|
|
else if( patternNode->type == asLPT_END )
|
|
{
|
|
SInfo info = stack.PopLast();
|
|
repeatCount = info.repeatCount;
|
|
if( repeatCount )
|
|
patternNode = info.startNode;
|
|
else
|
|
patternNode = patternNode->next;
|
|
|
|
lastOffset--;
|
|
return AdjustOffset(offset, listPatternType);
|
|
}
|
|
else
|
|
{
|
|
// Something is wrong with the pattern list declaration
|
|
asASSERT( false );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void asCWriter::SListAdjuster::SetRepeatCount(asUINT rc)
|
|
{
|
|
// Make sure the list is expecting a repeat at this location
|
|
asASSERT( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME );
|
|
|
|
// Now move to the next patternNode
|
|
patternNode = patternNode->next;
|
|
|
|
repeatCount = rc;
|
|
}
|
|
|
|
void asCWriter::SListAdjuster::SetNextType(int typeId)
|
|
{
|
|
// Make sure the list is expecting a type at this location
|
|
asASSERT( patternNode->type == asLPT_TYPE &&
|
|
reinterpret_cast<asSListPatternDataTypeNode*>(patternNode)->dataType.GetTokenType() == ttQuestion );
|
|
|
|
// Inform the type id for the next adjustment
|
|
nextTypeId = typeId;
|
|
}
|
|
|
|
void asCWriter::WriteUsedTypeIds()
|
|
{
|
|
TimeIt("asCWriter::WriteUsedTypeIds");
|
|
|
|
asUINT count = (asUINT)usedTypeIds.GetLength();
|
|
WriteEncodedInt64(count);
|
|
for( asUINT n = 0; n < count; n++ )
|
|
{
|
|
asCDataType dt = engine->GetDataTypeFromTypeId(usedTypeIds[n]);
|
|
WriteDataType(&dt);
|
|
}
|
|
}
|
|
|
|
int asCWriter::FindGlobalPropPtrIndex(void *ptr)
|
|
{
|
|
int i = usedGlobalProperties.IndexOf(ptr);
|
|
if( i >= 0 ) return i;
|
|
|
|
usedGlobalProperties.PushLast(ptr);
|
|
return (int)usedGlobalProperties.GetLength()-1;
|
|
}
|
|
|
|
void asCWriter::WriteUsedGlobalProps()
|
|
{
|
|
TimeIt("asCWriter::WriteUsedGlobalProps");
|
|
|
|
int c = (int)usedGlobalProperties.GetLength();
|
|
WriteEncodedInt64(c);
|
|
|
|
for( int n = 0; n < c; n++ )
|
|
{
|
|
asPWORD *p = (asPWORD*)usedGlobalProperties[n];
|
|
|
|
// Find the property descriptor from the address
|
|
asCGlobalProperty *prop = 0;
|
|
asSMapNode<void*, asCGlobalProperty*> *cursor;
|
|
if( engine->varAddressMap.MoveTo(&cursor, p) )
|
|
{
|
|
prop = engine->varAddressMap.GetValue(cursor);
|
|
}
|
|
|
|
asASSERT(prop);
|
|
|
|
// Store the name and type of the property so we can find it again on loading
|
|
WriteString(&prop->name);
|
|
WriteString(&prop->nameSpace->name);
|
|
WriteDataType(&prop->type);
|
|
|
|
// Also store whether the property is a module property or a registered property
|
|
char moduleProp = 0;
|
|
if( prop->realAddress == 0 )
|
|
moduleProp = 1;
|
|
WriteData(&moduleProp, 1);
|
|
}
|
|
}
|
|
|
|
void asCWriter::WriteUsedObjectProps()
|
|
{
|
|
TimeIt("asCWriter::WriteUsedObjectProps");
|
|
|
|
int c = (int)usedObjectProperties.GetLength();
|
|
WriteEncodedInt64(c);
|
|
|
|
for( asUINT n = 0; n < usedObjectProperties.GetLength(); n++ )
|
|
{
|
|
WriteTypeInfo(usedObjectProperties[n].objType);
|
|
WriteString(&usedObjectProperties[n].prop->name);
|
|
}
|
|
}
|
|
|
|
int asCWriter::FindObjectPropIndex(short offset, int typeId, asDWORD *bc)
|
|
{
|
|
// If the last property was a composite property, then just return 0, because it won't be translated
|
|
static bool lastWasComposite = false;
|
|
if (lastWasComposite)
|
|
{
|
|
lastWasComposite = false;
|
|
return 0;
|
|
}
|
|
|
|
asCObjectType *objType = engine->GetObjectTypeFromTypeId(typeId);
|
|
asCObjectProperty *objProp = 0;
|
|
|
|
// Look for composite properties first
|
|
for (asUINT n = 0; objProp == 0 && n < objType->properties.GetLength(); n++)
|
|
{
|
|
// TODO: Composite: Perhaps it would be better to add metadata to the bytecode instruction to give the exact property.
|
|
// That would also allow me to remove the typeId from the bytecode instruction itself
|
|
// Or perhaps a new bytecode instruction all together for accessing composite properties
|
|
// One that would do both offsets and indirection in a single go.
|
|
// TODO: Composite: Need to be able to handle instructions replaced in bytecode optimizations too
|
|
if (objType->properties[n]->compositeOffset == offset)
|
|
{
|
|
// This is a potential composite property. Need to check the following instructions to be sure
|
|
objProp = objType->properties[n];
|
|
asDWORD *bcTemp = bc;
|
|
bcTemp += asBCTypeSize[asBCInfo[*(asBYTE*)bcTemp].type];
|
|
if (objProp->isCompositeIndirect)
|
|
{
|
|
// The next instruction would be a asBC_RDSPtr
|
|
if ((*(asBYTE*)bcTemp) != asBC_RDSPtr)
|
|
{
|
|
objProp = 0;
|
|
continue;
|
|
}
|
|
bcTemp += asBCTypeSize[asBCInfo[*(asBYTE*)bcTemp].type];
|
|
}
|
|
// The next instruction would be asBC_ADDSi
|
|
if ((*(asBYTE*)bcTemp) != asBC_ADDSi)
|
|
{
|
|
objProp = 0;
|
|
continue;
|
|
}
|
|
// Make sure the offset is the expected one
|
|
if (*(((short*)bcTemp) + 1) != objProp->byteOffset)
|
|
{
|
|
objProp = 0;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If none of the composite properties matched, then look for ordinary property
|
|
for (asUINT n = 0; objProp == 0 && n < objType->properties.GetLength(); n++)
|
|
{
|
|
if (objType->properties[n]->byteOffset == offset && !(objType->properties[n]->compositeOffset || objType->properties[n]->isCompositeIndirect))
|
|
objProp = objType->properties[n];
|
|
}
|
|
|
|
asASSERT(objProp);
|
|
|
|
// Remember if this is a composite property as the next call will then be for the same property
|
|
if (objProp->compositeOffset || objProp->isCompositeIndirect)
|
|
lastWasComposite = true;
|
|
|
|
// Now check if the same property has already been accessed
|
|
for( asUINT n = 0; n < usedObjectProperties.GetLength(); n++ )
|
|
{
|
|
if( usedObjectProperties[n].objType == objType &&
|
|
usedObjectProperties[n].prop == objProp )
|
|
return n;
|
|
}
|
|
|
|
// Insert the new property
|
|
SObjProp prop = {objType, objProp};
|
|
usedObjectProperties.PushLast(prop);
|
|
return (int)usedObjectProperties.GetLength() - 1;
|
|
}
|
|
|
|
int asCWriter::FindFunctionIndex(asCScriptFunction *func)
|
|
{
|
|
for( asUINT n = 0; n < usedFunctions.GetLength(); n++ )
|
|
{
|
|
if( usedFunctions[n] == func )
|
|
return n;
|
|
}
|
|
|
|
usedFunctions.PushLast(func);
|
|
return (int)usedFunctions.GetLength() - 1;
|
|
}
|
|
|
|
int asCWriter::FindTypeIdIdx(int typeId)
|
|
{
|
|
asUINT n;
|
|
for( n = 0; n < usedTypeIds.GetLength(); n++ )
|
|
{
|
|
if( usedTypeIds[n] == typeId )
|
|
return n;
|
|
}
|
|
|
|
usedTypeIds.PushLast(typeId);
|
|
return (int)usedTypeIds.GetLength() - 1;
|
|
}
|
|
|
|
int asCWriter::FindTypeInfoIdx(asCTypeInfo *obj)
|
|
{
|
|
asUINT n;
|
|
for( n = 0; n < usedTypes.GetLength(); n++ )
|
|
{
|
|
if( usedTypes[n] == obj )
|
|
return n;
|
|
}
|
|
|
|
usedTypes.PushLast(obj);
|
|
return (int)usedTypes.GetLength() - 1;
|
|
}
|
|
|
|
#endif // AS_NO_COMPILER
|
|
|
|
END_AS_NAMESPACE
|
|
|