From fad38cd0e9313c6058f5ec7f7027f4904fdccbcc Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 4 Aug 2016 03:16:59 -0400 Subject: [PATCH 1/4] Add accessing members to function calls --- src/CBot/CBotInstr/CBotExprRetVar.cpp | 183 ++++++++++++++++++++++++ src/CBot/CBotInstr/CBotExprRetVar.h | 45 ++++++ src/CBot/CBotInstr/CBotIndexExpr.h | 1 + src/CBot/CBotInstr/CBotInstrCall.cpp | 93 ++++++------ src/CBot/CBotInstr/CBotInstrCall.h | 4 + src/CBot/CBotInstr/CBotInstrMethode.cpp | 43 +++++- src/CBot/CBotInstr/CBotInstrMethode.h | 4 + src/CBot/CMakeLists.txt | 2 + 8 files changed, 330 insertions(+), 45 deletions(-) create mode 100644 src/CBot/CBotInstr/CBotExprRetVar.cpp create mode 100644 src/CBot/CBotInstr/CBotExprRetVar.h diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp new file mode 100644 index 00000000..28b0fa83 --- /dev/null +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -0,0 +1,183 @@ + +#include +#include "CBot/CBotInstr/CBotExprRetVar.h" + +#include "CBot/CBotInstr/CBotExpression.h" +#include "CBot/CBotInstr/CBotInstrMethode.h" +#include "CBot/CBotInstr/CBotIndexExpr.h" +#include "CBot/CBotInstr/CBotFieldExpr.h" + +#include "CBot/CBotStack.h" + +namespace CBot +{ + +//////////////////////////////////////////////////////////////////////////////// +CBotExprRetVar::CBotExprRetVar() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +CBotExprRetVar::~CBotExprRetVar() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack) +{ + if (p->GetType() == ID_DOT) + { + CBotVar* var = pStack->GetVar(); + + if (var == nullptr) + { + pStack->SetError(CBotErrNoTerminator, p->GetStart()); + return nullptr; + } + + CBotCStack* pStk = pStack->TokenStack(); + CBotInstr* inst = new CBotExprRetVar(); + + while (true) + { + pStk->SetStartError(p->GetStart()); + if (var->GetType() == CBotTypArrayPointer) + { + if (IsOfType( p, ID_OPBRK )) + { + CBotIndexExpr* i = new CBotIndexExpr(); + i->m_expr = CBotExpression::Compile(p, pStk); + inst->AddNext3(i); + + var = var->GetItem(0,true); + + if (i->m_expr == nullptr || pStk->GetType() != CBotTypInt) + { + pStk->SetError(CBotErrBadIndex, p->GetStart()); + goto err; + } + if (!pStk->IsOk() || !IsOfType( p, ID_CLBRK )) + { + pStk->SetError(CBotErrCloseIndex, p->GetStart()); + goto err; + } + continue; + } + } + if (var->GetType(CBotVar::GetTypeMode::CLASS_AS_POINTER) == CBotTypPointer) + { + if (IsOfType(p, ID_DOT)) + { + CBotToken* pp = p; + + if (p->GetType() == TokenTypVar) + { + if (p->GetNext()->GetType() == ID_OPENPAR) + { + CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var); + if (!pStk->IsOk()) goto err; + inst->AddNext3(i); + return pStack->Return(inst, pStk); + } + else + { + CBotFieldExpr* i = new CBotFieldExpr(); + i->SetToken(pp); + inst->AddNext3(i); + var = var->GetItem(p->GetString()); + if (var != nullptr) + { + i->SetUniqNum(var->GetUniqNum()); + if ( var->IsPrivate() && + !pStk->GetProgram()->m_bCompileClass) + { + pStk->SetError(CBotErrPrivate, pp); + goto err; + } + } + } + + if (var != nullptr) + { + p = p->GetNext(); + continue; + } + pStk->SetError(CBotErrUndefItem, p); + goto err; + } + pStk->SetError(CBotErrUndefClass, p); + goto err; + } + } + break; + } + + pStk->SetCopyVar(var); + if (pStk->IsOk()) return pStack->Return(inst, pStk); + + pStk->SetError(CBotErrUndefVar, p); +err: + delete inst; + return pStack->Return(nullptr, pStk); + } + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +bool CBotExprRetVar::Execute(CBotStack* &pj) +{ + + CBotStack* pile = pj->AddStack(); + CBotStack* pile1 = pile; + CBotVar* pVar; + + if (pile1->GetState() == 0) + { + pVar = pj->GetVar(); + pVar->Update(pj->GetUserPtr()); + + if ( !m_next3->ExecuteVar(pVar, pile, &m_token, true, false) ) + return false; + + if (pVar) + pile1->SetCopyVar(pVar); + else + return pj->Return(pile1); + + pile1->IncState(); + } + pVar = pile1->GetVar(); + + if (pVar == nullptr) + { + return pj->Return(pile1); + } + + if (pVar->IsUndefined()) + { + pile1->SetError(CBotErrNotInit, &m_token); + return pj->Return(pile1); + } + return pj->Return(pile1); +} + +//////////////////////////////////////////////////////////////////////////////// +void CBotExprRetVar::RestoreState(CBotStack* &pj, bool bMain) +{ + if (!bMain) return; + + CBotStack* pile = pj->RestoreStack(); + if ( pile == nullptr ) return; + + if (pile->GetState() == 0) + m_next3->RestoreStateVar(pile, bMain); +} + +std::string CBotExprRetVar::GetDebugData() +{ + std::stringstream ss; + ss << m_token.GetString() << "func(...).something" << std::endl; + return ss.str(); +} + +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotExprRetVar.h b/src/CBot/CBotInstr/CBotExprRetVar.h new file mode 100644 index 00000000..79c72ac6 --- /dev/null +++ b/src/CBot/CBotInstr/CBotExprRetVar.h @@ -0,0 +1,45 @@ + +#pragma once + +#include "CBot/CBotInstr/CBotInstr.h" + +namespace CBot +{ + +/** + * \brief Access a member/element of the variable on the stack + * + * + * + */ +class CBotExprRetVar : public CBotInstr +{ +public: + CBotExprRetVar(); + ~CBotExprRetVar(); + + static CBotInstr* Compile(CBotToken*& p, CBotCStack* pStack); + + /*! + * \brief Execute + * \param pj + * \return + */ + bool Execute(CBotStack* &pj) override; + + /*! + * \brief RestoreState + * \param pj + * \param bMain + */ + void RestoreState(CBotStack* &pj, bool bMain) override; + +protected: + virtual const std::string GetDebugName() override { return "CBotExprRetVar"; } + virtual std::string GetDebugData() override; + +private: + +}; + +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotIndexExpr.h b/src/CBot/CBotInstr/CBotIndexExpr.h index 15fe2d43..8ac6ed5c 100644 --- a/src/CBot/CBotInstr/CBotIndexExpr.h +++ b/src/CBot/CBotInstr/CBotIndexExpr.h @@ -69,6 +69,7 @@ private: CBotInstr* m_expr; friend class CBotLeftExpr; friend class CBotExprVar; + friend class CBotExprRetVar; }; } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotInstrCall.cpp b/src/CBot/CBotInstr/CBotInstrCall.cpp index 111ec81c..6b45bb52 100644 --- a/src/CBot/CBotInstr/CBotInstrCall.cpp +++ b/src/CBot/CBotInstr/CBotInstrCall.cpp @@ -18,7 +18,8 @@ */ #include "CBot/CBotInstr/CBotInstrCall.h" -#include "CBot/CBotInstr/CBotExpression.h" +#include "CBot/CBotInstr/CBotExprRetVar.h" +#include "CBot/CBotInstr/CBotInstrUtils.h" #include "CBot/CBotStack.h" @@ -47,62 +48,26 @@ CBotInstrCall::~CBotInstrCall() //////////////////////////////////////////////////////////////////////////////// CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack) { - CBotVar* ppVars[1000]; - - int i = 0; CBotToken* pp = p; p = p->GetNext(); - pStack->SetStartError(p->GetStart()); - CBotCStack* pile = pStack; - - if ( IsOfType(p, ID_OPENPAR) ) + if (p->GetType() == ID_OPENPAR) { - int start, end; + + CBotVar* ppVars[1000]; + CBotInstrCall* inst = new CBotInstrCall(); inst->SetToken(pp); // compile la list of parameters - if (!IsOfType(p, ID_CLOSEPAR)) while (true) + inst->m_parameters = CompileParams(p, pStack, ppVars); + + if ( !pStack->IsOk() ) { - start = p->GetStart(); - pile = pile->TokenStack(); // keeps the results on the stack - - CBotInstr* param = CBotExpression::Compile(p, pile); - end = p->GetStart(); - if (inst->m_parameters == nullptr ) inst->m_parameters = param; - else inst->m_parameters->AddNext(param); // constructs the list - - if ( !pile->IsOk() ) - { - delete inst; - return pStack->Return(nullptr, pile); - } - - if ( param != nullptr ) - { - if ( pile->GetTypResult().Eq(99) ) - { - delete pStack->TokenStack(); - pStack->SetError(CBotErrVoid, p->GetStart()); - delete inst; - return nullptr; - } - ppVars[i] = pile->GetVar(); - ppVars[i]->GetToken()->SetPos(start, end); - i++; - - if (IsOfType(p, ID_COMMA)) continue; // skips the comma - if (IsOfType(p, ID_CLOSEPAR)) break; - } - - pStack->SetError(CBotErrClosePar, p->GetStart()); - delete pStack->TokenStack(); delete inst; return nullptr; } - ppVars[i] = nullptr; // the routine is known? // CBotClass* pClass = nullptr; @@ -124,6 +89,17 @@ CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack) } else pStack->SetVar(nullptr); // routine returns void + if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack))) + { + inst->m_exprRetVar->SetToken(&inst->m_token); + delete pStack->TokenStack(); + } + if ( !pStack->IsOk() ) + { + delete inst; + return nullptr; + } + return inst; } p = pp; @@ -138,6 +114,17 @@ bool CBotInstrCall::Execute(CBotStack* &pj) CBotStack* pile = pj->AddStack(this); if ( pile->StackOver() ) return pj->Return( pile ); + CBotStack* pile3 = nullptr; + if (m_exprRetVar != nullptr) // func().member + { + pile3 = pile->AddStack2(); + if (pile3->GetState() == 1) // function call is done? + { + if (!m_exprRetVar->Execute(pile3)) return false; + return pj->Return(pile3); + } + } + // CBotStack* pile1 = pile; int i = 0; @@ -165,6 +152,14 @@ bool CBotInstrCall::Execute(CBotStack* &pj) if ( !pile2->ExecuteCall(m_nFuncIdent, GetToken(), ppVars, m_typRes)) return false; // interrupt + if (m_exprRetVar != nullptr) // func().member + { + pile3->SetCopyVar( pile2->GetVar() ); // copy the result + pile2->SetVar(nullptr); + pile3->SetState(1); // set call is done + return false; // go back to the top ^^^ + } + return pj->Return(pile2); // release the entire stack } @@ -176,6 +171,16 @@ void CBotInstrCall::RestoreState(CBotStack* &pj, bool bMain) CBotStack* pile = pj->RestoreStack(this); if ( pile == nullptr ) return; + if (m_exprRetVar != nullptr) // func().member + { + CBotStack* pile3 = pile->AddStack2(); + if (pile3->GetState() == 1) // function call is done? + { + m_exprRetVar->RestoreState(pile3, bMain); + return; + } + } + // CBotStack* pile1 = pile; int i = 0; diff --git a/src/CBot/CBotInstr/CBotInstrCall.h b/src/CBot/CBotInstr/CBotInstrCall.h index 7e8aba09..e39534f6 100644 --- a/src/CBot/CBotInstr/CBotInstrCall.h +++ b/src/CBot/CBotInstr/CBotInstrCall.h @@ -69,6 +69,10 @@ private: CBotTypResult m_typRes; //! Id of a function. long m_nFuncIdent; + + //! Instruction to return a member of the returned object. + CBotInstr* m_exprRetVar; + friend class CBotDebug; }; diff --git a/src/CBot/CBotInstr/CBotInstrMethode.cpp b/src/CBot/CBotInstr/CBotInstrMethode.cpp index 3bdf8492..78ed6b47 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.cpp +++ b/src/CBot/CBotInstr/CBotInstrMethode.cpp @@ -20,6 +20,7 @@ #include #include "CBot/CBotInstr/CBotInstrMethode.h" +#include "CBot/CBotInstr/CBotExprRetVar.h" #include "CBot/CBotInstr/CBotInstrUtils.h" #include "CBot/CBotStack.h" @@ -86,7 +87,16 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* } pStack->SetVar(pResult); } - return inst; + else pStack->SetVar(nullptr); + + if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack))) + { + inst->m_exprRetVar->SetToken(&inst->m_token); + delete pStack->TokenStack(); + } + + if ( pStack->IsOk() ) + return inst; } delete inst; return nullptr; @@ -106,6 +116,18 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre return pj->Return(pile1); } + CBotStack* pile3 = nullptr; + if (m_exprRetVar != nullptr) // .func().member + { + pile3 = pile1->AddStack2(); + if (pile3->GetState() == 1) + { + if (!m_exprRetVar->Execute(pile3)) return false; + pVar = nullptr; + return pj->Return(pile3); + } + } + if (pile1->IfStep()) return false; CBotStack* pile2 = pile1->AddStack(); // for the next parameters @@ -159,6 +181,15 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre pResult, pile2, GetToken())) return false; if (pRes != pResult) delete pRes; + if (m_exprRetVar != nullptr) // .func().member + { + pile3->SetCopyVar( pile2->GetVar() ); + pile2->SetVar(nullptr); + pile3->SetState(1); // set call is done + pVar = nullptr; + return false; // go back to the top ^^^ + } + pVar = nullptr; // does not return value for this return pj->Return(pile2); // release the entire stack } @@ -172,6 +203,16 @@ void CBotInstrMethode::RestoreStateVar(CBotStack* &pile, bool bMain) CBotStack* pile1 = pile->RestoreStack(this); // place for the copy of This if (pile1 == nullptr) return; + if (m_exprRetVar != nullptr) // .func().member + { + CBotStack* pile3 = pile1->AddStack2(); + if (pile3->GetState() == 1) // function call is done? + { + m_exprRetVar->RestoreState(pile3, bMain); + return; + } + } + CBotStack* pile2 = pile1->RestoreStack(); // and for the parameters coming if (pile2 == nullptr) return; diff --git a/src/CBot/CBotInstr/CBotInstrMethode.h b/src/CBot/CBotInstr/CBotInstrMethode.h index 6c8d1731..fb2b2058 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.h +++ b/src/CBot/CBotInstr/CBotInstrMethode.h @@ -83,6 +83,10 @@ private: long m_MethodeIdent; //! Name of the class. std::string m_className; + + //! Instruction to return a member of the returned object. + CBotInstr* m_exprRetVar; + }; } // namespace CBot diff --git a/src/CBot/CMakeLists.txt b/src/CBot/CMakeLists.txt index b77d0bbc..6ac4d130 100644 --- a/src/CBot/CMakeLists.txt +++ b/src/CBot/CMakeLists.txt @@ -54,6 +54,8 @@ set(SOURCES CBotInstr/CBotExprLitNum.h CBotInstr/CBotExprLitString.cpp CBotInstr/CBotExprLitString.h + CBotInstr/CBotExprRetVar.cpp + CBotInstr/CBotExprRetVar.h CBotInstr/CBotExprUnaire.cpp CBotInstr/CBotExprUnaire.h CBotInstr/CBotExprVar.cpp From e48188b42931b34dbc753b47aca3b5f198e9eac6 Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 6 Aug 2016 04:56:57 -0400 Subject: [PATCH 2/4] Fix failed assert when literal null is returned --- src/CBot/CBotInstr/CBotExprRetVar.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp index 28b0fa83..223058eb 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.cpp +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -135,9 +135,14 @@ bool CBotExprRetVar::Execute(CBotStack* &pj) { pVar = pj->GetVar(); pVar->Update(pj->GetUserPtr()); + if (pVar->GetType(CBotVar::GetTypeMode::CLASS_AS_POINTER) == CBotTypNullPointer) + { + pile1->SetError(CBotErrNull, &m_token); + return pj->Return(pile1); + } if ( !m_next3->ExecuteVar(pVar, pile, &m_token, true, false) ) - return false; + return false; if (pVar) pile1->SetCopyVar(pVar); From f3bf56d9e3da0f27a05839bde7e99e1f1e5d650e Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 6 Aug 2016 05:22:40 -0400 Subject: [PATCH 3/4] Add unit tests for returned object member access --- test/unit/CBot/CBot_test.cpp | 251 +++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 2d96d59e..a9af2420 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1188,3 +1188,254 @@ TEST_F(CBotUT, AccessMembersInParameters_Issue256) "}\n" ); } + +TEST_F(CBotUT, InstrCallAccessMemberVoid) +{ + ExecuteTest( + "void Test() {}\n" + "extern void TestAccessMemberVoid() {\n" + " Test().x;\n" + "}\n", + CBotErrNoTerminator + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberNonObject) +{ + ExecuteTest( + "int GetInt() {\n" + " return 1;\n" + "}\n" + "extern void TestAccessMemberNonObject() {\n" + " GetInt().x;\n" + "}\n", + CBotErrNoTerminator + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberObjectNull) +{ + ExecuteTest( + "public class TestClass { int x = 1; }\n" + "TestClass GetObjectNull() {\n" + " TestClass t = null;" + " return t;\n" + "}\n" + "extern void TestAccessMemberObjectNull() {\n" + " GetObjectNull().x;\n" + "}\n", + CBotErrNull + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberReturnNull) +{ + ExecuteTest( + "public class TestClass { int x = 1; }\n" + "TestClass GetReturnNull() {\n" + " return null;\n" + "}\n" + "extern void TestAccessMemberReturnNull() {\n" + " GetReturnNull().x;\n" + "}\n", + CBotErrNull + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberNotVar) +{ + ExecuteTest( + "public class TestClass {}\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberNotVar() {\n" + " TestClass tc();\n" + " GetObject(tc).123;\n" + "}\n", + CBotErrUndefClass + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarNonMember) +{ + ExecuteTest( + "public class TestClass { int x = 1; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarNonMember() {\n" + " TestClass tc();\n" + " GetObject(tc).y;\n" + "}\n", + CBotErrUndefItem + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarUndefined) +{ + ExecuteTest( + "public class TestClass { int x; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarUndefined() {\n" + " TestClass tc();\n" + " GetObject(tc).x;\n" + "}\n", + CBotErrNotInit + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarPrivate) +{ + ExecuteTest( + "public class TestClass { private int x = 123; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarPrivate() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).x);\n" + "}\n", + CBotErrPrivate + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVar) +{ + ExecuteTest( + "public class TestClass { int x = 123; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVar() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).x);\n" + "}\n" + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarArrayBadIndex) +{ + ExecuteTest( + "public class TestClass { int[] a; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArrayEmpty() {\n" + " TestClass tc();\n" + " int i = GetObject(tc).a[4.7];\n" + "}\n", + CBotErrBadIndex + ); +} +TEST_F(CBotUT, InstrCallAccessMemberVarArrayCloseIndex) +{ + ExecuteTest( + "public class TestClass { int[] a = {123}; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArrayEmpty() {\n" + " TestClass tc();\n" + " int i = GetObject(tc).a[0;\n" + "}\n", + CBotErrCloseIndex + ); +} +TEST_F(CBotUT, InstrCallAccessMemberVarArrayEmpty) +{ + ExecuteTest( + "public class TestClass { int[] a; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArrayEmpty() {\n" + " TestClass tc();\n" + " int i = GetObject(tc).a[0];\n" + "}\n", + CBotErrOutArray + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarArrayOutOfRange) +{ + ExecuteTest( + "public class TestClass { int a[] = {123}; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArrayOut() {\n" + " TestClass tc();\n" + " int i = GetObject(tc).a[1];\n" + "}\n", + CBotErrOutArray + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarArray) +{ + ExecuteTest( + "public class TestClass { int a[] = {123}; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArray() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).a[0]);\n" + "}\n" + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberMethod) +{ + ExecuteTest( + "public class TestClass {\n" + " int x = 123;\n" + " int testGetX() { return x; }\n" + "}\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberMethod() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).testGetX());\n" + "}\n" + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberMethodChain) +{ + ExecuteTest( + "public class TestClass {\n" + " int x = 123;\n" + " TestClass testGetThis() { return this; }\n" + " int testGetX() { return x; }\n" + "}\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberMethodChain() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).testGetThis().testGetX());\n" + "}\n" + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberNewObjectDestructor) +{ + ExecuteTest( + "public class TestClass {\n" + " int x = 123;\n" + " static bool b = false;\n" + " void ~TestClass() { b = true; }\n" + "}\n" + "TestClass GetNewObject() { return new TestClass(); }\n" + "extern void TestAccessMemberNewObject() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetNewObject().x);\n" + " ASSERT(tc.b == true);\n" + "}\n" + ); +} From a205eace38d234feeb20a3feb75ce62caa03c0c4 Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 6 Aug 2016 16:29:02 -0400 Subject: [PATCH 4/4] Add missing license headers --- src/CBot/CBotInstr/CBotExprRetVar.cpp | 18 ++++++++++++++++++ src/CBot/CBotInstr/CBotExprRetVar.h | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp index 223058eb..9a1eeb72 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.cpp +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -1,3 +1,21 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ #include #include "CBot/CBotInstr/CBotExprRetVar.h" diff --git a/src/CBot/CBotInstr/CBotExprRetVar.h b/src/CBot/CBotInstr/CBotExprRetVar.h index 79c72ac6..a1c37f55 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.h +++ b/src/CBot/CBotInstr/CBotExprRetVar.h @@ -1,3 +1,21 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ #pragma once