Separate searching for functions and methods

fixes #207
fixes #1016
1164-fix
melex750 2017-11-15 16:21:01 -05:00 committed by Mateusz Przybył
parent 0102e45855
commit 4a62e9ed76
7 changed files with 641 additions and 96 deletions

View File

@ -20,6 +20,7 @@
#include "CBot/CBotCStack.h"
#include "CBot/CBotClass.h"
#include "CBot/CBotToken.h"
#include "CBot/CBotExternalCall.h"
@ -320,6 +321,50 @@ void CBotCStack::AddVar(CBotVar* pVar)
*pp = pVar; // added after
}
////////////////////////////////////////////////////////////////////////////////
void CBotCStack::CreateVarThis(CBotClass* pClass)
{
if ( pClass == nullptr ) return;
CBotVar* pThis = CBotVar::Create("this", CBotTypResult(CBotTypClass, pClass));
pThis->SetUniqNum(-2); // special ID for "this"
AddVar(pThis);
}
////////////////////////////////////////////////////////////////////////////////
void CBotCStack::CreateVarSuper(CBotClass* pClass)
{
if ( pClass == nullptr ) return;
CBotVar* pSuper = CBotVar::Create("super", CBotTypResult(CBotTypClass, pClass));
pSuper->SetUniqNum(-3); // special ID for "super"
AddVar(pSuper);
}
////////////////////////////////////////////////////////////////////////////////
void CBotCStack::CreateMemberVars(CBotClass* pClass, bool setDefined)
{
while (pClass != nullptr)
{
CBotVar* pv = pClass->GetVar();
while (pv != nullptr)
{
CBotVar* pcopy = CBotVar::Create(pv);
CBotVar::InitType initType = CBotVar::InitType::UNDEF;
if (setDefined || pv->IsStatic())
initType = CBotVar::InitType::DEF;
pcopy->SetInit(initType);
pcopy->SetUniqNum(pv->GetUniqNum());
pcopy->SetPrivate(pv->GetPrivate());
AddVar(pcopy);
pv = pv->GetNext();
}
pClass = pClass->GetParent();
}
}
////////////////////////////////////////////////////////////////////////////////
bool CBotCStack::CheckVarLocal(CBotToken* &pToken)
{
@ -350,7 +395,7 @@ CBotTypResult CBotCStack::CompileCall(CBotToken* &p, CBotVar** ppVars, long& nId
val = m_prog->GetExternalCalls()->CompileCall(p, nullptr, ppVars, this);
if (val.GetType() < 0)
{
val = CBotFunction::CompileCall(m_prog->GetFunctions(), p->GetString(), ppVars, nIdent);
val = CBotFunction::CompileCall(p->GetString(), ppVars, nIdent, m_prog);
if ( val.GetType() < 0 )
{
// pVar = nullptr; // the error is not on a particular parameter
@ -363,7 +408,7 @@ CBotTypResult CBotCStack::CompileCall(CBotToken* &p, CBotVar** ppVars, long& nId
}
////////////////////////////////////////////////////////////////////////////////
bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam)
bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam, const std::string& className)
{
std::string name = pToken->GetString();
@ -373,6 +418,9 @@ bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam)
{
if ( pToken->GetString() == pp->GetName() )
{
// ignore methods for a different class
if ( className != pp->GetClassName() )
continue;
// are parameters exactly the same?
if ( pp->CheckParam( pParam ) )
return true;
@ -383,6 +431,9 @@ bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam)
{
if ( pToken->GetString() == pp->GetName() )
{
// ignore methods for a different class
if ( className != pp->GetClassName() )
continue;
// are parameters exactly the same?
if ( pp->CheckParam( pParam ) )
return true;

View File

@ -100,6 +100,25 @@ public:
*/
void AddVar(CBotVar* p);
/*!
* \brief Create 'this' as a local variable.
* \param pClass The current class referred to by 'this'
*/
void CreateVarThis(CBotClass* pClass);
/*!
* \brief Create 'super' as a local variable.
* \param pClass The parent class referred to by 'super'
*/
void CreateVarSuper(CBotClass* pClass);
/*!
* \brief Create member variables of the current class as local variables.
* \param pClass The current class.
* \param setDefined Whether to mark the variables as initialized.
*/
void CreateMemberVars(CBotClass* pClass, bool setDefined);
/*!
* \brief FindVar Finds a variable. Seeks a variable on the stack the token
* may be a result of TokenTypVar (object of a class) or a pointer in the
@ -235,11 +254,12 @@ public:
/*!
* \brief CheckCall Test if a procedure name is already defined somewhere.
* \param pToken
* \param pParam
* \param pToken Token representing the name of a function.
* \param pParam List of parameters.
* \param className Name of a class when checking for methods.
* \return
*/
bool CheckCall(CBotToken* &pToken, CBotDefParam* pParam);
bool CheckCall(CBotToken* &pToken, CBotDefParam* pParam, const std::string& className);
/*!
* \brief NextToken

View File

@ -315,7 +315,7 @@ CBotTypResult CBotClass::CompileMethode(CBotToken* name,
// find the methods declared by user
r = CBotFunction::CompileCall(m_pMethod, name->GetString(), ppParams, nIdent);
r = CBotFunction::CompileMethodCall(name->GetString(), ppParams, nIdent, pStack, this);
if ( r.Eq(CBotErrUndefCall) && m_parent != nullptr )
return m_parent->CompileMethode(name, pThis, ppParams, pStack, nIdent);
return r;
@ -332,7 +332,7 @@ bool CBotClass::ExecuteMethode(long& nIdent,
int ret = m_externalMethods->DoCall(pToken, pThis, ppParams, pStack, pResultType);
if (ret >= 0) return ret;
ret = CBotFunction::DoCall(m_pMethod, nIdent, pToken->GetString(), pThis, ppParams, pStack, pToken, this);
ret = CBotFunction::DoCall(nIdent, pToken->GetString(), pThis, ppParams, pStack, pToken, this);
if (ret >= 0) return ret;
if (m_parent != nullptr)
@ -355,7 +355,7 @@ void CBotClass::RestoreMethode(long& nIdent,
CBotClass* pClass = this;
while (pClass != nullptr)
{
bool ok = CBotFunction::RestoreCall(pClass->m_pMethod, nIdent, name->GetString(), pThis, ppParams, pStack, pClass);
bool ok = CBotFunction::RestoreCall(nIdent, name->GetString(), pThis, ppParams, pStack, pClass);
if (ok) return;
pClass = pClass->m_parent;
}
@ -542,6 +542,11 @@ void CBotClass::DefineClasses(std::list<CBotClass*> pClassList, CBotCStack* pSta
}
}
const std::list<CBotFunction*>& CBotClass::GetFunctions()
{
return m_pMethod;
}
////////////////////////////////////////////////////////////////////////////////
bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
{
@ -609,45 +614,8 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
CBotFunction* pf = *pfIter;
delete params;
bool bConstructor = (pp == GetName());
CBotCStack* pile = pStack->TokenStack(nullptr, true);
// make "this" known
CBotToken TokenThis(std::string("this"), std::string());
CBotVar* pThis = CBotVar::Create(TokenThis, CBotTypResult( CBotTypClass, this ) );
pThis->SetUniqNum(-2);
pile->AddVar(pThis);
if (m_parent)
{
// makes "super" known
CBotToken TokenSuper(std::string("super"), std::string());
CBotVar* pThis = CBotVar::Create(TokenSuper, CBotTypResult(CBotTypClass, m_parent) );
pThis->SetUniqNum(-3);
pile->AddVar(pThis);
}
// int num = 1;
CBotClass* my = this;
while (my != nullptr)
{
// places a copy of variables of a class (this) on a stack
CBotVar* pv = my->m_pVar;
while (pv != nullptr)
{
CBotVar* pcopy = CBotVar::Create(pv);
CBotVar::InitType initType = CBotVar::InitType::UNDEF;
if (!bConstructor || pv->IsStatic())
initType = CBotVar::InitType::DEF;
pcopy->SetInit(initType);
pcopy->SetUniqNum(pv->GetUniqNum());
pcopy->SetPrivate(pv->GetPrivate());
pile->AddVar(pcopy);
pv = pv->GetNext();
}
my = my->m_parent;
}
// compiles a method
p = pBase;
CBotFunction* f =

View File

@ -303,6 +303,12 @@ public:
*/
static void DefineClasses(std::list<CBotClass*> pClassList, CBotCStack* pStack);
/*!
* \brief Get the list of user-defined methods in this class.
* \return List of methods, can be empty.
*/
const std::list<CBotFunction*>& GetFunctions();
/*!
* \brief CompileDefItem
* \param p

View File

@ -129,12 +129,10 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct
{
CBotToken* pp;
CBotFunction* func = finput;
if ( func == nullptr ) func = new CBotFunction();
assert(func != nullptr); // a pre-compiled function is required
CBotCStack* pStk = pStack->TokenStack(p, bLocal);
// func->m_nFuncIdent = CBotVar::NextUniqNum();
while (true)
{
if ( IsOfType(p, ID_PUBLIC) )
@ -183,10 +181,23 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct
goto bad;
}
// pp = p;
pp = p;
func->m_token = *p;
if (!IsOfType(p, TokenTypVar)) goto bad;
// check if the class has a method like this
if (pClass->CheckCall(pStack->GetProgram(), func->m_param, pp))
{
pStk->SetStartError(func->m_classToken.GetStart());
pStk->SetError(CBotErrRedefFunc, pp->GetEnd());
goto bad;
}
// check if a constructor has return type void
if (func->GetName() == pClass->GetName() && !func->m_retTyp.Eq(CBotTypVoid))
{
pp = &(func->m_retToken);
pStk->SetError(CBotErrFuncNotVoid, pp);
goto bad;
}
}
func->m_openpar = *p;
delete func->m_param;
@ -198,28 +209,13 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct
if (!func->m_MasterClass.empty())
{
// return "this" known
CBotVar* pThis = CBotVar::Create("this", CBotTypResult( CBotTypClass, func->m_MasterClass ));
pThis->SetInit(CBotVar::InitType::IS_POINTER);
// pThis->SetUniqNum(func->m_nThisIdent = -2); //CBotVar::NextUniqNum() will not
pThis->SetUniqNum(-2);
pStk->AddVar(pThis);
CBotClass* pClass = CBotClass::Find(func->m_MasterClass);
// initialize variables acording to This
// only saves the pointer to the first,
// the rest is chained
CBotVar* pv = pThis->GetItemList();
// int num = 1;
while (pv != nullptr)
{
CBotVar* pcopy = CBotVar::Create(pv);
// pcopy->SetInit(2);
pcopy->Copy(pv);
pcopy->SetPrivate(pv->GetPrivate());
// pcopy->SetUniqNum(pv->GetUniqNum()); //num++);
pStk->AddVar(pcopy);
pv = pv->GetNext();
}
pStk->CreateVarThis(pClass);
pStk->CreateVarSuper(pClass->GetParent());
bool bConstructor = (func->GetName() == func->m_MasterClass);
pStk->CreateMemberVars(pClass, !bConstructor);
}
// and compiles the following instruction block
@ -242,7 +238,6 @@ bad:
pStk->SetError(CBotErrNoFunc, p);
}
pStk->SetError(CBotErrNoType, p);
if ( finput == nullptr ) delete func;
return pStack->ReturnFunc(nullptr, pStk);
}
@ -303,6 +298,7 @@ CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClas
if (pStk->IsOk() && pClass != nullptr) // method in a class
{
func->m_MasterClass = pClass->GetName();
// check if a constructor has return type void
if (func->GetName() == pClass->GetName() && !func->m_retTyp.Eq(CBotTypVoid))
{
@ -331,7 +327,7 @@ CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClas
{
// looks if the function exists elsewhere
pp = &(func->m_token);
if (( pClass != nullptr || !pStack->CheckCall(pp, func->m_param)) &&
if (( pClass != nullptr || !pStack->CheckCall(pp, func->m_param, func->m_MasterClass)) &&
( pClass == nullptr || !pClass->CheckCall(pStack->GetProgram(), func->m_param, pp)) )
{
if (IsOfType(p, ID_OPBLK))
@ -472,10 +468,10 @@ void CBotFunction::RestoreState(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInst
}
////////////////////////////////////////////////////////////////////////////////
CBotTypResult CBotFunction::CompileCall(const std::list<CBotFunction*>& localFunctionList, const std::string &name, CBotVar** ppVars, long &nIdent)
CBotTypResult CBotFunction::CompileCall(const std::string &name, CBotVar** ppVars, long &nIdent, CBotProgram* program)
{
CBotTypResult type;
if (!FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type))
if (!FindLocalOrPublic(program->GetFunctions(), nIdent, name, ppVars, type, program))
{
// Reset the identifier to "not found" value
nIdent = 0;
@ -485,7 +481,7 @@ CBotTypResult CBotFunction::CompileCall(const std::list<CBotFunction*>& localFun
////////////////////////////////////////////////////////////////////////////////
CBotFunction* CBotFunction::FindLocalOrPublic(const std::list<CBotFunction*>& localFunctionList, long &nIdent, const std::string &name,
CBotVar** ppVars, CBotTypResult &TypeOrError, bool bPublic)
CBotVar** ppVars, CBotTypResult &TypeOrError, CBotProgram* baseProg)
{
TypeOrError.SetType(CBotErrUndefCall); // no routine of the name
@ -515,10 +511,39 @@ CBotFunction* CBotFunction::FindLocalOrPublic(const std::list<CBotFunction*>& lo
std::map<CBotFunction*, int> funcMap;
for (CBotFunction* pt : localFunctionList)
CBotFunction::SearchList(localFunctionList, name, ppVars, TypeOrError, funcMap);
CBotFunction::SearchPublic(name, ppVars, TypeOrError, funcMap);
if (baseProg != nullptr && baseProg->m_thisVar != nullptr)
{
// find object:: functions
CBotClass* pClass = baseProg->m_thisVar->GetClass();
CBotFunction::SearchList(localFunctionList, name, ppVars, TypeOrError, funcMap, pClass);
CBotFunction::SearchPublic(name, ppVars, TypeOrError, funcMap, pClass);
}
return CBotFunction::BestFunction(funcMap, nIdent, TypeOrError);
}
////////////////////////////////////////////////////////////////////////////////
void CBotFunction::SearchList(const std::list<CBotFunction*>& functionList,
const std::string& name, CBotVar** ppVars, CBotTypResult& TypeOrError,
std::map<CBotFunction*, int>& funcMap, CBotClass* pClass)
{
for (CBotFunction* pt : functionList)
{
if ( pt->m_token.GetString() == name )
{
if (pClass != nullptr) // looking for a method ?
{
if (pt->m_MasterClass != pClass->GetName()) continue;
}
else // looking for a function
{
if (!pt->m_MasterClass.empty()) continue;
}
int i = 0;
int alpha = 0; // signature of parameters
// parameters are compatible?
@ -575,13 +600,26 @@ CBotFunction* CBotFunction::FindLocalOrPublic(const std::list<CBotFunction*>& lo
funcMap.insert( std::pair<CBotFunction*, int>(pt, alpha) );
}
}
}
if ( bPublic )
////////////////////////////////////////////////////////////////////////////////
void CBotFunction::SearchPublic(const std::string& name, CBotVar** ppVars, CBotTypResult& TypeOrError,
std::map<CBotFunction*, int>& funcMap, CBotClass* pClass)
{
{
for (CBotFunction* pt : m_publicFunctions)
{
if ( pt->m_token.GetString() == name )
{
if (pClass != nullptr) // looking for a method ?
{
if (pt->m_MasterClass != pClass->GetName()) continue;
}
else // looking for a function
{
if (!pt->m_MasterClass.empty()) continue;
}
int i = 0;
int alpha = 0; // signature of parameters
// are parameters compatible ?
@ -639,7 +677,12 @@ CBotFunction* CBotFunction::FindLocalOrPublic(const std::list<CBotFunction*>& lo
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
CBotFunction* CBotFunction::BestFunction(std::map<CBotFunction*, int>& funcMap,
long& nIdent, CBotTypResult& TypeOrError)
{
if ( !funcMap.empty() )
{
auto it = funcMap.begin();
@ -675,7 +718,7 @@ int CBotFunction::DoCall(CBotProgram* program, const std::list<CBotFunction*>& l
CBotFunction* pt = nullptr;
CBotProgram* baseProg = pStack->GetProgram(true);
pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type);
pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type, baseProg);
if ( pt != nullptr )
{
@ -766,7 +809,7 @@ void CBotFunction::RestoreCall(const std::list<CBotFunction*>& localFunctionList
CBotStack* pStk3;
CBotProgram* baseProg = pStack->GetProgram(true);
pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type);
pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type, baseProg);
if ( pt != nullptr )
{
@ -824,13 +867,102 @@ void CBotFunction::RestoreCall(const std::list<CBotFunction*>& localFunctionList
}
////////////////////////////////////////////////////////////////////////////////
int CBotFunction::DoCall(const std::list<CBotFunction*>& localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis,
CBotTypResult CBotFunction::CompileMethodCall(const std::string& name, CBotVar** ppVars,
long& nIdent, CBotCStack* pStack, CBotClass* pClass)
{
nIdent = 0;
CBotTypResult type;
CBotFunction* pt = FindMethod(nIdent, name, ppVars, type, pClass, pStack->GetProgram());
if (pt != nullptr)
{
}
return type;
}
////////////////////////////////////////////////////////////////////////////////
CBotFunction* CBotFunction::FindMethod(long& nIdent, const std::string& name,
CBotVar** ppVars, CBotTypResult& TypeOrError,
CBotClass* pClass, CBotProgram* program)
{
TypeOrError.SetType(CBotErrUndefCall); // no routine of the name
auto methods = pClass->GetFunctions();
if ( nIdent )
{
// search methods in the class
for (CBotFunction* pt : methods)
{
if ( pt->m_nFuncIdent == nIdent )
{
TypeOrError = pt->m_retTyp;
return pt;
}
}
bool skipPublic = false;
if (program != nullptr)
{
// search the current program
for (CBotFunction* pt : program->GetFunctions())
{
if ( pt->m_nFuncIdent == nIdent )
{
// check if the method is inherited
if ( pt->GetClassName() != pClass->GetName() )
{
skipPublic = true;
break; // break in case there is an override
}
TypeOrError = pt->m_retTyp;
return pt;
}
}
}
// search the list of public functions
if (!skipPublic)
{
for (CBotFunction* pt : m_publicFunctions)
{
if (pt->m_nFuncIdent == nIdent)
{
// check if the method is inherited, break in case there is an override
if ( pt->GetClassName() != pClass->GetName() ) break;
TypeOrError = pt->m_retTyp;
return pt;
}
}
}
}
if ( name.empty() ) return nullptr;
std::map<CBotFunction*, int> funcMap;
// search methods in the class
CBotFunction::SearchList(methods, name, ppVars, TypeOrError, funcMap, pClass);
// search the current program for methods
if (program != nullptr)
CBotFunction::SearchList(program->GetFunctions(), name, ppVars, TypeOrError, funcMap, pClass);
CBotFunction::SearchPublic(name, ppVars, TypeOrError, funcMap, pClass);
return CBotFunction::BestFunction(funcMap, nIdent, TypeOrError);
}
////////////////////////////////////////////////////////////////////////////////
int CBotFunction::DoCall(long &nIdent, const std::string &name, CBotVar* pThis,
CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken, CBotClass* pClass)
{
CBotTypResult type;
CBotProgram* pProgCurrent = pStack->GetProgram();
CBotFunction* pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type, false);
CBotFunction* pt = FindMethod(nIdent, name, ppVars, type, pClass, pProgCurrent);
if ( pt != nullptr )
{
@ -925,11 +1057,11 @@ int CBotFunction::DoCall(const std::list<CBotFunction*>& localFunctionList, long
}
////////////////////////////////////////////////////////////////////////////////
bool CBotFunction::RestoreCall(const std::list<CBotFunction*>& localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis,
bool CBotFunction::RestoreCall(long &nIdent, const std::string &name, CBotVar* pThis,
CBotVar** ppVars, CBotStack* pStack, CBotClass* pClass)
{
CBotTypResult type;
CBotFunction* pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type);
CBotFunction* pt = FindMethod(nIdent, name, ppVars, type, pClass, pStack->GetProgram());
if ( pt != nullptr )
{
@ -1020,6 +1152,12 @@ std::string CBotFunction::GetParams()
return params;
}
////////////////////////////////////////////////////////////////////////////////
const std::string& CBotFunction::GetClassName()
{
return m_MasterClass;
}
////////////////////////////////////////////////////////////////////////////////
void CBotFunction::AddPublic(CBotFunction* func)
{

View File

@ -105,21 +105,19 @@ public:
*
* See FindLocalOrPublic for more detailed explanation
*
* \param localFunctionList Linked list of local functions to search in, can be null
* \param name Name of the function
* \param ppVars List of function arguments
* \param nIdent[in, out] Unique identifier of the function
* \param program The current program, to search for functions.
* \return Type returned by the function or error code
* \see FindLocalOrPublic
*/
static CBotTypResult CompileCall(const std::list<CBotFunction*>& localFunctionList,
const std::string &name, CBotVar** ppVars, long &nIdent);
static CBotTypResult CompileCall(const std::string &name, CBotVar** ppVars,
long &nIdent, CBotProgram* program);
/*!
* \brief Finds a local or public function
*
* <p>Finds a local or (if bPublic is true) public function to call
*
* <p>First, it looks for a function according to its unique identifier.<br>
* If the identifier is not found, looks by name and parameters.
*
@ -128,11 +126,46 @@ public:
* \param name Name of the function
* \param ppVars List of function arguments
* \param TypeOrError Type returned by the function or error code
* \param bPublic Whether to look in public functions or not
* \param baseProg Initial program, for context of the object/bot
* \return Pointer to found CBotFunction instance, or nullptr in case of no match or ambiguity (see TypeOrError for error code)
*/
static CBotFunction* FindLocalOrPublic(const std::list<CBotFunction*>& localFunctionList, long &nIdent, const std::string &name,
CBotVar** ppVars, CBotTypResult &TypeOrError, bool bPublic = true);
CBotVar** ppVars, CBotTypResult &TypeOrError, CBotProgram* baseProg);
/*!
* \brief Find all functions that match the name and arguments.
* \param functionList List of functions to search, can be empty.
* \param name Name of the function to find.
* \param ppVars Arguments to compare with parameters.
* \param TypeOrError Contains a CBotError when no useable function has been found.
* \param funcMap Container for suitable functions and their signature values.
* \param pClass Pointer to class when searching for methods.
*/
static void SearchList(const std::list<CBotFunction*>& functionList,
const std::string& name, CBotVar** ppVars, CBotTypResult& TypeOrError,
std::map<CBotFunction*, int>& funcMap, CBotClass* pClass = nullptr);
/*!
* \brief Find all public functions that match the name and arguments.
* \param name Name of the function to find.
* \param ppVars Arguments to compare with parameters.
* \param TypeOrError Contains a CBotError when no useable function has been found.
* \param funcMap Container for suitable functions and their signature values.
* \param pClass Pointer to class when searching for methods.
*/
static void SearchPublic(const std::string& name, CBotVar** ppVars, CBotTypResult& TypeOrError,
std::map<CBotFunction*, int>& funcMap, CBotClass* pClass = nullptr);
/*!
* \brief Find the function with the lowest signature value. If there is more
* than one of the same signature value, TypeOrError is set to CBotErrAmbiguousCall.
* \param funcMap List of functions and their signature values, can be empty.
* \param[out] nIdent Unique identifier of the function.
* \param TypeOrError Type returned by the function or error code.
* \return Pointer to the function with the lowest signature or nullptr.
*/
static CBotFunction* BestFunction(std::map<CBotFunction*, int>& funcMap,
long& nIdent, CBotTypResult& TypeOrError);
/*!
* \brief DoCall Fait un appel à une fonction.
@ -160,10 +193,34 @@ public:
static void RestoreCall(const std::list<CBotFunction*>& localFunctionList,
long &nIdent, const std::string &name, CBotVar** ppVars, CBotStack* pStack);
/*!
* \brief Find a method matching the name and arguments.
* \param name Name of the method to find.
* \param ppVars Arguments to compare with parameters.
* \param[out] nIdent Unique identifier of the method.
* \param pStack Current compilation stack frame.
* \param pClass Pointer to the class.
* \return The return type for the method or a CBotError.
*/
static CBotTypResult CompileMethodCall(const std::string& name, CBotVar** ppVars,
long& nIdent, CBotCStack* pStack, CBotClass* pClass);
/*!
* \brief Find a method by its unique identifier or by name and parameters.
* \param[in,out] nIdent Unique identifier of the method.
* \param name Name of the method to find.
* \param ppVars Arguments to compare with parameters.
* \param TypeOrError The return type for the method or a CBotError.
* \param pClass Pointer to the class.
* \param program The current program, to search for out-of-class methods.
* \return Pointer to the method that best matches the given arguments or nullptr.
*/
static CBotFunction* FindMethod(long& nIdent, const std::string& name,
CBotVar** ppVars, CBotTypResult& TypeOrError,
CBotClass* pClass, CBotProgram* program);
/*!
* \brief DoCall Makes call of a method
* note: this is already on the stack, the pointer pThis is just to simplify.
* \param localFunctionList
* \param nIdent
* \param name
* \param pThis
@ -173,12 +230,11 @@ public:
* \param pClass
* \return
*/
static int DoCall(const std::list<CBotFunction*>& localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis,
static int DoCall(long &nIdent, const std::string &name, CBotVar* pThis,
CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken, CBotClass* pClass);
/*!
* \brief RestoreCall
* \param localFunctionList
* \param nIdent
* \param name
* \param pThis
@ -187,7 +243,7 @@ public:
* \param pClass
* \return Returns true if the method call was restored.
*/
static bool RestoreCall(const std::list<CBotFunction*>& localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis,
static bool RestoreCall(long &nIdent, const std::string &name, CBotVar* pThis,
CBotVar** ppVars, CBotStack* pStack, CBotClass* pClass);
/*!
@ -215,6 +271,12 @@ public:
*/
std::string GetParams();
/*!
* \brief Get the name of the class for a method.
* \return The name of a class or empty string if it's not a method.
*/
const std::string& GetClassName();
/*!
* \brief IsPublic
* \return

View File

@ -2768,3 +2768,303 @@ TEST_F(CBotUT, ParametersWithDefaultValues)
"}\n"
);
}
TEST_F(CBotUT, ClassMethodsOutOfClass_Issue207)
{
auto publicProgram = ExecuteTest(
"public class OtherClass {}\n"
"public class TestClass {}\n"
);
ExecuteTest(
"extern void TestCallWithoutObject()\n"
"{\n"
" TestMethod();\n"
"}\n"
"public void TestClass::TestMethod() {}\n",
CBotErrUndefCall
);
ExecuteTest(
"extern void TestCallWithWrongObject()\n"
"{\n"
" OtherClass oc();\n"
" oc.TestMethod();\n"
"}\n"
"public void TestClass::TestMethod() {}\n",
CBotErrUndefCall
);
ExecuteTest(
"extern void OtherClass::TestCallWithWrongThis()\n"
"{\n"
" this.TestMethod();\n"
"}\n"
"public void TestClass::TestMethod() {}\n",
CBotErrUndefCall
);
}
TEST_F(CBotUT, ClassMethodsOutOfClass)
{
auto publicProgram = ExecuteTest(
"public class TestClass\n"
"{\n"
" int i = 123, j = 456, k = 789;\n"
" int InsideClass()\n"
" {\n"
" ASSERT(456 == PublicMethod());\n"
" ASSERT(789 == NotPublicMethod());\n"
" return this.i;\n"
" }\n"
"}\n"
"extern void TestMethodsOutOfClass()\n"
"{\n"
" TestClass tc();\n"
" ASSERT(123 == tc.InsideClass());\n"
" ASSERT(456 == tc.PublicMethod());\n"
" ASSERT(789 == tc.NotPublicMethod());\n"
"}\n"
"public int TestClass::PublicMethod()\n"
"{\n"
" return this.j;\n"
"}\n"
"int TestClass::NotPublicMethod()\n"
"{\n"
" return k;\n"
"}\n"
);
ExecuteTest(
"extern void TestFromOtherProgram()\n"
"{\n"
" TestClass tc();\n"
" ASSERT(123 == tc.InsideClass());\n"
" ASSERT(456 == tc.PublicMethod());\n"
" ASSERT(789 == tc.MethodInThisProgram());\n"
"}\n"
"int TestClass::MethodInThisProgram()\n"
"{\n"
" ASSERT(123 == InsideClass());\n"
" ASSERT(456 == PublicMethod());\n"
" ASSERT(123 == this.InsideClass());\n"
" ASSERT(456 == this.PublicMethod());\n"
" ASSERT(i == 123 && this.j == 456);\n"
" return this.k;\n"
"}\n"
);
}
TEST_F(CBotUT, ClassInheritanceMethodsOutOfClass)
{
ExecuteTest(
"public class BaseClass {\n"
" int a = 123;\n"
" int b = 456;\n"
" int c = 789;\n"
"}\n"
"int BaseClass::testOverride() { return 123; }\n"
"int BaseClass::testNoOverride() { return 456; }\n"
"int BaseClass::testInsideBaseClass() {\n"
" ASSERT(a == 123);\n"
" ASSERT(b == 456);\n"
" ASSERT(c == 789);\n"
" ASSERT(456 == testNoOverride());\n"
" return c;\n"
"}\n"
"int BaseClass::testInsideBaseOverride() { return testOverride(); }\n"
"\n"
"public class MidClass extends BaseClass {\n"
" int b = 1011;\n"
" int c = 1213;\n"
" int d = 1415;\n"
"}\n"
"int MidClass::testOverride() { return 1011; }\n"
"int MidClass::testInsideMidClass() {\n"
" ASSERT(a == 123);\n"
" ASSERT(b == 1011);\n"
" ASSERT(c == 1213);\n"
" ASSERT(d == 1415);\n"
" ASSERT(456 == testNoOverride());\n"
" ASSERT(789 == testInsideBaseClass());\n"
" return c;\n"
"}\n"
"int MidClass::testSuper() {\n"
" ASSERT(super.a == 123);\n"
" ASSERT(super.b == 456);\n"
" ASSERT(super.c == 789);\n"
" ASSERT(123 == super.testOverride());\n"
" ASSERT(789 == super.testInsideBaseClass());\n"
" return super.testInsideBaseOverride();\n"
"}\n"
"int MidClass::testInsideMidOverride() { return testOverride(); }\n"
"\n"
"public class SubClass extends MidClass {\n"
" int c = 1617;\n"
" int d = 1819;\n"
" int e = 2021;\n"
"}\n"
"int SubClass::testOverride() { return 1617; }\n"
"int SubClass::testInsideSubClass() {\n"
" ASSERT(a == 123);\n"
" ASSERT(b == 1011);\n"
" ASSERT(c == 1617);\n"
" ASSERT(d == 1819);\n"
" ASSERT(e == 2021);\n"
" ASSERT(456 == testNoOverride());\n"
" ASSERT(789 == testInsideBaseClass());\n"
" ASSERT(1213 == testInsideMidClass());\n"
" return c;\n"
"}\n"
"int SubClass::testSuper() {\n"
" ASSERT(super.a == 123);\n"
" ASSERT(super.b == 1011);\n"
" ASSERT(super.c == 1213);\n"
" ASSERT(super.d == 1415);\n"
" ASSERT(1011 == super.testOverride());\n"
" ASSERT(789 == super.testInsideBaseClass());\n"
" ASSERT(1213 == super.testInsideMidClass());\n"
" return super.testSuper();\n"
"}\n"
"int SubClass::testInsideSubOverride() { return testOverride(); }\n"
"\n"
"extern void InheritanceMethodsOutOfClass()\n"
"{\n"
" BaseClass bc();\n"
" ASSERT(123 == bc.testOverride());\n"
" ASSERT(456 == bc.testNoOverride());\n"
" ASSERT(789 == bc.testInsideBaseClass());\n"
" ASSERT(123 == bc.testInsideBaseOverride());\n"
" MidClass mc();\n"
" ASSERT(1011 == mc.testSuper());\n"
" ASSERT(1011 == mc.testOverride());\n"
" ASSERT(456 == mc.testNoOverride());\n"
" ASSERT(789 == mc.testInsideBaseClass());\n"
" ASSERT(1213 == mc.testInsideMidClass());\n"
" ASSERT(1011 == mc.testInsideBaseOverride());\n"
" ASSERT(1011 == mc.testInsideMidOverride());\n"
" SubClass sc();\n"
" ASSERT(1617 == sc.testSuper());\n"
" ASSERT(1617 == sc.testOverride());\n"
" ASSERT(456 == sc.testNoOverride());\n"
" ASSERT(789 == sc.testInsideBaseClass());\n"
" ASSERT(1213 == sc.testInsideMidClass());\n"
" ASSERT(1617 == sc.testInsideSubClass());\n"
" ASSERT(1617 == sc.testInsideBaseOverride());\n"
" ASSERT(1617 == sc.testInsideMidOverride());\n"
" ASSERT(1617 == sc.testInsideSubOverride());\n"
// Test polymorphism
" bc = mc;\n"
" ASSERT(1011 == bc.testOverride());\n"
" ASSERT(789 == bc.testInsideBaseClass());\n"
" ASSERT(1011 == bc.testInsideBaseOverride());\n"
" bc = sc;\n"
" ASSERT(1617 == bc.testOverride());\n"
" ASSERT(789 == bc.testInsideBaseClass());\n"
" ASSERT(1617 == bc.testInsideBaseOverride());\n"
" mc = sc;\n"
" ASSERT(1617 == mc.testSuper());\n"
" ASSERT(1617 == mc.testOverride());\n"
" ASSERT(789 == mc.testInsideBaseClass());\n"
" ASSERT(1213 == mc.testInsideMidClass());\n"
" ASSERT(1617 == mc.testInsideBaseOverride());\n"
" ASSERT(1617 == mc.testInsideMidOverride());\n"
"}\n"
);
}
TEST_F(CBotUT, ClassInheritanceTestThisOutOfClass)
{
ExecuteTest(
"public class BaseClass {\n"
" int a = 123;\n"
" int b = 456;\n"
" int c = 789;\n"
"}\n"
"void BaseClass::testBaseMembersAndParams(int a, int b, int c) {\n"
" ASSERT(a != 123);\n"
" ASSERT(b != 456);\n"
" ASSERT(c != 789);\n"
" ASSERT(this.a == 123);\n"
" ASSERT(this.b == 456);\n"
" ASSERT(this.c == 789);\n"
"}\n"
"BaseClass BaseClass::testSuperReturnThis(){ return this; }\n"
"BaseClass BaseClass::testReturnThisFromBaseClass() { return this; }\n"
"\n"
"public class MidClass extends BaseClass {\n"
" int b = 1011;\n"
" int c = 1213;\n"
" int d = 1415;\n"
"}\n"
"void MidClass::testMidMembersAndParams(int a, int b, int c, int d) {\n"
" ASSERT(a != 123);\n"
" ASSERT(b != 1011);\n"
" ASSERT(c != 1213);\n"
" ASSERT(d != 1415);\n"
" ASSERT(this.a == 123);\n"
" ASSERT(this.b == 1011);\n"
" ASSERT(this.c == 1213);\n"
" ASSERT(this.d == 1415);\n"
"}\n"
"MidClass MidClass::testSuperReturnThis(){ return super.testSuperReturnThis(); }\n"
"MidClass MidClass::testReturnThisFromMidClass() { return this; }\n"
"\n"
"public class SubClass extends MidClass {\n"
" int c = 1617;\n"
" int d = 1819;\n"
" int e = 2021;\n"
"}\n"
"void SubClass::testSubMembersAndParams(int a, int b, int c, int d, int e) {\n"
" ASSERT(a != 123);\n"
" ASSERT(b != 1011);\n"
" ASSERT(c != 1617);\n"
" ASSERT(d != 1819);\n"
" ASSERT(e != 2021);\n"
" ASSERT(this.a == 123);\n"
" ASSERT(this.b == 1011);\n"
" ASSERT(this.c == 1617);\n"
" ASSERT(this.d == 1819);\n"
" ASSERT(this.e == 2021);\n"
"}\n"
"SubClass SubClass::testSuperReturnThis(){ return super.testSuperReturnThis(); }\n"
"SubClass SubClass::testReturnThisFromSubClass() { return this; }\n"
"\n"
"extern void ClassInheritanceTestThisOutOfClass()\n"
"{\n"
" BaseClass bc();\n"
" MidClass mc();\n"
" SubClass sc();\n"
" ASSERT(bc == bc.testSuperReturnThis());\n"
" ASSERT(bc == bc.testReturnThisFromBaseClass());\n"
" bc.testBaseMembersAndParams(-1, -2, -3);\n"
" ASSERT(mc == mc.testSuperReturnThis());\n"
" ASSERT(mc == mc.testReturnThisFromBaseClass());\n"
" ASSERT(mc == mc.testReturnThisFromMidClass());\n"
" mc.testBaseMembersAndParams(-1, -2, -3);\n"
" mc.testMidMembersAndParams(-1, -2, -3, -4);\n"
" ASSERT(sc == sc.testSuperReturnThis());\n"
" ASSERT(sc == sc.testReturnThisFromBaseClass());\n"
" ASSERT(sc == sc.testReturnThisFromMidClass());\n"
" ASSERT(sc == sc.testReturnThisFromSubClass());\n"
" sc.testBaseMembersAndParams(-1, -2, -3);\n"
" sc.testMidMembersAndParams(-1, -2, -3, -4);\n"
" sc.testSubMembersAndParams(-1, -2, -3, -4, -5);\n"
// Test polymorphism
" bc = mc;\n"
" ASSERT(mc == bc.testSuperReturnThis());\n"
" ASSERT(mc == bc.testReturnThisFromBaseClass());\n"
" bc.testBaseMembersAndParams(-1, -2, -3);\n"
" bc = sc;\n"
" ASSERT(sc == bc.testSuperReturnThis());\n"
" ASSERT(sc == bc.testReturnThisFromBaseClass());\n"
" bc.testBaseMembersAndParams(-1, -2, -3);\n"
" mc = sc;\n"
" ASSERT(sc == mc.testSuperReturnThis());\n"
" ASSERT(sc == mc.testReturnThisFromBaseClass());\n"
" ASSERT(sc == mc.testReturnThisFromMidClass());\n"
" mc.testBaseMembersAndParams(-1, -2, -3);\n"
" mc.testMidMembersAndParams(-1, -2, -3, -4);\n"
"}\n"
);
}