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

2816 lines
78 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_builder.cpp
//
// This is the class that manages the compilation of the scripts
//
#include "as_config.h"
#include "as_builder.h"
#include "as_parser.h"
#include "as_compiler.h"
#include "as_tokendef.h"
#include "as_string_util.h"
#include "as_outputbuffer.h"
#include "as_texts.h"
#include "as_scriptobject.h"
BEGIN_AS_NAMESPACE
asCBuilder::asCBuilder(asCScriptEngine *engine, asCModule *module)
{
this->engine = engine;
this->module = module;
}
asCBuilder::~asCBuilder()
{
asUINT n;
// Free all functions
for( n = 0; n < functions.GetLength(); n++ )
{
if( functions[n] )
{
if( functions[n]->node )
{
functions[n]->node->Destroy(engine);
}
asDELETE(functions[n],sFunctionDescription);
}
functions[n] = 0;
}
// Free all global variables
for( n = 0; n < globVariables.GetLength(); n++ )
{
if( globVariables[n] )
{
if( globVariables[n]->nextNode )
{
globVariables[n]->nextNode->Destroy(engine);
}
asDELETE(globVariables[n],sGlobalVariableDescription);
globVariables[n] = 0;
}
}
// Free all the loaded files
for( n = 0; n < scripts.GetLength(); n++ )
{
if( scripts[n] )
{
asDELETE(scripts[n],asCScriptCode);
}
scripts[n] = 0;
}
// Free all class declarations
for( n = 0; n < classDeclarations.GetLength(); n++ )
{
if( classDeclarations[n] )
{
if( classDeclarations[n]->node )
{
classDeclarations[n]->node->Destroy(engine);
}
asDELETE(classDeclarations[n],sClassDeclaration);
classDeclarations[n] = 0;
}
}
for( n = 0; n < interfaceDeclarations.GetLength(); n++ )
{
if( interfaceDeclarations[n] )
{
if( interfaceDeclarations[n]->node )
{
interfaceDeclarations[n]->node->Destroy(engine);
}
asDELETE(interfaceDeclarations[n],sClassDeclaration);
interfaceDeclarations[n] = 0;
}
}
for( n = 0; n < namedTypeDeclarations.GetLength(); n++ )
{
if( namedTypeDeclarations[n] )
{
if( namedTypeDeclarations[n]->node )
{
namedTypeDeclarations[n]->node->Destroy(engine);
}
asDELETE(namedTypeDeclarations[n],sClassDeclaration);
namedTypeDeclarations[n] = 0;
}
}
}
int asCBuilder::AddCode(const char *name, const char *code, int codeLength, int lineOffset, int sectionIdx, bool makeCopy)
{
asCScriptCode *script = asNEW(asCScriptCode);
script->SetCode(name, code, codeLength, makeCopy);
script->lineOffset = lineOffset;
script->idx = sectionIdx;
scripts.PushLast(script);
return 0;
}
int asCBuilder::Build()
{
numErrors = 0;
numWarnings = 0;
preMessage.isSet = false;
ParseScripts();
CompileClasses();
CompileGlobalVariables();
CompileFunctions();
if( numErrors > 0 )
return asERROR;
return asSUCCESS;
}
#ifdef AS_DEPRECATED
// Deprecated since 2009-12-08, 2.18.0
int asCBuilder::BuildString(const char *string, asCContext *ctx)
{
asCScriptFunction *execFunc = 0;
int r = CompileFunction(TXT_EXECUTESTRING, string, -1, 0, &execFunc);
if( r >= 0 )
{
ctx->SetExecuteStringFunction(execFunc);
}
return r;
}
#endif
int asCBuilder::CompileGlobalVar(const char *sectionName, const char *code, int lineOffset)
{
numErrors = 0;
numWarnings = 0;
preMessage.isSet = false;
// Add the string to the script code
asCScriptCode *script = asNEW(asCScriptCode);
script->SetCode(sectionName, code, true);
script->lineOffset = lineOffset;
scripts.PushLast(script);
// Parse the string
asCParser parser(this);
if( parser.ParseScript(scripts[0]) < 0 )
return asERROR;
asCScriptNode *node = parser.GetScriptNode();
// Make sure there is nothing else than the global variable in the script code
if( node == 0 ||
node->firstChild == 0 ||
node->firstChild != node->lastChild ||
node->firstChild->nodeType != snGlobalVar )
{
WriteError(script->name.AddressOf(), TXT_ONLY_ONE_VARIABLE_ALLOWED, 0, 0);
return asERROR;
}
node = node->firstChild;
node->DisconnectParent();
RegisterGlobalVar(node, script);
CompileGlobalVariables();
if( numErrors > 0 )
{
// Remove the variable from the module, if it was registered
if( globVariables.GetLength() > 0 )
{
module->RemoveGlobalVar(module->GetGlobalVarCount()-1);
}
return asERROR;
}
return 0;
}
int asCBuilder::CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asCScriptFunction **outFunc)
{
asASSERT(outFunc != 0);
numErrors = 0;
numWarnings = 0;
preMessage.isSet = false;
// Add the string to the script code
asCScriptCode *script = asNEW(asCScriptCode);
script->SetCode(sectionName, code, true);
script->lineOffset = lineOffset;
scripts.PushLast(script);
// Parse the string
asCParser parser(this);
if( parser.ParseScript(scripts[0]) < 0 )
return asERROR;
asCScriptNode *node = parser.GetScriptNode();
// Make sure there is nothing else than the function in the script code
if( node == 0 ||
node->firstChild == 0 ||
node->firstChild != node->lastChild ||
node->firstChild->nodeType != snFunction )
{
WriteError(script->name.AddressOf(), TXT_ONLY_ONE_FUNCTION_ALLOWED, 0, 0);
return asERROR;
}
// Find the function node
node = node->firstChild;
// Create the function
bool isConstructor, isDestructor;
asCScriptFunction *func = asNEW(asCScriptFunction)(engine,module,asFUNC_SCRIPT);
GetParsedFunctionDetails(node, scripts[0], 0, func->name, func->returnType, func->parameterTypes, func->inOutFlags, func->isReadOnly, isConstructor, isDestructor);
func->id = engine->GetNextScriptFunctionId();
func->scriptSectionIdx = engine->GetScriptSectionNameIndex(sectionName ? sectionName : "");
// Tell the engine that the function exists already so the compiler can access it
if( compileFlags & asCOMP_ADD_TO_MODULE )
{
int r = CheckNameConflict(func->name.AddressOf(), node, scripts[0]);
if( r < 0 )
{
func->Release();
return asERROR;
}
module->globalFunctions.PushLast(func);
func->AddRef();
module->AddScriptFunction(func);
}
else
engine->SetScriptFunction(func);
// Fill in the function info for the builder too
node->DisconnectParent();
sFunctionDescription *funcDesc = asNEW(sFunctionDescription);
functions.PushLast(funcDesc);
funcDesc->script = scripts[0];
funcDesc->node = node;
funcDesc->name = func->name;
funcDesc->funcId = func->id;
asCCompiler compiler(engine);
if( compiler.CompileFunction(this, functions[0]->script, functions[0]->node, func) >= 0 )
{
// Return the function
*outFunc = func;
}
else
{
// If the function was added to the module then remove it again
if( compileFlags & asCOMP_ADD_TO_MODULE )
{
module->globalFunctions.RemoveValue(func);
module->scriptFunctions.RemoveValue(func);
func->Release();
func->Release();
}
func->Release();
return asERROR;
}
return asSUCCESS;
}
void asCBuilder::ParseScripts()
{
asCArray<asCParser*> parsers((int)scripts.GetLength());
// Parse all the files as if they were one
asUINT n = 0;
for( n = 0; n < scripts.GetLength(); n++ )
{
asCParser *parser = asNEW(asCParser)(this);
parsers.PushLast(parser);
// Parse the script file
parser->ParseScript(scripts[n]);
}
if( numErrors == 0 )
{
// Find all type declarations
for( n = 0; n < scripts.GetLength(); n++ )
{
asCScriptNode *node = parsers[n]->GetScriptNode();
// Find structure definitions first
node = node->firstChild;
while( node )
{
asCScriptNode *next = node->next;
if( node->nodeType == snClass )
{
node->DisconnectParent();
RegisterClass(node, scripts[n]);
}
else if( node->nodeType == snInterface )
{
node->DisconnectParent();
RegisterInterface(node, scripts[n]);
}
// Handle enumeration
else if( node->nodeType == snEnum )
{
node->DisconnectParent();
RegisterEnum(node, scripts[n]);
}
// Handle typedef
else if( node->nodeType == snTypedef )
{
node->DisconnectParent();
RegisterTypedef(node, scripts[n]);
}
node = next;
}
}
// Register script methods found in the interfaces
for( n = 0; n < interfaceDeclarations.GetLength(); n++ )
{
sClassDeclaration *decl = interfaceDeclarations[n];
asCScriptNode *node = decl->node->firstChild->next;
while( node )
{
asCScriptNode *next = node->next;
if( node->nodeType == snFunction )
{
node->DisconnectParent();
RegisterScriptFunction(engine->GetNextScriptFunctionId(), node, decl->script, decl->objType, true);
}
node = next;
}
}
// Now the interfaces have been completely established, now we need to determine if
// the same interface has already been registered before, and if so reuse the interface id.
module->ResolveInterfaceIds();
// Register script methods found in the structures
for( n = 0; n < classDeclarations.GetLength(); n++ )
{
sClassDeclaration *decl = classDeclarations[n];
asCScriptNode *node = decl->node->firstChild->next;
// Skip list of classes and interfaces
while( node && node->nodeType == snIdentifier )
node = node->next;
while( node )
{
asCScriptNode *next = node->next;
if( node->nodeType == snFunction )
{
node->DisconnectParent();
RegisterScriptFunction(engine->GetNextScriptFunctionId(), node, decl->script, decl->objType);
}
node = next;
}
// Make sure the default factory & constructor exists for classes
if( decl->objType->beh.construct == engine->scriptTypeBehaviours.beh.construct )
{
AddDefaultConstructor(decl->objType, decl->script);
}
}
// Find other global nodes
for( n = 0; n < scripts.GetLength(); n++ )
{
// Find other global nodes
asCScriptNode *node = parsers[n]->GetScriptNode();
node = node->firstChild;
while( node )
{
asCScriptNode *next = node->next;
node->DisconnectParent();
if( node->nodeType == snFunction )
{
RegisterScriptFunction(engine->GetNextScriptFunctionId(), node, scripts[n], 0, false, true);
}
else if( node->nodeType == snGlobalVar )
{
RegisterGlobalVar(node, scripts[n]);
}
else if( node->nodeType == snImport )
{
RegisterImportedFunction(module->GetNextImportedFunctionId(), node, scripts[n]);
}
else
{
// Unused script node
int r, c;
scripts[n]->ConvertPosToRowCol(node->tokenPos, &r, &c);
WriteWarning(scripts[n]->name.AddressOf(), TXT_UNUSED_SCRIPT_NODE, r, c);
node->Destroy(engine);
}
node = next;
}
}
}
for( n = 0; n < parsers.GetLength(); n++ )
{
asDELETE(parsers[n],asCParser);
}
}
void asCBuilder::CompileFunctions()
{
// Compile each function
for( asUINT n = 0; n < functions.GetLength(); n++ )
{
if( functions[n] == 0 ) continue;
asCCompiler compiler(engine);
asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId];
if( functions[n]->node )
{
int r, c;
functions[n]->script->ConvertPosToRowCol(functions[n]->node->tokenPos, &r, &c);
asCString str = func->GetDeclarationStr();
str.Format(TXT_COMPILING_s, str.AddressOf());
WriteInfo(functions[n]->script->name.AddressOf(), str.AddressOf(), r, c, true);
compiler.CompileFunction(this, functions[n]->script, functions[n]->node, func);
preMessage.isSet = false;
}
else
{
// This is the default constructor, that is generated
// automatically if not implemented by the user.
asASSERT( functions[n]->name == functions[n]->objType->name );
compiler.CompileDefaultConstructor(this, functions[n]->script, func);
}
}
}
int asCBuilder::ParseDataType(const char *datatype, asCDataType *result)
{
numErrors = 0;
numWarnings = 0;
preMessage.isSet = false;
asCScriptCode source;
source.SetCode("", datatype, true);
asCParser parser(this);
int r = parser.ParseDataType(&source);
if( r < 0 )
return asINVALID_TYPE;
// Get data type and property name
asCScriptNode *dataType = parser.GetScriptNode()->firstChild;
*result = CreateDataTypeFromNode(dataType, &source, true);
if( numErrors > 0 )
return asINVALID_TYPE;
return asSUCCESS;
}
int asCBuilder::ParseTemplateDecl(const char *decl, asCString *name, asCString *subtypeName)
{
numErrors = 0;
numWarnings = 0;
preMessage.isSet = false;
asCScriptCode source;
source.SetCode("", decl, true);
asCParser parser(this);
int r = parser.ParseTemplateDecl(&source);
if( r < 0 )
return asINVALID_TYPE;
// Get the template name and subtype name
asCScriptNode *node = parser.GetScriptNode()->firstChild;
name->Assign(&decl[node->tokenPos], node->tokenLength);
node = node->next;
subtypeName->Assign(&decl[node->tokenPos], node->tokenLength);
// TODO: template: check for name conflicts
if( numErrors > 0 )
return asINVALID_DECLARATION;
return asSUCCESS;
}
int asCBuilder::VerifyProperty(asCDataType *dt, const char *decl, asCString &name, asCDataType &type)
{
numErrors = 0;
numWarnings = 0;
preMessage.isSet = false;
if( dt )
{
// Verify that the object type exist
if( dt->GetObjectType() == 0 )
return asINVALID_OBJECT;
}
// Check property declaration and type
asCScriptCode source;
source.SetCode(TXT_PROPERTY, decl, true);
asCParser parser(this);
int r = parser.ParsePropertyDeclaration(&source);
if( r < 0 )
return asINVALID_DECLARATION;
// Get data type and property name
asCScriptNode *dataType = parser.GetScriptNode()->firstChild;
asCScriptNode *nameNode = dataType->next;
type = CreateDataTypeFromNode(dataType, &source);
name.Assign(&decl[nameNode->tokenPos], nameNode->tokenLength);
// Verify property name
if( dt )
{
if( CheckNameConflictMember(dt->GetObjectType(), name.AddressOf(), nameNode, &source) < 0 )
return asNAME_TAKEN;
}
else
{
if( CheckNameConflict(name.AddressOf(), nameNode, &source) < 0 )
return asNAME_TAKEN;
}
if( numErrors > 0 )
return asINVALID_DECLARATION;
return asSUCCESS;
}
asCObjectProperty *asCBuilder::GetObjectProperty(asCDataType &obj, const char *prop)
{
asASSERT(obj.GetObjectType() != 0);
// TODO: Only search in config groups to which the module has access
// TODO: optimize: Improve linear search
asCArray<asCObjectProperty *> &props = obj.GetObjectType()->properties;
for( asUINT n = 0; n < props.GetLength(); n++ )
if( props[n]->name == prop )
return props[n];
return 0;
}
asCGlobalProperty *asCBuilder::GetGlobalProperty(const char *prop, bool *isCompiled, bool *isPureConstant, asQWORD *constantValue)
{
asUINT n;
if( isCompiled ) *isCompiled = true;
if( isPureConstant ) *isPureConstant = false;
// TODO: optimize: Improve linear search
// Check application registered properties
asCArray<asCGlobalProperty *> *props = &(engine->registeredGlobalProps);
for( n = 0; n < props->GetLength(); ++n )
if( (*props)[n] && (*props)[n]->name == prop )
{
if( module )
{
// Find the config group for the global property
asCConfigGroup *group = engine->FindConfigGroupForGlobalVar((*props)[n]->id);
if( !group || group->HasModuleAccess(module->name.AddressOf()) )
return (*props)[n];
}
else
{
// We're not compiling a module right now, so it must be a registered global property
return (*props)[n];
}
}
// TODO: optimize: Improve linear search
// Check properties being compiled now
asCArray<sGlobalVariableDescription *> *gvars = &globVariables;
for( n = 0; n < gvars->GetLength(); ++n )
{
if( (*gvars)[n] && (*gvars)[n]->name == prop )
{
if( isCompiled ) *isCompiled = (*gvars)[n]->isCompiled;
if( isPureConstant ) *isPureConstant = (*gvars)[n]->isPureConstant;
if( constantValue ) *constantValue = (*gvars)[n]->constantValue;
return (*gvars)[n]->property;
}
}
// TODO: optimize: Improve linear search
// Check previously compiled global variables
if( module )
{
props = &module->scriptGlobals;
for( n = 0; n < props->GetLength(); ++n )
if( (*props)[n]->name == prop )
return (*props)[n];
}
return 0;
}
int asCBuilder::ParseFunctionDeclaration(asCObjectType *objType, const char *decl, asCScriptFunction *func, bool isSystemFunction, asCArray<bool> *paramAutoHandles, bool *returnAutoHandle)
{
numErrors = 0;
numWarnings = 0;
preMessage.isSet = false;
asCScriptCode source;
source.SetCode(TXT_SYSTEM_FUNCTION, decl, true);
asCParser parser(this);
int r = parser.ParseFunctionDefinition(&source);
if( r < 0 )
return asINVALID_DECLARATION;
asCScriptNode *node = parser.GetScriptNode();
// Find name
asCScriptNode *n = node->firstChild->next->next;
func->name.Assign(&source.code[n->tokenPos], n->tokenLength);
// Initialize a script function object for registration
bool autoHandle;
// Scoped reference types are allowed to use handle when returned from application functions
func->returnType = CreateDataTypeFromNode(node->firstChild, &source, true, objType);
func->returnType = ModifyDataTypeFromNode(func->returnType, node->firstChild->next, &source, 0, &autoHandle);
if( autoHandle && (!func->returnType.IsObjectHandle() || func->returnType.IsReference()) )
return asINVALID_DECLARATION;
if( returnAutoHandle ) *returnAutoHandle = autoHandle;
// Reference types cannot be returned by value from system functions
if( isSystemFunction &&
(func->returnType.GetObjectType() &&
(func->returnType.GetObjectType()->flags & asOBJ_REF)) &&
!(func->returnType.IsReference() ||
func->returnType.IsObjectHandle()) )
return asINVALID_DECLARATION;
// Count number of parameters
int paramCount = 0;
n = n->next->firstChild;
while( n )
{
paramCount++;
n = n->next->next;
if( n && n->nodeType == snIdentifier )
n = n->next;
}
// Preallocate memory
func->parameterTypes.Allocate(paramCount, false);
func->inOutFlags.Allocate(paramCount, false);
if( paramAutoHandles ) paramAutoHandles->Allocate(paramCount, false);
n = node->firstChild->next->next->next->firstChild;
while( n )
{
asETypeModifiers inOutFlags;
asCDataType type = CreateDataTypeFromNode(n, &source, false, objType);
type = ModifyDataTypeFromNode(type, n->next, &source, &inOutFlags, &autoHandle);
// Reference types cannot be passed by value to system functions
if( isSystemFunction &&
(type.GetObjectType() &&
(type.GetObjectType()->flags & asOBJ_REF)) &&
!(type.IsReference() ||
type.IsObjectHandle()) )
return asINVALID_DECLARATION;
// Store the parameter type
func->parameterTypes.PushLast(type);
func->inOutFlags.PushLast(inOutFlags);
// Don't permit void parameters
if( type.GetTokenType() == ttVoid )
return asINVALID_DECLARATION;
if( autoHandle && (!type.IsObjectHandle() || type.IsReference()) )
return asINVALID_DECLARATION;
if( paramAutoHandles ) paramAutoHandles->PushLast(autoHandle);
// Make sure that var type parameters are references
if( type.GetTokenType() == ttQuestion &&
!type.IsReference() )
return asINVALID_DECLARATION;
// Move to next parameter
n = n->next->next;
if( n && n->nodeType == snIdentifier )
n = n->next;
}
// Set the read-only flag if const is declared after parameter list
if( node->lastChild->nodeType == snUndefined && node->lastChild->tokenType == ttConst )
func->isReadOnly = true;
else
func->isReadOnly = false;
if( numErrors > 0 || numWarnings > 0 )
return asINVALID_DECLARATION;
return 0;
}
int asCBuilder::ParseVariableDeclaration(const char *decl, asCObjectProperty *var)
{
numErrors = 0;
numWarnings = 0;
preMessage.isSet = false;
asCScriptCode source;
source.SetCode(TXT_VARIABLE_DECL, decl, true);
asCParser parser(this);
int r = parser.ParsePropertyDeclaration(&source);
if( r < 0 )
return asINVALID_DECLARATION;
asCScriptNode *node = parser.GetScriptNode();
// Find name
asCScriptNode *n = node->firstChild->next;
var->name.Assign(&source.code[n->tokenPos], n->tokenLength);
// Initialize a script variable object for registration
var->type = CreateDataTypeFromNode(node->firstChild, &source);
if( numErrors > 0 || numWarnings > 0 )
return asINVALID_DECLARATION;
return 0;
}
int asCBuilder::CheckNameConflictMember(asCObjectType *t, const char *name, asCScriptNode *node, asCScriptCode *code)
{
// It's not necessary to check against object types
// TODO: optimize: Improve linear search
asCArray<asCObjectProperty *> &props = t->properties;
for( asUINT n = 0; n < props.GetLength(); n++ )
{
if( props[n]->name == name )
{
if( code )
{
int r, c;
code->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_NAME_CONFLICT_s_OBJ_PROPERTY, name);
WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
}
return -1;
}
}
// TODO: Property names must be checked against method names
return 0;
}
int asCBuilder::CheckNameConflict(const char *name, asCScriptNode *node, asCScriptCode *code)
{
// TODO: Must verify object types in all config groups, whether the module has access or not
// Check against object types
if( engine->GetObjectType(name) != 0 )
{
if( code )
{
int r, c;
code->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_NAME_CONFLICT_s_EXTENDED_TYPE, name);
WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
}
return -1;
}
// TODO: Must verify global properties in all config groups, whether the module has access or not
// Check against global properties
asCGlobalProperty *prop = GetGlobalProperty(name, 0, 0, 0);
if( prop )
{
if( code )
{
int r, c;
code->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_NAME_CONFLICT_s_GLOBAL_PROPERTY, name);
WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
}
return -1;
}
// TODO: Property names must be checked against function names
// Check against class types
asUINT n;
for( n = 0; n < classDeclarations.GetLength(); n++ )
{
if( classDeclarations[n]->name == name )
{
if( code )
{
int r, c;
code->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_NAME_CONFLICT_s_STRUCT, name);
WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
}
return -1;
}
}
// Check against named types
for( n = 0; n < namedTypeDeclarations.GetLength(); n++ )
{
if( namedTypeDeclarations[n]->name == name )
{
if( code )
{
int r, c;
code->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_NAME_CONFLICT_s_IS_NAMED_TYPE, name);
WriteError(code->name.AddressOf(), str.AddressOf(), r, c);
}
return -1;
}
}
return 0;
}
int asCBuilder::RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file)
{
// What data type is it?
asCDataType type = CreateDataTypeFromNode(node->firstChild, file);
if( !type.CanBeInstanciated() )
{
asCString str;
// TODO: Change to "'type' cannot be declared as variable"
str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf());
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
}
asCScriptNode *n = node->firstChild->next;
while( n )
{
// Verify that the name isn't taken
asCString name(&file->code[n->tokenPos], n->tokenLength);
CheckNameConflict(name.AddressOf(), n, file);
// Register the global variable
sGlobalVariableDescription *gvar = asNEW(sGlobalVariableDescription);
globVariables.PushLast(gvar);
gvar->script = file;
gvar->name = name;
gvar->isCompiled = false;
gvar->datatype = type;
gvar->isEnumValue = false;
// TODO: Give error message if wrong
asASSERT(!gvar->datatype.IsReference());
gvar->idNode = n;
gvar->nextNode = 0;
if( n->next &&
(n->next->nodeType == snAssignment ||
n->next->nodeType == snArgList ||
n->next->nodeType == snInitList ) )
{
gvar->nextNode = n->next;
n->next->DisconnectParent();
}
gvar->property = module->AllocateGlobalProperty(name.AddressOf(), gvar->datatype);
gvar->index = gvar->property->id;
n = n->next;
}
node->Destroy(engine);
return 0;
}
int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file)
{
asCScriptNode *n = node->firstChild;
asCString name(&file->code[n->tokenPos], n->tokenLength);
int r, c;
file->ConvertPosToRowCol(n->tokenPos, &r, &c);
CheckNameConflict(name.AddressOf(), n, file);
sClassDeclaration *decl = asNEW(sClassDeclaration);
classDeclarations.PushLast(decl);
decl->name = name;
decl->script = file;
decl->validState = 0;
decl->node = node;
asCObjectType *st = asNEW(asCObjectType)(engine);
st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT;
if( node->tokenType == ttHandle )
st->flags |= asOBJ_IMPLICIT_HANDLE;
st->size = sizeof(asCScriptObject);
st->name = name;
module->classTypes.PushLast(st);
engine->classTypes.PushLast(st);
st->AddRef();
decl->objType = st;
// Add script classes to the GC
engine->gc.AddScriptObjectToGC(st, &engine->objectTypeBehaviours);
// Use the default script class behaviours
st->beh = engine->scriptTypeBehaviours.beh;
// TODO: Move this to asCObjectType so that the asCRestore can reuse it
engine->scriptFunctions[st->beh.addref]->AddRef();
engine->scriptFunctions[st->beh.release]->AddRef();
engine->scriptFunctions[st->beh.gcEnumReferences]->AddRef();
engine->scriptFunctions[st->beh.gcGetFlag]->AddRef();
engine->scriptFunctions[st->beh.gcGetRefCount]->AddRef();
engine->scriptFunctions[st->beh.gcReleaseAllReferences]->AddRef();
engine->scriptFunctions[st->beh.gcSetFlag]->AddRef();
engine->scriptFunctions[st->beh.copy]->AddRef();
engine->scriptFunctions[st->beh.factory]->AddRef();
engine->scriptFunctions[st->beh.construct]->AddRef();
for( asUINT i = 1; i < st->beh.operators.GetLength(); i += 2 )
engine->scriptFunctions[st->beh.operators[i]]->AddRef();
return 0;
}
int asCBuilder::RegisterInterface(asCScriptNode *node, asCScriptCode *file)
{
asCScriptNode *n = node->firstChild;
asCString name(&file->code[n->tokenPos], n->tokenLength);
int r, c;
file->ConvertPosToRowCol(n->tokenPos, &r, &c);
CheckNameConflict(name.AddressOf(), n, file);
sClassDeclaration *decl = asNEW(sClassDeclaration);
interfaceDeclarations.PushLast(decl);
decl->name = name;
decl->script = file;
decl->validState = 0;
decl->node = node;
// Register the object type for the interface
asCObjectType *st = asNEW(asCObjectType)(engine);
st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT;
st->size = 0; // Cannot be instanciated
st->name = name;
module->classTypes.PushLast(st);
engine->classTypes.PushLast(st);
st->AddRef();
decl->objType = st;
// Use the default script class behaviours
st->beh.construct = 0;
st->beh.addref = engine->scriptTypeBehaviours.beh.addref;
engine->scriptFunctions[st->beh.addref]->AddRef();
st->beh.release = engine->scriptTypeBehaviours.beh.release;
engine->scriptFunctions[st->beh.release]->AddRef();
st->beh.copy = 0;
return 0;
}
void asCBuilder::CompileGlobalVariables()
{
asUINT n;
bool compileSucceeded = true;
// Store state of compilation (errors, warning, output)
int currNumErrors = numErrors;
int currNumWarnings = numWarnings;
// Backup the original message stream
bool msgCallback = engine->msgCallback;
asSSystemFunctionInterface msgCallbackFunc = engine->msgCallbackFunc;
void *msgCallbackObj = engine->msgCallbackObj;
// Set the new temporary message stream
asCOutputBuffer outBuffer;
engine->SetMessageCallback(asMETHOD(asCOutputBuffer, Callback), &outBuffer, asCALL_THISCALL);
asCOutputBuffer finalOutput;
asCScriptFunction *initFunc = 0;
asCArray<asCGlobalProperty*> initOrder;
// We first try to compile all the primitive global variables, and only after that
// compile the non-primitive global variables. This permits the constructors
// for the complex types to use the already initialized variables of primitive
// type. Note, we currently don't know which global variables are used in the
// constructors, so we cannot guarantee that variables of complex types are
// initialized in the correct order, so we won't reorder those.
bool compilingPrimitives = true;
// Compile each global variable
while( compileSucceeded )
{
compileSucceeded = false;
int accumErrors = 0;
int accumWarnings = 0;
// Restore state of compilation
finalOutput.Clear();
for( asUINT n = 0; n < globVariables.GetLength(); n++ )
{
asCByteCode init(engine);
numWarnings = 0;
numErrors = 0;
outBuffer.Clear();
sGlobalVariableDescription *gvar = globVariables[n];
if( gvar->isCompiled )
continue;
// Skip this for now if we're not compiling complex types yet
if( compilingPrimitives && !gvar->datatype.IsPrimitive() )
continue;
if( gvar->nextNode )
{
int r, c;
gvar->script->ConvertPosToRowCol(gvar->nextNode->tokenPos, &r, &c);
asCString str = gvar->datatype.Format();
str += " " + gvar->name;
str.Format(TXT_COMPILING_s, str.AddressOf());
WriteInfo(gvar->script->name.AddressOf(), str.AddressOf(), r, c, true);
}
if( gvar->isEnumValue )
{
int r;
if( gvar->nextNode )
{
asCCompiler comp(engine);
asCScriptFunction func(engine, module, -1);
// Temporarily switch the type of the variable to int so it can be compiled properly
asCDataType saveType;
saveType = gvar->datatype;
gvar->datatype = asCDataType::CreatePrimitive(ttInt, true);
r = comp.CompileGlobalVariable(this, gvar->script, gvar->nextNode, gvar, &func);
gvar->datatype = saveType;
}
else
{
r = 0;
// When there is no assignment the value is the last + 1
int enumVal = 0;
if( n > 0 )
{
sGlobalVariableDescription *gvar2 = globVariables[n-1];
if( gvar2->datatype == gvar->datatype )
{
// The integer value is stored in the lower bytes
enumVal = (*(int*)&gvar2->constantValue) + 1;
if( !gvar2->isCompiled )
{
// TODO: Need to get the correct script position
int row, col;
gvar->script->ConvertPosToRowCol(0, &row, &col);
asCString str = gvar->datatype.Format();
str += " " + gvar->name;
str.Format(TXT_COMPILING_s, str.AddressOf());
WriteInfo(gvar->script->name.AddressOf(), str.AddressOf(), row, col, true);
str.Format(TXT_UNINITIALIZED_GLOBAL_VAR_s, gvar2->name.AddressOf());
WriteError(gvar->script->name.AddressOf(), str.AddressOf(), row, col);
r = -1;
}
}
}
// The integer value is stored in the lower bytes
*(int*)&gvar->constantValue = enumVal;
}
if( r >= 0 )
{
// Set the value as compiled
gvar->isCompiled = true;
compileSucceeded = true;
}
}
else
{
// Compile the global variable
initFunc = asNEW(asCScriptFunction)(engine, module, -1);
asCCompiler comp(engine);
int r = comp.CompileGlobalVariable(this, gvar->script, gvar->nextNode, gvar, initFunc);
if( r >= 0 )
{
// Compilation succeeded
gvar->isCompiled = true;
compileSucceeded = true;
}
else
{
// Compilation failed
asDELETE(initFunc, asCScriptFunction);
initFunc = 0;
}
}
if( gvar->isCompiled )
{
// Add warnings for this constant to the total build
if( numWarnings )
{
currNumWarnings += numWarnings;
if( msgCallback )
outBuffer.SendToCallback(engine, &msgCallbackFunc, msgCallbackObj);
}
// Determine order of variable initializations
if( gvar->property && !gvar->isEnumValue )
initOrder.PushLast(gvar->property);
// Does the function contain more than just a RET instruction?
if( initFunc && initFunc->byteCode.GetLength() > 1 )
{
// Create the init function for this variable
initFunc->id = engine->GetNextScriptFunctionId();
engine->SetScriptFunction(initFunc);
// Finalize the init function for this variable
initFunc->funcType = asFUNC_SCRIPT;
initFunc->returnType = asCDataType::CreatePrimitive(ttVoid, false);
// Notify the GC of the new script function
engine->gc.AddScriptObjectToGC(initFunc, &engine->functionBehaviours);
// The function's refCount was already initialized to 1
gvar->property->initFunc = initFunc;
initFunc = 0;
}
else if( initFunc )
{
// Destroy the function as it won't be used
asDELETE(initFunc, asCScriptFunction);
initFunc = 0;
}
}
else
{
// Add output to final output
finalOutput.Append(outBuffer);
accumErrors += numErrors;
accumWarnings += numWarnings;
}
preMessage.isSet = false;
}
if( !compileSucceeded )
{
if( compilingPrimitives )
{
// No more primitives could be compiled, so
// switch to compiling the complex variables
compilingPrimitives = false;
compileSucceeded = true;
}
else
{
// No more variables can be compiled
// Add errors and warnings to total build
currNumWarnings += accumWarnings;
currNumErrors += accumErrors;
if( msgCallback )
finalOutput.SendToCallback(engine, &msgCallbackFunc, msgCallbackObj);
}
}
}
// Restore states
engine->msgCallback = msgCallback;
engine->msgCallbackFunc = msgCallbackFunc;
engine->msgCallbackObj = msgCallbackObj;
numWarnings = currNumWarnings;
numErrors = currNumErrors;
// Set the correct order of initialization
if( numErrors == 0 )
{
// If the length of the arrays are not the same, then this is the compilation
// of a single variable, in which case the initialization order of the previous
// variables must be preserved.
if( module->scriptGlobals.GetLength() == initOrder.GetLength() )
module->scriptGlobals = initOrder;
}
// Convert all variables compiled for the enums to true enum values
for( n = 0; n < globVariables.GetLength(); n++ )
{
asCObjectType *objectType;
sGlobalVariableDescription *gvar = globVariables[n];
if( !gvar->isEnumValue )
continue;
objectType = gvar->datatype.GetObjectType();
asASSERT(NULL != objectType);
asSEnumValue *e = asNEW(asSEnumValue);
e->name = gvar->name;
e->value = *(int*)&gvar->constantValue;
objectType->enumValues.PushLast(e);
// Destroy the gvar property
if( gvar->nextNode )
gvar->nextNode->Destroy(engine);
if( gvar->property )
asDELETE(gvar->property, asCGlobalProperty);
asDELETE(gvar, sGlobalVariableDescription);
globVariables[n] = 0;
}
}
void asCBuilder::CompileClasses()
{
asUINT n;
asCArray<sClassDeclaration*> toValidate((int)classDeclarations.GetLength());
// Determine class inheritances and interfaces
for( n = 0; n < classDeclarations.GetLength(); n++ )
{
sClassDeclaration *decl = classDeclarations[n];
asCScriptCode *file = decl->script;
// Find the base class that this class inherits from
bool multipleInheritance = false;
asCScriptNode *node = decl->node->firstChild->next;
while( node && node->nodeType == snIdentifier )
{
// Get the interface name from the node
asCString name(&file->code[node->tokenPos], node->tokenLength);
// Find the object type for the interface
asCObjectType *objType = GetObjectType(name.AddressOf());
if( objType == 0 )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, name.AddressOf());
WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
}
else if( !(objType->flags & asOBJ_SCRIPT_OBJECT) )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_CANNOT_INHERIT_FROM_s, objType->name.AddressOf());
WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
}
else if( objType->size != 0 )
{
// The class inherits from another script class
if( decl->objType->derivedFrom != 0 )
{
if( !multipleInheritance )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_CANNOT_INHERIT_FROM_MULTIPLE_CLASSES, r, c);
multipleInheritance = true;
}
}
else
{
// Make sure none of the base classes inherit from this one
asCObjectType *base = objType;
bool error = false;
while( base != 0 )
{
if( base == decl->objType )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_CANNOT_INHERIT_FROM_SELF, r, c);
error = true;
break;
}
base = base->derivedFrom;
}
if( !error )
{
decl->objType->derivedFrom = objType;
objType->AddRef();
}
}
}
else
{
// The class implements an interface
if( decl->objType->Implements(objType) )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString msg;
msg.Format(TXT_INTERFACE_s_ALREADY_IMPLEMENTED, objType->GetName());
WriteWarning(file->name.AddressOf(), msg.AddressOf(), r, c);
}
else
{
decl->objType->interfaces.PushLast(objType);
// Make sure all the methods of the interface are implemented
for( asUINT i = 0; i < objType->methods.GetLength(); i++ )
{
if( !DoesMethodExist(decl->objType, objType->methods[i]) )
{
int r, c;
file->ConvertPosToRowCol(decl->node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_MISSING_IMPLEMENTATION_OF_s,
engine->GetFunctionDeclaration(objType->methods[i]).AddressOf());
WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
}
}
}
}
node = node->next;
}
}
// Order class declarations so that base classes are compiled before derived classes.
// This will allow the derived classes to copy properties and methods in the next step.
for( n = 0; n < classDeclarations.GetLength(); n++ )
{
sClassDeclaration *decl = classDeclarations[n];
asCObjectType *derived = decl->objType;
asCObjectType *base = derived->derivedFrom;
if( base == 0 ) continue;
// If the base class is found after the derived class, then move the derived class to the end of the list
for( asUINT m = n+1; m < classDeclarations.GetLength(); m++ )
{
sClassDeclaration *declBase = classDeclarations[m];
if( base == declBase->objType )
{
classDeclarations.RemoveIndex(n);
classDeclarations.PushLast(decl);
// Decrease index so that we don't skip an entry
n--;
break;
}
}
}
// Go through each of the classes and register the object type descriptions
for( n = 0; n < classDeclarations.GetLength(); n++ )
{
sClassDeclaration *decl = classDeclarations[n];
// Add all properties and methods from the base class
if( decl->objType->derivedFrom )
{
asCObjectType *baseType = decl->objType->derivedFrom;
// The derived class inherits all interfaces from the base class
for( unsigned int n = 0; n < baseType->interfaces.GetLength(); n++ )
{
if( !decl->objType->Implements(baseType->interfaces[n]) )
{
decl->objType->interfaces.PushLast(baseType->interfaces[n]);
}
else
{
// Warn if derived class already implements the interface
int r, c;
decl->script->ConvertPosToRowCol(decl->node->tokenPos, &r, &c);
asCString msg;
msg.Format(TXT_INTERFACE_s_ALREADY_IMPLEMENTED, baseType->interfaces[n]->GetName());
WriteWarning(decl->script->name.AddressOf(), msg.AddressOf(), r, c);
}
}
// TODO: Need to check for name conflict with new class methods
// Copy properties from base class to derived class
for( asUINT p = 0; p < baseType->properties.GetLength(); p++ )
{
asCObjectProperty *prop = AddPropertyToClass(decl, baseType->properties[p]->name, baseType->properties[p]->type);
// The properties must maintain the same offset
asASSERT(prop->byteOffset == baseType->properties[p]->byteOffset); UNUSED_VAR(prop);
}
// Copy methods from base class to derived class
for( asUINT m = 0; m < baseType->methods.GetLength(); m++ )
{
// If the derived class implements the same method, then don't add the base class' method
asCScriptFunction *baseFunc = GetFunctionDescription(baseType->methods[m]);
asCScriptFunction *derivedFunc = 0;
bool found = false;
for( asUINT d = 0; d < decl->objType->methods.GetLength(); d++ )
{
derivedFunc = GetFunctionDescription(decl->objType->methods[d]);
if( derivedFunc->IsSignatureEqual(baseFunc) )
{
// Move the function from the methods array to the virtualFunctionTable
decl->objType->methods.RemoveIndex(d);
decl->objType->virtualFunctionTable.PushLast(derivedFunc);
found = true;
break;
}
}
if( !found )
{
// Push the base class function on the virtual function table
decl->objType->virtualFunctionTable.PushLast(baseType->virtualFunctionTable[m]);
baseType->virtualFunctionTable[m]->AddRef();
}
decl->objType->methods.PushLast(baseType->methods[m]);
engine->scriptFunctions[baseType->methods[m]]->AddRef();
}
}
// Move this class' methods into the virtual function table
for( asUINT m = 0; m < decl->objType->methods.GetLength(); m++ )
{
asCScriptFunction *func = GetFunctionDescription(decl->objType->methods[m]);
if( func->funcType != asFUNC_VIRTUAL )
{
// Move the reference from the method list to the virtual function list
decl->objType->methods.RemoveIndex(m);
decl->objType->virtualFunctionTable.PushLast(func);
// Substitute the function description in the method list for a virtual method
// Make sure the methods are in the same order as the virtual function table
decl->objType->methods.PushLast(CreateVirtualFunction(func, (int)decl->objType->virtualFunctionTable.GetLength() - 1));
m--;
}
}
// Enumerate each of the declared properties
asCScriptNode *node = decl->node->firstChild->next;
// Skip list of classes and interfaces
while( node && node->nodeType == snIdentifier )
node = node->next;
while( node )
{
if( node->nodeType == snDeclaration )
{
asCScriptCode *file = decl->script;
asCDataType dt = CreateDataTypeFromNode(node->firstChild, file);
asCString name(&file->code[node->lastChild->tokenPos], node->lastChild->tokenLength);
if( dt.IsReadOnly() )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_PROPERTY_CANT_BE_CONST, r, c);
}
CheckNameConflictMember(decl->objType, name.AddressOf(), node->lastChild, file);
AddPropertyToClass(decl, name, dt, file, node);
}
else
asASSERT(false);
node = node->next;
}
toValidate.PushLast(decl);
}
// Verify that the declared structures are valid, e.g. that the structure
// doesn't contain a member of its own type directly or indirectly
while( toValidate.GetLength() > 0 )
{
asUINT numClasses = (asUINT)toValidate.GetLength();
asCArray<sClassDeclaration*> toValidateNext((int)toValidate.GetLength());
while( toValidate.GetLength() > 0 )
{
sClassDeclaration *decl = toValidate[toValidate.GetLength()-1];
int validState = 1;
for( asUINT n = 0; n < decl->objType->properties.GetLength(); n++ )
{
// A valid structure is one that uses only primitives or other valid objects
asCObjectProperty *prop = decl->objType->properties[n];
asCDataType dt = prop->type;
if( dt.IsTemplate() )
{
asCDataType sub = dt;
while( sub.IsTemplate() && !sub.IsObjectHandle() )
sub = sub.GetSubType();
dt = sub;
}
if( dt.IsObject() && !dt.IsObjectHandle() )
{
// Find the class declaration
sClassDeclaration *pdecl = 0;
for( asUINT p = 0; p < classDeclarations.GetLength(); p++ )
{
if( classDeclarations[p]->objType == dt.GetObjectType() )
{
pdecl = classDeclarations[p];
break;
}
}
if( pdecl )
{
if( pdecl->objType == decl->objType )
{
int r, c;
decl->script->ConvertPosToRowCol(decl->node->tokenPos, &r, &c);
WriteError(decl->script->name.AddressOf(), TXT_ILLEGAL_MEMBER_TYPE, r, c);
validState = 2;
break;
}
else if( pdecl->validState != 1 )
{
validState = pdecl->validState;
break;
}
}
}
}
if( validState == 1 )
{
decl->validState = 1;
toValidate.PopLast();
}
else if( validState == 2 )
{
decl->validState = 2;
toValidate.PopLast();
}
else
{
toValidateNext.PushLast(toValidate.PopLast());
}
}
toValidate = toValidateNext;
toValidateNext.SetLength(0);
if( numClasses == toValidate.GetLength() )
{
int r, c;
toValidate[0]->script->ConvertPosToRowCol(toValidate[0]->node->tokenPos, &r, &c);
WriteError(toValidate[0]->script->name.AddressOf(), TXT_ILLEGAL_MEMBER_TYPE, r, c);
break;
}
}
if( numErrors > 0 ) return;
// TODO: The declarations form a graph, all circles in
// the graph must be flagged as potential circles
// Verify potential circular references
for( n = 0; n < classDeclarations.GetLength(); n++ )
{
sClassDeclaration *decl = classDeclarations[n];
asCObjectType *ot = decl->objType;
// Is there some path in which this structure is involved in circular references?
for( asUINT p = 0; p < ot->properties.GetLength(); p++ )
{
asCDataType dt = ot->properties[p]->type;
if( dt.IsObject() )
{
if( dt.IsObjectHandle() )
{
// TODO: Can this handle really generate a circular reference?
// Only if the handle is of a type that can reference this type, either directly or indirectly
ot->flags |= asOBJ_GC;
}
else if( dt.GetObjectType()->flags & asOBJ_GC )
{
// TODO: Just because the member type is a potential circle doesn't mean that this one is
// Only if the object is of a type that can reference this type, either directly or indirectly
ot->flags |= asOBJ_GC;
}
// TODO: array: The template type should define if the instanciation can form circles or not
if( dt.IsArrayType() )
{
asCDataType sub = dt.GetSubType();
while( sub.IsObject() )
{
if( sub.IsObjectHandle() || (sub.GetObjectType()->flags & asOBJ_GC) )
{
decl->objType->flags |= asOBJ_GC;
// Make sure the array object is also marked as potential circle
sub = dt;
while( sub.IsTemplate() )
{
sub.GetObjectType()->flags |= asOBJ_GC;
sub = sub.GetSubType();
}
break;
}
if( sub.IsTemplate() )
sub = sub.GetSubType();
else
break;
}
}
}
}
}
}
int asCBuilder::CreateVirtualFunction(asCScriptFunction *func, int idx)
{
asCScriptFunction *vf = asNEW(asCScriptFunction)(engine, module, asFUNC_VIRTUAL);
vf->funcType = asFUNC_VIRTUAL;
vf->name = func->name;
vf->returnType = func->returnType;
vf->parameterTypes = func->parameterTypes;
vf->inOutFlags = func->inOutFlags;
vf->id = engine->GetNextScriptFunctionId();
vf->scriptSectionIdx = func->scriptSectionIdx;
vf->isReadOnly = func->isReadOnly;
vf->objectType = func->objectType;
vf->signatureId = func->signatureId;
vf->vfTableIdx = idx;
module->AddScriptFunction(vf);
// Add a dummy to the builder so that it doesn't mix up function ids
functions.PushLast(0);
return vf->id;
}
asCObjectProperty *asCBuilder::AddPropertyToClass(sClassDeclaration *decl, const asCString &name, const asCDataType &dt, asCScriptCode *file, asCScriptNode *node)
{
// Store the properties in the object type descriptor
asCObjectProperty *prop = asNEW(asCObjectProperty);
prop->name = name;
prop->type = dt;
int propSize;
if( dt.IsObject() )
{
propSize = dt.GetSizeOnStackDWords()*4;
if( !dt.IsObjectHandle() )
{
if( !dt.CanBeInstanciated() )
{
asASSERT( file && node );
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf());
WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
}
prop->type.MakeReference(true);
}
}
else
{
propSize = dt.GetSizeInMemoryBytes();
if( propSize == 0 && file && node )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
asCString str;
str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf());
WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
}
}
// Add extra bytes so that the property will be properly aligned
if( propSize == 2 && (decl->objType->size & 1) ) decl->objType->size += 1;
if( propSize > 2 && (decl->objType->size & 3) ) decl->objType->size += 4 - (decl->objType->size & 3);
prop->byteOffset = decl->objType->size;
decl->objType->size += propSize;
decl->objType->properties.PushLast(prop);
// Make sure the struct holds a reference to the config group where the object is registered
asCConfigGroup *group = engine->FindConfigGroupForObjectType(prop->type.GetObjectType());
if( group != 0 ) group->AddRef();
return prop;
}
bool asCBuilder::DoesMethodExist(asCObjectType *objType, int methodId)
{
asCScriptFunction *method = GetFunctionDescription(methodId);
for( asUINT n = 0; n < objType->methods.GetLength(); n++ )
{
asCScriptFunction *m = GetFunctionDescription(objType->methods[n]);
if( m->name != method->name ) continue;
if( m->returnType != method->returnType ) continue;
if( m->isReadOnly != method->isReadOnly ) continue;
if( m->parameterTypes != method->parameterTypes ) continue;
if( m->inOutFlags != method->inOutFlags ) continue;
return true;
}
return false;
}
void asCBuilder::AddDefaultConstructor(asCObjectType *objType, asCScriptCode *file)
{
int funcId = engine->GetNextScriptFunctionId();
asCDataType returnType = asCDataType::CreatePrimitive(ttVoid, false);
asCArray<asCDataType> parameterTypes;
asCArray<asETypeModifiers> inOutFlags;
// Add the script function
module->AddScriptFunction(file->idx, funcId, objType->name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false, objType);
// Set it as default constructor
if( objType->beh.construct )
engine->scriptFunctions[objType->beh.construct]->Release();
objType->beh.construct = funcId;
objType->beh.constructors[0] = funcId;
engine->scriptFunctions[funcId]->AddRef();
// The bytecode for the default constructor will be generated
// only after the potential inheritance has been established
sFunctionDescription *func = asNEW(sFunctionDescription);
functions.PushLast(func);
func->script = file;
func->node = 0;
func->name = objType->name;
func->objType = objType;
func->funcId = funcId;
// Add a default factory as well
funcId = engine->GetNextScriptFunctionId();
if( objType->beh.factory )
engine->scriptFunctions[objType->beh.factory]->Release();
objType->beh.factory = funcId;
objType->beh.factories[0] = funcId;
returnType = asCDataType::CreateObjectHandle(objType, false);
module->AddScriptFunction(file->idx, funcId, objType->name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false);
functions.PushLast(0);
asCCompiler compiler(engine);
compiler.CompileFactory(this, file, engine->scriptFunctions[funcId]);
engine->scriptFunctions[funcId]->AddRef();
}
int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file)
{
// Grab the name of the enumeration
asCScriptNode *tmp = node->firstChild;
asASSERT(snDataType == tmp->nodeType);
asCString name;
asASSERT(snIdentifier == tmp->firstChild->nodeType);
name.Assign(&file->code[tmp->firstChild->tokenPos], tmp->firstChild->tokenLength);
// Check the name and add the enum
int r = CheckNameConflict(name.AddressOf(), tmp->firstChild, file);
if( asSUCCESS == r )
{
asCObjectType *st;
asCDataType dataType;
st = asNEW(asCObjectType)(engine);
dataType.CreatePrimitive(ttInt, false);
st->flags = asOBJ_ENUM;
st->size = dataType.GetSizeInMemoryBytes();
st->name = name;
module->enumTypes.PushLast(st);
st->AddRef();
engine->classTypes.PushLast(st);
// Store the location of this declaration for reference in name collisions
sClassDeclaration *decl = asNEW(sClassDeclaration);
decl->name = name;
decl->script = file;
decl->validState = 0;
decl->node = NULL;
decl->objType = st;
namedTypeDeclarations.PushLast(decl);
asCDataType type = CreateDataTypeFromNode(tmp, file);
asASSERT(!type.IsReference());
tmp = tmp->next;
while( tmp )
{
asASSERT(snIdentifier == tmp->nodeType);
asCString name(&file->code[tmp->tokenPos], tmp->tokenLength);
// TODO: Should only have to check for conflicts within the enum type
// Check for name conflict errors
r = CheckNameConflict(name.AddressOf(), tmp, file);
if(asSUCCESS != r)
{
continue;
}
// check for assignment
asCScriptNode *asnNode = tmp->next;
if( asnNode && snAssignment == asnNode->nodeType )
asnNode->DisconnectParent();
else
asnNode = 0;
// Create the global variable description so the enum value can be evaluated
sGlobalVariableDescription *gvar = asNEW(sGlobalVariableDescription);
globVariables.PushLast(gvar);
gvar->script = file;
gvar->idNode = 0;
gvar->nextNode = asnNode;
gvar->name = name;
gvar->datatype = type;
// No need to allocate space on the global memory stack since the values are stored in the asCObjectType
gvar->index = 0;
gvar->isCompiled = false;
gvar->isPureConstant = true;
gvar->isEnumValue = true;
gvar->constantValue = 0xdeadbeef;
// Allocate dummy property so we can compile the value.
// This will be removed later on so we don't add it to the engine.
gvar->property = asNEW(asCGlobalProperty);
gvar->property->name = name;
gvar->property->type = gvar->datatype;
gvar->property->id = 0;
tmp = tmp->next;
}
}
node->Destroy(engine);
return r;
}
int asCBuilder::RegisterTypedef(asCScriptNode *node, asCScriptCode *file)
{
// Get the native data type
asCScriptNode *tmp = node->firstChild;
asASSERT(NULL != tmp && snDataType == tmp->nodeType);
asCDataType dataType;
dataType.CreatePrimitive(tmp->tokenType, false);
dataType.SetTokenType(tmp->tokenType);
tmp = tmp->next;
// Grab the name of the typedef
asASSERT(NULL != tmp && NULL == tmp->next);
asCString name;
name.Assign(&file->code[tmp->tokenPos], tmp->tokenLength);
// If the name is not already in use add it
int r = CheckNameConflict(name.AddressOf(), tmp, file);
if( asSUCCESS == r )
{
// Create the new type
asCObjectType *st = asNEW(asCObjectType)(engine);
st->flags = asOBJ_TYPEDEF;
st->size = dataType.GetSizeInMemoryBytes();
st->name = name;
st->templateSubType = dataType;
st->AddRef();
module->typeDefs.PushLast(st);
engine->classTypes.PushLast(st);
// Store the location of this declaration for reference in name collisions
sClassDeclaration *decl = asNEW(sClassDeclaration);
decl->name = name;
decl->script = file;
decl->validState = 0;
decl->node = NULL;
decl->objType = st;
namedTypeDeclarations.PushLast(decl);
}
node->Destroy(engine);
if( r < 0 )
{
engine->ConfigError(r);
}
return 0;
}
void asCBuilder::GetParsedFunctionDetails(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, asCString &name, asCDataType &returnType, asCArray<asCDataType> &parameterTypes, asCArray<asETypeModifiers> &inOutFlags, bool &isConstMethod, bool &isConstructor, bool &isDestructor)
{
// Find the name
isConstructor = false;
isDestructor = false;
asCScriptNode *n = 0;
if( node->firstChild->nodeType == snDataType )
n = node->firstChild->next->next;
else
{
// If the first node is a ~ token, then we know it is a destructor
if( node->firstChild->tokenType == ttBitNot )
{
n = node->firstChild->next;
isDestructor = true;
}
else
{
n = node->firstChild;
isConstructor = true;
}
}
name.Assign(&file->code[n->tokenPos], n->tokenLength);
// Initialize a script function object for registration
if( !isConstructor && !isDestructor )
{
returnType = CreateDataTypeFromNode(node->firstChild, file);
returnType = ModifyDataTypeFromNode(returnType, node->firstChild->next, file, 0, 0);
}
else
returnType = asCDataType::CreatePrimitive(ttVoid, false);
// Is this a const method?
if( objType && n->next->next && n->next->next->tokenType == ttConst )
isConstMethod = true;
else
isConstMethod = false;
// Count the number of parameters
int count = 0;
asCScriptNode *c = n->next->firstChild;
while( c )
{
count++;
c = c->next->next;
if( c && c->nodeType == snIdentifier )
c = c->next;
}
// Get the parameter types
parameterTypes.Allocate(count, false);
inOutFlags.Allocate(count, false);
n = n->next->firstChild;
while( n )
{
asETypeModifiers inOutFlag;
asCDataType type = CreateDataTypeFromNode(n, file);
type = ModifyDataTypeFromNode(type, n->next, file, &inOutFlag, 0);
// Store the parameter type
parameterTypes.PushLast(type);
inOutFlags.PushLast(inOutFlag);
// Move to next parameter
n = n->next->next;
if( n && n->nodeType == snIdentifier )
n = n->next;
}
}
int asCBuilder::RegisterScriptFunction(int funcID, asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction)
{
asCString name;
asCDataType returnType;
asCArray<asCDataType> parameterTypes;
asCArray<asETypeModifiers> inOutFlags;
bool isConstMethod;
bool isConstructor;
bool isDestructor;
GetParsedFunctionDetails(node, file, objType, name, returnType, parameterTypes, inOutFlags, isConstMethod, isConstructor, isDestructor);
// Check for name conflicts
if( !isConstructor && !isDestructor )
{
if( objType )
CheckNameConflictMember(objType, name.AddressOf(), node, file);
else
CheckNameConflict(name.AddressOf(), node, file);
}
else
{
// Verify that the name of the constructor/destructor is the same as the class
if( name != objType->name )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_CONSTRUCTOR_NAME_ERROR, r, c);
}
if( isDestructor )
name = "~" + name;
}
if( !isInterface )
{
sFunctionDescription *func = asNEW(sFunctionDescription);
functions.PushLast(func);
func->script = file;
func->node = node;
func->name = name;
func->objType = objType;
func->funcId = funcID;
}
// Destructors may not have any parameters
if( isDestructor && parameterTypes.GetLength() > 0 )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_DESTRUCTOR_MAY_NOT_HAVE_PARM, r, c);
}
// TODO: Much of this can probably be reduced by using the IsSignatureEqual method
// Check that the same function hasn't been registered already
asCArray<int> funcs;
GetFunctionDescriptions(name.AddressOf(), funcs);
if( funcs.GetLength() )
{
for( asUINT n = 0; n < funcs.GetLength(); ++n )
{
asCScriptFunction *func = GetFunctionDescription(funcs[n]);
if( parameterTypes.GetLength() == func->parameterTypes.GetLength() )
{
bool match = true;
if( func->objectType != objType )
{
match = false;
break;
}
for( asUINT p = 0; p < parameterTypes.GetLength(); ++p )
{
if( parameterTypes[p] != func->parameterTypes[p] )
{
match = false;
break;
}
}
if( match )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_FUNCTION_ALREADY_EXIST, r, c);
break;
}
}
}
}
// Register the function
module->AddScriptFunction(file->idx, funcID, name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), isInterface, objType, isConstMethod, isGlobalFunction);
if( objType )
{
engine->scriptFunctions[funcID]->AddRef();
if( isConstructor )
{
int factoryId = engine->GetNextScriptFunctionId();
if( parameterTypes.GetLength() == 0 )
{
// Overload the default constructor
engine->scriptFunctions[objType->beh.construct]->Release();
objType->beh.construct = funcID;
objType->beh.constructors[0] = funcID;
// Register the default factory as well
engine->scriptFunctions[objType->beh.factory]->Release();
objType->beh.factory = factoryId;
objType->beh.factories[0] = factoryId;
}
else
{
objType->beh.constructors.PushLast(funcID);
// Register the factory as well
objType->beh.factories.PushLast(factoryId);
}
asCDataType dt = asCDataType::CreateObjectHandle(objType, false);
module->AddScriptFunction(file->idx, factoryId, name.AddressOf(), dt, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false);
// Add a dummy function to the module so that it doesn't mix up the fund Ids
functions.PushLast(0);
// Compile the factory immediately
asCCompiler compiler(engine);
compiler.CompileFactory(this, file, engine->scriptFunctions[factoryId]);
engine->scriptFunctions[factoryId]->AddRef();
}
else if( isDestructor )
objType->beh.destruct = funcID;
else
objType->methods.PushLast(funcID);
}
// We need to delete the node already if this is an interface method
if( isInterface && node )
{
node->Destroy(engine);
}
return 0;
}
int asCBuilder::RegisterImportedFunction(int importID, asCScriptNode *node, asCScriptCode *file)
{
// Find name
asCScriptNode *f = node->firstChild;
asCScriptNode *n = f->firstChild->next->next;
// Check for name conflicts
asCString name(&file->code[n->tokenPos], n->tokenLength);
CheckNameConflict(name.AddressOf(), n, file);
// Initialize a script function object for registration
asCDataType returnType;
returnType = CreateDataTypeFromNode(f->firstChild, file);
returnType = ModifyDataTypeFromNode(returnType, f->firstChild->next, file, 0, 0);
// Count the parameters
int count = 0;
asCScriptNode *c = n->next->firstChild;
while( c )
{
count++;
c = c->next->next;
if( c && c->nodeType == snIdentifier )
c = c->next;
}
asCArray<asCDataType> parameterTypes(count);
asCArray<asETypeModifiers> inOutFlags(count);
n = n->next->firstChild;
while( n )
{
asETypeModifiers inOutFlag;
asCDataType type = CreateDataTypeFromNode(n, file);
type = ModifyDataTypeFromNode(type, n->next, file, &inOutFlag, 0);
// Store the parameter type
n = n->next->next;
parameterTypes.PushLast(type);
inOutFlags.PushLast(inOutFlag);
// Move to next parameter
if( n && n->nodeType == snIdentifier )
n = n->next;
}
// Check that the same function hasn't been registered already
asCArray<int> funcs;
GetFunctionDescriptions(name.AddressOf(), funcs);
if( funcs.GetLength() )
{
for( asUINT n = 0; n < funcs.GetLength(); ++n )
{
asCScriptFunction *func = GetFunctionDescription(funcs[n]);
// TODO: Isn't the name guaranteed to be equal, because of GetFunctionDescriptions()?
if( name == func->name &&
parameterTypes.GetLength() == func->parameterTypes.GetLength() )
{
bool match = true;
for( asUINT p = 0; p < parameterTypes.GetLength(); ++p )
{
if( parameterTypes[p] != func->parameterTypes[p] )
{
match = false;
break;
}
}
if( match )
{
int r, c;
file->ConvertPosToRowCol(node->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_FUNCTION_ALREADY_EXIST, r, c);
break;
}
}
}
}
// Read the module name as well
n = node->firstChild->next;
asCString moduleName;
moduleName.Assign(&file->code[n->tokenPos+1], n->tokenLength-2);
node->Destroy(engine);
// Register the function
module->AddImportedFunction(importID, name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), moduleName);
return 0;
}
asCScriptFunction *asCBuilder::GetFunctionDescription(int id)
{
// TODO: This should be improved
// Get the description from the engine
if( (id & 0xFFFF0000) == 0 )
return engine->scriptFunctions[id];
else
return engine->importedFunctions[id & 0xFFFF]->importedFunctionSignature;
}
void asCBuilder::GetFunctionDescriptions(const char *name, asCArray<int> &funcs)
{
asUINT n;
// TODO: optimize: Improve linear search
for( n = 0; n < module->scriptFunctions.GetLength(); n++ )
{
if( module->scriptFunctions[n]->name == name &&
module->scriptFunctions[n]->objectType == 0 )
funcs.PushLast(module->scriptFunctions[n]->id);
}
// TODO: optimize: Improve linear search
for( n = 0; n < module->bindInformations.GetLength(); n++ )
{
if( module->bindInformations[n]->importedFunctionSignature->name == name )
funcs.PushLast(module->bindInformations[n]->importedFunctionSignature->id);
}
// TODO: optimize: Improve linear search
// TODO: optimize: Use the registeredGlobalFunctions array instead
for( n = 0; n < engine->scriptFunctions.GetLength(); n++ )
{
if( engine->scriptFunctions[n] &&
engine->scriptFunctions[n]->funcType == asFUNC_SYSTEM &&
engine->scriptFunctions[n]->objectType == 0 &&
engine->scriptFunctions[n]->name == name )
{
// Find the config group for the global function
asCConfigGroup *group = engine->FindConfigGroupForFunction(engine->scriptFunctions[n]->id);
if( !group || group->HasModuleAccess(module->name.AddressOf()) )
funcs.PushLast(engine->scriptFunctions[n]->id);
}
}
}
void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray<int> &methods, bool objIsConst, const asCString &scope)
{
if( scope != "" )
{
// Find the base class with the specified scope
while( objectType && objectType->name != scope )
objectType = objectType->derivedFrom;
// If the scope is not any of the base classes, then return no methods
if( objectType == 0 )
return;
}
// TODO: optimize: Improve linear search
if( objIsConst )
{
// Only add const methods to the list
for( asUINT n = 0; n < objectType->methods.GetLength(); n++ )
{
if( engine->scriptFunctions[objectType->methods[n]]->name == name &&
engine->scriptFunctions[objectType->methods[n]]->isReadOnly )
{
// When the scope is defined the returned methods should be the true methods, not the virtual method stubs
if( scope == "" )
methods.PushLast(engine->scriptFunctions[objectType->methods[n]]->id);
else
{
asCScriptFunction *virtFunc = engine->scriptFunctions[objectType->methods[n]];
asCScriptFunction *realFunc = objectType->virtualFunctionTable[virtFunc->vfTableIdx];
methods.PushLast(realFunc->id);
}
}
}
}
else
{
// TODO: Prefer non-const over const
for( asUINT n = 0; n < objectType->methods.GetLength(); n++ )
{
if( engine->scriptFunctions[objectType->methods[n]]->name == name )
{
// When the scope is defined the returned methods should be the true methods, not the virtual method stubs
if( scope == "" )
methods.PushLast(engine->scriptFunctions[objectType->methods[n]]->id);
else
{
asCScriptFunction *virtFunc = engine->scriptFunctions[objectType->methods[n]];
asCScriptFunction *realFunc = objectType->virtualFunctionTable[virtFunc->vfTableIdx];
methods.PushLast(realFunc->id);
}
}
}
}
}
void asCBuilder::WriteInfo(const char *scriptname, const char *message, int r, int c, bool pre)
{
// Need to store the pre message in a structure
if( pre )
{
preMessage.isSet = true;
preMessage.c = c;
preMessage.r = r;
preMessage.message = message;
}
else
{
preMessage.isSet = false;
engine->WriteMessage(scriptname, r, c, asMSGTYPE_INFORMATION, message);
}
}
void asCBuilder::WriteError(const char *scriptname, const char *message, int r, int c)
{
numErrors++;
// Need to pass the preMessage first
if( preMessage.isSet )
WriteInfo(scriptname, preMessage.message.AddressOf(), preMessage.r, preMessage.c, false);
engine->WriteMessage(scriptname, r, c, asMSGTYPE_ERROR, message);
}
void asCBuilder::WriteWarning(const char *scriptname, const char *message, int r, int c)
{
numWarnings++;
// Need to pass the preMessage first
if( preMessage.isSet )
WriteInfo(scriptname, preMessage.message.AddressOf(), preMessage.r, preMessage.c, false);
engine->WriteMessage(scriptname, r, c, asMSGTYPE_WARNING, message);
}
asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCode *file, bool acceptHandleForScope, asCObjectType *templateType)
{
asASSERT(node->nodeType == snDataType);
asCDataType dt;
asCScriptNode *n = node->firstChild;
bool isConst = false;
bool isImplicitHandle = false;
if( n->tokenType == ttConst )
{
isConst = true;
n = n->next;
}
if( n->tokenType == ttIdentifier )
{
asCString str;
str.Assign(&file->code[n->tokenPos], n->tokenLength);
asCObjectType *ot = 0;
// If this is for a template type, then we must first determine if the
// identifier matches any of the template subtypes
// TODO: template: it should be possible to have more than one subtypes
if( templateType && (templateType->flags & asOBJ_TEMPLATE) && str == templateType->templateSubType.GetObjectType()->name )
ot = templateType->templateSubType.GetObjectType();
if( ot == 0 )
ot = GetObjectType(str.AddressOf());
if( ot == 0 )
{
asCString msg;
msg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, (const char *)str.AddressOf());
int r, c;
file->ConvertPosToRowCol(n->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), msg.AddressOf(), r, c);
dt.SetTokenType(ttInt);
}
else
{
if( ot->flags & asOBJ_IMPLICIT_HANDLE )
isImplicitHandle = true;
// Find the config group for the object type
asCConfigGroup *group = engine->FindConfigGroupForObjectType(ot);
if( !module || !group || group->HasModuleAccess(module->name.AddressOf()) )
{
if(asOBJ_TYPEDEF == (ot->flags & asOBJ_TYPEDEF))
{
// TODO: typedef: A typedef should be considered different from the original type (though with implicit conversions between the two)
// Create primitive data type based on object flags
dt = ot->templateSubType;
dt.MakeReadOnly(isConst);
}
else
{
if( ot->flags & asOBJ_TEMPLATE )
{
n = n->next;
// Check if the subtype is a type or the template's subtype
// if it is the template's subtype then this is the actual template type,
// orderwise it is a template instance.
asCDataType subType = CreateDataTypeFromNode(n, file, false, ot);
if( subType.GetObjectType() != ot->templateSubType.GetObjectType() )
{
// This is a template instance
// Need to find the correct object type
asCObjectType *otInstance = engine->GetTemplateInstanceType(ot, subType);
if( !otInstance )
{
asCString msg;
msg.Format(TXT_CANNOT_INSTANCIATE_TEMPLATE_s_WITH_s, ot->name.AddressOf(), subType.Format().AddressOf());
int r, c;
file->ConvertPosToRowCol(n->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), msg.AddressOf(), r, c);
}
ot = otInstance;
}
}
// Create object data type
if( ot )
dt = asCDataType::CreateObject(ot, isConst);
else
dt = asCDataType::CreatePrimitive(ttInt, isConst);
}
}
else
{
asCString msg;
msg.Format(TXT_TYPE_s_NOT_AVAILABLE_FOR_MODULE, (const char *)str.AddressOf());
int r, c;
file->ConvertPosToRowCol(n->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), msg.AddressOf(), r, c);
dt.SetTokenType(ttInt);
}
}
}
else
{
// Create primitive data type
dt = asCDataType::CreatePrimitive(n->tokenType, isConst);
}
// Determine array dimensions and object handles
n = n->next;
while( n && (n->tokenType == ttOpenBracket || n->tokenType == ttHandle) )
{
if( n->tokenType == ttOpenBracket )
{
// Make sure the sub type can be instanciated
if( !dt.CanBeInstanciated() )
{
int r, c;
file->ConvertPosToRowCol(n->tokenPos, &r, &c);
asCString str;
// TODO: Change to "Array sub type cannot be 'type'"
str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf());
WriteError(file->name.AddressOf(), str.AddressOf(), r, c);
}
// Make the type an array (or multidimensional array)
if( dt.MakeArray(engine) < 0 )
{
int r, c;
file->ConvertPosToRowCol(n->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_TOO_MANY_ARRAY_DIMENSIONS, r, c);
break;
}
}
else
{
// Make the type a handle
if( dt.MakeHandle(true, acceptHandleForScope) < 0 )
{
int r, c;
file->ConvertPosToRowCol(n->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_OBJECT_HANDLE_NOT_SUPPORTED, r, c);
break;
}
}
n = n->next;
}
if( isImplicitHandle )
{
// Make the type a handle
if( dt.MakeHandle(true, acceptHandleForScope) < 0 )
{
int r, c;
file->ConvertPosToRowCol(n->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_OBJECT_HANDLE_NOT_SUPPORTED, r, c);
}
}
return dt;
}
asCDataType asCBuilder::ModifyDataTypeFromNode(const asCDataType &type, asCScriptNode *node, asCScriptCode *file, asETypeModifiers *inOutFlags, bool *autoHandle)
{
asCDataType dt = type;
if( inOutFlags ) *inOutFlags = asTM_NONE;
// Is the argument sent by reference?
asCScriptNode *n = node->firstChild;
if( n && n->tokenType == ttAmp )
{
dt.MakeReference(true);
n = n->next;
if( n )
{
if( inOutFlags )
{
if( n->tokenType == ttIn )
*inOutFlags = asTM_INREF;
else if( n->tokenType == ttOut )
*inOutFlags = asTM_OUTREF;
else if( n->tokenType == ttInOut )
*inOutFlags = asTM_INOUTREF;
else
asASSERT(false);
}
n = n->next;
}
else
{
if( inOutFlags )
*inOutFlags = asTM_INOUTREF; // ttInOut
}
if( !engine->ep.allowUnsafeReferences &&
inOutFlags && *inOutFlags == asTM_INOUTREF )
{
// Verify that the base type support &inout parameter types
if( !dt.IsObject() || dt.IsObjectHandle() || !dt.GetObjectType()->beh.addref || !dt.GetObjectType()->beh.release )
{
int r, c;
file->ConvertPosToRowCol(node->firstChild->tokenPos, &r, &c);
WriteError(file->name.AddressOf(), TXT_ONLY_OBJECTS_MAY_USE_REF_INOUT, r, c);
}
}
}
if( autoHandle ) *autoHandle = false;
if( n && n->tokenType == ttPlus )
{
if( autoHandle ) *autoHandle = true;
}
return dt;
}
asCObjectType *asCBuilder::GetObjectType(const char *type)
{
// TODO: Only search in config groups to which the module has access
asCObjectType *ot = engine->GetObjectType(type);
if( !ot && module )
ot = module->GetObjectType(type);
return ot;
}
int asCBuilder::GetEnumValueFromObjectType(asCObjectType *objType, const char *name, asCDataType &outDt, asDWORD &outValue)
{
if( !objType || !(objType->flags & asOBJ_ENUM) )
return 0;
for( asUINT n = 0; n < objType->enumValues.GetLength(); ++n )
{
if( objType->enumValues[n]->name == name )
{
outDt = asCDataType::CreateObject(objType, true);
outValue = objType->enumValues[n]->value;
return 1;
}
}
return 0;
}
int asCBuilder::GetEnumValue(const char *name, asCDataType &outDt, asDWORD &outValue)
{
bool found = false;
// Search all available enum types
asUINT t;
for( t = 0; t < engine->objectTypes.GetLength(); t++ )
{
asCObjectType *ot = engine->objectTypes[t];
if( GetEnumValueFromObjectType( ot, name, outDt, outValue ) )
{
if( !found )
{
found = true;
}
else
{
// Found more than one value in different enum types
return 2;
}
}
}
for( t = 0; t < module->enumTypes.GetLength(); t++ )
{
asCObjectType *ot = module->enumTypes[t];
if( GetEnumValueFromObjectType( ot, name, outDt, outValue ) )
{
if( !found )
{
found = true;
}
else
{
// Found more than one value in different enum types
return 2;
}
}
}
if( found )
return 1;
// Didn't find any value
return 0;
}
END_AS_NAMESPACE