//
// decl.cs: Declaration base class for structs, classes, enums and interfaces.
//
// Author: Miguel de Icaza (miguel@gnu.org)
//
// Licensed under the terms of the GNU GPL
//
// (C) 2001 Ximian, Inc (http://www.ximian.com)
//
// TODO: Move the method verification stuff from the class.cs and interface.cs here
//
using System;
using System.Collections;
using System.Reflection.Emit;
using System.Reflection;
namespace Mono.CSharp {
///
/// Base representation for members. This is only used to keep track
/// of Name, Location and Modifier flags.
///
public abstract class MemberCore {
///
/// Public name
///
public string Name;
///
/// Modifier flags that the user specified in the source code
///
public int ModFlags;
///
/// Location where this declaration happens
///
public readonly Location Location;
public MemberCore (string name, Location loc)
{
Name = name;
Location = loc;
}
protected void WarningNotHiding (TypeContainer parent)
{
Report.Warning (
109, Location,
"The member `" + parent.Name + "." + Name + "' does not hide an " +
"inherited member. The keyword new is not required");
}
static string MethodBaseName (MethodBase mb)
{
return "`" + mb.ReflectedType.Name + "." + mb.Name + "'";
}
void Error_CannotChangeAccessModifiers (TypeContainer parent, MethodInfo parent_method)
{
//
// FIXME: report the old/new permissions?
//
Report.Error (
507, "`" + parent_method + "." + Name +
": can't change the access modifiers from `" +
parent_method.DeclaringType.Name + "." + parent_method.Name + "'");
}
//
// Performs various checks on the MethodInfo `mb' regarding the modifier flags
// that have been defined.
//
// `name' is the user visible name for reporting errors (this is used to
// provide the right name regarding method names and properties)
//
protected bool CheckMethodAgainstBase (TypeContainer parent,
MethodAttributes my_attrs, MethodInfo mb)
{
bool ok = true;
if ((ModFlags & Modifiers.OVERRIDE) != 0){
if (!(mb.IsAbstract || mb.IsVirtual)){
Report.Error (
506, Location, parent.MakeName (Name) +
": cannot override inherited member " +
MethodBaseName (mb) + " because it is not " +
"virtual, abstract or override");
ok = false;
}
// Now we check that the overriden method is not final
if (mb.IsFinal) {
Report.Error (239, Location, parent.MakeName (Name) + " : cannot " +
"override inherited member " + MethodBaseName (mb) +
" because it is sealed.");
ok = false;
}
//
// Check that the permissions are not being changed
//
MethodAttributes thisp = my_attrs & MethodAttributes.MemberAccessMask;
MethodAttributes parentp = mb.Attributes & MethodAttributes.MemberAccessMask;
if (thisp != parentp){
Error_CannotChangeAccessModifiers (parent, mb);
ok = false;
}
}
if (mb.IsVirtual || mb.IsAbstract){
if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE)) == 0){
if (Name != "Finalize" && (RootContext.WarningLevel >= 2)){
Report.Warning (
114, Location, parent.MakeName (Name) +
" hides inherited member " + MethodBaseName (mb) +
". To make the current member override that " +
"implementation, add the override keyword, " +
"otherwise use the new keyword");
}
}
}
return ok;
}
public abstract bool Define (TypeContainer parent);
//
// Whehter is it ok to use an unsafe pointer in this type container
//
public bool UnsafeOK (DeclSpace parent)
{
//
// First check if this MemberCore modifier flags has unsafe set
//
if ((ModFlags & Modifiers.UNSAFE) != 0)
return true;
if (parent.UnsafeContext)
return true;
Expression.UnsafeError (Location);
return false;
}
}
//
// FIXME: This is temporary outside DeclSpace, because I have to fix a bug
// in MCS that makes it fail the lookup for the enum
//
///
/// The result value from adding an declaration into
/// a struct or a class
///
public enum AdditionResult {
///
/// The declaration has been successfully
/// added to the declation space.
///
Success,
///
/// The symbol has already been defined.
///
NameExists,
///
/// Returned if the declation being added to the
/// name space clashes with its container name.
///
/// The only exceptions for this are constructors
/// and static constructors
///
EnclosingClash,
///
/// Returned if a constructor was created (because syntactically
/// it looked like a constructor) but was not (because the name
/// of the method is not the same as the container class
///
NotAConstructor,
///
/// This is only used by static constructors to emit the
/// error 111, but this error for other things really
/// happens at another level for other functions.
///
MethodExists
}
///
/// Base class for structs, classes, enumerations and interfaces.
///
///
/// They all create new declaration spaces. This
/// provides the common foundation for managing those name
/// spaces.
///
public abstract class DeclSpace : MemberCore {
///
/// this points to the actual definition that is being
/// created with System.Reflection.Emit
///
public TypeBuilder TypeBuilder;
///
/// This variable tracks whether we have Closed the type
///
public bool Created = false;
//
// This is the namespace in which this typecontainer
// was declared. We use this to resolve names.
//
public Namespace Namespace;
public Hashtable Cache = new Hashtable ();
public string Basename;
///
/// defined_names is used for toplevel objects
///
protected Hashtable defined_names;
TypeContainer parent;
public DeclSpace (TypeContainer parent, string name, Location l)
: base (name, l)
{
Basename = name.Substring (1 + name.LastIndexOf ('.'));
defined_names = new Hashtable ();
this.parent = parent;
}
///
/// Returns a status code based purely on the name
/// of the member being added
///
protected AdditionResult IsValid (string name)
{
if (name == Basename)
return AdditionResult.EnclosingClash;
if (defined_names.Contains (name))
return AdditionResult.NameExists;
return AdditionResult.Success;
}
///
/// Introduce @name into this declaration space and
/// associates it with the object @o. Note that for
/// methods this will just point to the first method. o
///
protected void DefineName (string name, object o)
{
defined_names.Add (name, o);
}
///
/// Returns the object associated with a given name in the declaration
/// space. This is the inverse operation of `DefineName'
///
public object GetDefinition (string name)
{
return defined_names [name];
}
bool in_transit = false;
///
/// This function is used to catch recursive definitions
/// in declarations.
///
public bool InTransit {
get {
return in_transit;
}
set {
in_transit = value;
}
}
public TypeContainer Parent {
get {
return parent;
}
}
//
// root_types contains all the types. All TopLevel types
// hence have a parent that points to `root_types', that is
// why there is a non-obvious test down here.
//
public bool IsTopLevel {
get {
if (parent != null){
if (parent.parent == null)
return true;
}
return false;
}
}
public virtual void CloseType ()
{
if (!Created){
try {
TypeBuilder.CreateType ();
} catch {
//
// The try/catch is needed because
// nested enumerations fail to load when they
// are defined.
//
// Even if this is the right order (enumerations
// declared after types).
//
// Note that this still creates the type and
// it is possible to save it
}
Created = true;
}
}
///
/// Should be overriten by the appropriate declaration space
///
public abstract TypeBuilder DefineType ();
//
// Whether this is an `unsafe context'
//
public bool UnsafeContext {
get {
if ((ModFlags & Modifiers.UNSAFE) != 0)
return true;
if (parent != null)
return parent.UnsafeContext;
return false;
}
}
public static string MakeFQN (string nsn, string name)
{
string prefix = (nsn == "" ? "" : nsn + ".");
return prefix + name;
}
Type LookupInterfaceOrClass (string ns, string name, out bool error)
{
DeclSpace parent;
Type t;
error = false;
name = MakeFQN (ns, name);
t = TypeManager.LookupType (name);
if (t != null)
return t;
parent = (DeclSpace) RootContext.Tree.Decls [name];
if (parent == null)
return null;
t = parent.DefineType ();
if (t == null){
Report.Error (146, "Class definition is circular: `"+name+"'");
error = true;
return null;
}
return t;
}
///
/// GetType is used to resolve type names at the DeclSpace level.
/// Use this to lookup class/struct bases, interface bases or
/// delegate type references
///
///
///
/// Contrast this to LookupType which is used inside method bodies to
/// lookup types that have already been defined. GetType is used
/// during the tree resolution process and potentially define
/// recursively the type
///
public Type FindType (string name)
{
Type t;
bool error;
//
// For the case the type we are looking for is nested within this one
// or is in any base class
//
DeclSpace containing_ds = this;
while (containing_ds != null){
Type current_type = containing_ds.TypeBuilder;
while (current_type != null) {
string pre = current_type.FullName;
t = LookupInterfaceOrClass (pre, name, out error);
if (error)
return null;
if (t != null)
return t;
current_type = current_type.BaseType;
}
containing_ds = containing_ds.Parent;
}
//
// Attempt to lookup the class on our namespace and all it's implicit parents
//
for (string ns = Namespace.Name; ns != null; ns = RootContext.ImplicitParent (ns)) {
t = LookupInterfaceOrClass (ns, name, out error);
if (error)
return null;
if (t != null)
return t;
}
//
// Attempt to do a direct unqualified lookup
//
t = LookupInterfaceOrClass ("", name, out error);
if (error)
return null;
if (t != null)
return t;
//
// Attempt to lookup the class on any of the `using'
// namespaces
//
for (Namespace ns = Namespace; ns != null; ns = ns.Parent){
t = LookupInterfaceOrClass (ns.Name, name, out error);
if (error)
return null;
if (t != null)
return t;
//
// Now check the using clause list
//
ArrayList using_list = ns.UsingTable;
if (using_list == null)
continue;
foreach (string n in using_list){
t = LookupInterfaceOrClass (n, name, out error);
if (error)
return null;
if (t != null)
return t;
}
}
Report.Error (246, Location, "Can not find type `"+name+"'");
return null;
}
}
}