| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved |
| * written by Walter Bright |
| * http://www.digitalmars.com |
| * Distributed under the Boost Software License, Version 1.0. |
| * http://www.boost.org/LICENSE_1_0.txt |
| * https://github.com/D-Programming-Language/dmd/blob/master/src/access.c |
| */ |
| |
| #include "root/dsystem.h" |
| #include "root/root.h" |
| #include "root/rmem.h" |
| |
| #include "errors.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" |
| #include "template.h" |
| |
| /* Code to do access checks |
| */ |
| |
| bool hasPackageAccess(Scope *sc, Dsymbol *s); |
| bool hasPackageAccess(Module *mod, Dsymbol *s); |
| bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember); |
| bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd); |
| static Dsymbol *mostVisibleOverload(Dsymbol *s); |
| |
| /**************************************** |
| * Return Prot access for Dsymbol smember in this declaration. |
| */ |
| Prot getAccess(AggregateDeclaration *ad, Dsymbol *smember) |
| { |
| Prot access_ret = Prot(PROTnone); |
| |
| assert(ad->isStructDeclaration() || ad->isClassDeclaration()); |
| if (smember->toParent() == ad) |
| { |
| access_ret = smember->prot(); |
| } |
| else if (smember->isDeclaration()->isStatic()) |
| { |
| access_ret = smember->prot(); |
| } |
| if (ClassDeclaration *cd = ad->isClassDeclaration()) |
| { |
| for (size_t i = 0; i < cd->baseclasses->dim; i++) |
| { |
| BaseClass *b = (*cd->baseclasses)[i]; |
| |
| Prot access = getAccess(b->sym, smember); |
| switch (access.kind) |
| { |
| case PROTnone: |
| break; |
| |
| case PROTprivate: |
| access_ret = Prot(PROTnone); // private members of base class not accessible |
| break; |
| |
| case PROTpackage: |
| case PROTprotected: |
| case PROTpublic: |
| case PROTexport: |
| // If access is to be tightened |
| if (PROTpublic < access.kind) |
| access = Prot(PROTpublic); |
| |
| // Pick path with loosest access |
| if (access_ret.isMoreRestrictiveThan(access)) |
| access_ret = access; |
| break; |
| |
| default: |
| assert(0); |
| } |
| } |
| } |
| |
| return access_ret; |
| } |
| |
| /******************************************************** |
| * Helper function for checkAccess() |
| * Returns: |
| * false is not accessible |
| * true is accessible |
| */ |
| static bool isAccessible( |
| Dsymbol *smember, |
| Dsymbol *sfunc, |
| AggregateDeclaration *dthis, |
| AggregateDeclaration *cdscope) |
| { |
| assert(dthis); |
| |
| if (hasPrivateAccess(dthis, sfunc) || |
| isFriendOf(dthis, cdscope)) |
| { |
| if (smember->toParent() == dthis) |
| return true; |
| |
| if (ClassDeclaration *cdthis = dthis->isClassDeclaration()) |
| { |
| for (size_t i = 0; i < cdthis->baseclasses->dim; i++) |
| { |
| BaseClass *b = (*cdthis->baseclasses)[i]; |
| Prot access = getAccess(b->sym, smember); |
| if (access.kind >= PROTprotected || |
| isAccessible(smember, sfunc, b->sym, cdscope)) |
| { |
| return true; |
| } |
| } |
| } |
| } |
| else |
| { |
| if (smember->toParent() != dthis) |
| { |
| if (ClassDeclaration *cdthis = dthis->isClassDeclaration()) |
| { |
| for (size_t i = 0; i < cdthis->baseclasses->dim; i++) |
| { |
| BaseClass *b = (*cdthis->baseclasses)[i]; |
| if (isAccessible(smember, sfunc, b->sym, cdscope)) |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| /******************************* |
| * Do access check for member of this class, this class being the |
| * type of the 'this' pointer used to access smember. |
| * Returns true if the member is not accessible. |
| */ |
| bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember) |
| { |
| FuncDeclaration *f = sc->func; |
| AggregateDeclaration *cdscope = sc->getStructClassScope(); |
| |
| Dsymbol *smemberparent = smember->toParent(); |
| if (!smemberparent || !smemberparent->isAggregateDeclaration()) |
| { |
| return false; // then it is accessible |
| } |
| |
| // BUG: should enable this check |
| //assert(smember->parent->isBaseOf(this, NULL)); |
| |
| bool result; |
| Prot access; |
| if (smemberparent == ad) |
| { |
| access = smember->prot(); |
| result = access.kind >= PROTpublic || |
| hasPrivateAccess(ad, f) || |
| isFriendOf(ad, cdscope) || |
| (access.kind == PROTpackage && hasPackageAccess(sc, smember)) || |
| ad->getAccessModule() == sc->_module; |
| } |
| else if ((access = getAccess(ad, smember)).kind >= PROTpublic) |
| { |
| result = true; |
| } |
| else if (access.kind == PROTpackage && hasPackageAccess(sc, ad)) |
| { |
| result = true; |
| } |
| else |
| { |
| result = isAccessible(smember, f, ad, cdscope); |
| } |
| if (!result) |
| { |
| ad->error(loc, "member %s is not accessible", smember->toChars()); |
| //printf("smember = %s %s, prot = %d, semanticRun = %d\n", |
| // smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun); |
| return true; |
| } |
| return false; |
| } |
| |
| /**************************************** |
| * Determine if this is the same or friend of cd. |
| */ |
| bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd) |
| { |
| if (ad == cd) |
| return true; |
| |
| // Friends if both are in the same module |
| //if (toParent() == cd->toParent()) |
| if (cd && ad->getAccessModule() == cd->getAccessModule()) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /**************************************** |
| * Determine if scope sc has package level access to s. |
| */ |
| bool hasPackageAccess(Scope *sc, Dsymbol *s) |
| { |
| return hasPackageAccess(sc->_module, s); |
| } |
| |
| bool hasPackageAccess(Module *mod, Dsymbol *s) |
| { |
| Package *pkg = NULL; |
| |
| if (s->prot().pkg) |
| pkg = s->prot().pkg; |
| else |
| { |
| // no explicit package for protection, inferring most qualified one |
| for (; s; s = s->parent) |
| { |
| if (Module *m = s->isModule()) |
| { |
| DsymbolTable *dst = Package::resolve(m->md ? m->md->packages : NULL, NULL, NULL); |
| assert(dst); |
| Dsymbol *s2 = dst->lookup(m->ident); |
| assert(s2); |
| Package *p = s2->isPackage(); |
| if (p && p->isPackageMod()) |
| { |
| pkg = p; |
| break; |
| } |
| } |
| else if ((pkg = s->isPackage()) != NULL) |
| break; |
| } |
| } |
| |
| if (pkg) |
| { |
| if (pkg == mod->parent) |
| { |
| return true; |
| } |
| if (pkg->isPackageMod() == mod) |
| { |
| return true; |
| } |
| Dsymbol* ancestor = mod->parent; |
| for (; ancestor; ancestor = ancestor->parent) |
| { |
| if (ancestor == pkg) |
| { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /**************************************** |
| * Determine if scope sc has protected level access to cd. |
| */ |
| bool hasProtectedAccess(Scope *sc, Dsymbol *s) |
| { |
| if (ClassDeclaration *cd = s->isClassMember()) // also includes interfaces |
| { |
| for (Scope *scx = sc; scx; scx = scx->enclosing) |
| { |
| if (!scx->scopesym) |
| continue; |
| ClassDeclaration *cd2 = scx->scopesym->isClassDeclaration(); |
| if (cd2 && cd->isBaseOf(cd2, NULL)) |
| return true; |
| } |
| } |
| return sc->_module == s->getAccessModule(); |
| } |
| |
| /********************************** |
| * Determine if smember has access to private members of this declaration. |
| */ |
| bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember) |
| { |
| if (smember) |
| { |
| AggregateDeclaration *cd = NULL; |
| Dsymbol *smemberparent = smember->toParent(); |
| if (smemberparent) |
| cd = smemberparent->isAggregateDeclaration(); |
| |
| if (ad == cd) // smember is a member of this class |
| { |
| return true; // 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 && ad->toParent() == smember->toParent()) |
| { |
| return true; |
| } |
| if (!cd && ad->getAccessModule() == smember->getAccessModule()) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /**************************************** |
| * Check access to d for expression e.d |
| * Returns true if the declaration is not accessible. |
| */ |
| bool checkAccess(Loc loc, Scope *sc, Expression *e, Declaration *d) |
| { |
| if (sc->flags & SCOPEnoaccesscheck) |
| return false; |
| |
| if (d->isUnitTestDeclaration()) |
| { |
| // Unittests are always accessible. |
| return false; |
| } |
| if (!e) |
| { |
| if ((d->prot().kind == PROTprivate && d->getAccessModule() != sc->_module) || |
| (d->prot().kind == PROTpackage && !hasPackageAccess(sc, d))) |
| { |
| error(loc, "%s %s is not accessible from module %s", |
| d->kind(), d->toPrettyChars(), sc->_module->toChars()); |
| return true; |
| } |
| } |
| else if (e->type->ty == Tclass) |
| { |
| // Do access check |
| ClassDeclaration *cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym); |
| if (e->op == TOKsuper) |
| { |
| ClassDeclaration *cd2 = sc->func->toParent()->isClassDeclaration(); |
| if (cd2) |
| cd = cd2; |
| } |
| return checkAccess(cd, loc, sc, d); |
| } |
| else if (e->type->ty == Tstruct) |
| { |
| // Do access check |
| StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym); |
| return checkAccess(cd, loc, sc, d); |
| } |
| return false; |
| } |
| |
| /**************************************** |
| * Check access to package/module `p` from scope `sc`. |
| * |
| * Params: |
| * loc = source location for issued error message |
| * sc = scope from which to access to a fully qualified package name |
| * p = the package/module to check access for |
| * Returns: true if the package is not accessible. |
| * |
| * Because a global symbol table tree is used for imported packages/modules, |
| * access to them needs to be checked based on the imports in the scope chain |
| * (see Bugzilla 313). |
| * |
| */ |
| bool checkAccess(Loc loc, Scope *sc, Package *p) |
| { |
| if (sc->_module == p) |
| return false; |
| for (; sc; sc = sc->enclosing) |
| { |
| if (sc->scopesym && sc->scopesym->isPackageAccessible(p, Prot(PROTprivate))) |
| return false; |
| } |
| const char *name = p->toPrettyChars(); |
| if (p->isPkgMod == PKGmodule || p->isModule()) |
| deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", p->kind(), name, name); |
| else |
| deprecation(loc, "%s %s is not accessible here", p->kind(), name); |
| return true; |
| } |
| |
| /** |
| * Check whether symbols `s` is visible in `mod`. |
| * |
| * Params: |
| * mod = lookup origin |
| * s = symbol to check for visibility |
| * Returns: true if s is visible in mod |
| */ |
| bool symbolIsVisible(Module *mod, Dsymbol *s) |
| { |
| // should sort overloads by ascending protection instead of iterating here |
| s = mostVisibleOverload(s); |
| |
| switch (s->prot().kind) |
| { |
| case PROTundefined: |
| return true; |
| case PROTnone: |
| return false; // no access |
| case PROTprivate: |
| return s->getAccessModule() == mod; |
| case PROTpackage: |
| return s->getAccessModule() == mod || hasPackageAccess(mod, s); |
| case PROTprotected: |
| return s->getAccessModule() == mod; |
| case PROTpublic: |
| case PROTexport: |
| return true; |
| default: |
| assert(0); |
| } |
| } |
| |
| /** |
| * Same as above, but determines the lookup module from symbols `origin`. |
| */ |
| bool symbolIsVisible(Dsymbol *origin, Dsymbol *s) |
| { |
| return symbolIsVisible(origin->getAccessModule(), s); |
| } |
| |
| /** |
| * Same as above but also checks for protected symbols visible from scope `sc`. |
| * Used for qualified name lookup. |
| * |
| * Params: |
| * sc = lookup scope |
| * s = symbol to check for visibility |
| * Returns: true if s is visible by origin |
| */ |
| bool symbolIsVisible(Scope *sc, Dsymbol *s) |
| { |
| s = mostVisibleOverload(s); |
| |
| switch (s->prot().kind) |
| { |
| case PROTundefined: |
| return true; |
| case PROTnone: |
| return false; // no access |
| case PROTprivate: |
| return sc->_module == s->getAccessModule(); |
| case PROTpackage: |
| return sc->_module == s->getAccessModule() || hasPackageAccess(sc->_module, s); |
| case PROTprotected: |
| return hasProtectedAccess(sc, s); |
| case PROTpublic: |
| case PROTexport: |
| return true; |
| default: |
| assert(0); |
| } |
| } |
| |
| /** |
| * Use the most visible overload to check visibility. Later perform an access |
| * check on the resolved overload. This function is similar to overloadApply, |
| * but doesn't recurse nor resolve aliases because protection/visibility is an |
| * attribute of the alias not the aliasee. |
| */ |
| static Dsymbol *mostVisibleOverload(Dsymbol *s) |
| { |
| if (!s->isOverloadable()) |
| return s; |
| |
| Dsymbol *next = NULL; |
| Dsymbol *fstart = s; |
| Dsymbol *mostVisible = s; |
| for (; s; s = next) |
| { |
| // void func() {} |
| // private void func(int) {} |
| if (FuncDeclaration *fd = s->isFuncDeclaration()) |
| next = fd->overnext; |
| // template temp(T) {} |
| // private template temp(T:int) {} |
| else if (TemplateDeclaration *td = s->isTemplateDeclaration()) |
| next = td->overnext; |
| // alias common = mod1.func1; |
| // alias common = mod2.func2; |
| else if (FuncAliasDeclaration *fa = s->isFuncAliasDeclaration()) |
| next = fa->overnext; |
| // alias common = mod1.templ1; |
| // alias common = mod2.templ2; |
| else if (OverDeclaration *od = s->isOverDeclaration()) |
| next = od->overnext; |
| // alias name = sym; |
| // private void name(int) {} |
| else if (AliasDeclaration *ad = s->isAliasDeclaration()) |
| { |
| if (!ad->isOverloadable()) |
| { |
| //printf("Non overloadable Aliasee in overload list\n"); |
| assert(0); |
| } |
| // Yet unresolved aliases store overloads in overnext. |
| if (ad->semanticRun < PASSsemanticdone) |
| next = ad->overnext; |
| else |
| { |
| /* This is a bit messy due to the complicated implementation of |
| * alias. Aliases aren't overloadable themselves, but if their |
| * Aliasee is overloadable they can be converted to an overloadable |
| * alias. |
| * |
| * This is done by replacing the Aliasee w/ FuncAliasDeclaration |
| * (for functions) or OverDeclaration (for templates) which are |
| * simply overloadable aliases w/ weird names. |
| * |
| * Usually aliases should not be resolved for visibility checking |
| * b/c public aliases to private symbols are public. But for the |
| * overloadable alias situation, the Alias (_ad_) has been moved |
| * into it's own Aliasee, leaving a shell that we peel away here. |
| */ |
| Dsymbol *aliasee = ad->toAlias(); |
| if (aliasee->isFuncAliasDeclaration() || aliasee->isOverDeclaration()) |
| next = aliasee; |
| else |
| { |
| /* A simple alias can be at the end of a function or template overload chain. |
| * It can't have further overloads b/c it would have been |
| * converted to an overloadable alias. |
| */ |
| if (ad->overnext) |
| { |
| //printf("Unresolved overload of alias\n"); |
| assert(0); |
| } |
| break; |
| } |
| } |
| |
| // handled by overloadApply for unknown reason |
| assert(next != ad); // should not alias itself |
| assert(next != fstart); // should not alias the overload list itself |
| } |
| else |
| break; |
| |
| if (next && mostVisible->prot().isMoreRestrictiveThan(next->prot())) |
| mostVisible = next; |
| } |
| return mostVisible; |
| } |