//
// const.cs: Constant declarations.
//
// Author:
// Miguel de Icaza (miguel@ximian.com)
// Marek Safar (marek.safar@seznam.cz)
//
// Copyright 2001-2003 Ximian, Inc.
// Copyright 2003-2008 Novell, Inc.
//
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace Mono.CSharp {
public class Const : FieldBase
{
public const Modifiers AllowedModifiers =
Modifiers.NEW |
Modifiers.PUBLIC |
Modifiers.PROTECTED |
Modifiers.INTERNAL |
Modifiers.PRIVATE;
public Const (DeclSpace parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs)
: base (parent, type, mod_flags, AllowedModifiers, name, attrs)
{
ModFlags |= Modifiers.STATIC;
}
///
/// Defines the constant in the @parent
///
public override bool Define ()
{
if (!base.Define ())
return false;
if (!member_type.IsConstantCompatible) {
Error_InvalidConstantType (member_type, Location, Report);
}
FieldAttributes field_attr = FieldAttributes.Static | ModifiersExtensions.FieldAttr (ModFlags);
// Decimals cannot be emitted into the constant blob. So, convert to 'readonly'.
if (member_type == TypeManager.decimal_type) {
field_attr |= FieldAttributes.InitOnly;
} else {
field_attr |= FieldAttributes.Literal;
}
FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), field_attr);
spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer);
Parent.MemberCache.AddMember (spec);
if ((field_attr & FieldAttributes.InitOnly) != 0)
Parent.PartialContainer.RegisterFieldForInitialization (this,
new FieldInitializer (spec, initializer, this));
if (declarators != null) {
var t = new TypeExpression (MemberType, TypeExpression.Location);
int index = Parent.PartialContainer.Constants.IndexOf (this);
foreach (var d in declarators) {
var c = new Const (Parent, t, ModFlags & ~Modifiers.STATIC, new MemberName (d.Name.Value, d.Name.Location), OptAttributes);
c.initializer = d.Initializer;
((ConstInitializer) c.initializer).Name = d.Name.Value;
Parent.PartialContainer.Constants.Insert (++index, c);
}
}
return true;
}
public void DefineValue ()
{
var rc = new ResolveContext (this);
((ConstSpec) spec).GetConstant (rc);
}
///
/// Emits the field value by evaluating the expression
///
public override void Emit ()
{
var c = ((ConstSpec) spec).Value as Constant;
if (c.Type == TypeManager.decimal_type) {
FieldBuilder.SetCustomAttribute (CreateDecimalConstantAttribute (c));
} else {
FieldBuilder.SetConstant (c.GetTypedValue ());
}
base.Emit ();
}
public static CustomAttributeBuilder CreateDecimalConstantAttribute (Constant c)
{
PredefinedAttribute pa = PredefinedAttributes.Get.DecimalConstant;
if (pa.Constructor == null &&
!pa.ResolveConstructor (c.Location, TypeManager.byte_type, TypeManager.byte_type,
TypeManager.uint32_type, TypeManager.uint32_type, TypeManager.uint32_type))
return null;
Decimal d = (Decimal) c.GetValue ();
int [] bits = Decimal.GetBits (d);
object [] args = new object [] {
(byte) (bits [3] >> 16),
(byte) (bits [3] >> 31),
(uint) bits [2], (uint) bits [1], (uint) bits [0]
};
return new CustomAttributeBuilder (pa.Constructor, args);
}
public static void Error_InvalidConstantType (TypeSpec t, Location loc, Report Report)
{
if (t.IsGenericParameter) {
Report.Error (1959, loc,
"Type parameter `{0}' cannot be declared const", TypeManager.CSharpName (t));
} else {
Report.Error (283, loc,
"The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
}
}
}
public class ConstSpec : FieldSpec
{
Expression value;
public ConstSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, FieldInfo fi, Modifiers mod, Expression value)
: base (declaringType, definition, memberType, fi, mod)
{
this.value = value;
}
//
// This expresion is guarantee to be a constant at emit phase only
//
public Expression Value {
get {
return value;
}
}
//
// For compiled constants we have to resolve the value as there could be constant dependecies. This
// is needed for imported constants too to get the right context type
//
public Constant GetConstant (ResolveContext rc)
{
if (value.eclass != ExprClass.Value)
value = value.Resolve (rc);
return (Constant) value;
}
}
public class ConstInitializer : ShimExpression
{
bool in_transit;
readonly FieldBase field;
public ConstInitializer (FieldBase field, Expression value, Location loc)
: base (value)
{
this.loc = loc;
this.field = field;
}
public string Name { get; set; }
protected override Expression DoResolve (ResolveContext unused)
{
if (type != null)
return expr;
var opt = ResolveContext.Options.ConstantScope;
if (field is EnumMember)
opt |= ResolveContext.Options.EnumScope;
//
// Use a context in which the constant was declared and
// not the one in which is referenced
//
var rc = new ResolveContext (field, opt);
expr = DoResolveInitializer (rc);
type = expr.Type;
return expr;
}
protected virtual Expression DoResolveInitializer (ResolveContext rc)
{
if (in_transit) {
field.Compiler.Report.Error (110, expr.Location,
"The evaluation of the constant value for `{0}' involves a circular definition",
GetSignatureForError ());
expr = null;
} else {
in_transit = true;
expr = expr.Resolve (rc);
}
in_transit = false;
if (expr != null) {
Constant c = expr as Constant;
if (c != null)
c = field.ConvertInitializer (rc, c);
if (c == null) {
if (TypeManager.IsReferenceType (field.MemberType))
Error_ConstantCanBeInitializedWithNullOnly (rc, field.MemberType, expr.Location, GetSignatureForError ());
else if (!(expr is Constant))
Error_ExpressionMustBeConstant (rc, expr.Location, GetSignatureForError ());
else
expr.Error_ValueCannotBeConverted (rc, expr.Location, field.MemberType, false);
}
expr = c;
}
if (expr == null) {
expr = New.Constantify (field.MemberType);
if (expr == null)
expr = Constant.CreateConstantFromValue (field.MemberType, null, Location);
expr = expr.Resolve (rc);
}
return expr;
}
public override string GetSignatureForError ()
{
if (Name == null)
return field.GetSignatureForError ();
return field.Parent.GetSignatureForError () + "." + Name;
}
}
}