mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
5083 lines
154 KiB
C++
5083 lines
154 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(((const asBYTE *)data) + n, 1);
|
|
#else
|
|
for (int n = size - 1; ret >= 0 && n >= 0; n--)
|
|
ret = stream->Write(((const 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
|