ldc/dmd2/access.c

424 lines
8.9 KiB
C

// Copyright (c) 1999-2006 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "root.h"
#include "mem.h"
#include "enum.h"
#include "aggregate.h"
#include "init.h"
#include "attrib.h"
#include "scope.h"
#include "id.h"
#include "mtype.h"
#include "declaration.h"
#include "aggregate.h"
#include "expression.h"
#include "module.h"
#define LOG 0
/* Code to do access checks
*/
int hasPackageAccess(Scope *sc, Dsymbol *s);
/****************************************
* Return PROT access for Dsymbol smember in this declaration.
*/
enum PROT AggregateDeclaration::getAccess(Dsymbol *smember)
{
return PROTpublic;
}
enum PROT StructDeclaration::getAccess(Dsymbol *smember)
{
enum PROT access_ret = PROTnone;
#if LOG
printf("+StructDeclaration::getAccess(this = '%s', smember = '%s')\n",
toChars(), smember->toChars());
#endif
if (smember->toParent() == this)
{
access_ret = smember->prot();
}
else if (smember->isDeclaration()->isStatic())
{
access_ret = smember->prot();
}
return access_ret;
}
enum PROT ClassDeclaration::getAccess(Dsymbol *smember)
{
enum PROT access_ret = PROTnone;
#if LOG
printf("+ClassDeclaration::getAccess(this = '%s', smember = '%s')\n",
toChars(), smember->toChars());
#endif
if (smember->toParent() == this)
{
access_ret = smember->prot();
}
else
{
enum PROT access;
int i;
if (smember->isDeclaration()->isStatic())
{
access_ret = smember->prot();
}
for (i = 0; i < baseclasses.dim; i++)
{ BaseClass *b = (BaseClass *)baseclasses.data[i];
access = b->base->getAccess(smember);
switch (access)
{
case PROTnone:
break;
case PROTprivate:
access = PROTnone; // private members of base class not accessible
break;
case PROTpackage:
case PROTprotected:
case PROTpublic:
case PROTexport:
// If access is to be tightened
if (b->protection < access)
access = b->protection;
// Pick path with loosest access
if (access > access_ret)
access_ret = access;
break;
default:
assert(0);
}
}
}
#if LOG
printf("-ClassDeclaration::getAccess(this = '%s', smember = '%s') = %d\n",
toChars(), smember->toChars(), access_ret);
#endif
return access_ret;
}
/********************************************************
* Helper function for ClassDeclaration::accessCheck()
* Returns:
* 0 no access
* 1 access
*/
static int accessCheckX(
Dsymbol *smember,
Dsymbol *sfunc,
AggregateDeclaration *dthis,
AggregateDeclaration *cdscope)
{
assert(dthis);
#if 0
printf("accessCheckX for %s.%s in function %s() in scope %s\n",
dthis->toChars(), smember->toChars(),
sfunc ? sfunc->toChars() : "NULL",
cdscope ? cdscope->toChars() : "NULL");
#endif
if (dthis->hasPrivateAccess(sfunc) ||
dthis->isFriendOf(cdscope))
{
if (smember->toParent() == dthis)
return 1;
else
{
ClassDeclaration *cdthis = dthis->isClassDeclaration();
if (cdthis)
{
for (int i = 0; i < cdthis->baseclasses.dim; i++)
{ BaseClass *b = (BaseClass *)cdthis->baseclasses.data[i];
enum PROT access;
access = b->base->getAccess(smember);
if (access >= PROTprotected ||
accessCheckX(smember, sfunc, b->base, cdscope)
)
return 1;
}
}
}
}
else
{
if (smember->toParent() != dthis)
{
ClassDeclaration *cdthis = dthis->isClassDeclaration();
if (cdthis)
{
for (int i = 0; i < cdthis->baseclasses.dim; i++)
{ BaseClass *b = (BaseClass *)cdthis->baseclasses.data[i];
if (accessCheckX(smember, sfunc, b->base, cdscope))
return 1;
}
}
}
}
return 0;
}
/*******************************
* Do access check for member of this class, this class being the
* type of the 'this' pointer used to access smember.
*/
void AggregateDeclaration::accessCheck(Loc loc, Scope *sc, Dsymbol *smember)
{
int result;
FuncDeclaration *f = sc->func;
AggregateDeclaration *cdscope = sc->getStructClassScope();
enum PROT access;
#if LOG
printf("AggregateDeclaration::accessCheck() for %s.%s in function %s() in scope %s\n",
toChars(), smember->toChars(),
f ? f->toChars() : NULL,
cdscope ? cdscope->toChars() : NULL);
#endif
Dsymbol *smemberparent = smember->toParent();
if (!smemberparent || !smemberparent->isAggregateDeclaration())
{
#if LOG
printf("not an aggregate member\n");
#endif
return; // then it is accessible
}
// BUG: should enable this check
//assert(smember->parent->isBaseOf(this, NULL));
if (smemberparent == this)
{ enum PROT access = smember->prot();
result = access >= PROTpublic ||
hasPrivateAccess(f) ||
isFriendOf(cdscope) ||
(access == PROTpackage && hasPackageAccess(sc, this));
#if LOG
printf("result1 = %d\n", result);
#endif
}
else if ((access = this->getAccess(smember)) >= PROTpublic)
{
result = 1;
#if LOG
printf("result2 = %d\n", result);
#endif
}
else if (access == PROTpackage && hasPackageAccess(sc, this))
{
result = 1;
#if LOG
printf("result3 = %d\n", result);
#endif
}
else
{
result = accessCheckX(smember, f, this, cdscope);
#if LOG
printf("result4 = %d\n", result);
#endif
}
if (!result)
{
error(loc, "member %s is not accessible", smember->toChars());
halt();
}
}
/****************************************
* Determine if this is the same or friend of cd.
*/
int AggregateDeclaration::isFriendOf(AggregateDeclaration *cd)
{
#if LOG
printf("AggregateDeclaration::isFriendOf(this = '%s', cd = '%s')\n", toChars(), cd ? cd->toChars() : "null");
#endif
if (this == cd)
return 1;
// Friends if both are in the same module
//if (toParent() == cd->toParent())
if (cd && getModule() == cd->getModule())
{
#if LOG
printf("\tin same module\n");
#endif
return 1;
}
#if LOG
printf("\tnot friend\n");
#endif
return 0;
}
/****************************************
* Determine if scope sc has package level access to s.
*/
int hasPackageAccess(Scope *sc, Dsymbol *s)
{
#if LOG
printf("hasPackageAccess(s = '%s', sc = '%p')\n", s->toChars(), sc);
#endif
for (; s; s = s->parent)
{
if (s->isPackage() && !s->isModule())
break;
}
#if LOG
if (s)
printf("\tthis is in package '%s'\n", s->toChars());
#endif
if (s && s == sc->module->parent)
{
#if LOG
printf("\ts is in same package as sc\n");
#endif
return 1;
}
#if LOG
printf("\tno package access\n");
#endif
return 0;
}
/**********************************
* Determine if smember has access to private members of this declaration.
*/
int AggregateDeclaration::hasPrivateAccess(Dsymbol *smember)
{
if (smember)
{ AggregateDeclaration *cd = NULL;
Dsymbol *smemberparent = smember->toParent();
if (smemberparent)
cd = smemberparent->isAggregateDeclaration();
#if LOG
printf("AggregateDeclaration::hasPrivateAccess(class %s, member %s)\n",
toChars(), smember->toChars());
#endif
if (this == cd) // smember is a member of this class
{
#if LOG
printf("\tyes 1\n");
#endif
return 1; // so we get private access
}
// If both are members of the same module, grant access
while (1)
{ Dsymbol *sp = smember->toParent();
if (sp->isFuncDeclaration() && smember->isFuncDeclaration())
smember = sp;
else
break;
}
if (!cd && toParent() == smember->toParent())
{
#if LOG
printf("\tyes 2\n");
#endif
return 1;
}
if (!cd && getModule() == smember->getModule())
{
#if LOG
printf("\tyes 3\n");
#endif
return 1;
}
}
#if LOG
printf("\tno\n");
#endif
return 0;
}
/****************************************
* Check access to d for expression e.d
*/
void accessCheck(Loc loc, Scope *sc, Expression *e, Declaration *d)
{
#if LOG
if (e)
{ printf("accessCheck(%s . %s)\n", e->toChars(), d->toChars());
printf("\te->type = %s\n", e->type->toChars());
}
else
{
//printf("accessCheck(%s)\n", d->toChars());
}
#endif
if (!e)
{
if (d->prot() == PROTprivate && d->getModule() != sc->module ||
d->prot() == PROTpackage && !hasPackageAccess(sc, d))
error(loc, "%s %s.%s is not accessible from %s",
d->kind(), d->getModule()->toChars(), d->toChars(), sc->module->toChars());
}
else if (e->type->ty == Tclass)
{ // Do access check
ClassDeclaration *cd;
cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym);
#if 1
if (e->op == TOKsuper)
{ ClassDeclaration *cd2;
cd2 = sc->func->toParent()->isClassDeclaration();
if (cd2)
cd = cd2;
}
#endif
cd->accessCheck(loc, sc, d);
}
else if (e->type->ty == Tstruct)
{ // Do access check
StructDeclaration *cd;
cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym);
cd->accessCheck(loc, sc, d);
}
}