// 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 #include #include #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); } }