Allow circular references from different classes (#814)

Conflicts:
	test/unit/CBot/CBot_test.cpp
dev-new-models
krzys-h 2016-08-09 21:10:07 +02:00
commit d411c5ebc0
4 changed files with 80 additions and 2 deletions

View File

@ -509,16 +509,25 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack)
classe->Purge(); // empty the old definitions // TODO: Doesn't this remove all classes of the current program? classe->Purge(); // empty the old definitions // TODO: Doesn't this remove all classes of the current program?
classe->m_IsDef = false; // current definition classe->m_IsDef = false; // current definition
classe->m_pOpenblk = p;
if ( !IsOfType( p, ID_OPBLK) ) if ( !IsOfType( p, ID_OPBLK) )
{ {
pStack->SetError(CBotErrOpenBlock, p); pStack->SetError(CBotErrOpenBlock, p);
return nullptr; return nullptr;
} }
while ( pStack->IsOk() && !IsOfType( p, ID_CLBLK ) ) int level = 1;
do // skip over the definition
{ {
classe->CompileDefItem(p, pStack, false); int type = p->GetType();
p = p->GetNext();
if (type == ID_OPBLK) level++;
if (type == ID_CLBLK) level--;
} }
while (level > 0 && p != nullptr);
if (level > 0) pStack->SetError(CBotErrCloseBlock, classe->m_pOpenblk);
if (pStack->IsOk()) return classe; if (pStack->IsOk()) return classe;
} }
@ -526,6 +535,26 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack)
return nullptr; return nullptr;
} }
////////////////////////////////////////////////////////////////////////////////
void CBotClass::DefineClasses(CBotClass* pClass, CBotCStack* pStack)
{
while (pClass != nullptr)
{
CBotClass* pParent = pClass->m_parent;
pClass->m_nbVar = (pParent == nullptr) ? 0 : pParent->m_nbVar;
CBotToken* p = pClass->m_pOpenblk->GetNext();
while (pStack->IsOk() && !IsOfType(p, ID_CLBLK))
{
pClass->CompileDefItem(p, pStack, false);
}
if (!pStack->IsOk()) return;
pClass = pClass->GetNext();
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
{ {

View File

@ -291,6 +291,14 @@ public:
static CBotClass* Compile1(CBotToken* &p, static CBotClass* Compile1(CBotToken* &p,
CBotCStack* pStack); CBotCStack* pStack);
/*!
* \brief DefineClasses Calls CompileDefItem for each class in a list
* of classes, defining fields and pre-compiling methods.
* \param pClass List of classes
* \param pStack
*/
static void DefineClasses(CBotClass* pClass, CBotCStack* pStack);
/*! /*!
* \brief CompileDefItem * \brief CompileDefItem
* \param p * \param p
@ -385,6 +393,8 @@ private:
CBotFunction* m_pMethod; CBotFunction* m_pMethod;
void (*m_rUpdate)(CBotVar* thisVar, void* user); void (*m_rUpdate)(CBotVar* thisVar, void* user);
CBotToken* m_pOpenblk;
//! How many times the program currently holding the lock called Lock() //! How many times the program currently holding the lock called Lock()
int m_lockCurrentCount = 0; int m_lockCurrentCount = 0;
//! Programs waiting for lock. m_lockProg[0] is the program currently holding the lock, if any //! Programs waiting for lock. m_lockProg[0] is the program currently holding the lock, if any

View File

@ -99,6 +99,10 @@ bool CBotProgram::Compile(const std::string& program, std::vector<std::string>&
else m_functions->AddNext(next); else m_functions->AddNext(next);
} }
} }
// Define fields and pre-compile methods for each class in this program
if (pStack->IsOk()) CBotClass::DefineClasses(m_classes, pStack.get());
if ( !pStack->IsOk() ) if ( !pStack->IsOk() )
{ {
m_error = pStack->GetError(m_errorStart, m_errorEnd); m_error = pStack->GetError(m_errorStart, m_errorEnd);

View File

@ -1283,6 +1283,41 @@ TEST_F(CBotUT, ClassInheritanceTestThis)
); );
} }
TEST_F(CBotUT, ClassCompileCircularReference_Issue433)
{
ExecuteTest(
"public class OtherClass {\n"
" TestClass testclass;\n"
"}\n"
"public class TestClass {\n"
" int test;\n"
" OtherClass otherclass;\n"
"}\n"
"extern void TestCompileCircularReference()\n"
"{\n"
" TestClass t();\n"
"}\n"
);
}
TEST_F(CBotUT, ClassTestClassDefinedAfterReference)
{
ExecuteTest(
"public class OtherClass {\n"
" TestClass testclass = new TestClass();\n"
"}\n"
"public class TestClass {\n"
" int test = 246;\n"
"}\n"
"extern void TestDefinedAfterReference()\n"
"{\n"
" OtherClass o();\n"
" TestClass t = o.testclass;\n"
" ASSERT(t.test == 246);\n"
"}\n"
);
}
TEST_F(CBotUT, String) TEST_F(CBotUT, String)
{ {
ExecuteTest( ExecuteTest(