From 66218319ddeb7aa5fd3a236c2c1641679e554237 Mon Sep 17 00:00:00 2001 From: melex750 Date: Tue, 9 Aug 2016 11:59:07 -0400 Subject: [PATCH 1/2] Add support for circular references Issue #433 --- src/CBot/CBotClass.cpp | 33 +++++++++++++++++++++++++++++++-- src/CBot/CBotClass.h | 10 ++++++++++ src/CBot/CBotProgram.cpp | 4 ++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index 70c72ef5..45510763 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -504,16 +504,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; } @@ -521,6 +530,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) { diff --git a/src/CBot/CBotClass.h b/src/CBot/CBotClass.h index 234f5786..2fc494dc 100644 --- a/src/CBot/CBotClass.h +++ b/src/CBot/CBotClass.h @@ -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 diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index d0284ea2..77dac399 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -99,6 +99,10 @@ bool CBotProgram::Compile(const std::string& program, std::vector& 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); From 71aa7e468bcca287b1bbe6cb97e242626c2607d7 Mon Sep 17 00:00:00 2001 From: melex750 Date: Tue, 9 Aug 2016 14:55:56 -0400 Subject: [PATCH 2/2] Add unit tests for circular reference --- test/unit/CBot/CBot_test.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 2d96d59e..7158bf06 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1006,6 +1006,41 @@ TEST_F(CBotUT, ClassStringAdd_Issue535) ); } +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(