Fix access to protected and private variables

dev-new-models
melex750 2016-09-17 08:00:34 -04:00
parent 3debfb9182
commit 9ab7f7d140
9 changed files with 222 additions and 19 deletions

View File

@ -682,6 +682,7 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
initType = CBotVar::InitType::DEF; initType = CBotVar::InitType::DEF;
pcopy->SetInit(initType); pcopy->SetInit(initType);
pcopy->SetUniqNum(pv->GetUniqNum()); pcopy->SetUniqNum(pv->GetUniqNum());
pcopy->SetPrivate(pv->GetPrivate());
pile->AddVar(pcopy); pile->AddVar(pcopy);
pv = pv->GetNext(); pv = pv->GetNext();
} }

View File

@ -105,12 +105,12 @@ CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bMeth
CBotFieldExpr* i = new CBotFieldExpr(); CBotFieldExpr* i = new CBotFieldExpr();
i->SetToken(pp); i->SetToken(pp);
inst->AddNext3(i); inst->AddNext3(i);
CBotVar* preVar = var;
var = var->GetItem(p->GetString()); var = var->GetItem(p->GetString());
if (var != nullptr) if (var != nullptr)
{ {
i->SetUniqNum(var->GetUniqNum()); i->SetUniqNum(var->GetUniqNum());
if ( var->IsPrivate() && if (CBotFieldExpr::ProtectionError(pStk, preVar, var))
!pStk->GetProgram()->m_bCompileClass)
{ {
pStk->SetError(CBotErrPrivate, pp); pStk->SetError(CBotErrPrivate, pp);
goto err; goto err;

View File

@ -67,8 +67,7 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot
if (ident > 0 && ident < 9000) if (ident > 0 && ident < 9000)
{ {
if ( var->IsPrivate(privat) && if (CBotFieldExpr::ProtectionError(pStk, nullptr, var, privat))
!pStk->GetProgram()->m_bCompileClass)
{ {
pStk->SetError(CBotErrPrivate, p); pStk->SetError(CBotErrPrivate, p);
goto err; goto err;
@ -133,12 +132,12 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot
CBotFieldExpr* i = new CBotFieldExpr(); // new element CBotFieldExpr* i = new CBotFieldExpr(); // new element
i->SetToken(pp); // keeps the name of the token i->SetToken(pp); // keeps the name of the token
inst->AddNext3(i); // add after inst->AddNext3(i); // add after
CBotVar* preVar = var;
var = var->GetItem(p->GetString()); // get item correspondent var = var->GetItem(p->GetString()); // get item correspondent
if (var != nullptr) if (var != nullptr)
{ {
i->SetUniqNum(var->GetUniqNum()); i->SetUniqNum(var->GetUniqNum());
if ( var->IsPrivate() && if (CBotFieldExpr::ProtectionError(pStk, preVar, var, privat))
!pStk->GetProgram()->m_bCompileClass)
{ {
pStk->SetError(CBotErrPrivate, pp); pStk->SetError(CBotErrPrivate, pp);
goto err; goto err;

View File

@ -134,4 +134,57 @@ std::string CBotFieldExpr::GetDebugData()
return ss.str(); return ss.str();
} }
////////////////////////////////////////////////////////////////////////////////
bool CBotFieldExpr::ProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar,
CBotVar::ProtectionLevel privat)
{
CBotVar::ProtectionLevel varPriv = pVar->GetPrivate();
if (privat == CBotVar::ProtectionLevel::ReadOnly && varPriv == privat)
return true;
if (varPriv == CBotVar::ProtectionLevel::Public) return false;
std::string prevName = (pPrev == nullptr) ? "" : pPrev->GetName();
// implicit 'this.'var, this.var, or super.var
if (pPrev == nullptr || prevName == "this" || prevName == "super") // member of the current class
{
if (varPriv == CBotVar::ProtectionLevel::Private) // var is private ?
{
CBotToken token("this");
CBotVar* pThis = pStack->FindVar(token);
CBotClass* pClass = pThis->GetClass(); // the current class
CBotVar* pVarList = pClass->GetVar();
int ident = pVar->GetUniqNum();
// check if var is inherited from a parent class
if (pVarList == nullptr || ident < pVarList->GetUniqNum())
return true;
}
}
else // any other context
{
if (pVar->IsPrivate()) // var is protected or private ?
{
CBotToken token("this");
CBotVar* pThis = pStack->FindVar(token);
if (pThis == nullptr) return true; // inside a function ?
if (pThis->GetType() != CBotTypPointer) return true;
CBotClass* pClass = pThis->GetClass(); // the current class
if (!pClass->IsChildOf(pPrev->GetClass())) // var is member of some other class ?
return true;
if (varPriv == CBotVar::ProtectionLevel::Private && // private member of a parent class
pClass != pPrev->GetClass()) return true;
}
}
return false;
}
} // namespace CBot } // namespace CBot

View File

@ -65,6 +65,17 @@ public:
*/ */
void RestoreStateVar(CBotStack* &pj, bool bMain) override; void RestoreStateVar(CBotStack* &pj, bool bMain) override;
/*!
* \brief ProtectionError Test if access to a variable is not allowed.
* \param pStack
* \param pPrev
* \param pVar
* \param privat
* \return True if pVar is protected in the current context.
*/
static bool ProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar,
CBotVar::ProtectionLevel privat = CBotVar::ProtectionLevel::Protected);
protected: protected:
virtual const std::string GetDebugName() override { return "CBotFieldExpr"; } virtual const std::string GetDebugName() override { return "CBotFieldExpr"; }
virtual std::string GetDebugData() override; virtual std::string GetDebugData() override;

View File

@ -64,8 +64,7 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack)
inst->m_nIdent = var->GetUniqNum(); inst->m_nIdent = var->GetUniqNum();
if (inst->m_nIdent > 0 && inst->m_nIdent < 9000) if (inst->m_nIdent > 0 && inst->m_nIdent < 9000)
{ {
if ( var->IsPrivate(CBotVar::ProtectionLevel::ReadOnly) && if (CBotFieldExpr::ProtectionError(pStk, nullptr, var, CBotVar::ProtectionLevel::ReadOnly))
!pStk->GetProgram()->m_bCompileClass)
{ {
pStk->SetError(CBotErrPrivate, p); pStk->SetError(CBotErrPrivate, p);
goto err; goto err;
@ -125,11 +124,11 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack)
if (p->GetType() == TokenTypVar) // must be a name if (p->GetType() == TokenTypVar) // must be a name
{ {
CBotVar* preVar = var;
var = var->GetItem(p->GetString()); // get item correspondent var = var->GetItem(p->GetString()); // get item correspondent
if (var != nullptr) if (var != nullptr)
{ {
if ( var->IsPrivate(CBotVar::ProtectionLevel::ReadOnly) && if (CBotFieldExpr::ProtectionError(pStk, preVar, var, CBotVar::ProtectionLevel::ReadOnly))
!pStk->GetProgram()->m_bCompileClass)
{ {
pStk->SetError(CBotErrPrivate, pp); pStk->SetError(CBotErrPrivate, pp);
goto err; goto err;

View File

@ -124,12 +124,10 @@ bool CBotProgram::Compile(const std::string& program, std::vector<std::string>&
if ( p->GetType() == ID_CLASS || if ( p->GetType() == ID_CLASS ||
( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS )) ( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS ))
{ {
m_bCompileClass = true;
CBotClass::Compile(p, pStack.get()); // completes the definition of the class CBotClass::Compile(p, pStack.get()); // completes the definition of the class
} }
else else
{ {
m_bCompileClass = false;
CBotFunction::Compile(p, pStack.get(), next); CBotFunction::Compile(p, pStack.get(), next);
if (next->IsExtern()) functions.push_back(next->GetName()/* + next->GetParams()*/); if (next->IsExtern()) functions.push_back(next->GetName()/* + next->GetParams()*/);
if (next->IsPublic()) CBotFunction::AddPublic(next); if (next->IsPublic()) CBotFunction::AddPublic(next);

View File

@ -332,13 +332,6 @@ public:
*/ */
CBotFunction* GetFunctions(); CBotFunction* GetFunctions();
/**
* \brief true while compiling class
*
* TODO: refactor this
*/
bool m_bCompileClass;
/** /**
* \brief Returns static list of all registered external calls * \brief Returns static list of all registered external calls
*/ */

View File

@ -1904,3 +1904,152 @@ TEST_F(CBotUT, ClassMethodWithPublicKeyword)
CBotErrUndefCall CBotErrUndefCall
); );
} }
TEST_F(CBotUT, ClassTestProtectedMember)
{
auto publicProgram = ExecuteTest(
"public class BaseClass {\n"
" protected int a_protected = 1;\n"
" bool test() {\n"
" a_protected = 1;\n"
" int a = a_protected;\n"
" return true;\n"
" }\n"
"}\n"
"extern void Test() {\n"
" BaseClass b();\n"
" ASSERT(true == b.test());\n"
"}\n"
);
ExecuteTest(
"public class SubClass extends BaseClass {\n"
" bool testProtected() {\n"
" a_protected = 1;\n"
" int a = a_protected;\n"
" return true;\n"
" }\n"
"}\n"
"extern void TestSubClassAccessProtected() {\n"
" SubClass s();\n"
" ASSERT(true == s.test());\n"
" ASSERT(true == s.testProtected());\n"
"}\n"
);
ExecuteTest(
"extern void TestErrorProtected() {\n"
" BaseClass b();\n"
" int i = b.a_protected;\n"
"}\n",
CBotErrPrivate
);
ExecuteTest(
"extern void ErrorProtectedAssignment() {\n"
" BaseClass b();\n"
" b.a_protected = 1;\n"
"}\n",
CBotErrPrivate
);
ExecuteTest(
"public class SomeOtherClass {\n"
" void testErrorProtected() {\n"
" BaseClass b();\n"
" int i = b.a_protected;\n"
" }\n"
"}\n",
CBotErrPrivate
);
ExecuteTest(
"public class SomeOtherClass {\n"
" void testErrorProtectedAssignment() {\n"
" BaseClass b();\n"
" b.a_protected = 1;\n"
" }\n"
"}\n",
CBotErrPrivate
);
}
TEST_F(CBotUT, ClassTestPrivateMember)
{
auto publicProgram = ExecuteTest(
"public class BaseClass {\n"
" private int a_private = 2;\n"
"\n"
" bool test() {\n"
" a_private = 2;\n"
" int a = a_private;\n"
" return true;\n"
" }\n"
" bool NoErrorPrivateSameClass() {\n"
" BaseClass b = new BaseClass();\n"
" int a = b.a_private;\n"
" b.a_private = 2;\n"
" return true;\n"
" }\n"
"}\n"
"extern void Test() {\n"
" BaseClass b();\n"
" ASSERT(true == b.test());\n"
" ASSERT(true == b.NoErrorPrivateSameClass());\n"
"}\n"
);
ExecuteTest(
"public class SubClass extends BaseClass {\n"
" void testErrorPrivate() {\n"
" int a = a_private;\n"
" }\n"
"}\n",
CBotErrPrivate
);
ExecuteTest(
"public class SubClass extends BaseClass {\n"
" void testErrorPrivateAssignment() {\n"
" a_private = 2;\n"
" }\n"
"}\n",
CBotErrPrivate
);
ExecuteTest(
"extern void TestErrorPrivate() {\n"
" BaseClass b();\n"
" int i = b.a_private;\n"
"}\n",
CBotErrPrivate
);
ExecuteTest(
"extern void ErrorPrivateAssignment() {\n"
" BaseClass b();\n"
" b.a_private = 2;\n"
"}\n",
CBotErrPrivate
);
ExecuteTest(
"public class SomeOtherClass {\n"
" void testErrorPrivate() {\n"
" BaseClass b();\n"
" int i = b.a_private;\n"
" }\n"
"}\n",
CBotErrPrivate
);
ExecuteTest(
"public class SomeOtherClass {\n"
" void testErrorPrivateAssignment() {\n"
" BaseClass b();\n"
" b.a_private = 1;\n"
" }\n"
"}\n",
CBotErrPrivate
);
}