Fix access to protected and private variables
parent
3debfb9182
commit
9ab7f7d140
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue