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;
pcopy->SetInit(initType);
pcopy->SetUniqNum(pv->GetUniqNum());
pcopy->SetPrivate(pv->GetPrivate());
pile->AddVar(pcopy);
pv = pv->GetNext();
}

View File

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

View File

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

View File

@ -134,4 +134,57 @@ std::string CBotFieldExpr::GetDebugData()
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

View File

@ -65,6 +65,17 @@ public:
*/
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:
virtual const std::string GetDebugName() override { return "CBotFieldExpr"; }
virtual std::string GetDebugData() override;

View File

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

View File

@ -124,12 +124,10 @@ bool CBotProgram::Compile(const std::string& program, std::vector<std::string>&
if ( p->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
}
else
{
m_bCompileClass = false;
CBotFunction::Compile(p, pStack.get(), next);
if (next->IsExtern()) functions.push_back(next->GetName()/* + next->GetParams()*/);
if (next->IsPublic()) CBotFunction::AddPublic(next);

View File

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

View File

@ -1904,3 +1904,152 @@ TEST_F(CBotUT, ClassMethodWithPublicKeyword)
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
);
}