view dmd/access.c @ 1508:e1e93343fc11

Move function codegen data from IrFunction to new FuncGen. This change reduces memory consumption significantly by releasing the memory held by the STL containers that are now inside FuncGen.
author Christian Kamm <kamm incasoftware de>
date Sat, 20 Jun 2009 19:11:44 +0200
parents eb7cb41ee585
children 44b145be2ef5
line wrap: on
line source


// 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 "rmem.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->getModule() != sc->module)
	    if (d->prot() == PROTprivate ||
		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);
    }
}