Files
Descent3/AngelScript/source/as_scriptengine.cpp
Kevin Bentley df209742fc Initial import
2024-04-15 21:43:29 -06:00

4001 lines
112 KiB
C++

/*
AngelCode Scripting Library
Copyright (c) 2003-2009 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_scriptengine.cpp
//
// The implementation of the script engine interface
//
#include <stdlib.h>
#include "as_config.h"
#include "as_scriptengine.h"
#include "as_builder.h"
#include "as_context.h"
#include "as_string_util.h"
#include "as_tokenizer.h"
#include "as_texts.h"
#include "as_module.h"
#include "as_callfunc.h"
#include "as_arrayobject.h"
#include "as_generic.h"
#include "as_scriptobject.h"
#include "as_compiler.h"
BEGIN_AS_NAMESPACE
extern "C"
{
AS_API const char * asGetLibraryVersion()
{
#ifdef _DEBUG
return ANGELSCRIPT_VERSION_STRING " DEBUG";
#else
return ANGELSCRIPT_VERSION_STRING;
#endif
}
AS_API const char * asGetLibraryOptions()
{
const char *string = " "
// Options
#ifdef AS_MAX_PORTABILITY
"AS_MAX_PORTABILITY "
#endif
#ifdef AS_DEBUG
"AS_DEBUG "
#endif
#ifdef AS_NO_CLASS_METHODS
"AS_NO_CLASS_METHODS "
#endif
#ifdef AS_USE_DOUBLE_AS_FLOAT
"AS_USE_DOUBLE_AS_FLOAT "
#endif
#ifdef AS_64BIT_PTR
"AS_64BIT_PTR "
#endif
#ifdef AS_NO_THREADS
"AS_NO_THREADS "
#endif
#ifdef AS_NO_ATOMIC
"AS_NO_ATOMIC "
#endif
// Target system
#ifdef AS_WIN
"AS_WIN "
#endif
#ifdef AS_LINUX
"AS_LINUX "
#endif
#ifdef AS_MAC
"AS_MAC "
#endif
#ifdef AS_BSD
"AS_BSD "
#endif
#ifdef AS_XBOX
"AS_XBOX "
#endif
#ifdef AS_XBOX360
"AS_XBOX360 "
#endif
#ifdef AS_PSP
"AS_PSP "
#endif
#ifdef AS_PS2
"AS_PS2 "
#endif
#ifdef AS_PS3
"AS_PS3 "
#endif
#ifdef AS_DC
"AS_DC "
#endif
#ifdef AS_GC
"AS_GC "
#endif
#ifdef AS_WII
"AS_WII "
#endif
#ifdef AS_IPHONE
"AS_IPHONE "
#endif
#ifdef AS_ANDROID
"AS_ANDROID "
#endif
// CPU family
#ifdef AS_PPC
"AS_PPC "
#endif
#ifdef AS_PPC_64
"AS_PPC_64 "
#endif
#ifdef AS_X86
"AS_X86 "
#endif
#ifdef AS_MIPS
"AS_MIPS "
#endif
#ifdef AS_SH4
"AS_SH4 "
#endif
#ifdef AS_XENON
"AS_XENON "
#endif
#ifdef AS_ARM
"AS_ARM "
#endif
;
return string;
}
AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version)
{
// Verify the version that the application expects
if( (version/10000) != (ANGELSCRIPT_VERSION/10000) )
return 0;
if( (version/100)%100 != (ANGELSCRIPT_VERSION/100)%100 )
return 0;
if( (version%100) > (ANGELSCRIPT_VERSION%100) )
return 0;
// Verify the size of the types
asASSERT( sizeof(asBYTE) == 1 );
asASSERT( sizeof(asWORD) == 2 );
asASSERT( sizeof(asDWORD) == 4 );
asASSERT( sizeof(asQWORD) == 8 );
asASSERT( sizeof(asPWORD) == sizeof(void*) );
// Verify the boolean type
asASSERT( sizeof(bool) == AS_SIZEOF_BOOL );
asASSERT( true == VALUE_OF_BOOLEAN_TRUE );
// Verify endianess
#ifdef AS_BIG_ENDIAN
asASSERT( *(asDWORD*)"\x00\x01\x02\x03" == 0x00010203 );
asASSERT( *(asQWORD*)"\x00\x01\x02\x03\x04\x05\x06\x07" == I64(0x0001020304050607) );
#else
asASSERT( *(asDWORD*)"\x00\x01\x02\x03" == 0x03020100 );
asASSERT( *(asQWORD*)"\x00\x01\x02\x03\x04\x05\x06\x07" == I64(0x0706050403020100) );
#endif
return asNEW(asCScriptEngine)();
}
int asCScriptEngine::SetEngineProperty(asEEngineProp property, asPWORD value)
{
switch( property )
{
case asEP_ALLOW_UNSAFE_REFERENCES:
ep.allowUnsafeReferences = value ? true : false;
break;
case asEP_OPTIMIZE_BYTECODE:
ep.optimizeByteCode = value ? true : false;
break;
case asEP_COPY_SCRIPT_SECTIONS:
ep.copyScriptSections = value ? true : false;
break;
case asEP_MAX_STACK_SIZE:
// The size is given in bytes, but we only store dwords
ep.maximumContextStackSize = (int)value/4;
if( initialContextStackSize > ep.maximumContextStackSize )
initialContextStackSize = ep.maximumContextStackSize;
break;
case asEP_USE_CHARACTER_LITERALS:
ep.useCharacterLiterals = value ? true : false;
break;
case asEP_ALLOW_MULTILINE_STRINGS:
ep.allowMultilineStrings = value ? true : false;
break;
case asEP_ALLOW_IMPLICIT_HANDLE_TYPES:
ep.allowImplicitHandleTypes = value ? true : false;
break;
case asEP_BUILD_WITHOUT_LINE_CUES:
ep.buildWithoutLineCues = value ? true : false;
break;
case asEP_INIT_GLOBAL_VARS_AFTER_BUILD:
ep.initGlobalVarsAfterBuild = value ? true : false;
break;
case asEP_REQUIRE_ENUM_SCOPE:
ep.requireEnumScope = value ? true : false;
break;
case asEP_SCRIPT_SCANNER:
if( value <= 1 )
ep.scanner = (int)value;
else
return asINVALID_ARG;
break;
case asEP_INCLUDE_JIT_INSTRUCTIONS:
ep.includeJitInstructions = value ? true : false;
break;
case asEP_STRING_ENCODING:
if( value <= 1 )
ep.stringEncoding = (int)value;
else
return asINVALID_ARG;
break;
default:
return asINVALID_ARG;
}
return asSUCCESS;
}
asPWORD asCScriptEngine::GetEngineProperty(asEEngineProp property)
{
switch( property )
{
case asEP_ALLOW_UNSAFE_REFERENCES:
return ep.allowUnsafeReferences;
case asEP_OPTIMIZE_BYTECODE:
return ep.optimizeByteCode;
case asEP_COPY_SCRIPT_SECTIONS:
return ep.copyScriptSections;
case asEP_MAX_STACK_SIZE:
return ep.maximumContextStackSize*4;
case asEP_USE_CHARACTER_LITERALS:
return ep.useCharacterLiterals;
case asEP_ALLOW_MULTILINE_STRINGS:
return ep.allowMultilineStrings;
case asEP_ALLOW_IMPLICIT_HANDLE_TYPES:
return ep.allowImplicitHandleTypes;
case asEP_BUILD_WITHOUT_LINE_CUES:
return ep.buildWithoutLineCues;
case asEP_INIT_GLOBAL_VARS_AFTER_BUILD:
return ep.initGlobalVarsAfterBuild;
case asEP_REQUIRE_ENUM_SCOPE:
return ep.requireEnumScope;
case asEP_SCRIPT_SCANNER:
return ep.scanner;
case asEP_INCLUDE_JIT_INSTRUCTIONS:
return ep.includeJitInstructions;
case asEP_STRING_ENCODING:
return ep.stringEncoding;
}
return 0;
}
} // extern "C"
asCScriptEngine::asCScriptEngine()
{
// Instanciate the thread manager
if( threadManager == 0 )
threadManager = asNEW(asCThreadManager);
else
threadManager->AddRef();
// Engine properties
ep.allowUnsafeReferences = false;
ep.optimizeByteCode = true;
ep.copyScriptSections = true;
ep.maximumContextStackSize = 0; // no limit
ep.useCharacterLiterals = false;
ep.allowMultilineStrings = false;
ep.allowImplicitHandleTypes = false;
ep.buildWithoutLineCues = false;
ep.initGlobalVarsAfterBuild = true;
ep.requireEnumScope = false;
ep.scanner = 1; // utf8. 0 = ascii
ep.includeJitInstructions = false;
ep.stringEncoding = 0; // utf8. 1 = utf16
gc.engine = this;
refCount.set(1);
stringFactory = 0;
configFailed = false;
isPrepared = false;
isBuilding = false;
lastModule = 0;
userData = 0;
initialContextStackSize = 1024; // 1 KB
typeIdSeqNbr = 0;
currentGroup = &defaultGroup;
msgCallback = 0;
jitCompiler = 0;
// Reserve function id 0 for no function
scriptFunctions.PushLast(0);
// Make sure typeId for the built-in primitives are defined according to asETypeIdFlags
int id;
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttVoid, false)); asASSERT( id == asTYPEID_VOID );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttBool, false)); asASSERT( id == asTYPEID_BOOL );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt8, false)); asASSERT( id == asTYPEID_INT8 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt16, false)); asASSERT( id == asTYPEID_INT16 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt, false)); asASSERT( id == asTYPEID_INT32 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt64, false)); asASSERT( id == asTYPEID_INT64 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt8, false)); asASSERT( id == asTYPEID_UINT8 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt16, false)); asASSERT( id == asTYPEID_UINT16 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt, false)); asASSERT( id == asTYPEID_UINT32 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt64, false)); asASSERT( id == asTYPEID_UINT64 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttFloat, false)); asASSERT( id == asTYPEID_FLOAT );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttDouble, false)); asASSERT( id == asTYPEID_DOUBLE );
defaultArrayObjectType = 0;
RegisterArrayObject(this);
RegisterScriptObject(this);
RegisterScriptFunction(this);
RegisterObjectTypeGCBehaviours(this);
}
asCScriptEngine::~asCScriptEngine()
{
asASSERT(refCount.get() == 0);
asUINT n;
// The modules must be deleted first, as they may use
// object types from the config groups
for( n = (asUINT)scriptModules.GetLength(); n-- > 0; )
{
if( scriptModules[n] )
{
asDELETE(scriptModules[n],asCModule);
}
}
scriptModules.SetLength(0);
GarbageCollect(asGC_FULL_CYCLE);
// Delete the functions for template types that may references object types
for( n = 0; n < templateTypes.GetLength(); n++ )
{
if( templateTypes[n] )
{
asUINT f;
// Delete the factory stubs first
for( f = 0; f < templateTypes[n]->beh.factories.GetLength(); f++ )
{
scriptFunctions[templateTypes[n]->beh.factories[f]]->Release();
}
templateTypes[n]->beh.factories.Allocate(0, false);
// Delete the specialized functions
for( f = 1; f < templateTypes[n]->beh.operators.GetLength(); f += 2 )
{
if( scriptFunctions[templateTypes[n]->beh.operators[f]]->objectType == templateTypes[n] )
{
scriptFunctions[templateTypes[n]->beh.operators[f]]->Release();
templateTypes[n]->beh.operators[f] = 0;
}
}
}
}
// Do one more garbage collect to free gc objects that were global variables
GarbageCollect(asGC_FULL_CYCLE);
FreeUnusedGlobalProperties();
ClearUnusedTypes();
// Break all relationship between remaining class types and functions
for( n = 0; n < classTypes.GetLength(); n++ )
{
if( classTypes[n] )
classTypes[n]->ReleaseAllFunctions();
if( classTypes[n]->derivedFrom )
{
classTypes[n]->derivedFrom->Release();
classTypes[n]->derivedFrom = 0;
}
}
GarbageCollect(asGC_FULL_CYCLE);
FreeUnusedGlobalProperties();
ClearUnusedTypes();
asSMapNode<int,asCDataType*> *cursor = 0;
while( mapTypeIdToDataType.MoveFirst(&cursor) )
{
asDELETE(mapTypeIdToDataType.GetValue(cursor),asCDataType);
mapTypeIdToDataType.Erase(cursor);
}
defaultGroup.RemoveConfiguration(this);
while( configGroups.GetLength() )
{
// Delete config groups in the right order
asCConfigGroup *grp = configGroups.PopLast();
if( grp )
{
asDELETE(grp,asCConfigGroup);
}
}
for( n = 0; n < registeredGlobalProps.GetLength(); n++ )
{
if( registeredGlobalProps[n] )
{
asDELETE(registeredGlobalProps[n],asCGlobalProperty);
}
}
registeredGlobalProps.SetLength(0);
FreeUnusedGlobalProperties();
for( n = 0; n < templateTypes.GetLength(); n++ )
{
if( templateTypes[n] )
{
// Clear the sub type before deleting the template type so that the sub type isn't freed to soon
templateTypes[n]->templateSubType = asCDataType::CreateNullHandle();
asDELETE(templateTypes[n],asCObjectType);
}
}
templateTypes.SetLength(0);
for( n = 0; n < objectTypes.GetLength(); n++ )
{
if( objectTypes[n] )
{
// Clear the sub type before deleting the template type so that the sub type isn't freed to soon
objectTypes[n]->templateSubType = asCDataType::CreateNullHandle();
asDELETE(objectTypes[n],asCObjectType);
}
}
objectTypes.SetLength(0);
for( n = 0; n < templateSubTypes.GetLength(); n++ )
{
if( templateSubTypes[n] )
{
asDELETE(templateSubTypes[n], asCObjectType);
}
}
templateSubTypes.SetLength(0);
registeredTypeDefs.SetLength(0);
registeredEnums.SetLength(0);
registeredObjTypes.SetLength(0);
for( n = 0; n < registeredGlobalFuncs.GetLength(); n++ )
{
if( registeredGlobalFuncs[n] )
registeredGlobalFuncs[n]->Release();
}
registeredGlobalFuncs.SetLength(0);
scriptTypeBehaviours.ReleaseAllFunctions();
functionBehaviours.ReleaseAllFunctions();
objectTypeBehaviours.ReleaseAllFunctions();
// Free string constants
for( n = 0; n < stringConstants.GetLength(); n++ )
{
asDELETE(stringConstants[n],asCString);
}
stringConstants.SetLength(0);
// Free the script section names
for( n = 0; n < scriptSectionNames.GetLength(); n++ )
{
asDELETE(scriptSectionNames[n],asCString);
}
scriptSectionNames.SetLength(0);
// Release the thread manager
threadManager->Release();
}
// interface
int asCScriptEngine::AddRef()
{
return refCount.atomicInc();
}
// interface
int asCScriptEngine::Release()
{
int r = refCount.atomicDec();
if( r == 0 )
{
asDELETE(this,asCScriptEngine);
return 0;
}
return r;
}
// interface
void *asCScriptEngine::SetUserData(void *data)
{
void *old = userData;
userData = data;
return old;
}
// interface
void *asCScriptEngine::GetUserData()
{
return userData;
}
// interface
int asCScriptEngine::SetMessageCallback(const asSFuncPtr &callback, void *obj, asDWORD callConv)
{
msgCallback = true;
msgCallbackObj = obj;
bool isObj = false;
if( (unsigned)callConv == asCALL_GENERIC )
{
msgCallback = false;
return asNOT_SUPPORTED;
}
if( (unsigned)callConv >= asCALL_THISCALL )
{
isObj = true;
if( obj == 0 )
{
msgCallback = false;
return asINVALID_ARG;
}
}
int r = DetectCallingConvention(isObj, callback, callConv, &msgCallbackFunc);
if( r < 0 ) msgCallback = false;
return r;
}
// interface
int asCScriptEngine::ClearMessageCallback()
{
msgCallback = false;
return 0;
}
// interface
int asCScriptEngine::WriteMessage(const char *section, int row, int col, asEMsgType type, const char *message)
{
// Validate input parameters
if( section == 0 ||
message == 0 )
return asINVALID_ARG;
// If there is no callback then there's nothing to do
if( !msgCallback )
return 0;
asSMessageInfo msg;
msg.section = section;
msg.row = row;
msg.col = col;
msg.type = type;
msg.message = message;
if( msgCallbackFunc.callConv < ICC_THISCALL )
CallGlobalFunction(&msg, msgCallbackObj, &msgCallbackFunc, 0);
else
CallObjectMethod(msgCallbackObj, &msg, &msgCallbackFunc, 0);
return 0;
}
int asCScriptEngine::SetJITCompiler(asIJITCompiler *compiler)
{
jitCompiler = compiler;
return asSUCCESS;
}
asIJITCompiler *asCScriptEngine::GetJITCompiler()
{
return jitCompiler;
}
// interface
asETokenClass asCScriptEngine::ParseToken(const char *string, size_t stringLength, int *tokenLength)
{
if( stringLength == 0 )
stringLength = strlen(string);
size_t len;
asCTokenizer t;
asETokenClass tc;
t.GetToken(string, stringLength, &len, &tc);
if( tokenLength )
*tokenLength = (int)len;
return tc;
}
// interface
asIScriptModule *asCScriptEngine::GetModule(const char *module, asEGMFlags flag)
{
asCModule *mod = GetModule(module, false);
if( flag == asGM_ALWAYS_CREATE )
{
if( mod != 0 )
{
asDELETE(mod, asCModule);
}
return GetModule(module, true);
}
if( mod == 0 && flag == asGM_CREATE_IF_NOT_EXISTS )
{
return GetModule(module, true);
}
return mod;
}
// interface
int asCScriptEngine::DiscardModule(const char *module)
{
asCModule *mod = GetModule(module, false);
if( mod == 0 ) return asNO_MODULE;
asDELETE(mod, asCModule);
FreeUnusedGlobalProperties();
ClearUnusedTypes();
return 0;
}
void asCScriptEngine::ClearUnusedTypes()
{
// Build a list of all types to check for
asCArray<asCObjectType*> types;
types = classTypes;
types.Concatenate(templateInstanceTypes);
// Go through all modules
asUINT n;
for( n = 0; n < scriptModules.GetLength() && types.GetLength(); n++ )
{
asCModule *mod = scriptModules[n];
if( mod )
{
// Functions/Methods/Globals are handled after this
// Go through all type declarations
asUINT m;
for( m = 0; m < mod->classTypes.GetLength() && types.GetLength(); m++ )
RemoveTypeAndRelatedFromList(types, mod->classTypes[m]);
for( m = 0; m < mod->enumTypes.GetLength() && types.GetLength(); m++ )
RemoveTypeAndRelatedFromList(types, mod->enumTypes[m]);
for( m = 0; m < mod->typeDefs.GetLength() && types.GetLength(); m++ )
RemoveTypeAndRelatedFromList(types, mod->typeDefs[m]);
}
}
// Go through all function parameters and remove used types
for( n = 0; n < scriptFunctions.GetLength() && types.GetLength(); n++ )
{
asCScriptFunction *func = scriptFunctions[n];
if( func )
{
// Ignore factory stubs
if( func->name == "factstub" )
continue;
asCObjectType *ot = func->returnType.GetObjectType();
if( ot != 0 && ot != func->objectType )
if( func->name != ot->name )
RemoveTypeAndRelatedFromList(types, ot);
for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ )
{
ot = func->parameterTypes[p].GetObjectType();
if( ot != 0 && ot != func->objectType )
if( func->name != ot->name )
RemoveTypeAndRelatedFromList(types, ot);
}
}
}
// Go through all global properties
for( n = 0; n < globalProperties.GetLength() && types.GetLength(); n++ )
{
if( globalProperties[n] && globalProperties[n]->type.GetObjectType() )
RemoveTypeAndRelatedFromList(types, globalProperties[n]->type.GetObjectType());
}
// All that remains in the list after this can be discarded, since they are no longer used
for(;;)
{
bool didClearTemplateInstanceType = false;
for( n = 0; n < types.GetLength(); n++ )
{
// Template types and script classes will have two references for each factory stub
int refCount = ((types[n]->flags & asOBJ_TEMPLATE) || (types[n]->flags & asOBJ_SCRIPT_OBJECT)) ? 2*(int)types[n]->beh.factories.GetLength() : 0;
if( types[n]->GetRefCount() == refCount )
{
if( types[n]->flags & asOBJ_TEMPLATE )
{
didClearTemplateInstanceType = true;
RemoveTemplateInstanceType(types[n]);
}
else
{
RemoveFromTypeIdMap(types[n]);
asDELETE(types[n],asCObjectType);
int i = classTypes.IndexOf(types[n]);
if( i == (signed)classTypes.GetLength() - 1 )
classTypes.PopLast();
else
classTypes[i] = classTypes.PopLast();
}
// Remove the type from the array
if( n < types.GetLength() - 1 )
types[n] = types.PopLast();
else
types.PopLast();
n--;
}
}
if( didClearTemplateInstanceType == false )
break;
}
}
void asCScriptEngine::RemoveTypeAndRelatedFromList(asCArray<asCObjectType*> &types, asCObjectType *ot)
{
// Remove the type from the list
int i = types.IndexOf(ot);
if( i == -1 ) return;
if( i == (signed)types.GetLength() - 1 )
types.PopLast();
else
types[i] = types.PopLast();
// If the type is an template type, then remove all sub types as well
if( ot->templateSubType.GetObjectType() )
{
while( ot->templateSubType.GetObjectType() )
{
ot = ot->templateSubType.GetObjectType();
RemoveTypeAndRelatedFromList(types, ot);
}
return;
}
// If the type is a class, then remove all properties types as well
if( ot->properties.GetLength() )
{
for( asUINT n = 0; n < ot->properties.GetLength(); n++ )
RemoveTypeAndRelatedFromList(types, ot->properties[n]->type.GetObjectType());
}
}
// internal
int asCScriptEngine::GetFactoryIdByDecl(const asCObjectType *ot, const char *decl)
{
asCModule *mod = 0;
// Is this a script class?
if( ot->flags & asOBJ_SCRIPT_OBJECT && ot->size > 0 )
mod = scriptFunctions[ot->beh.factory]->module;
asCBuilder bld(this, mod);
asCScriptFunction func(this, mod,-1);
int r = bld.ParseFunctionDeclaration(0, decl, &func, false);
if( r < 0 )
return asINVALID_DECLARATION;
// Search for matching factory function
int id = -1;
for( size_t n = 0; n < ot->beh.factories.GetLength(); n++ )
{
asCScriptFunction *f = scriptFunctions[ot->beh.factories[n]];
if( f->IsSignatureEqual(&func) )
{
id = ot->beh.factories[n];
break;
}
}
if( id == -1 ) return asNO_FUNCTION;
return id;
}
// internal
int asCScriptEngine::GetMethodIdByDecl(const asCObjectType *ot, const char *decl, asCModule *mod)
{
asCBuilder bld(this, mod);
asCScriptFunction func(this, mod, -1);
int r = bld.ParseFunctionDeclaration(0, decl, &func, false);
if( r < 0 )
return asINVALID_DECLARATION;
// Set the object type so that the signature can be properly compared
// This cast is OK, it will only be used for comparison
func.objectType = const_cast<asCObjectType*>(ot);
// Search script functions for matching interface
int id = -1;
for( size_t n = 0; n < ot->methods.GetLength(); ++n )
{
if( func.IsSignatureEqual(scriptFunctions[ot->methods[n]]) )
{
if( id == -1 )
id = ot->methods[n];
else
return asMULTIPLE_FUNCTIONS;
}
}
if( id == -1 ) return asNO_FUNCTION;
return id;
}
// Internal
asCString asCScriptEngine::GetFunctionDeclaration(int funcID)
{
asCString str;
asCScriptFunction *func = GetScriptFunction(funcID);
if( func )
str = func->GetDeclarationStr();
return str;
}
asCScriptFunction *asCScriptEngine::GetScriptFunction(int funcId)
{
if( funcId < 0 || funcId >= (int)scriptFunctions.GetLength() )
return 0;
return scriptFunctions[funcId];
}
asIScriptContext *asCScriptEngine::CreateContext()
{
asIScriptContext *ctx = 0;
CreateContext(&ctx, false);
return ctx;
}
int asCScriptEngine::CreateContext(asIScriptContext **context, bool isInternal)
{
*context = asNEW(asCContext)(this, !isInternal);
// We need to make sure the engine has been
// prepared before any context is executed
PrepareEngine();
return 0;
}
int asCScriptEngine::RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset)
{
int r;
asCDataType dt;
asCBuilder bld(this, 0);
r = bld.ParseDataType(obj, &dt);
if( r < 0 )
return ConfigError(r);
// Verify that the correct config group is used
if( currentGroup->FindType(dt.GetObjectType()->name.AddressOf()) == 0 )
return ConfigError(asWRONG_CONFIG_GROUP);
asCDataType type;
asCString name;
if( (r = bld.VerifyProperty(&dt, declaration, name, type)) < 0 )
return ConfigError(r);
// Store the property info
if( dt.GetObjectType() == 0 )
return ConfigError(asINVALID_OBJECT);
asCObjectProperty *prop = asNEW(asCObjectProperty);
prop->name = name;
prop->type = type;
prop->byteOffset = byteOffset;
dt.GetObjectType()->properties.PushLast(prop);
currentGroup->RefConfigGroup(FindConfigGroupForObjectType(type.GetObjectType()));
return asSUCCESS;
}
int asCScriptEngine::RegisterInterface(const char *name)
{
if( name == 0 ) return ConfigError(asINVALID_NAME);
// Verify if the name has been registered as a type already
asUINT n;
for( n = 0; n < objectTypes.GetLength(); n++ )
{
if( objectTypes[n] && objectTypes[n]->name == name )
return asALREADY_REGISTERED;
}
// Use builder to parse the datatype
asCDataType dt;
asCBuilder bld(this, 0);
bool oldMsgCallback = msgCallback; msgCallback = false;
int r = bld.ParseDataType(name, &dt);
msgCallback = oldMsgCallback;
if( r >= 0 ) return ConfigError(asERROR);
// Make sure the name is not a reserved keyword
asCTokenizer t;
size_t tokenLen;
int token = t.GetToken(name, strlen(name), &tokenLen);
if( token != ttIdentifier || strlen(name) != tokenLen )
return ConfigError(asINVALID_NAME);
r = bld.CheckNameConflict(name, 0, 0);
if( r < 0 )
return ConfigError(asNAME_TAKEN);
// Don't have to check against members of object
// types as they are allowed to use the names
// Register the object type for the interface
asCObjectType *st = asNEW(asCObjectType)(this);
st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT;
st->size = 0; // Cannot be instanciated
st->name = name;
// Use the default script class behaviours
st->beh.factory = 0;
st->beh.addref = scriptTypeBehaviours.beh.addref;
scriptFunctions[st->beh.addref]->AddRef();
st->beh.release = scriptTypeBehaviours.beh.release;
scriptFunctions[st->beh.release]->AddRef();
st->beh.copy = 0;
objectTypes.PushLast(st);
registeredObjTypes.PushLast(st);
currentGroup->objTypes.PushLast(st);
return asSUCCESS;
}
int asCScriptEngine::RegisterInterfaceMethod(const char *intf, const char *declaration)
{
// Verify that the correct config group is set.
if( currentGroup->FindType(intf) == 0 )
return ConfigError(asWRONG_CONFIG_GROUP);
asCDataType dt;
asCBuilder bld(this, 0);
int r = bld.ParseDataType(intf, &dt);
if( r < 0 )
return ConfigError(r);
asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_INTERFACE);
func->objectType = dt.GetObjectType();
r = bld.ParseFunctionDeclaration(func->objectType, declaration, func, false);
if( r < 0 )
{
asDELETE(func,asCScriptFunction);
return ConfigError(asINVALID_DECLARATION);
}
// Check name conflicts
r = bld.CheckNameConflictMember(dt.GetObjectType(), func->name.AddressOf(), 0, 0);
if( r < 0 )
{
asDELETE(func,asCScriptFunction);
return ConfigError(asNAME_TAKEN);
}
func->id = GetNextScriptFunctionId();
SetScriptFunction(func);
func->objectType->methods.PushLast(func->id);
// The refCount was already set to 1
func->ComputeSignatureId();
// If parameter type from other groups are used, add references
// TODO: The code for adding references to config groups is repeated in a lot of places
if( func->returnType.GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType());
currentGroup->RefConfigGroup(group);
}
for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ )
{
if( func->parameterTypes[n].GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType());
currentGroup->RefConfigGroup(group);
}
}
// Return function id as success
return func->id;
}
int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD flags)
{
int r;
isPrepared = false;
// Verify flags
// Must have either asOBJ_REF or asOBJ_VALUE
if( flags & asOBJ_REF )
{
// Can optionally have the asOBJ_GC, asOBJ_NOHANDLE, asOBJ_SCOPED, or asOBJ_TEMPLATE flag set, but nothing else
if( flags & ~(asOBJ_REF | asOBJ_GC | asOBJ_NOHANDLE | asOBJ_SCOPED | asOBJ_TEMPLATE) )
return ConfigError(asINVALID_ARG);
// flags are exclusive
if( (flags & asOBJ_GC) && (flags & (asOBJ_NOHANDLE|asOBJ_SCOPED)) )
return ConfigError(asINVALID_ARG);
if( (flags & asOBJ_NOHANDLE) && (flags & (asOBJ_GC|asOBJ_SCOPED)) )
return ConfigError(asINVALID_ARG);
if( (flags & asOBJ_SCOPED) && (flags & (asOBJ_GC|asOBJ_NOHANDLE)) )
return ConfigError(asINVALID_ARG);
}
else if( flags & asOBJ_VALUE )
{
// Cannot use reference flags
// TODO: template: Should be possible to register a value type as template type
if( flags & (asOBJ_REF | asOBJ_GC | asOBJ_SCOPED) )
return ConfigError(asINVALID_ARG);
// If the app type is given, we must validate the flags
if( flags & asOBJ_APP_CLASS )
{
// Must not set the primitive or float flag
if( flags & (asOBJ_APP_PRIMITIVE |
asOBJ_APP_FLOAT) )
return ConfigError(asINVALID_ARG);
}
else if( flags & asOBJ_APP_PRIMITIVE )
{
// Must not set the class flags nor the float flag
if( flags & (asOBJ_APP_CLASS |
asOBJ_APP_CLASS_CONSTRUCTOR |
asOBJ_APP_CLASS_DESTRUCTOR |
asOBJ_APP_CLASS_ASSIGNMENT |
asOBJ_APP_FLOAT) )
return ConfigError(asINVALID_ARG);
}
else if( flags & asOBJ_APP_FLOAT )
{
// Must not set the class flags nor the primitive flag
if( flags & (asOBJ_APP_CLASS |
asOBJ_APP_CLASS_CONSTRUCTOR |
asOBJ_APP_CLASS_DESTRUCTOR |
asOBJ_APP_CLASS_ASSIGNMENT |
asOBJ_APP_PRIMITIVE) )
return ConfigError(asINVALID_ARG);
}
else if( flags & (asOBJ_APP_CLASS_CONSTRUCTOR |
asOBJ_APP_CLASS_DESTRUCTOR |
asOBJ_APP_CLASS_ASSIGNMENT) )
{
// Must not set the class properties, without the class flag
return ConfigError(asINVALID_ARG);
}
}
else
return ConfigError(asINVALID_ARG);
// Don't allow anything else than the defined flags
if( flags - (flags & asOBJ_MASK_VALID_FLAGS) )
return ConfigError(asINVALID_ARG);
// Value types must have a defined size
if( (flags & asOBJ_VALUE) && byteSize == 0 )
{
WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_VALUE_TYPE_MUST_HAVE_SIZE);
return ConfigError(asINVALID_ARG);
}
// Verify type name
if( name == 0 )
return ConfigError(asINVALID_NAME);
asCString typeName;
asCBuilder bld(this, 0);
if( flags & asOBJ_TEMPLATE )
{
asCString subtypeName;
r = bld.ParseTemplateDecl(name, &typeName, &subtypeName);
if( r < 0 )
return r;
// Verify that the template name hasn't been registered as a type already
asUINT n;
for( n = 0; n < objectTypes.GetLength(); n++ )
{
if( objectTypes[n] && objectTypes[n]->name == typeName )
return asALREADY_REGISTERED;
}
asCObjectType *type = asNEW(asCObjectType)(this);
type->name = typeName;
type->size = byteSize;
type->flags = flags;
// Store it in the object types
objectTypes.PushLast(type);
// Define a template subtype
asCObjectType *subtype = 0;
for( n = 0; n < templateSubTypes.GetLength(); n++ )
{
if( templateSubTypes[n]->name == subtypeName )
{
subtype = templateSubTypes[n];
break;
}
}
if( subtype == 0 )
{
// Create the new subtype if not already existing
subtype = asNEW(asCObjectType)(this);
subtype->name = subtypeName;
subtype->size = 0;
subtype->flags = asOBJ_TEMPLATE_SUBTYPE;
templateSubTypes.PushLast(subtype);
subtype->AddRef();
}
type->templateSubType = asCDataType::CreateObject(subtype, false);
subtype->AddRef();
currentGroup->objTypes.PushLast(type);
if( defaultArrayObjectType == 0 )
{
// TODO: The default array object type should be defined by the application
// The default array object type is registered by the engine itself
defaultArrayObjectType = type;
type->AddRef();
}
else
{
registeredObjTypes.PushLast(type);
}
}
else
{
typeName = name;
// Verify if the name has been registered as a type already
asUINT n;
for( n = 0; n < objectTypes.GetLength(); n++ )
{
if( objectTypes[n] && objectTypes[n]->name == typeName )
return asALREADY_REGISTERED;
}
for( n = 0; n < templateTypes.GetLength(); n++ )
{
if( templateTypes[n] && templateTypes[n]->name == typeName )
return asALREADY_REGISTERED;
}
// Verify the most recently created template instance type
asCObjectType *mostRecentTemplateInstanceType = 0;
if( templateInstanceTypes.GetLength() )
mostRecentTemplateInstanceType = templateInstanceTypes[templateInstanceTypes.GetLength()-1];
// Use builder to parse the datatype
asCDataType dt;
bool oldMsgCallback = msgCallback; msgCallback = false;
r = bld.ParseDataType(name, &dt);
msgCallback = oldMsgCallback;
// If the builder fails, then the type name
// is new and it should be registered
if( r < 0 )
{
// Make sure the name is not a reserved keyword
asCTokenizer t;
size_t tokenLen;
int token = t.GetToken(name, typeName.GetLength(), &tokenLen);
if( token != ttIdentifier || typeName.GetLength() != tokenLen )
return ConfigError(asINVALID_NAME);
int r = bld.CheckNameConflict(name, 0, 0);
if( r < 0 )
return ConfigError(asNAME_TAKEN);
// Don't have to check against members of object
// types as they are allowed to use the names
// Put the data type in the list
asCObjectType *type = asNEW(asCObjectType)(this);
type->name = typeName;
type->size = byteSize;
type->flags = flags;
objectTypes.PushLast(type);
registeredObjTypes.PushLast(type);
currentGroup->objTypes.PushLast(type);
}
else
{
// The application is registering a template specialization so we
// need to replace the template instance type with the new type.
// TODO: Template: We don't require the lower dimensions to be registered first for registered template types
// int[][] must not be allowed to be registered
// if int[] hasn't been registered first
if( dt.GetSubType().IsTemplate() )
return ConfigError(asLOWER_ARRAY_DIMENSION_NOT_REGISTERED);
if( dt.IsReadOnly() ||
dt.IsReference() )
return ConfigError(asINVALID_TYPE);
// Was the template instance type created before?
if( templateInstanceTypes[templateInstanceTypes.GetLength()-1] == mostRecentTemplateInstanceType ||
mostRecentTemplateInstanceType == dt.GetObjectType() )
// TODO: Should have a better error message
return ConfigError(asNOT_SUPPORTED);
// TODO: Add this again. The type is used by the factory stubs so we need to discount that
// Is the template instance type already being used?
// if( dt.GetObjectType()->GetRefCount() > 1 )
// return ConfigError(asNOT_SUPPORTED);
// Put the data type in the list
asCObjectType *type = asNEW(asCObjectType)(this);
type->name = dt.GetObjectType()->name;
type->templateSubType = dt.GetSubType();
if( type->templateSubType.GetObjectType() ) type->templateSubType.GetObjectType()->AddRef();
type->size = byteSize;
type->flags = flags;
templateTypes.PushLast(type);
currentGroup->objTypes.PushLast(type);
// Remove the template instance type, which will no longer be used.
RemoveTemplateInstanceType(dt.GetObjectType());
}
}
return asSUCCESS;
}
// interface
int asCScriptEngine::RegisterObjectBehaviour(const char *datatype, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv)
{
if( datatype == 0 ) return ConfigError(asINVALID_ARG);
// Determine the object type
asCBuilder bld(this, 0);
asCDataType type;
int r = bld.ParseDataType(datatype, &type);
if( r < 0 )
return ConfigError(r);
if( type.GetObjectType() == 0 )
return ConfigError(asINVALID_TYPE);
if( type.IsReadOnly() || type.IsReference() )
return ConfigError(asINVALID_TYPE);
return RegisterBehaviourToObjectType(type.GetObjectType(), behaviour, decl, funcPointer, callConv);
}
// internal
int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv)
{
asSSystemFunctionInterface internal;
if( behaviour == asBEHAVE_FACTORY ||
behaviour == asBEHAVE_TEMPLATE_CALLBACK )
{
#ifdef AS_MAX_PORTABILITY
if( callConv != asCALL_GENERIC )
return ConfigError(asNOT_SUPPORTED);
#endif
int r = DetectCallingConvention(false, funcPointer, callConv, &internal);
if( r < 0 )
return ConfigError(r);
}
else
{
#ifdef AS_MAX_PORTABILITY
if( callConv != asCALL_GENERIC )
return ConfigError(asNOT_SUPPORTED);
#else
if( callConv != asCALL_THISCALL &&
callConv != asCALL_CDECL_OBJLAST &&
callConv != asCALL_CDECL_OBJFIRST &&
callConv != asCALL_GENERIC )
return ConfigError(asNOT_SUPPORTED);
#endif
int r = DetectCallingConvention(true, funcPointer, callConv, &internal);
if( r < 0 )
return ConfigError(r);
}
isPrepared = false;
asSTypeBehaviour *beh = &objectType->beh;
// Verify function declaration
asCScriptFunction func(this, 0, -1);
asCBuilder bld(this, 0);
int r = bld.ParseFunctionDeclaration(objectType, decl, &func, true, &internal.paramAutoHandles, &internal.returnAutoHandle);
if( r < 0 )
return ConfigError(asINVALID_DECLARATION);
func.name.Format("_beh_%d_", behaviour);
if( behaviour != asBEHAVE_FACTORY )
func.objectType = objectType;
// Check if the method restricts that use of the template to value types or reference types
if( objectType->flags & asOBJ_TEMPLATE )
{
if( func.returnType.GetObjectType() == objectType->templateSubType.GetObjectType() )
{
if( func.returnType.IsObjectHandle() )
objectType->acceptValueSubType = false;
else if( !func.returnType.IsReference() )
objectType->acceptRefSubType = false;
}
for( asUINT n = 0; n < func.parameterTypes.GetLength(); n++ )
{
if( func.parameterTypes[n].GetObjectType() == objectType->templateSubType.GetObjectType() )
{
// TODO: If unsafe references are allowed, then inout references allow value types
if( func.parameterTypes[n].IsObjectHandle() || (func.parameterTypes[n].IsReference() && func.inOutFlags[n] == asTM_INOUTREF) )
objectType->acceptValueSubType = false;
else if( !func.parameterTypes[n].IsReference() )
objectType->acceptRefSubType = false;
}
}
}
if( behaviour == asBEHAVE_CONSTRUCT )
{
// TODO: Add asBEHAVE_IMPLICIT_CONSTRUCT
// Verify that the return type is void
if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) )
return ConfigError(asINVALID_DECLARATION);
if( objectType->flags & asOBJ_SCRIPT_OBJECT )
{
// The script object is a special case
asASSERT(func.parameterTypes.GetLength() == 1);
beh->construct = AddBehaviourFunction(func, internal);
beh->factory = beh->construct;
scriptFunctions[beh->factory]->AddRef();
beh->constructors.PushLast(beh->construct);
beh->factories.PushLast(beh->factory);
func.id = beh->construct;
}
else
{
// Verify that it is a value type
if( !(func.objectType->flags & asOBJ_VALUE) )
{
WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE);
return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE);
}
// TODO: Add support for implicit constructors
// TODO: Verify that the same constructor hasn't been registered already
// Store all constructors in a list
func.id = AddBehaviourFunction(func, internal);
beh->constructors.PushLast(func.id);
if( func.parameterTypes.GetLength() == 0 )
{
beh->construct = func.id;
}
else if( func.parameterTypes.GetLength() == 1 )
{
// Is this the copy constructor?
asCDataType paramType = func.parameterTypes[0];
// If the parameter is object, and const reference for input,
// and same type as this class, then this is a copy constructor.
if( paramType.IsObject() && paramType.IsReference() && paramType.IsReadOnly() && func.inOutFlags[0] == asTM_INREF && paramType.GetObjectType() == objectType )
beh->copyconstruct = func.id;
}
}
}
else if( behaviour == asBEHAVE_DESTRUCT )
{
// Must be a value type
if( !(func.objectType->flags & asOBJ_VALUE) )
{
WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE);
return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE);
}
if( beh->destruct )
return ConfigError(asALREADY_REGISTERED);
// Verify that the return type is void
if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) )
return ConfigError(asINVALID_DECLARATION);
// Verify that there are no parameters
if( func.parameterTypes.GetLength() > 0 )
return ConfigError(asINVALID_DECLARATION);
func.id = beh->destruct = AddBehaviourFunction(func, internal);
}
else if( behaviour == asBEHAVE_FACTORY )
{
// TODO: Add asBEHAVE_IMPLICIT_FACTORY
// Must be a ref type and must not have asOBJ_NOHANDLE
if( !(objectType->flags & asOBJ_REF) || (objectType->flags & asOBJ_NOHANDLE) )
{
WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE);
return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE);
}
// Verify that the return type is a handle to the type
if( func.returnType != asCDataType::CreateObjectHandle(objectType, false) )
return ConfigError(asINVALID_DECLARATION);
// TODO: Add support for implicit factories
// TODO: Verify that the same factory function hasn't been registered already
// The templates take a hidden parameter with the object type
if( (objectType->flags & asOBJ_TEMPLATE) &&
(func.parameterTypes.GetLength() == 0 ||
!func.parameterTypes[0].IsReference()) )
{
return ConfigError(asINVALID_DECLARATION);
}
// Store all factory functions in a list
func.id = AddBehaviourFunction(func, internal);
beh->factories.PushLast(func.id);
if( (func.parameterTypes.GetLength() == 0) ||
(func.parameterTypes.GetLength() == 1 && (objectType->flags & asOBJ_TEMPLATE)) )
{
beh->factory = func.id;
}
else if( (func.parameterTypes.GetLength() == 1) ||
(func.parameterTypes.GetLength() == 2 && (objectType->flags & asOBJ_TEMPLATE)) )
{
// Is this the copy factory?
asCDataType paramType = func.parameterTypes[func.parameterTypes.GetLength()-1];
// If the parameter is object, and const reference for input,
// and same type as this class, then this is a copy constructor.
if( paramType.IsObject() && paramType.IsReference() && paramType.IsReadOnly() && func.inOutFlags[func.parameterTypes.GetLength()-1] == asTM_INREF && paramType.GetObjectType() == objectType )
beh->copyfactory = func.id;
}
}
else if( behaviour == asBEHAVE_ADDREF )
{
// Must be a ref type and must not have asOBJ_NOHANDLE, nor asOBJ_SCOPED
if( !(func.objectType->flags & asOBJ_REF) ||
(func.objectType->flags & asOBJ_NOHANDLE) ||
(func.objectType->flags & asOBJ_SCOPED) )
{
WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE);
return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE);
}
if( beh->addref )
return ConfigError(asALREADY_REGISTERED);
// Verify that the return type is void
if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) )
return ConfigError(asINVALID_DECLARATION);
// Verify that there are no parameters
if( func.parameterTypes.GetLength() > 0 )
return ConfigError(asINVALID_DECLARATION);
func.id = beh->addref = AddBehaviourFunction(func, internal);
}
else if( behaviour == asBEHAVE_RELEASE )
{
// Must be a ref type and must not have asOBJ_NOHANDLE
if( !(func.objectType->flags & asOBJ_REF) || (func.objectType->flags & asOBJ_NOHANDLE) )
{
WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE);
return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE);
}
if( beh->release )
return ConfigError(asALREADY_REGISTERED);
// Verify that the return type is void
if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) )
return ConfigError(asINVALID_DECLARATION);
// Verify that there are no parameters
if( func.parameterTypes.GetLength() > 0 )
return ConfigError(asINVALID_DECLARATION);
func.id = beh->release = AddBehaviourFunction(func, internal);
}
else if( behaviour == asBEHAVE_TEMPLATE_CALLBACK )
{
// Must be a template type
if( !(func.objectType->flags & asOBJ_TEMPLATE) )
{
WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE);
return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE);
}
if( beh->templateCallback )
return ConfigError(asALREADY_REGISTERED);
// Verify that the return type is bool
if( func.returnType != asCDataType::CreatePrimitive(ttBool, false) )
return ConfigError(asINVALID_DECLARATION);
// Verify that there is one parameters
if( func.parameterTypes.GetLength() != 1 )
return ConfigError(asINVALID_DECLARATION);
func.id = beh->templateCallback = AddBehaviourFunction(func, internal);
}
else if( behaviour == asBEHAVE_INDEX )
{
// Verify that the var type is not used
if( VerifyVarTypeNotInFunction(&func) < 0 )
return ConfigError(asINVALID_DECLARATION);
// Verify that there is only one parameter
if( func.parameterTypes.GetLength() != 1 )
return ConfigError(asINVALID_DECLARATION);
// Verify that the return type is not void
if( func.returnType.GetTokenType() == ttVoid )
return ConfigError(asINVALID_DECLARATION);
// TODO: Verify that the operator hasn't been registered already
beh->operators.PushLast(behaviour);
func.id = AddBehaviourFunction(func, internal);
beh->operators.PushLast(func.id);
}
else if( behaviour >= asBEHAVE_FIRST_GC &&
behaviour <= asBEHAVE_LAST_GC )
{
// Only allow GC behaviours for types registered to be garbage collected
if( !(func.objectType->flags & asOBJ_GC) )
{
WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE);
return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE);
}
// Verify parameter count
if( (behaviour == asBEHAVE_GETREFCOUNT ||
behaviour == asBEHAVE_SETGCFLAG ||
behaviour == asBEHAVE_GETGCFLAG) &&
func.parameterTypes.GetLength() != 0 )
return ConfigError(asINVALID_DECLARATION);
if( (behaviour == asBEHAVE_ENUMREFS ||
behaviour == asBEHAVE_RELEASEREFS) &&
func.parameterTypes.GetLength() != 1 )
return ConfigError(asINVALID_DECLARATION);
// Verify return type
if( behaviour == asBEHAVE_GETREFCOUNT &&
func.returnType != asCDataType::CreatePrimitive(ttInt, false) )
return ConfigError(asINVALID_DECLARATION);
if( behaviour == asBEHAVE_GETGCFLAG &&
func.returnType != asCDataType::CreatePrimitive(ttBool, false) )
return ConfigError(asINVALID_DECLARATION);
if( (behaviour == asBEHAVE_SETGCFLAG ||
behaviour == asBEHAVE_ENUMREFS ||
behaviour == asBEHAVE_RELEASEREFS) &&
func.returnType != asCDataType::CreatePrimitive(ttVoid, false) )
return ConfigError(asINVALID_DECLARATION);
if( behaviour == asBEHAVE_GETREFCOUNT )
func.id = beh->gcGetRefCount = AddBehaviourFunction(func, internal);
else if( behaviour == asBEHAVE_SETGCFLAG )
func.id = beh->gcSetFlag = AddBehaviourFunction(func, internal);
else if( behaviour == asBEHAVE_GETGCFLAG )
func.id = beh->gcGetFlag = AddBehaviourFunction(func, internal);
else if( behaviour == asBEHAVE_ENUMREFS )
func.id = beh->gcEnumReferences = AddBehaviourFunction(func, internal);
else if( behaviour == asBEHAVE_RELEASEREFS )
func.id = beh->gcReleaseAllReferences = AddBehaviourFunction(func, internal);
}
else if( behaviour == asBEHAVE_IMPLICIT_VALUE_CAST ||
behaviour == asBEHAVE_VALUE_CAST )
{
// Verify parameter count
if( func.parameterTypes.GetLength() != 0 )
return ConfigError(asINVALID_DECLARATION);
// Verify return type
if( func.returnType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, false)) )
return ConfigError(asNOT_SUPPORTED);
if( func.returnType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttVoid, false)) )
return ConfigError(asINVALID_DECLARATION);
// TODO: verify that the same cast is not registered already (const or non-const is treated the same for the return type)
beh->operators.PushLast(behaviour);
func.id = AddBehaviourFunction(func, internal);
beh->operators.PushLast(func.id);
}
else if( behaviour == asBEHAVE_REF_CAST ||
behaviour == asBEHAVE_IMPLICIT_REF_CAST )
{
// Verify parameter count
if( func.parameterTypes.GetLength() != 0 )
return ConfigError(asINVALID_DECLARATION);
// Verify return type
if( !func.returnType.IsObjectHandle() )
return ConfigError(asINVALID_DECLARATION);
// TODO: verify that the same cast is not registered already (cosnt or non-const is treated the same for the return type)
beh->operators.PushLast(behaviour);
func.id = AddBehaviourFunction(func, internal);
beh->operators.PushLast(func.id);
}
else
{
asASSERT(false);
return ConfigError(asINVALID_ARG);
}
// Return function id as success
return func.id;
}
int asCScriptEngine::VerifyVarTypeNotInFunction(asCScriptFunction *func)
{
// Don't allow var type in this function
if( func->returnType.GetTokenType() == ttQuestion )
return asINVALID_DECLARATION;
for( unsigned int n = 0; n < func->parameterTypes.GetLength(); n++ )
if( func->parameterTypes[n].GetTokenType() == ttQuestion )
return asINVALID_DECLARATION;
return 0;
}
int asCScriptEngine::AddBehaviourFunction(asCScriptFunction &func, asSSystemFunctionInterface &internal)
{
asUINT n;
int id = GetNextScriptFunctionId();
asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface);
newInterface->func = internal.func;
newInterface->baseOffset = internal.baseOffset;
newInterface->callConv = internal.callConv;
newInterface->scriptReturnSize = internal.scriptReturnSize;
newInterface->hostReturnInMemory = internal.hostReturnInMemory;
newInterface->hostReturnFloat = internal.hostReturnFloat;
newInterface->hostReturnSize = internal.hostReturnSize;
newInterface->paramSize = internal.paramSize;
newInterface->takesObjByVal = internal.takesObjByVal;
newInterface->paramAutoHandles = internal.paramAutoHandles;
newInterface->returnAutoHandle = internal.returnAutoHandle;
newInterface->hasAutoHandles = internal.hasAutoHandles;
asCScriptFunction *f = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM);
asASSERT(func.name != "" && func.name != "f");
f->name = func.name;
f->sysFuncIntf = newInterface;
f->returnType = func.returnType;
f->objectType = func.objectType;
f->id = id;
f->isReadOnly = func.isReadOnly;
for( n = 0; n < func.parameterTypes.GetLength(); n++ )
{
f->parameterTypes.PushLast(func.parameterTypes[n]);
f->inOutFlags.PushLast(func.inOutFlags[n]);
}
SetScriptFunction(f);
// If parameter type from other groups are used, add references
if( f->returnType.GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(f->returnType.GetObjectType());
currentGroup->RefConfigGroup(group);
}
for( n = 0; n < f->parameterTypes.GetLength(); n++ )
{
if( f->parameterTypes[n].GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(f->parameterTypes[n].GetObjectType());
currentGroup->RefConfigGroup(group);
}
}
return id;
}
// interface
int asCScriptEngine::RegisterGlobalProperty(const char *declaration, void *pointer)
{
asCDataType type;
asCString name;
int r;
asCBuilder bld(this, 0);
if( (r = bld.VerifyProperty(0, declaration, name, type)) < 0 )
return ConfigError(r);
// Don't allow registering references as global properties
if( type.IsReference() )
return ConfigError(asINVALID_TYPE);
// Store the property info
asCGlobalProperty *prop = AllocateGlobalProperty();
prop->name = name;
prop->type = type;
prop->SetRegisteredAddress(pointer);
registeredGlobalProps.PushLast(prop);
currentGroup->globalProps.PushLast(prop);
// If from another group add reference
if( type.GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(type.GetObjectType());
currentGroup->RefConfigGroup(group);
}
return asSUCCESS;
}
// internal
asCGlobalProperty *asCScriptEngine::AllocateGlobalProperty()
{
asCGlobalProperty *prop = asNEW(asCGlobalProperty);
// First check the availability of a free slot
if( freeGlobalPropertyIds.GetLength() )
{
prop->id = freeGlobalPropertyIds.PopLast();
globalProperties[prop->id] = prop;
return prop;
}
prop->id = (asUINT)globalProperties.GetLength();
globalProperties.PushLast(prop);
return prop;
}
// internal
void asCScriptEngine::FreeUnusedGlobalProperties()
{
for( asUINT n = 0; n < globalProperties.GetLength(); n++ )
{
if( globalProperties[n] && globalProperties[n]->refCount.get() == 0 )
{
freeGlobalPropertyIds.PushLast(n);
asDELETE(globalProperties[n], asCGlobalProperty);
globalProperties[n] = 0;
}
}
}
// interface
int asCScriptEngine::GetGlobalPropertyCount()
{
return (int)registeredGlobalProps.GetLength();
}
// interface
// TODO: If the typeId ever encodes the const flag, then the isConst parameter should be removed
int asCScriptEngine::GetGlobalPropertyByIndex(asUINT index, const char **name, int *typeId, bool *isConst, const char **configGroup, void **pointer)
{
if( index >= registeredGlobalProps.GetLength() )
return asINVALID_ARG;
if( name )
*name = registeredGlobalProps[index]->name.AddressOf();
if( configGroup )
{
asCConfigGroup *group = FindConfigGroupForGlobalVar(index);
if( group )
*configGroup = group->groupName.AddressOf();
else
*configGroup = 0;
}
if( typeId )
*typeId = GetTypeIdFromDataType(registeredGlobalProps[index]->type);
if( isConst )
*isConst = registeredGlobalProps[index]->type.IsReadOnly();
if( pointer )
*pointer = registeredGlobalProps[index]->realAddress;
return asSUCCESS;
}
// interface
int asCScriptEngine::RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv)
{
if( obj == 0 )
return ConfigError(asINVALID_ARG);
// Determine the object type
asCDataType dt;
asCBuilder bld(this, 0);
int r = bld.ParseDataType(obj, &dt);
if( r < 0 )
return ConfigError(r);
if( dt.GetObjectType() == 0 )
return ConfigError(asINVALID_ARG);
return RegisterMethodToObjectType(dt.GetObjectType(), declaration, funcPointer, callConv);
}
// internal
int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv)
{
asSSystemFunctionInterface internal;
int r = DetectCallingConvention(true, funcPointer, callConv, &internal);
if( r < 0 )
return ConfigError(r);
// We only support these calling conventions for object methods
#ifdef AS_MAX_PORTABILITY
if( callConv != asCALL_GENERIC )
return ConfigError(asNOT_SUPPORTED);
#else
if( callConv != asCALL_THISCALL &&
callConv != asCALL_CDECL_OBJLAST &&
callConv != asCALL_CDECL_OBJFIRST &&
callConv != asCALL_GENERIC )
return ConfigError(asNOT_SUPPORTED);
#endif
isPrepared = false;
// Put the system function in the list of system functions
asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal);
asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM);
func->sysFuncIntf = newInterface;
func->objectType = objectType;
asCBuilder bld(this, 0);
r = bld.ParseFunctionDeclaration(func->objectType, declaration, func, true, &newInterface->paramAutoHandles, &newInterface->returnAutoHandle);
if( r < 0 )
{
// Set as dummy function before deleting
func->funcType = -1;
asDELETE(func,asCScriptFunction);
return ConfigError(asINVALID_DECLARATION);
}
// Check name conflicts
r = bld.CheckNameConflictMember(objectType, func->name.AddressOf(), 0, 0);
if( r < 0 )
{
asDELETE(func,asCScriptFunction);
return ConfigError(asNAME_TAKEN);
}
func->id = GetNextScriptFunctionId();
func->objectType->methods.PushLast(func->id);
SetScriptFunction(func);
// TODO: This code is repeated in many places
// If parameter type from other groups are used, add references
if( func->returnType.GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType());
currentGroup->RefConfigGroup(group);
}
for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ )
{
if( func->parameterTypes[n].GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType());
currentGroup->RefConfigGroup(group);
}
}
// Check if the method restricts that use of the template to value types or reference types
if( func->objectType->flags & asOBJ_TEMPLATE )
{
if( func->returnType.GetObjectType() == func->objectType->templateSubType.GetObjectType() )
{
if( func->returnType.IsObjectHandle() )
func->objectType->acceptValueSubType = false;
else if( !func->returnType.IsReference() )
func->objectType->acceptRefSubType = false;
}
for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ )
{
if( func->parameterTypes[n].GetObjectType() == func->objectType->templateSubType.GetObjectType() )
{
// TODO: If unsafe references are allowed, then inout references allow value types
if( func->parameterTypes[n].IsObjectHandle() || (func->parameterTypes[n].IsReference() && func->inOutFlags[n] == asTM_INOUTREF) )
func->objectType->acceptValueSubType = false;
else if( !func->parameterTypes[n].IsReference() )
func->objectType->acceptRefSubType = false;
}
}
}
// TODO: beh.copy member will be removed, so this is not necessary
// Is this the default copy behaviour?
if( func->name == "opAssign" && func->parameterTypes.GetLength() == 1 && func->isReadOnly == false &&
(objectType->flags & asOBJ_SCRIPT_OBJECT || func->parameterTypes[0].IsEqualExceptRefAndConst(asCDataType::CreateObject(func->objectType, false))) )
{
func->objectType->beh.copy = func->id;
func->AddRef();
}
// Return the function id as success
return func->id;
}
// interface
int asCScriptEngine::RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv)
{
asSSystemFunctionInterface internal;
int r = DetectCallingConvention(false, funcPointer, callConv, &internal);
if( r < 0 )
return ConfigError(r);
#ifdef AS_MAX_PORTABILITY
if( callConv != asCALL_GENERIC )
return ConfigError(asNOT_SUPPORTED);
#else
if( callConv != asCALL_CDECL &&
callConv != asCALL_STDCALL &&
callConv != asCALL_GENERIC )
return ConfigError(asNOT_SUPPORTED);
#endif
isPrepared = false;
// Put the system function in the list of system functions
asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal);
asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM);
func->sysFuncIntf = newInterface;
asCBuilder bld(this, 0);
r = bld.ParseFunctionDeclaration(0, declaration, func, true, &newInterface->paramAutoHandles, &newInterface->returnAutoHandle);
if( r < 0 )
{
// Set as dummy function before deleting
func->funcType = -1;
asDELETE(func,asCScriptFunction);
return ConfigError(asINVALID_DECLARATION);
}
// Check name conflicts
r = bld.CheckNameConflict(func->name.AddressOf(), 0, 0);
if( r < 0 )
{
asDELETE(func,asCScriptFunction);
return ConfigError(asNAME_TAKEN);
}
func->id = GetNextScriptFunctionId();
SetScriptFunction(func);
currentGroup->scriptFunctions.PushLast(func);
registeredGlobalFuncs.PushLast(func);
// If parameter type from other groups are used, add references
if( func->returnType.GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType());
currentGroup->RefConfigGroup(group);
}
for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ )
{
if( func->parameterTypes[n].GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType());
currentGroup->RefConfigGroup(group);
}
}
// Return the function id as success
return func->id;
}
// interface
int asCScriptEngine::GetGlobalFunctionCount()
{
return (int)registeredGlobalFuncs.GetLength();
}
// interface
int asCScriptEngine::GetGlobalFunctionIdByIndex(asUINT index)
{
if( index >= registeredGlobalFuncs.GetLength() )
return asINVALID_ARG;
return registeredGlobalFuncs[index]->id;
}
asCObjectType *asCScriptEngine::GetObjectType(const char *type)
{
// TODO: optimize: Improve linear search
for( asUINT n = 0; n < objectTypes.GetLength(); n++ )
if( objectTypes[n] &&
objectTypes[n]->name == type ) // TODO: template: Should we check the subtype in case of template instances?
return objectTypes[n];
return 0;
}
void asCScriptEngine::PrepareEngine()
{
if( isPrepared ) return;
if( configFailed ) return;
asUINT n;
for( n = 0; n < scriptFunctions.GetLength(); n++ )
{
// Determine the host application interface
if( scriptFunctions[n] && scriptFunctions[n]->funcType == asFUNC_SYSTEM )
{
if( scriptFunctions[n]->sysFuncIntf->callConv == ICC_GENERIC_FUNC ||
scriptFunctions[n]->sysFuncIntf->callConv == ICC_GENERIC_METHOD )
PrepareSystemFunctionGeneric(scriptFunctions[n], scriptFunctions[n]->sysFuncIntf, this);
else
PrepareSystemFunction(scriptFunctions[n], scriptFunctions[n]->sysFuncIntf, this);
}
}
// Validate object type registrations
for( n = 0; n < objectTypes.GetLength(); n++ )
{
if( objectTypes[n] && !(objectTypes[n]->flags & asOBJ_SCRIPT_OBJECT) )
{
bool missingBehaviour = false;
const char *infoMsg = 0;
// Verify that GC types have all behaviours
if( objectTypes[n]->flags & asOBJ_GC )
{
if( objectTypes[n]->beh.addref == 0 ||
objectTypes[n]->beh.release == 0 ||
objectTypes[n]->beh.gcGetRefCount == 0 ||
objectTypes[n]->beh.gcSetFlag == 0 ||
objectTypes[n]->beh.gcGetFlag == 0 ||
objectTypes[n]->beh.gcEnumReferences == 0 ||
objectTypes[n]->beh.gcReleaseAllReferences == 0 )
{
infoMsg = TXT_GC_REQUIRE_ADD_REL_GC_BEHAVIOUR;
missingBehaviour = true;
}
}
// Verify that scoped ref types have the release behaviour
else if( objectTypes[n]->flags & asOBJ_SCOPED )
{
if( objectTypes[n]->beh.release == 0 )
{
infoMsg = TXT_SCOPE_REQUIRE_REL_BEHAVIOUR;
missingBehaviour = true;
}
}
// Verify that ref types have add ref and release behaviours
else if( (objectTypes[n]->flags & asOBJ_REF) &&
!(objectTypes[n]->flags & asOBJ_NOHANDLE) )
{
if( objectTypes[n]->beh.addref == 0 ||
objectTypes[n]->beh.release == 0 )
{
infoMsg = TXT_REF_REQUIRE_ADD_REL_BEHAVIOUR;
missingBehaviour = true;
}
}
// Verify that non-pod value types have the constructor and destructor registered
else if( (objectTypes[n]->flags & asOBJ_VALUE) &&
!(objectTypes[n]->flags & asOBJ_POD) )
{
if( objectTypes[n]->beh.construct == 0 ||
objectTypes[n]->beh.destruct == 0 )
{
infoMsg = TXT_NON_POD_REQUIRE_CONSTR_DESTR_BEHAVIOUR;
missingBehaviour = true;
}
}
if( missingBehaviour )
{
asCString str;
str.Format(TXT_TYPE_s_IS_MISSING_BEHAVIOURS, objectTypes[n]->name.AddressOf());
WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, infoMsg);
ConfigError(asINVALID_CONFIGURATION);
}
}
}
isPrepared = true;
}
int asCScriptEngine::ConfigError(int err)
{
configFailed = true;
return err;
}
// interface
int asCScriptEngine::RegisterStringFactory(const char *datatype, const asSFuncPtr &funcPointer, asDWORD callConv)
{
asSSystemFunctionInterface internal;
int r = DetectCallingConvention(false, funcPointer, callConv, &internal);
if( r < 0 )
return ConfigError(r);
#ifdef AS_MAX_PORTABILITY
if( callConv != asCALL_GENERIC )
return ConfigError(asNOT_SUPPORTED);
#else
if( callConv != asCALL_CDECL &&
callConv != asCALL_STDCALL &&
callConv != asCALL_GENERIC )
return ConfigError(asNOT_SUPPORTED);
#endif
// Put the system function in the list of system functions
asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal);
asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM);
func->name = "_string_factory_";
func->sysFuncIntf = newInterface;
asCBuilder bld(this, 0);
asCDataType dt;
r = bld.ParseDataType(datatype, &dt);
if( r < 0 )
{
// Set as dummy before deleting
func->funcType = -1;
asDELETE(func,asCScriptFunction);
return ConfigError(asINVALID_TYPE);
}
func->returnType = dt;
func->parameterTypes.PushLast(asCDataType::CreatePrimitive(ttInt, true));
asCDataType parm1 = asCDataType::CreatePrimitive(ttUInt8, true);
parm1.MakeReference(true);
func->parameterTypes.PushLast(parm1);
func->id = GetNextScriptFunctionId();
SetScriptFunction(func);
stringFactory = func;
if( func->returnType.GetObjectType() )
{
asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType());
if( group == 0 ) group = &defaultGroup;
group->scriptFunctions.PushLast(func);
}
// Register function id as success
return func->id;
}
// interface
int asCScriptEngine::GetStringFactoryReturnTypeId()
{
if( stringFactory == 0 )
return asNO_FUNCTION;
return GetTypeIdFromDataType(stringFactory->returnType);
}
// interface
asCModule *asCScriptEngine::GetModule(const char *_name, bool create)
{
// Accept null as well as zero-length string
const char *name = "";
if( _name != 0 ) name = _name;
if( lastModule && lastModule->name == name )
return lastModule;
// TODO: optimize: Improve linear search
for( asUINT n = 0; n < scriptModules.GetLength(); ++n )
if( scriptModules[n] && scriptModules[n]->name == name )
{
lastModule = scriptModules[n];
return lastModule;
}
if( create )
{
asCModule *module = asNEW(asCModule)(name, this);
scriptModules.PushLast(module);
lastModule = module;
return lastModule;
}
return 0;
}
asCModule *asCScriptEngine::GetModuleFromFuncId(int id)
{
if( id < 0 ) return 0;
if( id >= (int)scriptFunctions.GetLength() ) return 0;
asCScriptFunction *func = scriptFunctions[id];
if( func == 0 ) return 0;
return func->module;
}
// internal
int asCScriptEngine::RequestBuild()
{
ENTERCRITICALSECTION(engineCritical);
if( isBuilding )
{
LEAVECRITICALSECTION(engineCritical);
return asBUILD_IN_PROGRESS;
}
isBuilding = true;
LEAVECRITICALSECTION(engineCritical);
return 0;
}
// internal
void asCScriptEngine::BuildCompleted()
{
// Always free up pooled memory after a completed build
memoryMgr.FreeUnusedMemory();
isBuilding = false;
}
#ifdef AS_DEPRECATED
// Deprecated since 2009-12-08, 2.18.0
// interface
int asCScriptEngine::ExecuteString(const char *module, const char *script, asIScriptContext **ctx, asDWORD flags)
{
int r;
if( (r = RequestBuild()) < 0 )
return r;
PrepareEngine();
// Make sure the config worked
if( configFailed )
{
if( ctx && !(flags & asEXECSTRING_USE_MY_CONTEXT) )
*ctx = 0;
WriteMessage("",0,0,asMSGTYPE_ERROR,TXT_INVALID_CONFIGURATION);
isBuilding = false;
return asINVALID_CONFIGURATION;
}
asIScriptContext *exec = 0;
if( !(flags & asEXECSTRING_USE_MY_CONTEXT) )
{
int r = CreateContext(&exec, false);
if( r < 0 )
{
if( ctx && !(flags & asEXECSTRING_USE_MY_CONTEXT) )
*ctx = 0;
isBuilding = false;
return r;
}
if( ctx )
{
*ctx = exec;
exec->AddRef();
}
}
else
{
if( *ctx == 0 )
{
isBuilding = false;
return asINVALID_ARG;
}
exec = *ctx;
exec->AddRef();
}
// Make sure the context isn't holding a reference to the previous ExecuteString function()
exec->Unprepare();
// Get the module to compile the string in
asCModule *mod = GetModule(module, true);
// Compile string function
asCBuilder builder(this, mod);
asCString str = script;
str = "void ExecuteString(){\n" + str + "\n;}";
r = builder.BuildString(str.AddressOf(), (asCContext*)exec);
BuildCompleted();
if( r < 0 )
{
if( ctx && !(flags & asEXECSTRING_USE_MY_CONTEXT) )
{
(*ctx)->Release();
*ctx = 0;
}
exec->Release();
return asERROR;
}
// Prepare and execute the context
r = ((asCContext*)exec)->Prepare(((asCContext*)exec)->stringFunction->id);
if( r < 0 )
{
if( ctx && !(flags & asEXECSTRING_USE_MY_CONTEXT) )
{
(*ctx)->Release();
*ctx = 0;
}
exec->Release();
return r;
}
if( flags & asEXECSTRING_ONLY_PREPARE )
r = asEXECUTION_PREPARED;
else
r = exec->Execute();
exec->Release();
return r;
}
#endif
void asCScriptEngine::RemoveTemplateInstanceType(asCObjectType *t)
{
int n;
// Destroy the factory stubs
for( n = 0; n < (int)t->beh.factories.GetLength(); n++ )
{
// Make sure the factory stub isn't referencing this object anymore
scriptFunctions[t->beh.factories[n]]->ReleaseAllHandles(this);
scriptFunctions[t->beh.factories[n]]->Release();
}
t->beh.factories.SetLength(0);
// Destroy the specialized functions
for( n = 1; n < (int)t->beh.operators.GetLength(); n += 2 )
{
if( t->beh.operators[n] && scriptFunctions[t->beh.operators[n]]->objectType == t )
{
scriptFunctions[t->beh.operators[n]]->Release();
}
}
t->beh.operators.SetLength(0);
// Start searching from the end of the list, as most of
// the time it will be the last two types
for( n = (int)templateTypes.GetLength()-1; n >= 0; n-- )
{
if( templateTypes[n] == t )
{
if( n == (signed)templateTypes.GetLength()-1 )
templateTypes.PopLast();
else
templateTypes[n] = templateTypes.PopLast();
}
}
for( n = (int)templateInstanceTypes.GetLength()-1; n >= 0; n-- )
{
if( templateInstanceTypes[n] == t )
{
if( n == (signed)templateInstanceTypes.GetLength()-1 )
templateInstanceTypes.PopLast();
else
templateInstanceTypes[n] = templateInstanceTypes.PopLast();
}
}
asDELETE(t,asCObjectType);
}
asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateType, asCDataType &subType)
{
asUINT n;
// Is there any template instance type or template specialization already with this subtype?
for( n = 0; n < templateTypes.GetLength(); n++ )
{
if( templateTypes[n] &&
templateTypes[n]->name == templateType->name &&
templateTypes[n]->templateSubType == subType )
return templateTypes[n];
}
// No previous template instance exists
// Make sure this template supports the subtype
if( !templateType->acceptValueSubType && (subType.IsPrimitive() || (subType.GetObjectType()->flags & asOBJ_VALUE)) )
return 0;
if( !templateType->acceptRefSubType && (subType.IsObject() && (subType.GetObjectType()->flags & asOBJ_REF)) )
return 0;
// Create a new template instance type based on the templateType
asCObjectType *ot = asNEW(asCObjectType)(this);
ot->templateSubType = subType;
ot->flags = templateType->flags;
ot->size = templateType->size;
ot->name = templateType->name;
// Before filling in the methods, call the template instance callback behaviour to validate the type
if( templateType->beh.templateCallback )
{
asCScriptFunction *callback = scriptFunctions[templateType->beh.templateCallback];
if( !CallGlobalFunctionRetBool(ot, 0, callback->sysFuncIntf, callback) )
{
// The type cannot be instanciated
ot->templateSubType = asCDataType();
asDELETE(ot, asCObjectType);
return 0;
}
}
ot->methods = templateType->methods;
for( n = 0; n < ot->methods.GetLength(); n++ )
scriptFunctions[ot->methods[n]]->AddRef();
// Store the real factory in the constructor
ot->beh.construct = templateType->beh.factory;
ot->beh.constructors = templateType->beh.factories;
for( n = 0; n < ot->beh.constructors.GetLength(); n++ )
scriptFunctions[ot->beh.constructors[n]]->AddRef();
// Generate factory stubs for each of the factories
for( n = 0; n < templateType->beh.factories.GetLength(); n++ )
{
int factoryId = templateType->beh.factories[n];
asCScriptFunction *factory = scriptFunctions[factoryId];
asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SCRIPT);
func->name = "factstub";
func->id = GetNextScriptFunctionId();
func->returnType = asCDataType::CreateObjectHandle(ot, false);
// Skip the first parameter as this is the object type pointer that the stub will add
for( asUINT p = 1; p < factory->parameterTypes.GetLength(); p++ )
{
func->parameterTypes.PushLast(factory->parameterTypes[p]);
func->inOutFlags.PushLast(factory->inOutFlags[p]);
}
SetScriptFunction(func);
asCBuilder builder(this, 0);
asCCompiler compiler(this);
compiler.CompileTemplateFactoryStub(&builder, factoryId, ot, func);
// The function's refCount was already initialized to 1
ot->beh.factories.PushLast(func->id);
}
if( ot->beh.factories.GetLength() )
ot->beh.factory = ot->beh.factories[0];
else
{
asASSERT(false);
ot->beh.factory = templateType->beh.factory;
}
ot->beh.addref = templateType->beh.addref;
if( scriptFunctions[ot->beh.addref] ) scriptFunctions[ot->beh.addref]->AddRef();
ot->beh.release = templateType->beh.release;
if( scriptFunctions[ot->beh.release] ) scriptFunctions[ot->beh.release]->AddRef();
ot->beh.copy = templateType->beh.copy;
if( scriptFunctions[ot->beh.copy] ) scriptFunctions[ot->beh.copy]->AddRef();
ot->beh.operators = templateType->beh.operators;
for( n = 1; n < ot->beh.operators.GetLength(); n += 2 )
{
scriptFunctions[ot->beh.operators[n]]->AddRef();
}
ot->beh.gcGetRefCount = templateType->beh.gcGetRefCount;
if( scriptFunctions[ot->beh.gcGetRefCount] ) scriptFunctions[ot->beh.gcGetRefCount]->AddRef();
ot->beh.gcSetFlag = templateType->beh.gcSetFlag;
if( scriptFunctions[ot->beh.gcSetFlag] ) scriptFunctions[ot->beh.gcSetFlag]->AddRef();
ot->beh.gcGetFlag = templateType->beh.gcGetFlag;
if( scriptFunctions[ot->beh.gcGetFlag] ) scriptFunctions[ot->beh.gcGetFlag]->AddRef();
ot->beh.gcEnumReferences = templateType->beh.gcEnumReferences;
if( scriptFunctions[ot->beh.gcEnumReferences] ) scriptFunctions[ot->beh.gcEnumReferences]->AddRef();
ot->beh.gcReleaseAllReferences = templateType->beh.gcReleaseAllReferences;
if( scriptFunctions[ot->beh.gcReleaseAllReferences] ) scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRef();
// As the new template type is instanciated, the engine should
// generate new functions to substitute the ones with the template subtype.
for( n = 1; n < ot->beh.operators.GetLength(); n += 2 )
{
int funcId = ot->beh.operators[n];
asCScriptFunction *func = scriptFunctions[funcId];
if( GenerateNewTemplateFunction(templateType, ot, subType, func, &func) )
{
// Release the old function, the new one already has its ref count set to 1
scriptFunctions[ot->beh.operators[n]]->Release();
ot->beh.operators[n] = func->id;
}
}
// As the new template type is instanciated, the engine should
// generate new functions to substitute the ones with the template subtype.
for( n = 0; n < ot->methods.GetLength(); n++ )
{
int funcId = ot->methods[n];
asCScriptFunction *func = scriptFunctions[funcId];
if( GenerateNewTemplateFunction(templateType, ot, subType, func, &func) )
{
// Release the old function, the new one already has its ref count set to 1
scriptFunctions[ot->methods[n]]->Release();
ot->methods[n] = func->id;
}
}
// Increase ref counter for sub type if it is an object type
if( ot->templateSubType.GetObjectType() ) ot->templateSubType.GetObjectType()->AddRef();
// Verify if the subtype contains a garbage collected object, in which case this template is a potential circular reference
// TODO: We may be a bit smarter here. If we can guarantee that the array type cannot be part of the
// potential circular reference then we don't need to set the flag
if( ot->templateSubType.GetObjectType() && (ot->templateSubType.GetObjectType()->flags & asOBJ_GC) )
ot->flags |= asOBJ_GC;
else if( ot->name == defaultArrayObjectType->name )
ot->flags &= ~asOBJ_GC;
templateTypes.PushLast(ot);
// We need to store the object type somewhere for clean-up later
// TODO: Why do we need both templateTypes and templateInstanceTypes? It is possible to differ between template instance and template specialization by checking for the asOBJ_TEMPLATE flag
templateInstanceTypes.PushLast(ot);
return ot;
}
bool asCScriptEngine::GenerateNewTemplateFunction(asCObjectType *templateType, asCObjectType *ot, asCDataType &subType, asCScriptFunction *func, asCScriptFunction **newFunc)
{
bool needNewFunc = false;
if( func->returnType.GetObjectType() == templateType->templateSubType.GetObjectType() ||
func->returnType.GetObjectType() == templateType )
needNewFunc = true;
else
{
for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ )
{
if( func->parameterTypes[p].GetObjectType() == templateType->templateSubType.GetObjectType() ||
func->parameterTypes[p].GetObjectType() == templateType )
{
needNewFunc = true;
break;
}
}
}
if( needNewFunc )
{
asCScriptFunction *func2 = asNEW(asCScriptFunction)(this, 0, func->funcType);
func2->name = func->name;
func2->id = GetNextScriptFunctionId();
if( func->returnType.GetObjectType() == templateType->templateSubType.GetObjectType() )
{
func2->returnType = subType;
if( func->returnType.IsObjectHandle() )
func2->returnType.MakeHandle(true, true);
func2->returnType.MakeReference(func->returnType.IsReference());
func2->returnType.MakeReadOnly(func->returnType.IsReadOnly());
}
else if( func->returnType.GetObjectType() == templateType )
{
if( func2->returnType.IsObjectHandle() )
func2->returnType = asCDataType::CreateObjectHandle(ot, false);
else
func2->returnType = asCDataType::CreateObject(ot, false);
func2->returnType.MakeReference(func->returnType.IsReference());
func2->returnType.MakeReadOnly(func->returnType.IsReadOnly());
}
else
func2->returnType = func->returnType;
func2->parameterTypes.SetLength(func->parameterTypes.GetLength());
for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ )
{
if( func->parameterTypes[p].GetObjectType() == templateType->templateSubType.GetObjectType() )
{
func2->parameterTypes[p] = subType;
if( func->parameterTypes[p].IsObjectHandle() )
func2->parameterTypes[p].MakeHandle(true);
func2->parameterTypes[p].MakeReference(func->parameterTypes[p].IsReference());
func2->parameterTypes[p].MakeReadOnly(func->parameterTypes[p].IsReference());
}
else if( func->parameterTypes[p].GetObjectType() == templateType )
{
if( func2->parameterTypes[p].IsObjectHandle() )
func2->parameterTypes[p] = asCDataType::CreateObjectHandle(ot, false);
else
func2->parameterTypes[p] = asCDataType::CreateObject(ot, false);
func2->parameterTypes[p].MakeReference(func->parameterTypes[p].IsReference());
func2->parameterTypes[p].MakeReadOnly(func->parameterTypes[p].IsReadOnly());
}
else
func2->parameterTypes[p] = func->parameterTypes[p];
}
// TODO: template: Must be careful when instanciating templates for garbage collected types
// If the template hasn't been registered with the behaviours, it shouldn't
// permit instanciation of garbage collected types that in turn may refer to
// this instance.
func2->inOutFlags = func->inOutFlags;
func2->isReadOnly = func->isReadOnly;
func2->objectType = ot;
func2->stackNeeded = func->stackNeeded;
func2->sysFuncIntf = asNEW(asSSystemFunctionInterface)(*func->sysFuncIntf);
SetScriptFunction(func2);
// Return the new function
*newFunc = func2;
}
return needNewFunc;
}
void asCScriptEngine::CallObjectMethod(void *obj, int func)
{
asCScriptFunction *s = scriptFunctions[func];
CallObjectMethod(obj, s->sysFuncIntf, s);
}
void asCScriptEngine::CallObjectMethod(void *obj, asSSystemFunctionInterface *i, asCScriptFunction *s)
{
#ifdef __GNUC__
if( i->callConv == ICC_GENERIC_METHOD )
{
asCGeneric gen(this, s, obj, 0);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
}
else if( i->callConv == ICC_VIRTUAL_THISCALL )
{
// For virtual thiscalls we must call the method as a true class method
// so that the compiler will lookup the function address in the vftable
union
{
asSIMPLEMETHOD_t mthd;
struct
{
asFUNCTION_t func;
asPWORD baseOffset; // Same size as the pointer
} f;
} p;
p.f.func = (void (*)())(i->func);
p.f.baseOffset = asPWORD(i->baseOffset);
void (asCSimpleDummy::*f)() = p.mthd;
(((asCSimpleDummy*)obj)->*f)();
}
else /*if( i->callConv == ICC_THISCALL || i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/
{
// Non-virtual thiscall can be called just like any global function, passing the object as the first parameter
void (*f)(void *) = (void (*)(void *))(i->func);
f(obj);
}
#else
#ifndef AS_NO_CLASS_METHODS
if( i->callConv == ICC_THISCALL )
{
union
{
asSIMPLEMETHOD_t mthd;
asFUNCTION_t func;
} p;
p.func = (void (*)())(i->func);
void (asCSimpleDummy::*f)() = p.mthd;
(((asCSimpleDummy*)obj)->*f)();
}
else
#endif
if( i->callConv == ICC_GENERIC_METHOD )
{
asCGeneric gen(this, s, obj, 0);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
}
else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/
{
void (*f)(void *) = (void (*)(void *))(i->func);
f(obj);
}
#endif
}
bool asCScriptEngine::CallObjectMethodRetBool(void *obj, int func)
{
asCScriptFunction *s = scriptFunctions[func];
asSSystemFunctionInterface *i = s->sysFuncIntf;
#ifdef __GNUC__
if( i->callConv == ICC_GENERIC_METHOD )
{
asCGeneric gen(this, s, obj, 0);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
return *(bool*)gen.GetReturnPointer();
}
else if( i->callConv == ICC_VIRTUAL_THISCALL )
{
// For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable
union
{
asSIMPLEMETHOD_t mthd;
struct
{
asFUNCTION_t func;
asDWORD baseOffset;
} f;
} p;
p.f.func = (void (*)())(i->func);
p.f.baseOffset = i->baseOffset;
bool (asCSimpleDummy::*f)() = (bool (asCSimpleDummy::*)())(p.mthd);
return (((asCSimpleDummy*)obj)->*f)();
}
else /*if( i->callConv == ICC_THISCALL || i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/
{
// Non-virtual thiscall can be called just like any global function, passing the object as the first parameter
bool (*f)(void *) = (bool (*)(void *))(i->func);
return f(obj);
}
#else
#ifndef AS_NO_CLASS_METHODS
if( i->callConv == ICC_THISCALL )
{
union
{
asSIMPLEMETHOD_t mthd;
asFUNCTION_t func;
} p;
p.func = (void (*)())(i->func);
bool (asCSimpleDummy::*f)() = (bool (asCSimpleDummy::*)())p.mthd;
return (((asCSimpleDummy*)obj)->*f)();
}
else
#endif
if( i->callConv == ICC_GENERIC_METHOD )
{
asCGeneric gen(this, s, obj, 0);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
return *(bool*)gen.GetReturnPointer();
}
else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/
{
bool (*f)(void *) = (bool (*)(void *))(i->func);
return f(obj);
}
#endif
}
int asCScriptEngine::CallObjectMethodRetInt(void *obj, int func)
{
asCScriptFunction *s = scriptFunctions[func];
asSSystemFunctionInterface *i = s->sysFuncIntf;
#ifdef __GNUC__
if( i->callConv == ICC_GENERIC_METHOD )
{
asCGeneric gen(this, s, obj, 0);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
return *(int*)gen.GetReturnPointer();
}
else if( i->callConv == ICC_VIRTUAL_THISCALL )
{
// For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable
union
{
asSIMPLEMETHOD_t mthd;
struct
{
asFUNCTION_t func;
asDWORD baseOffset;
} f;
} p;
p.f.func = (void (*)())(i->func);
p.f.baseOffset = i->baseOffset;
int (asCSimpleDummy::*f)() = (int (asCSimpleDummy::*)())(p.mthd);
return (((asCSimpleDummy*)obj)->*f)();
}
else /*if( i->callConv == ICC_THISCALL || i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/
{
// Non-virtual thiscall can be called just like any global function, passing the object as the first parameter
int (*f)(void *) = (int (*)(void *))(i->func);
return f(obj);
}
#else
#ifndef AS_NO_CLASS_METHODS
if( i->callConv == ICC_THISCALL )
{
union
{
asSIMPLEMETHOD_t mthd;
asFUNCTION_t func;
} p;
p.func = (void (*)())(i->func);
int (asCSimpleDummy::*f)() = (int (asCSimpleDummy::*)())p.mthd;
return (((asCSimpleDummy*)obj)->*f)();
}
else
#endif
if( i->callConv == ICC_GENERIC_METHOD )
{
asCGeneric gen(this, s, obj, 0);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
return *(int*)gen.GetReturnPointer();
}
else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/
{
int (*f)(void *) = (int (*)(void *))(i->func);
return f(obj);
}
#endif
}
void *asCScriptEngine::CallGlobalFunctionRetPtr(int func)
{
asCScriptFunction *s = scriptFunctions[func];
return CallGlobalFunctionRetPtr(s->sysFuncIntf, s);
}
void *asCScriptEngine::CallGlobalFunctionRetPtr(int func, void *param1)
{
asCScriptFunction *s = scriptFunctions[func];
return CallGlobalFunctionRetPtr(s->sysFuncIntf, s, param1);
}
void *asCScriptEngine::CallGlobalFunctionRetPtr(asSSystemFunctionInterface *i, asCScriptFunction *s)
{
if( i->callConv == ICC_CDECL )
{
void *(*f)() = (void *(*)())(i->func);
return f();
}
else if( i->callConv == ICC_STDCALL )
{
void *(STDCALL *f)() = (void *(STDCALL *)())(i->func);
return f();
}
else
{
asCGeneric gen(this, s, 0, 0);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
return *(void**)gen.GetReturnPointer();
}
}
void *asCScriptEngine::CallGlobalFunctionRetPtr(asSSystemFunctionInterface *i, asCScriptFunction *s, void *param1)
{
if( i->callConv == ICC_CDECL )
{
void *(*f)(void *) = (void *(*)(void *))(i->func);
return f(param1);
}
else if( i->callConv == ICC_STDCALL )
{
void *(STDCALL *f)(void *) = (void *(STDCALL *)(void *))(i->func);
return f(param1);
}
else
{
asCGeneric gen(this, s, 0, (asDWORD*)&param1);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
return *(void**)gen.GetReturnPointer();
}
}
void asCScriptEngine::CallObjectMethod(void *obj, void *param, int func)
{
asCScriptFunction *s = scriptFunctions[func];
CallObjectMethod(obj, param, s->sysFuncIntf, s);
}
void asCScriptEngine::CallObjectMethod(void *obj, void *param, asSSystemFunctionInterface *i, asCScriptFunction *s)
{
#ifdef __GNUC__
if( i->callConv == ICC_CDECL_OBJLAST )
{
void (*f)(void *, void *) = (void (*)(void *, void *))(i->func);
f(param, obj);
}
else if( i->callConv == ICC_GENERIC_METHOD )
{
asCGeneric gen(this, s, obj, (asDWORD*)&param);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
}
else /*if( i->callConv == ICC_CDECL_OBJFIRST || i->callConv == ICC_THISCALL )*/
{
void (*f)(void *, void *) = (void (*)(void *, void *))(i->func);
f(obj, param);
}
#else
#ifndef AS_NO_CLASS_METHODS
if( i->callConv == ICC_THISCALL )
{
union
{
asSIMPLEMETHOD_t mthd;
asFUNCTION_t func;
} p;
p.func = (void (*)())(i->func);
void (asCSimpleDummy::*f)(void *) = (void (asCSimpleDummy::*)(void *))(p.mthd);
(((asCSimpleDummy*)obj)->*f)(param);
}
else
#endif
if( i->callConv == ICC_CDECL_OBJLAST )
{
void (*f)(void *, void *) = (void (*)(void *, void *))(i->func);
f(param, obj);
}
else if( i->callConv == ICC_GENERIC_METHOD )
{
asCGeneric gen(this, s, obj, (asDWORD*)&param);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
}
else /*if( i->callConv == ICC_CDECL_OBJFIRST )*/
{
void (*f)(void *, void *) = (void (*)(void *, void *))(i->func);
f(obj, param);
}
#endif
}
void asCScriptEngine::CallGlobalFunction(void *param1, void *param2, asSSystemFunctionInterface *i, asCScriptFunction *s)
{
if( i->callConv == ICC_CDECL )
{
void (*f)(void *, void *) = (void (*)(void *, void *))(i->func);
f(param1, param2);
}
else if( i->callConv == ICC_STDCALL )
{
void (STDCALL *f)(void *, void *) = (void (STDCALL *)(void *, void *))(i->func);
f(param1, param2);
}
else
{
asCGeneric gen(this, s, 0, (asDWORD*)&param1);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
}
}
bool asCScriptEngine::CallGlobalFunctionRetBool(void *param1, void *param2, asSSystemFunctionInterface *i, asCScriptFunction *s)
{
if( i->callConv == ICC_CDECL )
{
bool (*f)(void *, void *) = (bool (*)(void *, void *))(i->func);
return f(param1, param2);
}
else if( i->callConv == ICC_STDCALL )
{
bool (STDCALL *f)(void *, void *) = (bool (STDCALL *)(void *, void *))(i->func);
return f(param1, param2);
}
else
{
// TODO: When simulating a 64bit environment by defining AS_64BIT_PTR on a 32bit platform this code
// fails, because the stack given to asCGeneric is not prepared with two 64bit arguments.
asCGeneric gen(this, s, 0, (asDWORD*)&param1);
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
return *(bool*)gen.GetReturnPointer();
}
}
void *asCScriptEngine::CallAlloc(asCObjectType *type)
{
// Allocate 4 bytes as the smallest size. Otherwise CallSystemFunction may try to
// copy a DWORD onto a smaller memory block, in case the object type is return in registers.
#if defined(AS_DEBUG)
return ((asALLOCFUNCDEBUG_t)(userAlloc))(type->size < 4 ? 4 : type->size, __FILE__, __LINE__);
#else
return userAlloc(type->size < 4 ? 4 : type->size);
#endif
}
void asCScriptEngine::CallFree(void *obj)
{
userFree(obj);
}
// interface
void asCScriptEngine::NotifyGarbageCollectorOfNewObject(void *obj, int typeId)
{
asCObjectType *objType = GetObjectTypeFromTypeId(typeId);
gc.AddScriptObjectToGC(obj, objType);
}
// interface
int asCScriptEngine::GarbageCollect(asDWORD flags)
{
return gc.GarbageCollect(flags);
}
// interface
void asCScriptEngine::GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected)
{
gc.GetStatistics(currentSize, totalDestroyed, totalDetected);
}
// interface
void asCScriptEngine::GCEnumCallback(void *reference)
{
gc.GCEnumCallback(reference);
}
// TODO: multithread: The mapTypeIdToDataType must be protected with critical sections in all functions that access it
int asCScriptEngine::GetTypeIdFromDataType(const asCDataType &dt)
{
if( dt.IsNullHandle() ) return 0;
// Find the existing type id
asSMapNode<int,asCDataType*> *cursor = 0;
mapTypeIdToDataType.MoveFirst(&cursor);
while( cursor )
{
if( mapTypeIdToDataType.GetValue(cursor)->IsEqualExceptRefAndConst(dt) )
return mapTypeIdToDataType.GetKey(cursor);
mapTypeIdToDataType.MoveNext(&cursor, cursor);
}
// The type id doesn't exist, create it
// Setup the basic type id
int typeId = typeIdSeqNbr++;
if( dt.GetObjectType() )
{
if( dt.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT ) typeId |= asTYPEID_SCRIPTOBJECT;
else if( dt.GetObjectType()->flags & asOBJ_TEMPLATE ) typeId |= asTYPEID_SCRIPTARRAY; // TODO: Should be asTYPEID_TEMPLATE
else if( dt.GetObjectType()->flags & asOBJ_ENUM ); // TODO: Should we have a specific bit for this?
else typeId |= asTYPEID_APPOBJECT;
}
// Insert the basic object type
asCDataType *newDt = asNEW(asCDataType)(dt);
newDt->MakeReference(false);
newDt->MakeReadOnly(false);
newDt->MakeHandle(false);
mapTypeIdToDataType.Insert(typeId, newDt);
// If the object type supports object handles then register those types as well
// Note: Don't check for addref, as asOBJ_SCOPED don't have this
if( dt.IsObject() && dt.GetObjectType()->beh.release )
{
newDt = asNEW(asCDataType)(dt);
newDt->MakeReference(false);
newDt->MakeReadOnly(false);
newDt->MakeHandle(true);
newDt->MakeHandleToConst(false);
mapTypeIdToDataType.Insert(typeId | asTYPEID_OBJHANDLE, newDt);
newDt = asNEW(asCDataType)(dt);
newDt->MakeReference(false);
newDt->MakeReadOnly(false);
newDt->MakeHandle(true);
newDt->MakeHandleToConst(true);
mapTypeIdToDataType.Insert(typeId | asTYPEID_OBJHANDLE | asTYPEID_HANDLETOCONST, newDt);
}
// Call the method recursively to get the correct type id
return GetTypeIdFromDataType(dt);
}
const asCDataType *asCScriptEngine::GetDataTypeFromTypeId(int typeId)
{
asSMapNode<int,asCDataType*> *cursor = 0;
if( mapTypeIdToDataType.MoveTo(&cursor, typeId) )
return mapTypeIdToDataType.GetValue(cursor);
return 0;
}
asCObjectType *asCScriptEngine::GetObjectTypeFromTypeId(int typeId)
{
asSMapNode<int,asCDataType*> *cursor = 0;
if( mapTypeIdToDataType.MoveTo(&cursor, typeId) )
return mapTypeIdToDataType.GetValue(cursor)->GetObjectType();
return 0;
}
void asCScriptEngine::RemoveFromTypeIdMap(asCObjectType *type)
{
asSMapNode<int,asCDataType*> *cursor = 0;
mapTypeIdToDataType.MoveFirst(&cursor);
while( cursor )
{
asCDataType *dt = mapTypeIdToDataType.GetValue(cursor);
asSMapNode<int,asCDataType*> *old = cursor;
mapTypeIdToDataType.MoveNext(&cursor, cursor);
if( dt->GetObjectType() == type )
{
asDELETE(dt,asCDataType);
mapTypeIdToDataType.Erase(old);
}
}
}
// interface
int asCScriptEngine::GetTypeIdByDecl(const char *decl)
{
asCDataType dt;
asCBuilder bld(this, 0);
int r = bld.ParseDataType(decl, &dt);
if( r < 0 )
return asINVALID_TYPE;
return GetTypeIdFromDataType(dt);
}
const char *asCScriptEngine::GetTypeDeclaration(int typeId)
{
const asCDataType *dt = GetDataTypeFromTypeId(typeId);
if( dt == 0 ) return 0;
asASSERT(threadManager);
asCString *tempString = &threadManager->GetLocalData()->string;
*tempString = dt->Format();
return tempString->AddressOf();
}
int asCScriptEngine::GetSizeOfPrimitiveType(int typeId)
{
const asCDataType *dt = GetDataTypeFromTypeId(typeId);
if( dt == 0 ) return 0;
if( !dt->IsPrimitive() ) return 0;
return dt->GetSizeInMemoryBytes();
}
void *asCScriptEngine::CreateScriptObject(int typeId)
{
// Make sure the type id is for an object type, and not a primitive or a handle
if( (typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR)) != typeId ) return 0;
if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return 0;
const asCDataType *dt = GetDataTypeFromTypeId(typeId);
// Is the type id valid?
if( !dt ) return 0;
// Allocate the memory
asCObjectType *objType = dt->GetObjectType();
void *ptr = 0;
// Construct the object
if( objType->flags & asOBJ_SCRIPT_OBJECT )
ptr = ScriptObjectFactory(objType, this);
else if( objType->flags & asOBJ_TEMPLATE )
// The registered factory is moved to the construct behaviour when the type is instanciated
ptr = CallGlobalFunctionRetPtr(objType->beh.construct, objType);
else if( objType->flags & asOBJ_REF )
ptr = CallGlobalFunctionRetPtr(objType->beh.factory);
else
{
ptr = CallAlloc(objType);
int funcIndex = objType->beh.construct;
if( funcIndex )
CallObjectMethod(ptr, funcIndex);
}
return ptr;
}
void *asCScriptEngine::CreateScriptObjectCopy(void *origObj, int typeId)
{
void *newObj = CreateScriptObject(typeId);
if( newObj == 0 ) return 0;
CopyScriptObject(newObj, origObj, typeId);
return newObj;
}
void asCScriptEngine::CopyScriptObject(void *dstObj, void *srcObj, int typeId)
{
// Make sure the type id is for an object type, and not a primitive or a handle
if( (typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR)) != typeId ) return;
if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return;
// Copy the contents from the original object, using the assignment operator
const asCDataType *dt = GetDataTypeFromTypeId(typeId);
// Is the type id valid?
if( !dt ) return;
asCObjectType *objType = dt->GetObjectType();
// TODO: beh.copy will be removed, so we need to find the default opAssign method instead
if( objType->beh.copy )
{
CallObjectMethod(dstObj, srcObj, objType->beh.copy);
}
else if( objType->size )
{
memcpy(dstObj, srcObj, objType->size);
}
}
void asCScriptEngine::AddRefScriptObject(void *obj, int typeId)
{
// Make sure it is not a null pointer
if( obj == 0 ) return;
// Make sure the type id is for an object type or a handle
if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return;
const asCDataType *dt = GetDataTypeFromTypeId(typeId);
// Is the type id valid?
if( !dt ) return;
asCObjectType *objType = dt->GetObjectType();
if( objType->beh.addref )
{
// Call the addref behaviour
CallObjectMethod(obj, objType->beh.addref);
}
}
void asCScriptEngine::ReleaseScriptObject(void *obj, int typeId)
{
// Make sure it is not a null pointer
if( obj == 0 ) return;
// Make sure the type id is for an object type or a handle
if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return;
const asCDataType *dt = GetDataTypeFromTypeId(typeId);
// Is the type id valid?
if( !dt ) return;
asCObjectType *objType = dt->GetObjectType();
if( objType->beh.release )
{
// Call the release behaviour
CallObjectMethod(obj, objType->beh.release);
}
else
{
// Call the destructor
if( objType->beh.destruct )
CallObjectMethod(obj, objType->beh.destruct);
// Then free the memory
CallFree(obj);
}
}
bool asCScriptEngine::IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId)
{
// if equal, then it is obvious they are compatible
if( objTypeId == handleTypeId )
return true;
// Get the actual data types from the type ids
const asCDataType *objDt = GetDataTypeFromTypeId(objTypeId);
const asCDataType *hdlDt = GetDataTypeFromTypeId(handleTypeId);
// A handle to const cannot be passed to a handle that is not referencing a const object
if( objDt->IsHandleToConst() && !hdlDt->IsHandleToConst() )
return false;
if( objDt->GetObjectType() == hdlDt->GetObjectType() )
{
// The object type is equal
return true;
}
else if( objDt->IsScriptObject() && obj )
{
// There's still a chance the object implements the requested interface
asCObjectType *objType = ((asCScriptObject*)obj)->objType;
if( objType->Implements(hdlDt->GetObjectType()) )
return true;
}
return false;
}
int asCScriptEngine::BeginConfigGroup(const char *groupName)
{
// Make sure the group name doesn't already exist
for( asUINT n = 0; n < configGroups.GetLength(); n++ )
{
if( configGroups[n]->groupName == groupName )
return asNAME_TAKEN;
}
if( currentGroup != &defaultGroup )
return asNOT_SUPPORTED;
asCConfigGroup *group = asNEW(asCConfigGroup)();
group->groupName = groupName;
configGroups.PushLast(group);
currentGroup = group;
return 0;
}
int asCScriptEngine::EndConfigGroup()
{
// Raise error if trying to end the default config
if( currentGroup == &defaultGroup )
return asNOT_SUPPORTED;
currentGroup = &defaultGroup;
return 0;
}
int asCScriptEngine::RemoveConfigGroup(const char *groupName)
{
// It is not allowed to remove a group that is still in use.
// It would be possible to change the code in such a way that
// the group could be removed even though it was still in use,
// but that would cause severe negative impact on runtime
// performance, since the VM would then have to be able handle
// situations where the types, functions, and global variables
// can be removed at any time.
for( asUINT n = 0; n < configGroups.GetLength(); n++ )
{
if( configGroups[n]->groupName == groupName )
{
asCConfigGroup *group = configGroups[n];
// Make sure the group isn't referenced by anyone
if( group->refCount > 0 )
return asCONFIG_GROUP_IS_IN_USE;
// Verify if any objects registered in this group is still alive
if( group->HasLiveObjects() )
return asCONFIG_GROUP_IS_IN_USE;
// Remove the group from the list
if( n == configGroups.GetLength() - 1 )
configGroups.PopLast();
else
configGroups[n] = configGroups.PopLast();
// Remove the configurations registered with this group
group->RemoveConfiguration(this);
asDELETE(group,asCConfigGroup);
}
}
return 0;
}
asCConfigGroup *asCScriptEngine::FindConfigGroupForFunction(int funcId)
{
for( asUINT n = 0; n < configGroups.GetLength(); n++ )
{
// Check global functions
asUINT m;
for( m = 0; m < configGroups[n]->scriptFunctions.GetLength(); m++ )
{
if( configGroups[n]->scriptFunctions[m]->id == funcId )
return configGroups[n];
}
}
return 0;
}
asCConfigGroup *asCScriptEngine::FindConfigGroupForGlobalVar(int gvarId)
{
for( asUINT n = 0; n < configGroups.GetLength(); n++ )
{
for( asUINT m = 0; m < configGroups[n]->globalProps.GetLength(); m++ )
{
if( configGroups[n]->globalProps[m]->id == gvarId )
return configGroups[n];
}
}
return 0;
}
asCConfigGroup *asCScriptEngine::FindConfigGroupForObjectType(const asCObjectType *objType)
{
for( asUINT n = 0; n < configGroups.GetLength(); n++ )
{
for( asUINT m = 0; m < configGroups[n]->objTypes.GetLength(); m++ )
{
if( configGroups[n]->objTypes[m] == objType )
return configGroups[n];
}
}
return 0;
}
int asCScriptEngine::SetConfigGroupModuleAccess(const char *groupName, const char *module, bool hasAccess)
{
asCConfigGroup *group = 0;
// Make sure the group name doesn't already exist
for( asUINT n = 0; n < configGroups.GetLength(); n++ )
{
if( configGroups[n]->groupName == groupName )
{
group = configGroups[n];
break;
}
}
if( group == 0 )
return asWRONG_CONFIG_GROUP;
return group->SetModuleAccess(module, hasAccess);
}
int asCScriptEngine::GetNextScriptFunctionId()
{
if( freeScriptFunctionIds.GetLength() )
return freeScriptFunctionIds.PopLast();
int id = (int)scriptFunctions.GetLength();
scriptFunctions.PushLast(0);
return id;
}
void asCScriptEngine::SetScriptFunction(asCScriptFunction *func)
{
scriptFunctions[func->id] = func;
}
void asCScriptEngine::FreeScriptFunctionId(int id)
{
if( id < 0 ) return;
id &= 0xFFFF;
if( id >= (int)scriptFunctions.GetLength() ) return;
if( scriptFunctions[id] )
{
asCScriptFunction *func = scriptFunctions[id];
// Remove the function from the list of script functions
if( id == (int)scriptFunctions.GetLength() - 1 )
{
scriptFunctions.PopLast();
}
else
{
scriptFunctions[id] = 0;
freeScriptFunctionIds.PushLast(id);
}
// Is the function used as signature id?
if( func->signatureId == id )
{
// Remove the signature id
signatureIds.RemoveValue(func);
// Update all functions using the signature id
int newSigId = 0;
for( asUINT n = 0; n < scriptFunctions.GetLength(); n++ )
{
if( scriptFunctions[n] && scriptFunctions[n]->signatureId == id )
{
if( newSigId == 0 )
{
newSigId = scriptFunctions[n]->id;
signatureIds.PushLast(scriptFunctions[n]);
}
scriptFunctions[n]->signatureId = newSigId;
}
}
}
}
}
// interface
// TODO: typedef: Accept complex types for the typedefs
int asCScriptEngine::RegisterTypedef(const char *type, const char *decl)
{
if( type == 0 ) return ConfigError(asINVALID_NAME);
// Verify if the name has been registered as a type already
asUINT n;
for( n = 0; n < objectTypes.GetLength(); n++ )
{
if( objectTypes[n] && objectTypes[n]->name == type )
return asALREADY_REGISTERED;
}
// Grab the data type
asCTokenizer t;
size_t tokenLen;
eTokenType token;
asCDataType dataType;
// Create the data type
token = t.GetToken(decl, strlen(decl), &tokenLen);
switch(token)
{
case ttBool:
case ttInt:
case ttInt8:
case ttInt16:
case ttInt64:
case ttUInt:
case ttUInt8:
case ttUInt16:
case ttUInt64:
case ttFloat:
case ttDouble:
if( strlen(decl) != tokenLen )
{
return ConfigError(asINVALID_TYPE);
}
break;
default:
return ConfigError(asINVALID_TYPE);
}
dataType = asCDataType::CreatePrimitive(token, false);
// Make sure the name is not a reserved keyword
token = t.GetToken(type, strlen(type), &tokenLen);
if( token != ttIdentifier || strlen(type) != tokenLen )
return ConfigError(asINVALID_NAME);
asCBuilder bld(this, 0);
int r = bld.CheckNameConflict(type, 0, 0);
if( r < 0 )
return ConfigError(asNAME_TAKEN);
// Don't have to check against members of object
// types as they are allowed to use the names
// Put the data type in the list
asCObjectType *object= asNEW(asCObjectType)(this);
object->flags = asOBJ_TYPEDEF;
object->size = dataType.GetSizeInMemoryBytes();
object->name = type;
object->templateSubType = dataType;
objectTypes.PushLast(object);
registeredTypeDefs.PushLast(object);
currentGroup->objTypes.PushLast(object);
return asSUCCESS;
}
// interface
int asCScriptEngine::GetTypedefCount()
{
return (int)registeredTypeDefs.GetLength();
}
// interface
const char *asCScriptEngine::GetTypedefByIndex(asUINT index, int *typeId, const char **configGroup)
{
if( index >= registeredTypeDefs.GetLength() )
return 0;
if( typeId )
*typeId = GetTypeIdByDecl(registeredTypeDefs[index]->name.AddressOf());
if( configGroup )
{
asCConfigGroup *group = FindConfigGroupForObjectType(registeredTypeDefs[index]);
if( group )
*configGroup = group->groupName.AddressOf();
else
*configGroup = 0;
}
return registeredTypeDefs[index]->name.AddressOf();
}
// interface
int asCScriptEngine::RegisterEnum(const char *name)
{
// Check the name
if( NULL == name )
return ConfigError(asINVALID_NAME);
// Verify if the name has been registered as a type already
asUINT n;
for( n = 0; n < objectTypes.GetLength(); n++ )
if( objectTypes[n] && objectTypes[n]->name == name )
return asALREADY_REGISTERED;
// Use builder to parse the datatype
asCDataType dt;
asCBuilder bld(this, 0);
bool oldMsgCallback = msgCallback; msgCallback = false;
int r = bld.ParseDataType(name, &dt);
msgCallback = oldMsgCallback;
if( r >= 0 )
return ConfigError(asERROR);
// Make sure the name is not a reserved keyword
asCTokenizer t;
size_t tokenLen;
int token = t.GetToken(name, strlen(name), &tokenLen);
if( token != ttIdentifier || strlen(name) != tokenLen )
return ConfigError(asINVALID_NAME);
r = bld.CheckNameConflict(name, 0, 0);
if( r < 0 )
return ConfigError(asNAME_TAKEN);
asCObjectType *st = asNEW(asCObjectType)(this);
asCDataType dataType;
dataType.CreatePrimitive(ttInt, false);
st->flags = asOBJ_ENUM;
st->size = dataType.GetSizeInMemoryBytes();
st->name = name;
objectTypes.PushLast(st);
registeredEnums.PushLast(st);
currentGroup->objTypes.PushLast(st);
return asSUCCESS;
}
// interface
int asCScriptEngine::RegisterEnumValue(const char *typeName, const char *valueName, int value)
{
// Verify that the correct config group is used
if( currentGroup->FindType(typeName) == 0 )
return asWRONG_CONFIG_GROUP;
asCDataType dt;
int r;
asCBuilder bld(this, 0);
r = bld.ParseDataType(typeName, &dt);
if( r < 0 )
return ConfigError(r);
// Store the enum value
asCObjectType *ot = dt.GetObjectType();
if( ot == 0 || !(ot->flags & asOBJ_ENUM) )
return ConfigError(asINVALID_TYPE);
if( NULL == valueName )
return ConfigError(asINVALID_NAME);
for( unsigned int n = 0; n < ot->enumValues.GetLength(); n++ )
{
if( ot->enumValues[n]->name == valueName )
return ConfigError(asALREADY_REGISTERED);
}
asSEnumValue *e = asNEW(asSEnumValue);
e->name = valueName;
e->value = value;
ot->enumValues.PushLast(e);
return asSUCCESS;
}
// interface
int asCScriptEngine::GetEnumCount()
{
return (int)registeredEnums.GetLength();
}
// interface
const char *asCScriptEngine::GetEnumByIndex(asUINT index, int *enumTypeId, const char **configGroup)
{
if( index >= registeredEnums.GetLength() )
return 0;
if( configGroup )
{
asCConfigGroup *group = FindConfigGroupForObjectType(registeredEnums[index]);
if( group )
*configGroup = group->groupName.AddressOf();
else
*configGroup = 0;
}
if( enumTypeId )
*enumTypeId = GetTypeIdByDecl(registeredEnums[index]->name.AddressOf());
return registeredEnums[index]->name.AddressOf();
}
// interface
int asCScriptEngine::GetEnumValueCount(int enumTypeId)
{
const asCDataType *dt = GetDataTypeFromTypeId(enumTypeId);
asCObjectType *t = dt->GetObjectType();
if( t == 0 || !(t->GetFlags() & asOBJ_ENUM) )
return asINVALID_TYPE;
return (int)t->enumValues.GetLength();
}
// interface
const char *asCScriptEngine::GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue)
{
// TODO: This same function is implemented in as_module.cpp as well. Perhaps it should be moved to asCObjectType?
const asCDataType *dt = GetDataTypeFromTypeId(enumTypeId);
asCObjectType *t = dt->GetObjectType();
if( t == 0 || !(t->GetFlags() & asOBJ_ENUM) )
return 0;
if( index >= t->enumValues.GetLength() )
return 0;
if( outValue )
*outValue = t->enumValues[index]->value;
return t->enumValues[index]->name.AddressOf();
}
// interface
int asCScriptEngine::GetObjectTypeCount()
{
return (int)registeredObjTypes.GetLength();
}
// interface
asIObjectType *asCScriptEngine::GetObjectTypeByIndex(asUINT index)
{
if( index >= registeredObjTypes.GetLength() )
return 0;
return registeredObjTypes[index];
}
// interface
asIObjectType *asCScriptEngine::GetObjectTypeById(int typeId)
{
const asCDataType *dt = GetDataTypeFromTypeId(typeId);
// Is the type id valid?
if( !dt ) return 0;
// Enum types are not objects, so we shouldn't return an object type for them
if( dt->GetObjectType() && dt->GetObjectType()->GetFlags() & asOBJ_ENUM )
return 0;
return dt->GetObjectType();
}
asIScriptFunction *asCScriptEngine::GetFunctionDescriptorById(int funcId)
{
return GetScriptFunction(funcId);
}
// internal
bool asCScriptEngine::IsTemplateType(const char *name)
{
// TODO: optimize: Improve linear search
for( unsigned int n = 0; n < objectTypes.GetLength(); n++ )
{
if( objectTypes[n] && objectTypes[n]->name == name )
{
return objectTypes[n]->flags & asOBJ_TEMPLATE ? true : false;
}
}
return false;
}
// internal
int asCScriptEngine::AddConstantString(const char *str, size_t len)
{
// The str may contain null chars, so we cannot use strlen, or strcmp, or strcpy
// TODO: optimize: Improve linear search
// Has the string been registered before?
for( size_t n = 0; n < stringConstants.GetLength(); n++ )
{
if( stringConstants[n]->Compare(str, len) == 0 )
{
return (int)n;
}
}
// No match was found, add the string
asCString *cstr = asNEW(asCString)(str, len);
stringConstants.PushLast(cstr);
// The VM currently doesn't handle string ids larger than 65535
asASSERT(stringConstants.GetLength() <= 65536);
return (int)stringConstants.GetLength() - 1;
}
// internal
const asCString &asCScriptEngine::GetConstantString(int id)
{
return *stringConstants[id];
}
// internal
int asCScriptEngine::GetScriptSectionNameIndex(const char *name)
{
// TODO: These names are only released when the engine is freed. The assumption is that
// the same script section names will be reused instead of there always being new
// names. Is this assumption valid? Do we need to add reference counting?
// Store the script section names for future reference
for( asUINT n = 0; n < scriptSectionNames.GetLength(); n++ )
{
if( scriptSectionNames[n]->Compare(name) == 0 )
return n;
}
scriptSectionNames.PushLast(asNEW(asCString)(name));
return int(scriptSectionNames.GetLength()-1);
}
END_AS_NAMESPACE