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->m_IsDef = false; // current definition
classe->m_pOpenblk = p;
if ( !IsOfType( p, ID_OPBLK) )
{
pStack->SetError(CBotErrOpenBlock, p);
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;
}
@ -526,6 +535,26 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack)
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)
{

View File

@ -291,6 +291,14 @@ public:
static CBotClass* Compile1(CBotToken* &p,
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
* \param p
@ -385,6 +393,8 @@ private:
CBotFunction* m_pMethod;
void (*m_rUpdate)(CBotVar* thisVar, void* user);
CBotToken* m_pOpenblk;
//! How many times the program currently holding the lock called Lock()
int m_lockCurrentCount = 0;
//! 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);
}
}
// Define fields and pre-compile methods for each class in this program
if (pStack->IsOk()) CBotClass::DefineClasses(m_classes, pStack.get());
if ( !pStack->IsOk() )
{
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)
{
ExecuteTest(