diff options
Diffstat (limited to 'mcs/mbas')
51 files changed, 41537 insertions, 0 deletions
diff --git a/mcs/mbas/.cvsignore b/mcs/mbas/.cvsignore new file mode 100644 index 00000000000..632875e2664 --- /dev/null +++ b/mcs/mbas/.cvsignore @@ -0,0 +1,14 @@ +mbas.pdb +mbas.exe +mb-parser.cs +y.output +*.pdb +mbas.csproj.user +mbas.suo +D/bin +D/obj +D/bin/* +D/obj/* +update.bat +mcs.*.log +*.dll diff --git a/mcs/mbas/AssemblyInfo.cs b/mcs/mbas/AssemblyInfo.cs new file mode 100644 index 00000000000..de3cf1c56cc --- /dev/null +++ b/mcs/mbas/AssemblyInfo.cs @@ -0,0 +1,88 @@ +// MonoBASIC Compiler, this is a compiler for the MonoBASIC language, which is a superset of Visual Basic.NET +// Copyright (C) 2002 Rafael Teixeira +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// + +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("MonoBASIC Compiler")] +[assembly: AssemblyDescription("This is a compiler for the MonoBASIC language, \nwhich is a superset of Visual Basic.NET")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("(c)2002 Rafael Teixeira")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: Mono.Author("Ravi Pratap, Miguel de Icaza, Rafael Teixeira, Marco Ridoni")] +//[assembly: Mono.Author("Miguel de Icaza")] +//[assembly: Mono.Author("Rafael Teixeira")] + +[assembly: Mono.About("Distributed under the GPL")] + +[assembly: Mono.UsageComplement("SOURCE-FILES")] + +//[assembly: Mono.LicensingWith(Mono.GetOptions.Licenses.GPL)] +//[assembly: Mono.ForMoreInformation("http://www.go-mono.com")] +//[assembly: Mono.UsageClause("mbas [options] source-files")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\<configuration>. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/mcs/mbas/ChangeLog b/mcs/mbas/ChangeLog new file mode 100644 index 00000000000..9e49a98b702 --- /dev/null +++ b/mcs/mbas/ChangeLog @@ -0,0 +1,126 @@ +2003-01-13 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * added cleanup method to tokenizer as needed but modifications made in jay + +2003-01-12 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * changed test target in makefile work + * corrected authors list to include Marco + +2002-10-23 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged many sources from mcs/mcs, to resync + +2002-10-20 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * using Mono.GetOptions preliminar support for response files, + changed the makefile target 'test' for use o response file testmbas/filelist + +2002-10-20 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged many sources from mcs/mcs, to resync + +2002-10-05 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged many sources from mcs/mcs, to resync + +2002-08-31 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged many sources from mcs/mcs, to resync + +2002-09-03 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * integrated new version of Mono.GetOptions (reflection/attributes-based) + +2002-08-31 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged expression.cs from mcs/mcs, to resync + +2002-08-29 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged assign.cs, attribute.cs, class.cs, codegen.cs, const.cs, decl.cs, delegate.cs, ecore.cs, enum.cs, + expression.cs, interface.cs, pending.cs, report.cs, rootcontext.cs, statement.cs, support.cs and + typemanager.cs from mcs/mcs, to resync + +2002-08-06 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * added "test" target to makefile + * merged assign.cs, attribute.cs, cfold.cs, class.cs, codegen.cs, const.cs, constant.cs, + decl.cs, delegate.cs, ecore.cs, enum.cs, expression.cs, interface.cs, modifiers.cs, parameter.cs, + pending.cs, report.cs, rootcontext.cs, statement.cs, support.cs and typemanager.cs from mcs/mcs, to resync + +2002-07-14 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged assign.cs, class.cs, ecore.cs, expression.cs, statement.cs and typemanager from mcs/mcs, to resync + +2002-07-09 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged attribute.cs, ecore.cs, namespace.cs and statement.cs from mcs/mcs, to resync + +2002-07-06 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged attribute.cs, class.cs, codegen.cs, ecore.cs, expression.cs, + modifiers.cs, namespace.cs, report.cs, rootcontext.cs, statement.cs and typemanager.cs from mcs/mcs, to resync + * changed driver.cs to follow mcs lead on error/warning counting and reporting + +2002-06-23 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged attribute.cs, class.cs, ecore.cs, rootcontext.cs, support.cs and typemanager.cs from mcs/mcs, to resync + * makefile makes csc reference a copy of Mono.GetOptions.dll (mbas.sln now compiles to mbas dir instead of mbas/bin/Debug) + +2002-06-21 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged attribute.cs, class.cs, interface.cs, expression.cs, ecore.cs, + modifiers.cs, rootcontext.cs, statement.cs and typemanager.cs from mcs/mcs, to resync + * added pending.cs from mcs/mcs, to resync + +2002-06-15 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged assign.cs, attribute.cs, enum.cs and namespace.cs from mcs/mcs, to resync + * namespace.cs needed some fixing, because CSharpParser isnŽt available + +2002-06-15 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged delegate.cs, ecore.cs, typemanager.cs and rootcontext.cs from mcs/mcs, to resolve expression.cs blues + +2002-06-15 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * oops merged expression.cs from mcs/mcs is breaking my make + * driver.cs, assemblyinfo.cs wasnŽt ready for prime time (offending lines were commented out) + +2002-06-15 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged expression.cs from mcs/mcs + +2002-06-12 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * corrected Module.TypeAttr property getter in module.cs + +2002-06-12 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged typemanager.cs from mcs/mcs + +2002-06-10 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged expression.cs and interface.cs from mcs/mcs + +2002-06-09 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged const.cs, enum.cs, expression.cs and typemanager.cs from mcs/mcs + * comments on module.cs + +2002-06-07 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged class.cs, attribute.cs from mcs/mcs + +2002-06-07 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged class.cs, enum.cs, expression.cs, interface.cs, rootcontext.cs and typemanager.cs from mcs/mcs + * added module.cs (class Mono.MonoBASIC.Module - derived from Mono.CSharp.Class) + * added System.XML and Microsoft.VisualBasic to the default config (driver.cs) + +2002-06-07 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged class.cs and ecore.cs from mcs/mcs + +2002-06-02 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * copied methods MakeName and CheckDef from mcs\cs-parser.jay to GenericParser.cs + where they are inherited by mb-parser.jay/cs + * put some code on the Module rule in mb-parser.jay to at least generate a class in the assembly, + if I jump over the entry-point check code, while debugging + +2002-05-31 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * class Mono.MonoBASIC.Tokenizer now handles + - all valid line-terminators (CR, LF, CRLF, LS and PS) + - escaped identifiers (like [Integer]) + - old-fashioned comments syntax (REM Blah-Blah) + +2002-05-31 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * modified mbas.ico to be a small version of monoŽs logo (see mcs\MonoIcon.png) + +2002-05-31 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * squashed some reduce/reduce conflicts out of mb-parser.jay + +2002-05-31 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + * merged codegen.cs from mcs + * altered Driver.cs to work with new codegen.cs + +2002-05-27 Rafael Teixeira <rafaelteixeirabr@hotmail.com> + + * merged all I could from mcs source files into mbas + * added VS.NET Solution and Project Files for mbas + * added icon file and a vb-sources-filled testmbas directory + * started this ChangeLog diff --git a/mcs/mbas/assign.cs b/mcs/mbas/assign.cs new file mode 100644 index 00000000000..225283189fd --- /dev/null +++ b/mcs/mbas/assign.cs @@ -0,0 +1,492 @@ +// +// assign.cs: Assignments. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Martin Baulig (martin@gnome.org) +// +// (C) 2001, 2002 Ximian, Inc. +// +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace Mono.CSharp { + + /// <summary> + /// This interface is implemented by expressions that can be assigned to. + /// </summary> + /// <remarks> + /// This interface is implemented by Expressions whose values can not + /// store the result on the top of the stack. + /// + /// Expressions implementing this (Properties, Indexers and Arrays) would + /// perform an assignment of the Expression "source" into its final + /// location. + /// + /// No values on the top of the stack are expected to be left by + /// invoking this method. + /// </remarks> + public interface IAssignMethod { + // + // This method will emit the code for the actual assignment + // + void EmitAssign (EmitContext ec, Expression source); + + // + // This method is invoked before any code generation takes + // place, and it is a mechanism to inform that the expression + // will be invoked more than once, and that the method should + // use temporary values to avoid having side effects + // + // Example: a [ g () ] ++ + // + void CacheTemporaries (EmitContext ec); + } + + /// <summary> + /// An Expression to hold a temporary value. + /// </summary> + /// <remarks> + /// The LocalTemporary class is used to hold temporary values of a given + /// type to "simulate" the expression semantics on property and indexer + /// access whose return values are void. + /// + /// The local temporary is used to alter the normal flow of code generation + /// basically it creates a local variable, and its emit instruction generates + /// code to access this value, return its address or save its value. + /// </remarks> + public class LocalTemporary : Expression, IMemoryLocation { + LocalBuilder builder; + + public LocalTemporary (EmitContext ec, Type t) + { + type = t; + eclass = ExprClass.Value; + loc = Location.Null; + builder = ec.GetTemporaryStorage (t); + } + + public void Release (EmitContext ec) + { + ec.FreeTemporaryStorage (builder); + builder = null; + } + + public LocalTemporary (LocalBuilder b, Type t) + { + type = t; + eclass = ExprClass.Value; + loc = Location.Null; + builder = b; + } + + public override Expression DoResolve (EmitContext ec) + { + return this; + } + + public override void Emit (EmitContext ec) + { + ec.ig.Emit (OpCodes.Ldloc, builder); + } + + public void Store (EmitContext ec) + { + ec.ig.Emit (OpCodes.Stloc, builder); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + ec.ig.Emit (OpCodes.Ldloca, builder); + } + } + + /// <summary> + /// The Assign node takes care of assigning the value of source into + /// the expression represented by target. + /// </summary> + public class Assign : ExpressionStatement { + protected Expression target, source, real_source; + protected LocalTemporary temp = null, real_temp = null; + protected Assign embedded = null; + protected bool is_embedded = false; + protected bool must_free_temp = false; + + public Assign (Expression target, Expression source, Location l) + { + this.target = target; + this.source = this.real_source = source; + this.loc = l; + } + + protected Assign (Assign embedded, Location l) + : this (embedded.target, embedded.source, l) + { + this.is_embedded = true; + } + + public Expression Target { + get { + return target; + } + + set { + target = value; + } + } + + public Expression Source { + get { + return source; + } + + set { + source = value; + } + } + + public static void error70 (EventInfo ei, Location l) + { + Report.Error (70, l, "The event '" + ei.Name + + "' can only appear on the left-side of a += or -= (except when" + + " used from within the type '" + ei.DeclaringType + "')"); + } + + // + // Will return either `this' or an instance of `New'. + // + public override Expression DoResolve (EmitContext ec) + { + // Create an embedded assignment if our source is an assignment. + if (source is Assign) + source = embedded = new Assign ((Assign) source, loc); + + real_source = source = source.Resolve (ec); + if (source == null) + return null; + + // + // This is used in an embedded assignment. + // As an example, consider the statement "A = X = Y = Z". + // + if (is_embedded && !(source is Constant)) { + // If this is the innermost assignment (the "Y = Z" in our example), + // create a new temporary local, otherwise inherit that variable + // from our child (the "X = (Y = Z)" inherits the local from the + // "Y = Z" assignment). + if (embedded == null) + real_temp = temp = new LocalTemporary (ec, source.Type); + else + temp = embedded.temp; + + // Set the source to the new temporary variable. + // This means that the following target.ResolveLValue () will tell + // the target to read it's source value from that variable. + source = temp; + } + + // If we have an embedded assignment, use the embedded assignment's temporary + // local variable as source. + if (embedded != null) + source = (embedded.temp != null) ? embedded.temp : embedded.source; + + target = target.ResolveLValue (ec, source); + + if (target == null) + return null; + + Type target_type = target.Type; + Type source_type = real_source.Type; + + // If we're an embedded assignment, our parent will reuse our source as its + // source, it won't read from our target. + if (is_embedded) + type = source_type; + else + type = target_type; + eclass = ExprClass.Value; + + // + // If we are doing a property assignment, then + // set the `value' field on the property, and Resolve + // it. + // + if (target is PropertyExpr){ + PropertyExpr property_assign = (PropertyExpr) target; + + if (source_type != target_type){ + source = ConvertImplicitRequired (ec, source, target_type, loc); + if (source == null) + return null; + } + + // + // FIXME: Maybe handle this in the LValueResolve + // + if (!property_assign.VerifyAssignable ()) + return null; + + return this; + } + + if (target is IndexerAccess) { + return this; + } + + if (target is EventExpr) { + EventInfo ei = ((EventExpr) target).EventInfo; + + Expression ml = MemberLookup ( + ec, ec.ContainerType, ei.Name, + MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc); + + if (ml == null) { + // + // If this is the case, then the Event does not belong + // to this Type and so, according to the spec + // is allowed to only appear on the left hand of + // the += and -= operators + // + // Note that target will not appear as an EventExpr + // in the case it is being referenced within the same type container; + // it will appear as a FieldExpr in that case. + // + + if (!(source is Binary)) { + error70 (ei, loc); + return null; + } else { + Binary tmp = ((Binary) source); + if (tmp.Oper != Binary.Operator.Addition && + tmp.Oper != Binary.Operator.Subtraction) { + error70 (ei, loc); + return null; + } + } + } + } + + if (source is New && target_type.IsValueType){ + New n = (New) source; + + n.ValueTypeVariable = target; + return n; + } + + if (target.eclass != ExprClass.Variable && target.eclass != ExprClass.EventAccess){ + Report.Error (131, loc, + "Left hand of an assignment must be a variable, " + + "a property or an indexer"); + return null; + } + + if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) { + source.Error118 ("variable or value"); + return null; + } else if (source is MethodGroupExpr){ + ((MethodGroupExpr) source).ReportUsageError (); + return null; + } + + if (target_type == source_type) + return this; + + // + // If this assignemnt/operator was part of a compound binary + // operator, then we allow an explicit conversion, as detailed + // in the spec. + // + + if (this is CompoundAssign){ + CompoundAssign a = (CompoundAssign) this; + + Binary b = source as Binary; + if (b != null && b.IsBuiltinOperator){ + // + // 1. if the source is explicitly convertible to the + // target_type + // + + source = ConvertExplicit (ec, source, target_type, loc); + if (source == null){ + Error_CannotConvertImplicit (loc, source_type, target_type); + return null; + } + + // + // 2. and the original right side is implicitly convertible to + // the type of target_type. + // + if (StandardConversionExists (a.original_source, target_type)) + return this; + + Error_CannotConvertImplicit (loc, a.original_source.Type, target_type); + return null; + } + } + + source = ConvertImplicitRequired (ec, source, target_type, loc); + if (source == null) + return null; + + // If we're an embedded assignment, we need to create a new temporary variable + // for the converted value. Our parent will use this new variable as its source. + // The same applies when we have an embedded assignment - in this case, we need + // to convert our embedded assignment's temporary local variable to the correct + // type and store it in a new temporary local. + if (is_embedded || embedded != null) { + type = target_type; + temp = new LocalTemporary (ec, type); + must_free_temp = true; + } + + return this; + } + + Expression EmitEmbedded (EmitContext ec) + { + // Emit an embedded assignment. + + if (real_temp != null) { + // If we're the innermost assignment, `real_source' is the right-hand + // expression which gets assigned to all the variables left of it. + // Emit this expression and store its result in real_temp. + real_source.Emit (ec); + real_temp.Store (ec); + } + + if (embedded != null) + embedded.EmitEmbedded (ec); + + // This happens when we've done a type conversion, in this case source will be + // the expression which does the type conversion from real_temp. + // So emit it and store the result in temp; this is the var which will be read + // by our parent. + if (temp != real_temp) { + source.Emit (ec); + temp.Store (ec); + } + + Expression temp_source = (temp != null) ? temp : source; + ((IAssignMethod) target).EmitAssign (ec, temp_source); + return temp_source; + } + + void ReleaseEmbedded (EmitContext ec) + { + if (embedded != null) + embedded.ReleaseEmbedded (ec); + + if (real_temp != null) + real_temp.Release (ec); + + if (must_free_temp) + temp.Release (ec); + } + + void Emit (EmitContext ec, bool is_statement) + { + if (target is EventExpr) { + ((EventExpr) target).EmitAddOrRemove (ec, source); + return; + } + + // + // FIXME! We need a way to "probe" if the process can + // just use `dup' to propagate the result + // + IAssignMethod am = (IAssignMethod) target; + + if (this is CompoundAssign){ + am.CacheTemporaries (ec); + } + + Expression temp_source; + if (embedded != null) { + temp_source = embedded.EmitEmbedded (ec); + + if (temp != null) { + source.Emit (ec); + temp.Store (ec); + temp_source = temp; + } + } else + temp_source = source; + + if (is_statement) + am.EmitAssign (ec, temp_source); + else { + LocalTemporary tempo; + + tempo = new LocalTemporary (ec, source.Type); + + temp_source.Emit (ec); + tempo.Store (ec); + am.EmitAssign (ec, tempo); + tempo.Emit (ec); + tempo.Release (ec); + } + + if (embedded != null) { + if (temp != null) + temp.Release (ec); + embedded.ReleaseEmbedded (ec); + } + } + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + public override void EmitStatement (EmitContext ec) + { + Emit (ec, true); + } + } + + + // + // This class is used for compound assignments. + // + class CompoundAssign : Assign { + Binary.Operator op; + public Expression original_source; + + public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location l) + : base (target, source, l) + { + original_source = source; + this.op = op; + } + + public Expression ResolveSource (EmitContext ec) + { + return original_source.Resolve (ec); + } + + public override Expression DoResolve (EmitContext ec) + { + target = target.ResolveLValue (ec, source); + if (target == null) + return null; + + original_source = original_source.Resolve (ec); + if (original_source == null) + return null; + + // + // Only now we can decouple the original source/target + // into a tree, to guarantee that we do not have side + // effects. + // + source = new Binary (op, target, original_source, loc); + return base.DoResolve (ec); + } + } +} + + + + diff --git a/mcs/mbas/attribute.cs b/mcs/mbas/attribute.cs new file mode 100644 index 00000000000..988c435b52f --- /dev/null +++ b/mcs/mbas/attribute.cs @@ -0,0 +1,910 @@ +// +// attribute.cs: Attribute Handler +// +// Author: Ravi Pratap (ravi@ximian.com) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// +// + +using System; +using System.Diagnostics; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Mono.CSharp { + + public class Attribute { + public readonly string Name; + public readonly ArrayList Arguments; + + Location Location; + + public Type Type; + + // + // The following are only meaningful when the attribute + // being emitted is one of the builtin ones + // + AttributeTargets Targets; + bool AllowMultiple; + bool Inherited; + + bool UsageAttr = false; + + MethodImplOptions ImplOptions; + UnmanagedType UnmanagedType; + CustomAttributeBuilder cb; + + public Attribute (string name, ArrayList args, Location loc) + { + Name = name; + Arguments = args; + Location = loc; + } + + void Error_InvalidNamedArgument (string name) + { + Report.Error (617, Location, "'" + name + "' is not a valid named attribute " + + "argument. Named attribute arguments must be fields which are not " + + "readonly, static or const, or properties with a set accessor which "+ + "are not static."); + } + + void Error_AttributeArgumentNotValid () + { + Report.Error (182, Location, + "An attribute argument must be a constant expression, typeof " + + "expression or array creation expression"); + } + + static void Error_AttributeConstructorMismatch (Location loc) + { + Report.Error ( + -6, loc, + "Could not find a constructor for this argument list."); + } + + private Type CheckAttributeType (EmitContext ec) { + Type t; + bool isattributeclass = true; + + t = RootContext.LookupType (ec.DeclSpace, Name, true, Location); + if (t != null) { + isattributeclass = t.IsSubclassOf (TypeManager.attribute_type); + if (isattributeclass) + return t; + } + t = RootContext.LookupType (ec.DeclSpace, Name + "Attribute", true, Location); + if (t != null) { + if (t.IsSubclassOf (TypeManager.attribute_type)) + return t; + } + if (!isattributeclass) { + Report.Error (616, Location, "'" + Name + "': is not an attribute class"); + return null; + } + if (t != null) { + Report.Error (616, Location, "'" + Name + "Attribute': is not an attribute class"); + return null; + } + Report.Error ( + 246, Location, "Could not find attribute '" + Name + "' (are you" + + " missing a using directive or an assembly reference ?)"); + return null; + } + + public Type ResolveType (EmitContext ec) + { + Type = CheckAttributeType (ec); + return Type; + } + + + public CustomAttributeBuilder Resolve (EmitContext ec) + { + if (Type == null) + Type = CheckAttributeType (ec); + if (Type == null) + return null; + + bool MethodImplAttr = false; + bool MarshalAsAttr = false; + + UsageAttr = false; + + if (Type == TypeManager.attribute_usage_type) + UsageAttr = true; + if (Type == TypeManager.methodimpl_attr_type) + MethodImplAttr = true; + if (Type == TypeManager.marshal_as_attr_type) + MarshalAsAttr = true; + + // Now we extract the positional and named arguments + + ArrayList pos_args = new ArrayList (); + ArrayList named_args = new ArrayList (); + int pos_arg_count = 0; + + if (Arguments != null) { + pos_args = (ArrayList) Arguments [0]; + if (pos_args != null) + pos_arg_count = pos_args.Count; + if (Arguments.Count > 1) + named_args = (ArrayList) Arguments [1]; + } + + object [] pos_values = new object [pos_arg_count]; + + // + // First process positional arguments + // + + int i; + for (i = 0; i < pos_arg_count; i++) { + Argument a = (Argument) pos_args [i]; + Expression e; + + if (!a.Resolve (ec, Location)) + return null; + + e = a.Expr; + + if (e is Constant) { + pos_values [i] = ((Constant) e).GetValue (); + } else if (e is TypeOf) { + pos_values [i] = ((TypeOf) e).TypeArg; + } else { + Error_AttributeArgumentNotValid (); + return null; + } + + if (UsageAttr) + this.Targets = (AttributeTargets) pos_values [0]; + + if (MethodImplAttr) + this.ImplOptions = (MethodImplOptions) pos_values [0]; + + if (MarshalAsAttr) + this.UnmanagedType = + (System.Runtime.InteropServices.UnmanagedType) pos_values [0]; + } + + // + // Now process named arguments + // + + ArrayList field_infos = new ArrayList (); + ArrayList prop_infos = new ArrayList (); + ArrayList field_values = new ArrayList (); + ArrayList prop_values = new ArrayList (); + + for (i = 0; i < named_args.Count; i++) { + DictionaryEntry de = (DictionaryEntry) named_args [i]; + string member_name = (string) de.Key; + Argument a = (Argument) de.Value; + Expression e; + + if (!a.Resolve (ec, Location)) + return null; + + Expression member = Expression.MemberLookup ( + ec, Type, member_name, + MemberTypes.Field | MemberTypes.Property, + BindingFlags.Public | BindingFlags.Instance, + Location); + + if (member == null || !(member is PropertyExpr || member is FieldExpr)) { + Error_InvalidNamedArgument (member_name); + return null; + } + + e = a.Expr; + if (member is PropertyExpr) { + PropertyExpr pe = (PropertyExpr) member; + PropertyInfo pi = pe.PropertyInfo; + + if (!pi.CanWrite) { + Error_InvalidNamedArgument (member_name); + return null; + } + + if (e is Constant) { + object o = ((Constant) e).GetValue (); + prop_values.Add (o); + + if (UsageAttr) { + if (member_name == "AllowMultiple") + this.AllowMultiple = (bool) o; + if (member_name == "Inherited") + this.Inherited = (bool) o; + } + + } else if (e is TypeOf) { + prop_values.Add (((TypeOf) e).TypeArg); + } else { + Error_AttributeArgumentNotValid (); + return null; + } + + prop_infos.Add (pi); + + } else if (member is FieldExpr) { + FieldExpr fe = (FieldExpr) member; + FieldInfo fi = fe.FieldInfo; + + if (fi.IsInitOnly) { + Error_InvalidNamedArgument (member_name); + return null; + } + + // + // Handle charset here, and set the TypeAttributes + + if (e is Constant){ + object value = ((Constant) e).GetValue (); + + field_values.Add (value); + } else if (e is TypeOf) { + field_values.Add (((TypeOf) e).TypeArg); + } else { + Error_AttributeArgumentNotValid (); + return null; + } + + field_infos.Add (fi); + } + } + + Expression mg = Expression.MemberLookup ( + ec, Type, ".ctor", MemberTypes.Constructor, + BindingFlags.Public | BindingFlags.Instance, Location); + + if (mg == null) { + Error_AttributeConstructorMismatch (Location); + return null; + } + + MethodBase constructor = Invocation.OverloadResolve ( + ec, (MethodGroupExpr) mg, pos_args, Location); + + if (constructor == null) { + Error_AttributeConstructorMismatch (Location); + return null; + } + + // + // Now we perform some checks on the positional args as they + // cannot be null for a constructor which expects a parameter + // of type object + // + + ParameterData pd = Invocation.GetParameterData (constructor); + + for (int j = 0; j < pos_arg_count; ++j) { + Argument a = (Argument) pos_args [j]; + + if (a.Expr is NullLiteral && pd.ParameterType (j) == TypeManager.object_type) { + Error_AttributeArgumentNotValid (); + return null; + } + } + + PropertyInfo [] prop_info_arr = new PropertyInfo [prop_infos.Count]; + FieldInfo [] field_info_arr = new FieldInfo [field_infos.Count]; + object [] field_values_arr = new object [field_values.Count]; + object [] prop_values_arr = new object [prop_values.Count]; + + field_infos.CopyTo (field_info_arr, 0); + field_values.CopyTo (field_values_arr, 0); + + prop_values.CopyTo (prop_values_arr, 0); + prop_infos.CopyTo (prop_info_arr, 0); + + try { + cb = new CustomAttributeBuilder ( + (ConstructorInfo) constructor, pos_values, + prop_info_arr, prop_values_arr, + field_info_arr, field_values_arr); + + } catch (NullReferenceException) { + // + // Don't know what to do here + // + } catch { + // + // Sample: + // using System.ComponentModel; + // [DefaultValue (CollectionChangeAction.Add)] + // class X { static void Main () {} } + // + Report.Warning ( + -23, Location, + "The compiler can not encode this attribute in .NET due to\n" + + "\ta bug in the .NET runtime. Try the Mono runtime"); + } + + return cb; + } + + static string GetValidPlaces (Attribute attr) + { + StringBuilder sb = new StringBuilder (); + AttributeTargets targets = 0; + + TypeContainer a = TypeManager.LookupAttr (attr.Type); + + if (a == null) { + + System.Attribute [] attrs = null; + + try { + attrs = System.Attribute.GetCustomAttributes (attr.Type); + + } catch { + Report.Error (-20, attr.Location, "Cannot find attribute type " + attr.Name + + " (maybe you forgot to set the usage using the" + + " AttributeUsage attribute ?)."); + return null; + } + + foreach (System.Attribute tmp in attrs) + if (tmp is AttributeUsageAttribute) { + targets = ((AttributeUsageAttribute) tmp).ValidOn; + break; + } + } else + targets = a.Targets; + + + if ((targets & AttributeTargets.Assembly) != 0) + sb.Append ("'assembly' "); + + if ((targets & AttributeTargets.Class) != 0) + sb.Append ("'class' "); + + if ((targets & AttributeTargets.Constructor) != 0) + sb.Append ("'constructor' "); + + if ((targets & AttributeTargets.Delegate) != 0) + sb.Append ("'delegate' "); + + if ((targets & AttributeTargets.Enum) != 0) + sb.Append ("'enum' "); + + if ((targets & AttributeTargets.Event) != 0) + sb.Append ("'event' "); + + if ((targets & AttributeTargets.Field) != 0) + sb.Append ("'field' "); + + if ((targets & AttributeTargets.Interface) != 0) + sb.Append ("'interface' "); + + if ((targets & AttributeTargets.Method) != 0) + sb.Append ("'method' "); + + if ((targets & AttributeTargets.Module) != 0) + sb.Append ("'module' "); + + if ((targets & AttributeTargets.Parameter) != 0) + sb.Append ("'parameter' "); + + if ((targets & AttributeTargets.Property) != 0) + sb.Append ("'property' "); + + if ((targets & AttributeTargets.ReturnValue) != 0) + sb.Append ("'return value' "); + + if ((targets & AttributeTargets.Struct) != 0) + sb.Append ("'struct' "); + + return sb.ToString (); + + } + + public static void Error_AttributeNotValidForElement (Attribute a, Location loc) + { + Report.Error ( + 592, loc, "Attribute '" + a.Name + + "' is not valid on this declaration type. " + + "It is valid on " + GetValidPlaces (a) + "declarations only."); + } + + public static bool CheckAttribute (Attribute a, object element) + { + TypeContainer attr = TypeManager.LookupAttr (a.Type); + AttributeTargets targets = 0; + + + if (attr == null) { + + System.Attribute [] attrs = null; + + try { + attrs = System.Attribute.GetCustomAttributes (a.Type); + + } catch { + Report.Error (-20, a.Location, "Cannot find attribute type " + a.Name + + " (maybe you forgot to set the usage using the" + + " AttributeUsage attribute ?)."); + return false; + } + + foreach (System.Attribute tmp in attrs) + if (tmp is AttributeUsageAttribute) + targets = ((AttributeUsageAttribute) tmp).ValidOn; + } else + targets = attr.Targets; + + if (element is Class) { + if ((targets & AttributeTargets.Class) != 0) + return true; + else + return false; + + } else if (element is Struct) { + if ((targets & AttributeTargets.Struct) != 0) + return true; + else + return false; + } else if (element is Constructor) { + if ((targets & AttributeTargets.Constructor) != 0) + return true; + else + return false; + } else if (element is Delegate) { + if ((targets & AttributeTargets.Delegate) != 0) + return true; + else + return false; + } else if (element is Enum) { + if ((targets & AttributeTargets.Enum) != 0) + return true; + else + return false; + } else if (element is Event || element is InterfaceEvent) { + if ((targets & AttributeTargets.Event) != 0) + return true; + else + return false; + } else if (element is Field || element is FieldBuilder) { + if ((targets & AttributeTargets.Field) != 0) + return true; + else + return false; + } else if (element is Interface) { + if ((targets & AttributeTargets.Interface) != 0) + return true; + else + return false; + } else if (element is Method || element is Operator || element is InterfaceMethod || element is Accessor) { + if ((targets & AttributeTargets.Method) != 0) + return true; + else + return false; + } else if (element is ParameterBuilder) { + if ((targets & AttributeTargets.Parameter) != 0) + return true; + else + return false; + } else if (element is Property || element is Indexer || + element is InterfaceProperty || element is InterfaceIndexer) { + if ((targets & AttributeTargets.Property) != 0) + return true; + else + return false; + } else if (element is AssemblyBuilder){ + if ((targets & AttributeTargets.Assembly) != 0) + return true; + else + return false; + } + + return false; + } + + // + // This method should be invoked to pull the IndexerName attribute from an + // Indexer if it exists. + // + public static string ScanForIndexerName (EmitContext ec, Attributes opt_attrs) + { + if (opt_attrs == null) + return null; + if (opt_attrs.AttributeSections == null) + return null; + + foreach (AttributeSection asec in opt_attrs.AttributeSections) { + if (asec.Attributes == null) + continue; + + foreach (Attribute a in asec.Attributes){ + if (a.ResolveType (ec) == null) + return null; + + if (a.Type != TypeManager.indexer_name_type) + continue; + + // + // So we have found an IndexerName, pull the data out. + // + if (a.Arguments == null || a.Arguments [0] == null){ + Error_AttributeConstructorMismatch (a.Location); + return null; + } + ArrayList pos_args = (ArrayList) a.Arguments [0]; + if (pos_args.Count == 0){ + Error_AttributeConstructorMismatch (a.Location); + return null; + } + + Argument arg = (Argument) pos_args [0]; + if (!arg.Resolve (ec, a.Location)) + return null; + + Expression e = arg.Expr; + if (!(e is StringConstant)){ + Error_AttributeConstructorMismatch (a.Location); + return null; + } + + // + // Remove the attribute from the list + // + asec.Attributes.Remove (a); + + return (((StringConstant) e).Value); + } + } + return null; + } + + // + // This pulls the condition name out of a Conditional attribute + // + public string Conditional_GetConditionName () + { + // + // So we have a Conditional, pull the data out. + // + if (Arguments == null || Arguments [0] == null){ + Error_AttributeConstructorMismatch (Location); + return null; + } + + ArrayList pos_args = (ArrayList) Arguments [0]; + if (pos_args.Count != 1){ + Error_AttributeConstructorMismatch (Location); + return null; + } + + Argument arg = (Argument) pos_args [0]; + if (!(arg.Expr is StringConstant)){ + Error_AttributeConstructorMismatch (Location); + return null; + } + + return ((StringConstant) arg.Expr).Value; + } + + // + // This pulls the obsolete message and error flag out of an Obsolete attribute + // + public string Obsolete_GetObsoleteMessage (out bool is_error) + { + is_error = false; + // + // So we have an Obsolete, pull the data out. + // + if (Arguments == null || Arguments [0] == null) + return ""; + + ArrayList pos_args = (ArrayList) Arguments [0]; + if (pos_args.Count == 0) + return ""; + else if (pos_args.Count > 2){ + Error_AttributeConstructorMismatch (Location); + return null; + } + + Argument arg = (Argument) pos_args [0]; + if (!(arg.Expr is StringConstant)){ + Error_AttributeConstructorMismatch (Location); + return null; + } + + if (pos_args.Count == 2){ + Argument arg2 = (Argument) pos_args [1]; + if (!(arg2.Expr is BoolConstant)){ + Error_AttributeConstructorMismatch (Location); + return null; + } + is_error = ((BoolConstant) arg2.Expr).Value; + } + + return ((StringConstant) arg.Expr).Value; + } + + // + // Applies the attributes to the `builder'. + // + public static void ApplyAttributes (EmitContext ec, object builder, object kind, + Attributes opt_attrs, Location loc) + { + if (opt_attrs == null) + return; + if (opt_attrs.AttributeSections == null) + return; + + foreach (AttributeSection asec in opt_attrs.AttributeSections) { + if (asec.Attributes == null) + continue; + + if (asec.Target == "assembly" && !(builder is AssemblyBuilder)) + continue; + + foreach (Attribute a in asec.Attributes) { + CustomAttributeBuilder cb = a.Resolve (ec); + + if (cb == null) + continue; + + if (!(kind is TypeContainer)) + if (!CheckAttribute (a, kind)) { + Error_AttributeNotValidForElement (a, loc); + return; + } + + if (kind is Method || kind is Operator || kind is InterfaceMethod || + kind is Accessor) { + if (a.Type == TypeManager.methodimpl_attr_type) { + if (a.ImplOptions == MethodImplOptions.InternalCall) + ((MethodBuilder) builder). + SetImplementationFlags ( + MethodImplAttributes.InternalCall | + MethodImplAttributes.Runtime); + } else if (a.Type != TypeManager.dllimport_type){ + ((MethodBuilder) builder).SetCustomAttribute (cb); + } + } else if (kind is Constructor) { + ((ConstructorBuilder) builder).SetCustomAttribute (cb); + } else if (kind is Field) { + ((FieldBuilder) builder).SetCustomAttribute (cb); + } else if (kind is Property || kind is Indexer || + kind is InterfaceProperty || kind is InterfaceIndexer) { + ((PropertyBuilder) builder).SetCustomAttribute (cb); + } else if (kind is Event || kind is InterfaceEvent) { + ((MyEventBuilder) builder).SetCustomAttribute (cb); + } else if (kind is ParameterBuilder) { + + if (a.Type == TypeManager.marshal_as_attr_type) { + UnmanagedMarshal marshal = + UnmanagedMarshal.DefineUnmanagedMarshal (a.UnmanagedType); + + ((ParameterBuilder) builder).SetMarshal (marshal); + } else + ((ParameterBuilder) builder).SetCustomAttribute (cb); + + } else if (kind is Enum) { + ((TypeBuilder) builder).SetCustomAttribute (cb); + + } else if (kind is TypeContainer) { + TypeContainer tc = (TypeContainer) kind; + + if (a.UsageAttr) { + tc.Targets = a.Targets; + tc.AllowMultiple = a.AllowMultiple; + tc.Inherited = a.Inherited; + + } else if (a.Type == TypeManager.default_member_type) { + if (tc.Indexers != null) { + Report.Error (646, loc, + "Cannot specify the DefaultMember attribute on" + + " a type containing an indexer"); + return; + } + + } else { + if (!CheckAttribute (a, kind)) { + Error_AttributeNotValidForElement (a, loc); + return; + } + } + + try { + ((TypeBuilder) builder).SetCustomAttribute (cb); + } catch (System.ArgumentException) { + Report.Warning ( + -21, loc, + "The CharSet named property on StructLayout\n"+ + "\tdoes not work correctly on Microsoft.NET\n"+ + "\tYou might want to remove the CharSet declaration\n"+ + "\tor compile using the Mono runtime instead of the\n"+ + "\tMicrosoft .NET runtime"); + } + + } else if (kind is Interface) { + Interface iface = (Interface) kind; + + if ((a.Type == TypeManager.default_member_type) && + (iface.InterfaceIndexers != null)) { + Report.Error ( + 646, loc, + "Cannot specify the DefaultMember attribute on" + + " a type containing an indexer"); + return; + } + + if (!CheckAttribute (a, kind)) { + Error_AttributeNotValidForElement (a, loc); + return; + } + + ((TypeBuilder) builder).SetCustomAttribute (cb); + } else if (kind is AssemblyBuilder){ + ((AssemblyBuilder) builder).SetCustomAttribute (cb); + } else if (kind is ModuleBuilder) { + ((ModuleBuilder) builder).SetCustomAttribute (cb); + } else if (kind is FieldBuilder) { + ((FieldBuilder) builder).SetCustomAttribute (cb); + } else + throw new Exception ("Unknown kind: " + kind); + } + } + } + + public MethodBuilder DefinePInvokeMethod (EmitContext ec, TypeBuilder builder, string name, + MethodAttributes flags, Type ret_type, Type [] param_types) + { + // + // We extract from the attribute the information we need + // + + if (Arguments == null) { + Console.WriteLine ("Internal error : this is not supposed to happen !"); + return null; + } + + Type = CheckAttributeType (ec); + if (Type == null) + return null; + + ArrayList named_args = new ArrayList (); + + ArrayList pos_args = (ArrayList) Arguments [0]; + if (Arguments.Count > 1) + named_args = (ArrayList) Arguments [1]; + + + string dll_name = null; + + Argument tmp = (Argument) pos_args [0]; + + if (!tmp.Resolve (ec, Location)) + return null; + + if (tmp.Expr is Constant) + dll_name = (string) ((Constant) tmp.Expr).GetValue (); + else { + Error_AttributeArgumentNotValid (); + return null; + } + + // Now we process the named arguments + CallingConvention cc = CallingConvention.Winapi; + CharSet charset = CharSet.Ansi; + bool preserve_sig = true; + bool exact_spelling = false; + bool set_last_err = false; + string entry_point = null; + + for (int i = 0; i < named_args.Count; i++) { + + DictionaryEntry de = (DictionaryEntry) named_args [i]; + + string member_name = (string) de.Key; + Argument a = (Argument) de.Value; + + if (!a.Resolve (ec, Location)) + return null; + + Expression member = Expression.MemberLookup ( + ec, Type, member_name, + MemberTypes.Field | MemberTypes.Property, + BindingFlags.Public | BindingFlags.Instance, + Location); + + if (member == null || !(member is FieldExpr)) { + Error_InvalidNamedArgument (member_name); + return null; + } + + if (member is FieldExpr) { + FieldExpr fe = (FieldExpr) member; + FieldInfo fi = fe.FieldInfo; + + if (fi.IsInitOnly) { + Error_InvalidNamedArgument (member_name); + return null; + } + + if (a.Expr is Constant) { + Constant c = (Constant) a.Expr; + + if (member_name == "CallingConvention") + cc = (CallingConvention) c.GetValue (); + else if (member_name == "CharSet") + charset = (CharSet) c.GetValue (); + else if (member_name == "EntryPoint") + entry_point = (string) c.GetValue (); + else if (member_name == "SetLastError") + set_last_err = (bool) c.GetValue (); + else if (member_name == "ExactSpelling") + exact_spelling = (bool) c.GetValue (); + else if (member_name == "PreserveSig") + preserve_sig = (bool) c.GetValue (); + } else { + Error_AttributeArgumentNotValid (); + return null; + } + + } + } + + if (entry_point == null) + entry_point = name; + + MethodBuilder mb = builder.DefinePInvokeMethod ( + name, dll_name, entry_point, flags | MethodAttributes.HideBySig, + CallingConventions.Standard, + ret_type, + param_types, + cc, + charset); + + if (preserve_sig) + mb.SetImplementationFlags (MethodImplAttributes.PreserveSig); + + return mb; + } + + } + + public class AttributeSection { + + public readonly string Target; + public readonly ArrayList Attributes; + + public AttributeSection (string target, ArrayList attrs) + { + Target = target; + Attributes = attrs; + } + + } + + public class Attributes { + public ArrayList AttributeSections; + public Location Location; + + public Attributes (AttributeSection a, Location loc) + { + AttributeSections = new ArrayList (); + AttributeSections.Add (a); + + } + + public void AddAttribute (AttributeSection a) + { + if (a != null) + AttributeSections.Add (a); + } + } +} diff --git a/mcs/mbas/cfold.cs b/mcs/mbas/cfold.cs new file mode 100644 index 00000000000..72726d97335 --- /dev/null +++ b/mcs/mbas/cfold.cs @@ -0,0 +1,1088 @@ +// +// cfold.cs: Constant Folding +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2002 Ximian, Inc. +// + +using System; + +namespace Mono.CSharp { + + public class ConstantFold { + + // + // Performs the numeric promotions on the left and right expresions + // and desposits the results on `lc' and `rc'. + // + // On success, the types of `lc' and `rc' on output will always match, + // and the pair will be one of: + // + // (double, double) + // (float, float) + // (ulong, ulong) + // (long, long) + // (uint, uint) + // (int, int) + // + static void DoConstantNumericPromotions (EmitContext ec, Binary.Operator oper, + ref Constant left, ref Constant right, + Location loc) + { + if (left is DoubleConstant || right is DoubleConstant){ + // + // If either side is a double, convert the other to a double + // + if (!(left is DoubleConstant)) + left = left.ToDouble (loc); + + if (!(right is DoubleConstant)) + right = right.ToDouble (loc); + return; + } else if (left is FloatConstant || right is FloatConstant) { + // + // If either side is a float, convert the other to a float + // + if (!(left is FloatConstant)) + left = left.ToFloat (loc); + + if (!(right is FloatConstant)) + right = right.ToFloat (loc); +; return; + } else if (left is ULongConstant || right is ULongConstant){ + // + // If either operand is of type ulong, the other operand is + // converted to type ulong. or an error ocurrs if the other + // operand is of type sbyte, short, int or long + // + Constant match, other; + + if (left is ULongConstant){ + other = right; + match = left; + if (!(right is ULongConstant)) + right = right.ToULong (loc); + } else { + other = left; + match = right; + left = left.ToULong (loc); + } + +#if WRONG + if (other is SByteConstant || other is ShortConstant || + other is IntConstant || other is LongConstant){ + Binary.Error_OperatorAmbiguous + (loc, oper, other.Type, match.Type); + left = null; + right = null; + } +#endif + return; + } else if (left is LongConstant || right is LongConstant){ + // + // If either operand is of type long, the other operand is converted + // to type long. + // + if (!(left is LongConstant)) + left = left.ToLong (loc); + else if (!(right is LongConstant)) + right = right.ToLong (loc); + return; + } else if (left is UIntConstant || right is UIntConstant){ + // + // If either operand is of type uint, and the other + // operand is of type sbyte, short or int, the operands are + // converted to type long. + // + Constant match, other; + if (left is UIntConstant){ + other = right; + match = left; + } else { + other = left; + match = right; + } + + // Nothing to do. + if (other is UIntConstant) + return; + + if (other is SByteConstant || other is ShortConstant || + other is IntConstant){ + left = left.ToLong (loc); + right = right.ToLong (loc); + } + + return; + } else if (left is EnumConstant || right is EnumConstant){ + // + // If either operand is an enum constant, the other one must + // be implicitly convertable to that enum's underlying type. + // + EnumConstant match; + Constant other; + if (left is EnumConstant){ + other = right; + match = (EnumConstant) left; + } else { + other = left; + match = (EnumConstant) right; + } + + bool need_check = (other is EnumConstant) || + ((oper != Binary.Operator.Addition) && + (oper != Binary.Operator.Subtraction)); + + if (need_check && + !Expression.ImplicitConversionExists (ec, match, other.Type)) { + Expression.Error_CannotConvertImplicit (loc, match.Type, other.Type); + left = null; + right = null; + return; + } + + if (left is EnumConstant) + left = ((EnumConstant) left).Child; + if (right is EnumConstant) + right = ((EnumConstant) right).Child; + return; + + } else { + // + // Force conversions to int32 + // + if (!(left is IntConstant)) + left = left.ToInt (loc); + if (!(right is IntConstant)) + right = right.ToInt (loc); + } + return; + } + + static void Error_CompileTimeOverflow (Location loc) + { + Report.Error (220, loc, "The operation overflows at compile time in checked mode"); + } + + /// <summary> + /// Constant expression folder for binary operations. + /// + /// Returns null if the expression can not be folded. + /// </summary> + static public Expression BinaryFold (EmitContext ec, Binary.Operator oper, + Constant left, Constant right, Location loc) + { + Type lt = left.Type; + Type rt = right.Type; + Type result_type = null; + bool bool_res; + + // + // Enumerator folding + // + if (rt == lt && left is EnumConstant) + result_type = lt; + + // + // During an enum evaluation, we need to unwrap enumerations + // + if (ec.InEnumContext){ + if (left is EnumConstant) + left = ((EnumConstant) left).Child; + + if (right is EnumConstant) + right = ((EnumConstant) right).Child; + } + + Type wrap_as; + Constant result = null; + switch (oper){ + case Binary.Operator.BitwiseOr: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + if (left is IntConstant){ + IntConstant v; + int res = ((IntConstant) left).Value | ((IntConstant) right).Value; + + v = new IntConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } else if (left is UIntConstant){ + UIntConstant v; + uint res = ((UIntConstant)left).Value | ((UIntConstant)right).Value; + + v = new UIntConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } else if (left is LongConstant){ + LongConstant v; + long res = ((LongConstant)left).Value | ((LongConstant)right).Value; + + v = new LongConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } else if (left is ULongConstant){ + ULongConstant v; + ulong res = ((ULongConstant)left).Value | + ((ULongConstant)right).Value; + + v = new ULongConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } + break; + + case Binary.Operator.BitwiseAnd: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + if (left is IntConstant){ + IntConstant v; + int res = ((IntConstant) left).Value & ((IntConstant) right).Value; + + v = new IntConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } else if (left is UIntConstant){ + UIntConstant v; + uint res = ((UIntConstant)left).Value & ((UIntConstant)right).Value; + + v = new UIntConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } else if (left is LongConstant){ + LongConstant v; + long res = ((LongConstant)left).Value & ((LongConstant)right).Value; + + v = new LongConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } else if (left is ULongConstant){ + ULongConstant v; + ulong res = ((ULongConstant)left).Value & + ((ULongConstant)right).Value; + + v = new ULongConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } + break; + + case Binary.Operator.ExclusiveOr: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + if (left is IntConstant){ + IntConstant v; + int res = ((IntConstant) left).Value ^ ((IntConstant) right).Value; + + v = new IntConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } else if (left is UIntConstant){ + UIntConstant v; + uint res = ((UIntConstant)left).Value ^ ((UIntConstant)right).Value; + + v = new UIntConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } else if (left is LongConstant){ + LongConstant v; + long res = ((LongConstant)left).Value ^ ((LongConstant)right).Value; + + v = new LongConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } else if (left is ULongConstant){ + ULongConstant v; + ulong res = ((ULongConstant)left).Value ^ + ((ULongConstant)right).Value; + + v = new ULongConstant (res); + if (result_type == null) + return v; + else + return new EnumConstant (v, result_type); + } + break; + + case Binary.Operator.Addition: + bool left_is_string = left is StringConstant; + bool right_is_string = right is StringConstant; + + // + // If both sides are strings, then concatenate, if + // one is a string, and the other is not, then defer + // to runtime concatenation + // + wrap_as = null; + if (left_is_string || right_is_string){ + if (left_is_string && right_is_string) + return new StringConstant ( + ((StringConstant) left).Value + + ((StringConstant) right).Value); + + return null; + } + + // + // handle "E operator + (E x, U y)" + // handle "E operator + (Y y, E x)" + // + // note that E operator + (E x, E y) is invalid + // + if (left is EnumConstant){ + if (right is EnumConstant){ + return null; + } + if (((EnumConstant) left).Child.Type != right.Type) + return null; + + wrap_as = left.Type; + } else if (right is EnumConstant){ + if (((EnumConstant) right).Child.Type != left.Type) + return null; + wrap_as = right.Type; + } + + result = null; + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value + + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value + + ((DoubleConstant) right).Value); + + result = new DoubleConstant (res); + } else if (left is FloatConstant){ + float res; + + if (ec.ConstantCheckState) + res = checked (((FloatConstant) left).Value + + ((FloatConstant) right).Value); + else + res = unchecked (((FloatConstant) left).Value + + ((FloatConstant) right).Value); + + result = new FloatConstant (res); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value + + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value + + ((ULongConstant) right).Value); + + result = new ULongConstant (res); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value + + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value + + ((LongConstant) right).Value); + + result = new LongConstant (res); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value + + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value + + ((UIntConstant) right).Value); + + result = new UIntConstant (res); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value + + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value + + ((IntConstant) right).Value); + + result = new IntConstant (res); + } else { + throw new Exception ( "Unexepected input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (loc); + } + + if (wrap_as != null) + return new EnumConstant (result, wrap_as); + else + return result; + + case Binary.Operator.Subtraction: + // + // handle "E operator - (E x, U y)" + // handle "E operator - (Y y, E x)" + // handle "U operator - (E x, E y)" + // + wrap_as = null; + if (left is EnumConstant){ + if (right is EnumConstant){ + if (left.Type == right.Type) + wrap_as = TypeManager.EnumToUnderlying (left.Type); + else + return null; + } + if (((EnumConstant) left).Child.Type != right.Type) + return null; + + wrap_as = left.Type; + } else if (right is EnumConstant){ + if (((EnumConstant) right).Child.Type != left.Type) + return null; + wrap_as = right.Type; + } + + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value - + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value - + ((DoubleConstant) right).Value); + + result = new DoubleConstant (res); + } else if (left is FloatConstant){ + float res; + + if (ec.ConstantCheckState) + res = checked (((FloatConstant) left).Value - + ((FloatConstant) right).Value); + else + res = unchecked (((FloatConstant) left).Value - + ((FloatConstant) right).Value); + + result = new FloatConstant (res); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value - + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value - + ((ULongConstant) right).Value); + + result = new ULongConstant (res); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value - + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value - + ((LongConstant) right).Value); + + result = new LongConstant (res); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value - + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value - + ((UIntConstant) right).Value); + + result = new UIntConstant (res); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value - + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value - + ((IntConstant) right).Value); + + result = new IntConstant (res); + } else { + throw new Exception ( "Unexepected input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (loc); + } + if (wrap_as != null) + return new EnumConstant (result, wrap_as); + else + return result; + + case Binary.Operator.Multiply: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value * + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value * + ((DoubleConstant) right).Value); + + return new DoubleConstant (res); + } else if (left is FloatConstant){ + float res; + + if (ec.ConstantCheckState) + res = checked (((FloatConstant) left).Value * + ((FloatConstant) right).Value); + else + res = unchecked (((FloatConstant) left).Value * + ((FloatConstant) right).Value); + + return new FloatConstant (res); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value * + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value * + ((ULongConstant) right).Value); + + return new ULongConstant (res); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value * + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value * + ((LongConstant) right).Value); + + return new LongConstant (res); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value * + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value * + ((UIntConstant) right).Value); + + return new UIntConstant (res); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value * + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value * + ((IntConstant) right).Value); + + return new IntConstant (res); + } else { + throw new Exception ( "Unexepected input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (loc); + } + break; + + case Binary.Operator.Division: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value / + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value / + ((DoubleConstant) right).Value); + + return new DoubleConstant (res); + } else if (left is FloatConstant){ + float res; + + if (ec.ConstantCheckState) + res = checked (((FloatConstant) left).Value / + ((FloatConstant) right).Value); + else + res = unchecked (((FloatConstant) left).Value / + ((FloatConstant) right).Value); + + return new FloatConstant (res); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value / + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value / + ((ULongConstant) right).Value); + + return new ULongConstant (res); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value / + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value / + ((LongConstant) right).Value); + + return new LongConstant (res); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value / + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value / + ((UIntConstant) right).Value); + + return new UIntConstant (res); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value / + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value / + ((IntConstant) right).Value); + + return new IntConstant (res); + } else { + throw new Exception ( "Unexepected input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (loc); + + } catch (DivideByZeroException) { + Report.Error (020, loc, "Division by constant zero"); + } + + break; + + case Binary.Operator.Modulus: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value % + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value % + ((DoubleConstant) right).Value); + + return new DoubleConstant (res); + } else if (left is FloatConstant){ + float res; + + if (ec.ConstantCheckState) + res = checked (((FloatConstant) left).Value % + ((FloatConstant) right).Value); + else + res = unchecked (((FloatConstant) left).Value % + ((FloatConstant) right).Value); + + return new FloatConstant (res); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value % + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value % + ((ULongConstant) right).Value); + + return new ULongConstant (res); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value % + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value % + ((LongConstant) right).Value); + + return new LongConstant (res); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value % + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value % + ((UIntConstant) right).Value); + + return new UIntConstant (res); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value % + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value % + ((IntConstant) right).Value); + + return new IntConstant (res); + } else { + throw new Exception ( "Unexepected input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (loc); + } + break; + + // + // There is no overflow checking on left shift + // + case Binary.Operator.LeftShift: + IntConstant ic = right.ToInt (loc); + if (ic == null){ + Binary.Error_OperatorCannotBeApplied (loc, "<<", lt, rt); + return null; + } + int lshift_val = ic.Value; + + IntConstant lic; + if ((lic = left.ConvertToInt ()) != null) + return new IntConstant (lic.Value << lshift_val); + + UIntConstant luic; + if ((luic = left.ConvertToUInt ()) != null) + return new UIntConstant (luic.Value << lshift_val); + + LongConstant llc; + if ((llc = left.ConvertToLong ()) != null) + return new LongConstant (llc.Value << lshift_val); + + ULongConstant lulc; + if ((lulc = left.ConvertToULong ()) != null) + return new ULongConstant (lulc.Value << lshift_val); + + Binary.Error_OperatorCannotBeApplied (loc, "<<", lt, rt); + break; + + // + // There is no overflow checking on right shift + // + case Binary.Operator.RightShift: + IntConstant sic = right.ToInt (loc); + if (sic == null){ + Binary.Error_OperatorCannotBeApplied (loc, ">>", lt, rt); + return null; + } + int rshift_val = sic.Value; + + IntConstant ric; + if ((ric = left.ConvertToInt ()) != null) + return new IntConstant (ric.Value >> rshift_val); + + UIntConstant ruic; + if ((ruic = left.ConvertToUInt ()) != null) + return new UIntConstant (ruic.Value >> rshift_val); + + LongConstant rlc; + if ((rlc = left.ConvertToLong ()) != null) + return new LongConstant (rlc.Value >> rshift_val); + + ULongConstant rulc; + if ((rulc = left.ConvertToULong ()) != null) + return new ULongConstant (rulc.Value >> rshift_val); + + Binary.Error_OperatorCannotBeApplied (loc, ">>", lt, rt); + break; + + case Binary.Operator.LogicalAnd: + if (left is BoolConstant && right is BoolConstant){ + return new BoolConstant ( + ((BoolConstant) left).Value && + ((BoolConstant) right).Value); + } + break; + + case Binary.Operator.LogicalOr: + if (left is BoolConstant && right is BoolConstant){ + return new BoolConstant ( + ((BoolConstant) left).Value || + ((BoolConstant) right).Value); + } + break; + + case Binary.Operator.Equality: + if (left is BoolConstant && right is BoolConstant){ + return new BoolConstant ( + ((BoolConstant) left).Value == + ((BoolConstant) right).Value); + + } + if (left is StringConstant && right is StringConstant){ + return new BoolConstant ( + ((StringConstant) left).Value == + ((StringConstant) right).Value); + + } + + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value == + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).Value == + ((FloatConstant) right).Value; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value == + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value == + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value == + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value == + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (bool_res); + + case Binary.Operator.Inequality: + if (left is BoolConstant && right is BoolConstant){ + return new BoolConstant ( + ((BoolConstant) left).Value != + ((BoolConstant) right).Value); + } + if (left is StringConstant && right is StringConstant){ + return new BoolConstant ( + ((StringConstant) left).Value != + ((StringConstant) right).Value); + + } + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value != + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).Value != + ((FloatConstant) right).Value; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value != + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value != + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value != + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value != + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (bool_res); + + case Binary.Operator.LessThan: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value < + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).Value < + ((FloatConstant) right).Value; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value < + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value < + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value < + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value < + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (bool_res); + + case Binary.Operator.GreaterThan: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value > + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).Value > + ((FloatConstant) right).Value; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value > + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value > + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value > + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value > + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (bool_res); + + case Binary.Operator.GreaterThanOrEqual: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value >= + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).Value >= + ((FloatConstant) right).Value; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value >= + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value >= + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value >= + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value >= + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (bool_res); + + case Binary.Operator.LessThanOrEqual: + DoConstantNumericPromotions (ec, oper, ref left, ref right, loc); + if (left == null || right == null) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value <= + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).Value <= + ((FloatConstant) right).Value; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value <= + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value <= + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value <= + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value <= + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (bool_res); + } + + return null; + } + } +} diff --git a/mcs/mbas/class.cs b/mcs/mbas/class.cs new file mode 100644 index 00000000000..79bc25f53a0 --- /dev/null +++ b/mcs/mbas/class.cs @@ -0,0 +1,4835 @@ + +// +// class.cs: Class and Struct handlers +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Martin Baulig (martin@gnome.org) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001, 2002 Ximian, Inc (http://www.ximian.com) +// +// +// 2002-10-11 Miguel de Icaza <miguel@ximian.com> +// +// * class.cs: Following the comment from 2002-09-26 to AddMethod, I +// have fixed a remaining problem: not every AddXXXX was adding a +// fully qualified name. +// +// Now everyone registers a fully qualified name in the DeclSpace as +// being defined instead of the partial name. +// +// Downsides: we are slower than we need to be due to the excess +// copies and the names being registered this way. +// +// The reason for this is that we currently depend (on the corlib +// bootstrap for instance) that types are fully qualified, because +// we dump all the types in the namespace, and we should really have +// types inserted into the proper namespace, so we can only store the +// basenames in the defined_names array. +// +// +#define CACHE +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Diagnostics.SymbolStore; + +namespace Mono.CSharp { + + /// <summary> + /// This is the base class for structs and classes. + /// </summary> + public class TypeContainer : DeclSpace, IMemberContainer { + // Holds a list of classes and structures + ArrayList types; + + // Holds the list of properties + ArrayList properties; + + // Holds the list of enumerations + ArrayList enums; + + // Holds the list of delegates + ArrayList delegates; + + // Holds the list of constructors + ArrayList instance_constructors; + + // Holds the list of fields + ArrayList fields; + + // Holds a list of fields that have initializers + ArrayList initialized_fields; + + // Holds a list of static fields that have initializers + ArrayList initialized_static_fields; + + // Holds the list of constants + ArrayList constants; + + // Holds the list of + ArrayList interfaces; + + // Holds order in which interfaces must be closed + ArrayList interface_order; + + // Holds the methods. + ArrayList methods; + + // Holds the events + ArrayList events; + + // Holds the indexers + ArrayList indexers; + + // Holds the operators + ArrayList operators; + + // The emit context for toplevel objects. + EmitContext ec; + + // + // Pointers to the default constructor and the default static constructor + // + Constructor default_constructor; + Constructor default_static_constructor; + + // + // Whether we have seen a static constructor for this class or not + // + bool have_static_constructor = false; + + // + // Whether we have at least one non-static field + // + bool have_nonstatic_fields = false; + + // + // This one is computed after we can distinguish interfaces + // from classes from the arraylist `type_bases' + // + string base_class_name; + + ArrayList type_bases; + + // Attributes for this type + protected Attributes attributes; + + // Information in the case we are an attribute type + + public AttributeTargets Targets = AttributeTargets.All; + public bool AllowMultiple = false; + public bool Inherited; + + // The interfaces we implement. + Type [] ifaces; + + // The parent member container and our member cache + IMemberContainer parent_container; + MemberCache member_cache; + + // + // The indexer name for this class + // + public string IndexerName; + + public TypeContainer (TypeContainer parent, string name, Location l) + : base (parent, name, l) + { + string n; + types = new ArrayList (); + + if (parent == null) + n = ""; + else + n = parent.Name; + + base_class_name = null; + + //Console.WriteLine ("New class " + name + " inside " + n); + } + + public AdditionResult AddConstant (Const constant) + { + AdditionResult res; + string basename = constant.Name; + + if ((res = IsValid (basename)) != AdditionResult.Success) + return res; + + if (constants == null) + constants = new ArrayList (); + + constants.Add (constant); + DefineName (Name + "." + basename, constant); + + return AdditionResult.Success; + } + + public AdditionResult AddEnum (Mono.CSharp.Enum e) + { + AdditionResult res; + + if ((res = IsValid (e.Basename)) != AdditionResult.Success) + return res; + + if (enums == null) + enums = new ArrayList (); + + enums.Add (e); + DefineName (e.Name, e); + + return AdditionResult.Success; + } + + public AdditionResult AddClass (Class c) + { + AdditionResult res; + + if ((res = IsValid (c.Basename)) != AdditionResult.Success) + return res; + + + + DefineName (c.Name, c); + types.Add (c); + + // FIXME: Do we really need to explicitly add an empty default static constructor? + if (c.default_static_constructor == null) + { + bool isModule = c is Mono.MonoBASIC.Module; + Constructor dc = new Constructor ("New", Parameters.EmptyReadOnlyParameters, null, c.Location); + dc.ModFlags = isModule ? Modifiers.PUBLIC | Modifiers.STATIC : Modifiers.PUBLIC; + c.AddConstructor (dc); + } + // -------------------------------------------------------------- + + return AdditionResult.Success; + } + + public AdditionResult AddStruct (Struct s) + { + AdditionResult res; + + if ((res = IsValid (s.Basename)) != AdditionResult.Success) + return res; + + DefineName (s.Name, s); + types.Add (s); + + return AdditionResult.Success; + } + + public AdditionResult AddDelegate (Delegate d) + { + AdditionResult res; + + if ((res = IsValid (d.Basename)) != AdditionResult.Success) + return res; + + if (delegates == null) + delegates = new ArrayList (); + + DefineName (d.Name, d); + delegates.Add (d); + + return AdditionResult.Success; + } + + public AdditionResult AddMethod (Method method) + { + string basename = method.Name; + string fullname = Name + "." + basename; + + Object value = defined_names [fullname]; + + if (value != null && (!(value is Method))) + return AdditionResult.NameExists; + + if (basename == Basename) + return AdditionResult.EnclosingClash; + + if (methods == null) + methods = new ArrayList (); + + if (method.Name.IndexOf (".") != -1) + methods.Insert (0, method); + else + methods.Add (method); + + if (value == null) + DefineName (fullname, method); + + return AdditionResult.Success; + } + + public AdditionResult AddConstructor (Constructor c) + { + if (c.Name != "New") + return AdditionResult.NotAConstructor; + + bool is_static = (c.ModFlags & Modifiers.STATIC) != 0; + + if (is_static){ + have_static_constructor = true; + if (default_static_constructor != null){ + Console.WriteLine ("I have a static constructor already"); + Console.WriteLine (" " + default_static_constructor); + return AdditionResult.MethodExists; + } + + default_static_constructor = c; + } else { + if (c.IsDefault ()){ + if (default_constructor != null) + return AdditionResult.MethodExists; + default_constructor = c; + } + + if (instance_constructors == null) + instance_constructors = new ArrayList (); + + instance_constructors.Add (c); + } + + return AdditionResult.Success; + } + + public AdditionResult AddInterface (Interface iface) + { + AdditionResult res; + + if ((res = IsValid (iface.Basename)) != AdditionResult.Success) + return res; + + if (interfaces == null) + interfaces = new ArrayList (); + interfaces.Add (iface); + DefineName (iface.Name, iface); + + return AdditionResult.Success; + } + + public AdditionResult AddField (Field field) + { + AdditionResult res; + string basename = field.Name; + + if ((res = IsValid (basename)) != AdditionResult.Success) + return res; + + if (fields == null) + fields = new ArrayList (); + + fields.Add (field); + + if (field.HasInitializer){ + if ((field.ModFlags & Modifiers.STATIC) != 0) { + if (initialized_static_fields == null) + initialized_static_fields = new ArrayList (); + + initialized_static_fields.Add (field); + + // + // We have not seen a static constructor, + // but we will provide static initialization of fields + // + have_static_constructor = true; + } else { + if (initialized_fields == null) + initialized_fields = new ArrayList (); + + initialized_fields.Add (field); + } + } + + if ((field.ModFlags & Modifiers.STATIC) == 0) + have_nonstatic_fields = true; + + DefineName (Name + "." + basename, field); + return AdditionResult.Success; + } + + public AdditionResult AddProperty (Property prop) + { + AdditionResult res; + string basename = prop.Name; + + if ((res = IsValid (basename)) != AdditionResult.Success) + return res; + + if (properties == null) + properties = new ArrayList (); + + if (prop.Name.IndexOf (".") != -1) + properties.Insert (0, prop); + else + properties.Add (prop); + DefineName (Name + "." + basename, prop); + + return AdditionResult.Success; + } + + public AdditionResult AddEvent (Event e) + { + AdditionResult res; + string basename = e.Name; + + if ((res = IsValid (basename)) != AdditionResult.Success) + return res; + + if (events == null) + events = new ArrayList (); + + events.Add (e); + DefineName (Name + "." + basename, e); + + return AdditionResult.Success; + } + + public AdditionResult AddIndexer (Indexer i) + { + if (indexers == null) + indexers = new ArrayList (); + + if (i.InterfaceType != null) + indexers.Insert (0, i); + else + indexers.Add (i); + + return AdditionResult.Success; + } + + public AdditionResult AddOperator (Operator op) + { + if (operators == null) + operators = new ArrayList (); + + operators.Add (op); + + return AdditionResult.Success; + } + + public void RegisterOrder (Interface iface) + { + if (interface_order == null) + interface_order = new ArrayList (); + + interface_order.Add (iface); + } + + public ArrayList Types { + get { + return types; + } + } + + public ArrayList Methods { + get { + return methods; + } + } + + public ArrayList Constants { + get { + return constants; + } + } + + public ArrayList Interfaces { + get { + return interfaces; + } + } + + public string Base { + get { + return base_class_name; + } + } + + public ArrayList Bases { + get { + return type_bases; + } + + set { + type_bases = value; + } + } + + public ArrayList Fields { + get { + return fields; + } + + set { + fields = value; + } + } + + public ArrayList InstanceConstructors { + get { + return instance_constructors; + } + } + + public ArrayList Properties { + get { + return properties; + } + } + + public ArrayList Events { + get { + return events; + } + } + + public ArrayList Enums { + get { + return enums; + } + } + + public ArrayList Indexers { + get { + return indexers; + } + } + + public ArrayList Operators { + get { + return operators; + } + } + + public ArrayList Delegates { + get { + return delegates; + } + } + + public Attributes OptAttributes { + get { + return attributes; + } + } + + public bool HaveStaticConstructor { + get { + return have_static_constructor; + } + } + + public virtual TypeAttributes TypeAttr { + get { + return Modifiers.TypeAttr (ModFlags, this); + } + } + + // + // Emits the instance field initializers + // + public bool EmitFieldInitializers (EmitContext ec) + { + ArrayList fields; + ILGenerator ig = ec.ig; + Expression instance_expr; + + if (ec.IsStatic){ + fields = initialized_static_fields; + instance_expr = null; + } else { + fields = initialized_fields; + instance_expr = new This (Location.Null).Resolve (ec); + } + + if (fields == null) + return true; + + foreach (Field f in fields){ + Expression e = f.GetInitializerExpression (ec); + if (e == null) + return false; + + Location l = f.Location; + FieldExpr fe = new FieldExpr (f.FieldBuilder, l); + fe.InstanceExpression = instance_expr; + Expression a = new Assign (fe, e, l); + + a = a.Resolve (ec); + if (a == null) + return false; + + if (a is ExpressionStatement) + ((ExpressionStatement) a).EmitStatement (ec); + else { + throw new Exception ("Assign.Resolve returned a non ExpressionStatement"); + } + } + + return true; + } + + // + // Defines the default constructors + // + void DefineDefaultConstructor (bool is_static) + { + Constructor c; + int mods = 0; + + c = new Constructor (Basename, Parameters.EmptyReadOnlyParameters, + new ConstructorBaseInitializer ( + null, Parameters.EmptyReadOnlyParameters, + Location.Null), + Location.Null); + + if (is_static) + mods = Modifiers.STATIC; + + c.ModFlags = mods; + + AddConstructor (c); + + c.Block = new Block (null); + + } + + public void ReportStructInitializedInstanceError () + { + string n = TypeBuilder.FullName; + + foreach (Field f in initialized_fields){ + Report.Error ( + 573, Location, + "`" + n + "." + f.Name + "': can not have " + + "instance field initializers in structs"); + } + } + + /// <remarks> + /// The pending methods that need to be implemented (interfaces or abstract methods) + /// </remarks> + public PendingImplementation Pending; + + /// <summary> + /// This function computes the Base class and also the + /// list of interfaces that the class or struct @c implements. + /// + /// The return value is an array (might be null) of + /// interfaces implemented (as Types). + /// + /// The @parent argument is set to the parent object or null + /// if this is `System.Object'. + /// </summary> + Type [] GetClassBases (bool is_class, out Type parent, out bool error) + { + ArrayList bases = Bases; + int count; + int start, j, i; + + error = false; + + if (is_class) + parent = null; + else + parent = TypeManager.value_type; + + if (bases == null){ + if (is_class){ + if (RootContext.StdLib) + parent = TypeManager.object_type; + else if (Name != "System.Object") + parent = TypeManager.object_type; + } else { + // + // If we are compiling our runtime, + // and we are defining ValueType, then our + // parent is `System.Object'. + // + if (!RootContext.StdLib && Name == "System.ValueType") + parent = TypeManager.object_type; + } + + return null; + } + + // + // Bases should be null if there are no bases at all + // + count = bases.Count; + + if (is_class){ + Expression name = (Expression) bases [0]; + name = ResolveTypeExpr (name, false, Location); + + if (name == null){ + error = true; + return null; + } + + Type first = name.Type; + + if (first.IsClass){ + parent = first; + start = 1; + } else { + parent = TypeManager.object_type; + start = 0; + } + + if (!AsAccessible (parent, ModFlags)) + Report.Error (60, Location, + "Inconsistent accessibility: base class `" + + TypeManager.CSharpName (parent) + "' is less " + + "accessible than class `" + + Name + "'"); + + } else { + start = 0; + } + + Type [] ifaces = new Type [count-start]; + + for (i = start, j = 0; i < count; i++, j++){ + Expression name = (Expression) bases [i]; + Expression resolved = ResolveTypeExpr (name, false, Location); + bases [i] = resolved; + Type t = resolved.Type; + + if (t == null){ + error = true; + return null; + } + + if (is_class == false && !t.IsInterface){ + Report.Error (527, "In Struct `" + Name + "', type `"+ + name +"' is not an interface"); + error = true; + return null; + } + + if (t.IsSealed) { + string detail = ""; + + if (t.IsValueType) + detail = " (a class can not inherit from a struct/enum)"; + + Report.Error (509, "class `"+ Name + + "': Cannot inherit from sealed class `"+ + bases [i]+"'"+detail); + error = true; + return null; + } + + if (t.IsClass) { + if (parent != null){ + Report.Error (527, "In Class `" + Name + "', type `"+ + name+"' is not an interface"); + error = true; + return null; + } + } + + for (int x = 0; x < j; x++) { + if (t == ifaces [x]) { + Report.Error (528, "`" + name + "' is already listed in interface list"); + error = true; + return null; + } + } + + ifaces [j] = t; + } + + return TypeManager.ExpandInterfaces (ifaces); + } + + // + // Defines the type in the appropriate ModuleBuilder or TypeBuilder. + // + public override TypeBuilder DefineType () + { + Type parent; + bool error; + bool is_class; + + if (TypeBuilder != null) + return TypeBuilder; + + if (InTransit) + return null; + + InTransit = true; + + if (this is Class) + is_class = true; + else + is_class = false; + + ec = new EmitContext (this, Mono.CSharp.Location.Null, null, null, ModFlags); + + ifaces = GetClassBases (is_class, out parent, out error); + + if (error) + return null; + + if (is_class && parent != null){ + if (parent == TypeManager.enum_type || + (parent == TypeManager.value_type && RootContext.StdLib) || + parent == TypeManager.delegate_type || + parent == TypeManager.array_type){ + Report.Error ( + 644, Location, "`" + Name + "' cannot inherit from " + + "special class `" + TypeManager.CSharpName (parent) + "'"); + return null; + } + } + + if (!is_class && TypeManager.value_type == null) + throw new Exception (); + + TypeAttributes type_attributes = TypeAttr; + + // if (parent_builder is ModuleBuilder) { + if (IsTopLevel){ + ModuleBuilder builder = CodeGen.ModuleBuilder; + TypeBuilder = builder.DefineType ( + Name, type_attributes, parent, ifaces); + + } else { + TypeBuilder builder = Parent.TypeBuilder; + TypeBuilder = builder.DefineNestedType ( + Basename, type_attributes, parent, ifaces); + } + + // + // Structs with no fields need to have at least one byte. + // The right thing would be to set the PackingSize in a DefineType + // but there are no functions that allow interfaces *and* the size to + // be specified. + // + + if (!is_class && !have_nonstatic_fields){ + TypeBuilder.DefineField ("$PRIVATE$", TypeManager.byte_type, + FieldAttributes.Private); + } + + // add interfaces that were not added at type creation (weird API issue) + if (!is_class && !have_nonstatic_fields && (ifaces != null)) { + foreach (Type i in ifaces) + TypeBuilder.AddInterfaceImplementation (i); + } + + // + // Finish the setup for the EmitContext + // + ec.ContainerType = TypeBuilder; + + TypeManager.AddUserType (Name, TypeBuilder, this, ifaces); + + if ((parent != null) && + (parent == TypeManager.attribute_type || + parent.IsSubclassOf (TypeManager.attribute_type))) { + RootContext.RegisterAttribute (this); + TypeManager.RegisterAttrType (TypeBuilder, this); + } else + RootContext.RegisterOrder (this); + + if (Interfaces != null) { + foreach (Interface iface in Interfaces) + iface.DefineType (); + } + + if (Types != null) { + foreach (TypeContainer tc in Types) + tc.DefineType (); + } + + if (Delegates != null) { + foreach (Delegate d in Delegates) + d.DefineType (); + } + + if (Enums != null) { + foreach (Enum en in Enums) + en.DefineType (); + } + + InTransit = false; + return TypeBuilder; + } + + + /// <summary> + /// Defines the MemberCore objects that are in the `list' Arraylist + /// + /// The `defined_names' array contains a list of members defined in + /// a base class + /// </summary> + static ArrayList remove_list = new ArrayList (); + void DefineMembers (ArrayList list, MemberInfo [] defined_names) + { + int idx; + + remove_list.Clear (); + + foreach (MemberCore mc in list){ + if (!mc.Define (this)){ + remove_list.Add (mc); + continue; + } + + if (defined_names == null) + continue; + + idx = Array.BinarySearch (defined_names, mc.Name, mif_compare); + if (idx < 0){ + if (RootContext.WarningLevel >= 4){ + if ((mc.ModFlags & Modifiers.NEW) != 0) + Warning_KewywordNewNotRequired (mc.Location, mc); + } + continue; + } + + MemberInfo match = defined_names [idx]; + + if (match is PropertyInfo && ((mc.ModFlags & Modifiers.OVERRIDE) != 0)) + continue; + + // + // If we are both methods, let the method resolution emit warnings + // + if (match is MethodBase && mc is MethodCore) + continue; + + if ((mc.ModFlags & Modifiers.NEW) == 0) + Warning_KeywordNewRequired (mc.Location, defined_names [idx]); + } + + foreach (object o in remove_list) + list.Remove (o); + + remove_list.Clear (); + } + + // + // Defines the indexers, and also verifies that the IndexerNameAttribute in the + // class is consisten. Either it is `Item' or it is the name defined by all the + // indexers with the `IndexerName' attribute. + // + // Turns out that the IndexerNameAttribute is applied to each indexer, + // but it is never emitted, instead a DefaultName attribute is attached + // to the class. + // + void DefineIndexers () + { + string class_indexer_name = null; + + foreach (Indexer i in Indexers){ + string name; + + i.Define (this); + + name = i.IndexerName; + + if (i.InterfaceType != null) + continue; + + if (class_indexer_name == null){ + class_indexer_name = name; + continue; + } + + if (name == class_indexer_name) + continue; + + Report.Error ( + 668, "Two indexers have different names, " + + " you should use the same name for all your indexers"); + } + if (class_indexer_name == null) + class_indexer_name = "Item"; + IndexerName = class_indexer_name; + } + + static void Error_KeywordNotAllowed (Location loc) + { + Report.Error (1530, loc, "Keyword new not allowed for namespace elements"); + } + + /// <summary> + /// Populates our TypeBuilder with fields and methods + /// </summary> + public override bool DefineMembers (TypeContainer parent) + { + MemberInfo [] defined_names = null; + + if (interface_order != null){ + foreach (Interface iface in interface_order) + if ((iface.ModFlags & Modifiers.NEW) == 0) + iface.DefineMembers (this); + else + Error_KeywordNotAllowed (iface.Location); + } + + if (RootContext.WarningLevel > 1){ + Type ptype; + + // + // This code throws an exception in the comparer + // I guess the string is not an object? + // + ptype = TypeBuilder.BaseType; + if (ptype != null){ + defined_names = (MemberInfo []) FindMembers ( + ptype, MemberTypes.All & ~MemberTypes.Constructor, + BindingFlags.Public | BindingFlags.Instance | + BindingFlags.Static, null, null); + + Array.Sort (defined_names, mif_compare); + } + } + + if (constants != null) + DefineMembers (constants, defined_names); + + if (fields != null) + DefineMembers (fields, defined_names); + + if (this is Class){ + if (instance_constructors == null){ + if (default_constructor == null) + DefineDefaultConstructor (false); + } + + if (initialized_static_fields != null && + default_static_constructor == null) + DefineDefaultConstructor (true); + } + + if (this is Struct){ + // + // Structs can not have initialized instance + // fields + // + if (initialized_static_fields != null && + default_static_constructor == null) + DefineDefaultConstructor (true); + + if (initialized_fields != null) + ReportStructInitializedInstanceError (); + } + + Pending = PendingImplementation.GetPendingImplementations (this); + + // + // Constructors are not in the defined_names array + // + if (instance_constructors != null) + DefineMembers (instance_constructors, null); + + if (default_static_constructor != null) + default_static_constructor.Define (this); + + if (methods != null) + DefineMembers (methods, defined_names); + + if (properties != null) + DefineMembers (properties, defined_names); + + if (events != null) + DefineMembers (events, defined_names); + + if (indexers != null) { + DefineIndexers (); + } else + IndexerName = "Item"; + + if (operators != null){ + DefineMembers (operators, null); + + CheckPairedOperators (); + } + + if (enums != null) + DefineMembers (enums, defined_names); + + if (delegates != null) + DefineMembers (delegates, defined_names); + +#if CACHE + if (TypeBuilder.BaseType != null) + parent_container = TypeManager.LookupMemberContainer (TypeBuilder.BaseType); + + member_cache = new MemberCache (this); +#endif + + return true; + } + + public override bool Define (TypeContainer parent) + { + if (interface_order != null){ + foreach (Interface iface in interface_order) + if ((iface.ModFlags & Modifiers.NEW) == 0) + iface.Define (this); + } + + return true; + } + + /// <summary> + /// This function is based by a delegate to the FindMembers routine + /// </summary> + static bool AlwaysAccept (MemberInfo m, object filterCriteria) + { + return true; + } + + /// <summary> + /// This filter is used by FindMembers, and we just keep + /// a global for the filter to `AlwaysAccept' + /// </summary> + static MemberFilter accepting_filter; + + + /// <summary> + /// A member comparission method based on name only + /// </summary> + static IComparer mif_compare; + + static TypeContainer () + { + accepting_filter = new MemberFilter (AlwaysAccept); + mif_compare = new MemberInfoCompare (); + } + + /// <summary> + /// This method returns the members of this type just like Type.FindMembers would + /// Only, we need to use this for types which are _being_ defined because MS' + /// implementation can't take care of that. + /// </summary> + // + // FIXME: return an empty static array instead of null, that cleans up + // some code and is consistent with some coding conventions I just found + // out existed ;-) + // + // + // Notice that in various cases we check if our field is non-null, + // something that would normally mean that there was a bug elsewhere. + // + // The problem happens while we are defining p-invoke methods, as those + // will trigger a FindMembers, but this happens before things are defined + // + // Since the whole process is a no-op, it is fine to check for null here. + // + public override MemberList FindMembers (MemberTypes mt, BindingFlags bf, + MemberFilter filter, object criteria) + { + ArrayList members = new ArrayList (); + + int modflags = 0; + if ((bf & BindingFlags.Public) != 0) + modflags |= Modifiers.PUBLIC | Modifiers.PROTECTED | + Modifiers.INTERNAL; + if ((bf & BindingFlags.NonPublic) != 0) + modflags |= Modifiers.PRIVATE; + + int static_mask = 0, static_flags = 0; + switch (bf & (BindingFlags.Static | BindingFlags.Instance)) { + case BindingFlags.Static: + static_mask = static_flags = Modifiers.STATIC; + break; + + case BindingFlags.Instance: + static_mask = Modifiers.STATIC; + static_flags = 0; + break; + + default: + static_mask = static_flags = 0; + break; + } + + Timer.StartTimer (TimerType.TcFindMembers); + + if (filter == null) + filter = accepting_filter; + + if ((mt & MemberTypes.Field) != 0) { + if (fields != null) { + foreach (Field f in fields) { + if ((f.ModFlags & modflags) == 0) + continue; + if ((f.ModFlags & static_mask) != static_flags) + continue; + + FieldBuilder fb = f.FieldBuilder; + if (fb != null && filter (fb, criteria) == true) + members.Add (fb); + } + } + + if (constants != null) { + foreach (Const con in constants) { + if ((con.ModFlags & modflags) == 0) + continue; + if ((con.ModFlags & static_mask) != static_flags) + continue; + + FieldBuilder fb = con.FieldBuilder; + if (fb != null && filter (fb, criteria) == true) + members.Add (fb); + } + } + } + + if ((mt & MemberTypes.Method) != 0) { + if (methods != null) { + foreach (Method m in methods) { + if ((m.ModFlags & modflags) == 0) + continue; + if ((m.ModFlags & static_mask) != static_flags) + continue; + + MethodBuilder mb = m.MethodBuilder; + + if (mb != null && filter (mb, criteria) == true) + members.Add (mb); + } + } + + if (operators != null){ + foreach (Operator o in operators) { + if ((o.ModFlags & modflags) == 0) + continue; + if ((o.ModFlags & static_mask) != static_flags) + continue; + + MethodBuilder ob = o.OperatorMethodBuilder; + if (ob != null && filter (ob, criteria) == true) + members.Add (ob); + } + } + + if (properties != null){ + foreach (Property p in properties){ + if ((p.ModFlags & modflags) == 0) + continue; + if ((p.ModFlags & static_mask) != static_flags) + continue; + + MethodBuilder b; + + b = p.GetBuilder; + if (b != null && filter (b, criteria) == true) + members.Add (b); + + b = p.SetBuilder; + if (b != null && filter (b, criteria) == true) + members.Add (b); + } + } + + if (indexers != null){ + foreach (Indexer ix in indexers){ + if ((ix.ModFlags & modflags) == 0) + continue; + if ((ix.ModFlags & static_mask) != static_flags) + continue; + + MethodBuilder b; + + b = ix.GetBuilder; + if (b != null && filter (b, criteria) == true) + members.Add (b); + + b = ix.SetBuilder; + if (b != null && filter (b, criteria) == true) + members.Add (b); + } + } + } + + if ((mt & MemberTypes.Event) != 0) { + if (events != null) + foreach (Event e in events) { + if ((e.ModFlags & modflags) == 0) + continue; + if ((e.ModFlags & static_mask) != static_flags) + continue; + + MemberInfo eb = e.EventBuilder; + if (eb != null && filter (eb, criteria) == true) + members.Add (e.EventBuilder); + } + } + + if ((mt & MemberTypes.Property) != 0){ + if (properties != null) + foreach (Property p in properties) { + if ((p.ModFlags & modflags) == 0) + continue; + if ((p.ModFlags & static_mask) != static_flags) + continue; + + MemberInfo pb = p.PropertyBuilder; + if (pb != null && filter (pb, criteria) == true) { + members.Add (p.PropertyBuilder); + } + } + + if (indexers != null) + foreach (Indexer ix in indexers) { + if ((ix.ModFlags & modflags) == 0) + continue; + if ((ix.ModFlags & static_mask) != static_flags) + continue; + + MemberInfo ib = ix.PropertyBuilder; + if (ib != null && filter (ib, criteria) == true) { + members.Add (ix.PropertyBuilder); + } + } + } + + if ((mt & MemberTypes.NestedType) != 0) { + if (types != null){ + foreach (TypeContainer t in types) { + if ((t.ModFlags & modflags) == 0) + continue; + + TypeBuilder tb = t.TypeBuilder; + if (tb != null && (filter (tb, criteria) == true)) + members.Add (tb); + } + } + + if (enums != null){ + foreach (Enum en in enums){ + if ((en.ModFlags & modflags) == 0) + continue; + + TypeBuilder tb = en.TypeBuilder; + if (tb != null && (filter (tb, criteria) == true)) + members.Add (tb); + } + } + + if (delegates != null){ + foreach (Delegate d in delegates){ + if ((d.ModFlags & modflags) == 0) + continue; + + TypeBuilder tb = d.TypeBuilder; + if (tb != null && (filter (tb, criteria) == true)) + members.Add (tb); + } + } + + if (interfaces != null){ + foreach (Interface iface in interfaces){ + if ((iface.ModFlags & modflags) == 0) + continue; + + TypeBuilder tb = iface.TypeBuilder; + if (tb != null && (filter (tb, criteria) == true)) + members.Add (tb); + } + } + } + + if ((mt & MemberTypes.Constructor) != 0){ + if (((bf & BindingFlags.Instance) != 0) && (instance_constructors != null)){ + foreach (Constructor c in instance_constructors){ + ConstructorBuilder cb = c.ConstructorBuilder; + if (cb != null) + if (filter (cb, criteria) == true) + members.Add (cb); + } + } + + if (((bf & BindingFlags.Static) != 0) && (default_static_constructor != null)){ + ConstructorBuilder cb = + default_static_constructor.ConstructorBuilder; + + if (cb != null) + if (filter (cb, criteria) == true) + members.Add (cb); + } + } + + // + // Lookup members in parent if requested. + // + if (((bf & BindingFlags.DeclaredOnly) == 0) && (TypeBuilder.BaseType != null)) { + MemberList list = FindMembers (TypeBuilder.BaseType, mt, bf, filter, criteria); + members.AddRange (list); + } + + Timer.StopTimer (TimerType.TcFindMembers); + + return new MemberList (members); + } + + public override MemberCache MemberCache { + get { + return member_cache; + } + } + + public static MemberList FindMembers (Type t, MemberTypes mt, BindingFlags bf, + MemberFilter filter, object criteria) + { + TypeContainer tc = TypeManager.LookupTypeContainer (t); + + if (tc != null) + return tc.FindMembers (mt, bf, filter, criteria); + else + return new MemberList (t.FindMembers (mt, bf, filter, criteria)); + } + + // + // FindMethods will look for methods not only in the type `t', but in + // any interfaces implemented by the type. + // + public static MethodInfo [] FindMethods (Type t, BindingFlags bf, + MemberFilter filter, object criteria) + { + return null; + } + + /// <summary> + /// Emits the values for the constants + /// </summary> + public void EmitConstants () + { + if (constants != null) + foreach (Const con in constants) + con.EmitConstant (this); + return; + } + + /// <summary> + /// Emits the code, this step is performed after all + /// the types, enumerations, constructors + /// </summary> + public void Emit () + { + if (instance_constructors != null) + foreach (Constructor c in instance_constructors) + c.Emit (this); + + if (default_static_constructor != null) + default_static_constructor.Emit (this); + + if (methods != null) + foreach (Method m in methods) + m.Emit (this); + + if (operators != null) + foreach (Operator o in operators) + o.Emit (this); + + if (properties != null) + foreach (Property p in properties) + p.Emit (this); + + if (indexers != null){ + foreach (Indexer ix in indexers) + ix.Emit (this); + + CustomAttributeBuilder cb = Interface.EmitDefaultMemberAttr ( + this, IndexerName, ModFlags, Location); + TypeBuilder.SetCustomAttribute (cb); + } + + if (fields != null) + foreach (Field f in fields) + f.Emit (this); + + if (events != null){ + foreach (Event e in Events) + e.Emit (this); + } + + if (Pending != null) + if (Pending.VerifyPendingMethods ()) + return; + + Attribute.ApplyAttributes (ec, TypeBuilder, this, OptAttributes, Location); + + // + // Check for internal or private fields that were never assigned + // + if (fields != null && RootContext.WarningLevel >= 3) { + foreach (Field f in fields) { + if ((f.ModFlags & Modifiers.PUBLIC) != 0) + continue; + + if (f.status == 0){ + Report.Warning ( + 169, f.Location, "Private field " + + MakeName (f.Name) + " is never used"); + continue; + } + + // + // Only report 649 on level 4 + // + if (RootContext.WarningLevel < 4) + continue; + + if ((f.status & Field.Status.ASSIGNED) != 0) + continue; + + Report.Warning ( + 649, f.Location, + "Field " + MakeName (f.Name) + " is never assigned " + + " to and will always have its default value"); + } + } + +// if (types != null) +// foreach (TypeContainer tc in types) +// tc.Emit (); + } + + public override void CloseType () + { + try { + if (!Created){ + Created = true; + TypeBuilder.CreateType (); + } + } catch (TypeLoadException){ + // + // This is fine, the code still created the type + // +// Report.Warning (-20, "Exception while creating class: " + TypeBuilder.Name); +// Console.WriteLine (e.Message); + } catch { + Console.WriteLine ("In type: " + Name); + throw; + } + + if (Enums != null) + foreach (Enum en in Enums) + en.CloseType (); + + if (interface_order != null){ + foreach (Interface iface in interface_order) + iface.CloseType (); + } + + if (Types != null){ + foreach (TypeContainer tc in Types) + if (tc is Struct) + tc.CloseType (); + + foreach (TypeContainer tc in Types) + if (!(tc is Struct)) + tc.CloseType (); + } + + if (Delegates != null) + foreach (Delegate d in Delegates) + d.CloseType (); + } + + public string MakeName (string n) + { + return "`" + Name + "." + n + "'"; + } + + public void Warning_KeywordNewRequired (Location l, MemberInfo mi) + { + Report.Warning ( + 108, l, "The keyword new is required on " + + MakeName (mi.Name) + " because it hides `" + + mi.ReflectedType.Name + "." + mi.Name + "'"); + } + + public void Warning_KewywordNewNotRequired (Location l, MemberCore mc) + { + Report.Warning ( + 109, l, "The member " + MakeName (mc.Name) + " does not hide an " + + "inherited member, the keyword new is not required"); + } + + public static int CheckMember (string name, MemberInfo mi, int ModFlags) + { + return 0; + } + + // + // Performs the validation on a Method's modifiers (properties have + // the same properties). + // + public bool MethodModifiersValid (int flags, string n, Location loc) + { + const int vao = (Modifiers.VIRTUAL | Modifiers.ABSTRACT | Modifiers.OVERRIDE); + const int va = (Modifiers.VIRTUAL | Modifiers.ABSTRACT); + const int nv = (Modifiers.NEW | Modifiers.VIRTUAL); + bool ok = true; + string name = MakeName (n); + + // + // At most one of static, virtual or override + // + if ((flags & Modifiers.STATIC) != 0){ + if ((flags & vao) != 0){ + Report.Error ( + 112, loc, "static method " + name + "can not be marked " + + "as virtual, abstract or override"); + ok = false; + } + } + + if (this is Struct){ + if ((flags & va) != 0){ + Modifiers.Error_InvalidModifier (loc, "virtual or abstract"); + ok = false; + } + } + + if ((flags & Modifiers.OVERRIDE) != 0 && (flags & nv) != 0){ + Report.Error ( + 113, loc, name + + " marked as override cannot be marked as new or virtual"); + ok = false; + } + + // + // If the declaration includes the abstract modifier, then the + // declaration does not include static, virtual or extern + // + if ((flags & Modifiers.ABSTRACT) != 0){ + if ((flags & Modifiers.EXTERN) != 0){ + Report.Error ( + 180, loc, name + " can not be both abstract and extern"); + ok = false; + } + + if ((flags & Modifiers.VIRTUAL) != 0){ + Report.Error ( + 503, loc, name + " can not be both abstract and virtual"); + ok = false; + } + + if ((ModFlags & Modifiers.ABSTRACT) == 0){ + Report.Error ( + 513, loc, name + + " is abstract but its container class is not"); + ok = false; + + } + } + + if ((flags & Modifiers.PRIVATE) != 0){ + if ((flags & vao) != 0){ + Report.Error ( + 621, loc, name + + " virtual or abstract members can not be private"); + ok = false; + } + } + + if ((flags & Modifiers.SEALED) != 0){ + if ((flags & Modifiers.OVERRIDE) == 0){ + Report.Error ( + 238, loc, name + + " cannot be sealed because it is not an override"); + ok = false; + } + } + + return ok; + } + + // Access level of a type. + enum AccessLevel { + Public = 0, + ProtectedInternal = 1, + Internal = 2, + Protected = 3, + Private = 4 + } + + // Check whether `flags' denotes a more restricted access than `level' + // and return the new level. + static AccessLevel CheckAccessLevel (AccessLevel level, int flags) + { + AccessLevel old_level = level; + + if ((flags & Modifiers.INTERNAL) != 0) { + if ((flags & Modifiers.PROTECTED) != 0) { + if ((int) level < (int) AccessLevel.ProtectedInternal) + level = AccessLevel.ProtectedInternal; + } else { + if ((int) level < (int) AccessLevel.Internal) + level = AccessLevel.Internal; + } + } else if ((flags & Modifiers.PROTECTED) != 0) { + if ((int) level < (int) AccessLevel.Protected) + level = AccessLevel.Protected; + } else if ((flags & Modifiers.PRIVATE) != 0) + level = AccessLevel.Private; + + return level; + } + + // Return the access level for a new member which is defined in the current + // TypeContainer with access modifiers `flags'. + AccessLevel GetAccessLevel (int flags) + { + if ((flags & Modifiers.PRIVATE) != 0) + return AccessLevel.Private; + + AccessLevel level; + if (!IsTopLevel && (Parent != null)) + level = Parent.GetAccessLevel (flags); + else + level = AccessLevel.Public; + + return CheckAccessLevel (CheckAccessLevel (level, flags), ModFlags); + } + + // Return the access level for type `t', but don't give more access than `flags'. + static AccessLevel GetAccessLevel (Type t, int flags) + { + if (((flags & Modifiers.PRIVATE) != 0) || t.IsNestedPrivate) + return AccessLevel.Private; + + AccessLevel level; + if (TypeManager.IsBuiltinType (t)) + return AccessLevel.Public; + else if ((t.DeclaringType != null) && (t != t.DeclaringType)) + level = GetAccessLevel (t.DeclaringType, flags); + else { + level = CheckAccessLevel (AccessLevel.Public, flags); + } + + if (t.IsNestedPublic) + return level; + + if (t.IsNestedAssembly || t.IsNotPublic) { + if ((int) level < (int) AccessLevel.Internal) + level = AccessLevel.Internal; + } + + if (t.IsNestedFamily) { + if ((int) level < (int) AccessLevel.Protected) + level = AccessLevel.Protected; + } + + if (t.IsNestedFamORAssem) { + if ((int) level < (int) AccessLevel.ProtectedInternal) + level = AccessLevel.ProtectedInternal; + } + + return level; + } + + // + // Returns true if `parent' is as accessible as the flags `flags' + // given for this member. + // + public bool AsAccessible (Type parent, int flags) + { + while (parent.IsArray || parent.IsPointer || parent.IsByRef) + parent = parent.GetElementType (); + + AccessLevel level = GetAccessLevel (flags); + AccessLevel level2 = GetAccessLevel (parent, flags); + + return (int) level >= (int) level2; + } + + Hashtable builder_and_args; + + public bool RegisterMethod (MethodBuilder mb, InternalParameters ip, Type [] args) + { + if (builder_and_args == null) + builder_and_args = new Hashtable (); + return true; + } + + /// <summary> + /// Performs checks for an explicit interface implementation. First it + /// checks whether the `interface_type' is a base inteface implementation. + /// Then it checks whether `name' exists in the interface type. + /// </summary> + public bool VerifyImplements (Type interface_type, string full, string name, Location loc) + { + bool found = false; + + if (ifaces != null){ + foreach (Type t in ifaces){ + if (t == interface_type){ + found = true; + break; + } + } + } + + if (!found){ + Report.Error (540, "`" + full + "': containing class does not implement interface `" + interface_type.FullName + "'"); + return false; + } + + return true; + } + + public static void Error_ExplicitInterfaceNotMemberInterface (Location loc, string name) + { + Report.Error (539, loc, "Explicit implementation: `" + name + "' is not a member of the interface"); + } + + // + // IMemberContainer + // + + string IMemberContainer.Name { + get { + return Name; + } + } + + Type IMemberContainer.Type { + get { + return TypeBuilder; + } + } + + IMemberContainer IMemberContainer.Parent { + get { + return parent_container; + } + } + + MemberCache IMemberContainer.MemberCache { + get { + return member_cache; + } + } + + bool IMemberContainer.IsInterface { + get { + return false; + } + } + + MemberList IMemberContainer.GetMembers (MemberTypes mt, BindingFlags bf) + { + return FindMembers (mt, bf | BindingFlags.DeclaredOnly, null, null); + } + + // + // Operator pair checking + // + + class OperatorEntry { + public int flags; + public Type ret_type; + public Type type1, type2; + public Operator op; + public Operator.OpType ot; + + public OperatorEntry (int f, Operator o) + { + flags = f; + + ret_type = o.OperatorMethod.GetReturnType (); + Type [] pt = o.OperatorMethod.ParameterTypes; + type1 = pt [0]; + type2 = pt [1]; + op = o; + ot = o.OperatorType; + } + + public override int GetHashCode () + { + return ret_type.GetHashCode (); + } + + public override bool Equals (object o) + { + OperatorEntry other = (OperatorEntry) o; + + if (other.ret_type != ret_type) + return false; + if (other.type1 != type1) + return false; + if (other.type2 != type2) + return false; + return true; + } + } + + // + // Checks that some operators come in pairs: + // == and != + // > and < + // >= and <= + // + // They are matched based on the return type and the argument types + // + void CheckPairedOperators () + { + Hashtable pairs = new Hashtable (null, null); + + // Register all the operators we care about. + foreach (Operator op in operators){ + int reg = 0; + + switch (op.OperatorType){ + case Operator.OpType.Equality: + reg = 1; break; + case Operator.OpType.Inequality: + reg = 2; break; + + case Operator.OpType.GreaterThan: + reg = 1; break; + case Operator.OpType.LessThan: + reg = 2; break; + + case Operator.OpType.GreaterThanOrEqual: + reg = 1; break; + case Operator.OpType.LessThanOrEqual: + reg = 2; break; + } + if (reg == 0) + continue; + + OperatorEntry oe = new OperatorEntry (reg, op); + + object o = pairs [oe]; + if (o == null) + pairs [oe] = oe; + else { + oe = (OperatorEntry) o; + oe.flags |= reg; + } + } + + // + // Look for the mistakes. + // + foreach (DictionaryEntry de in pairs){ + OperatorEntry oe = (OperatorEntry) de.Key; + + if (oe.flags == 3) + continue; + + string s = ""; + switch (oe.ot){ + case Operator.OpType.Equality: + s = "!="; + break; + case Operator.OpType.Inequality: + s = "=="; + break; + case Operator.OpType.GreaterThan: + s = "<"; + break; + case Operator.OpType.LessThan: + s = ">"; + break; + case Operator.OpType.GreaterThanOrEqual: + s = "<="; + break; + case Operator.OpType.LessThanOrEqual: + s = ">="; + break; + } + Report.Error (216, oe.op.Location, + "The operator `" + oe.op + "' requires a matching operator `" + s + "' to also be defined"); + } + } + + + } + + public class Class : TypeContainer { + // <summary> + // Modifiers allowed in a class declaration + // </summary> + public const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.ABSTRACT | + Modifiers.SEALED | + Modifiers.UNSAFE; + + public Class (TypeContainer parent, string name, int mod, Attributes attrs, Location l) + : base (parent, name, l) + { + int accmods; + + if (parent.Parent == null) + accmods = Modifiers.INTERNAL; + else + accmods = Modifiers.PRIVATE; + + this.ModFlags = Modifiers.Check (AllowedModifiers, mod, accmods, l); + this.attributes = attrs; + } + + // + // FIXME: How do we deal with the user specifying a different + // layout? + // + public override TypeAttributes TypeAttr { + get { + return base.TypeAttr | TypeAttributes.AutoLayout | TypeAttributes.Class; + } + } + } + + public class Struct : TypeContainer { + // <summary> + // Modifiers allowed in a struct declaration + // </summary> + public const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.UNSAFE | + Modifiers.PRIVATE; + + public Struct (TypeContainer parent, string name, int mod, Attributes attrs, Location l) + : base (parent, name, l) + { + int accmods; + + if (parent.Parent == null) + accmods = Modifiers.INTERNAL; + else + accmods = Modifiers.PRIVATE; + + this.ModFlags = Modifiers.Check (AllowedModifiers, mod, accmods, l); + + this.ModFlags |= Modifiers.SEALED; + this.attributes = attrs; + + } + + // + // FIXME: Allow the user to specify a different set of attributes + // in some cases (Sealed for example is mandatory for a class, + // but what SequentialLayout can be changed + // + public override TypeAttributes TypeAttr { + get { + return base.TypeAttr | + TypeAttributes.SequentialLayout | + TypeAttributes.Sealed | + TypeAttributes.BeforeFieldInit; + } + } + } + + public abstract class MethodCore : MemberBase { + public /* readonly */ Parameters Parameters; + Block block; + + // + // Parameters, cached for semantic analysis. + // + protected InternalParameters parameter_info; + protected Type [] parameter_types; + + public MethodCore (Expression type, int mod, int allowed_mod, string name, + Attributes attrs, Parameters parameters, Location loc) + : base (type, mod, allowed_mod, name, attrs, loc) + { + Parameters = parameters; + } + + // + // Returns the System.Type array for the parameters of this method + // + public Type [] ParameterTypes { + get { + return parameter_types; + } + } + + public InternalParameters ParameterInfo + { + get { + return parameter_info; + } + } + + public Block Block { + get { + return block; + } + + set { + block = value; + } + } + + protected virtual bool DoDefineParameters (TypeContainer parent) + { + // Check if arguments were correct + parameter_types = Parameters.GetParameterInfo (parent); + if ((parameter_types == null) || !CheckParameters (parent, parameter_types)) + return false; + + parameter_info = new InternalParameters (parent, Parameters); + + return true; + } + + public CallingConventions GetCallingConvention (bool is_class) + { + CallingConventions cc = 0; + + cc = Parameters.GetCallingConvention (); + + if (is_class) + if ((ModFlags & Modifiers.STATIC) == 0) + cc |= CallingConventions.HasThis; + + // FIXME: How is `ExplicitThis' used in C#? + + return cc; + } + + public void LabelParameters (EmitContext ec, Type [] parameters, MethodBase builder) + { + LabelParameters (ec, parameters, builder, null); + } + + public void LabelParameters (EmitContext ec, Type [] parameters, MethodBase builder, Parameters p_params) + { + // + // Define each type attribute (in/out/ref) and + // the argument names. + // + Parameter [] p = p_params == null ? Parameters.FixedParameters : p_params.FixedParameters; + int i = 0; + + MethodBuilder mb = null; + ConstructorBuilder cb = null; + + if (builder is MethodBuilder) + mb = (MethodBuilder) builder; + else + cb = (ConstructorBuilder) builder; + + if (p != null){ + for (i = 0; i < p.Length; i++) { + ParameterBuilder pb; + + if (mb == null) + pb = cb.DefineParameter ( + i + 1, p [i].Attributes, p [i].Name); + else + pb = mb.DefineParameter ( + i + 1, p [i].Attributes, p [i].Name); + + Attributes attr = p [i].OptAttributes; + if (attr != null) + Attribute.ApplyAttributes (ec, pb, pb, attr, Location); + } + } + + if (Parameters.ArrayParameter != null){ + ParameterBuilder pb; + Parameter array_param = Parameters.ArrayParameter; + + if (mb == null) + pb = cb.DefineParameter ( + i + 1, array_param.Attributes, + array_param.Name); + else + pb = mb.DefineParameter ( + i + 1, array_param.Attributes, + array_param.Name); + + CustomAttributeBuilder a = new CustomAttributeBuilder ( + TypeManager.cons_param_array_attribute, new object [0]); + + pb.SetCustomAttribute (a); + } + } + } + + public class Method : MethodCore { + public MethodBuilder MethodBuilder; + public MethodData MethodData; + + /// <summary> + /// Modifiers allowed in a class declaration + /// </summary> + const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.VIRTUAL | + Modifiers.SEALED | + Modifiers.OVERRIDE | + Modifiers.ABSTRACT | + Modifiers.UNSAFE | + Modifiers.EXTERN; + + // + // return_type can be "null" for VOID values. + // + public Method (Expression return_type, int mod, string name, Parameters parameters, + Attributes attrs, Location l) + : base (return_type, mod, AllowedModifiers, name, attrs, parameters, l) + { } + + // + // Returns the `System.Type' for the ReturnType of this + // function. Provides a nice cache. (used between semantic analysis + // and actual code generation + // + public Type GetReturnType () + { + return MemberType; + } + + // Whether this is an operator method. + public bool IsOperator; + + void DuplicateEntryPoint (MethodInfo b, Location location) + { + Report.Error ( + 17, location, + "Program `" + CodeGen.FileName + + "' has more than one entry point defined: `" + + TypeManager.CSharpSignature(b) + "'"); + } + + void Report28 (MethodInfo b) + { + if (RootContext.WarningLevel < 4) + return; + + Report.Warning ( + 28, Location, + "`" + TypeManager.CSharpSignature(b) + + "' has the wrong signature to be an entry point"); + } + + public bool IsEntryPoint (MethodBuilder b, InternalParameters pinfo) + { + if (b.ReturnType != TypeManager.void_type && + b.ReturnType != TypeManager.int32_type) + return false; + + if (pinfo.Count == 0) + return true; + + if (pinfo.Count > 1) + return false; + + Type t = pinfo.ParameterType(0); + if (t.IsArray && + (t.GetArrayRank() == 1) && + (t.GetElementType() == TypeManager.string_type) && + (pinfo.ParameterModifier(0) == Parameter.Modifier.NONE)) + return true; + else + return false; + } + + // + // Checks our base implementation if any + // + protected override bool CheckBase (TypeContainer parent) + { + // Check whether arguments were correct. + if (!DoDefineParameters (parent)) + return false; + + MethodSignature ms = new MethodSignature (Name, null, ParameterTypes); + if (!IsOperator) { + MemberList mi_this; + + mi_this = TypeContainer.FindMembers ( + parent.TypeBuilder, MemberTypes.Method, + BindingFlags.NonPublic | BindingFlags.Public | + BindingFlags.Static | BindingFlags.Instance | + BindingFlags.DeclaredOnly, + MethodSignature.method_signature_filter, ms); + + if (mi_this.Count > 0) { + Report.Error (111, Location, "Class `" + parent.Name + "' " + + "already defines a member called `" + Name + "' " + + "with the same parameter types"); + return false; + } + } + + // + // Verify if the parent has a type with the same name, and then + // check whether we have to create a new slot for it or not. + // + Type ptype = parent.TypeBuilder.BaseType; + + // ptype is only null for System.Object while compiling corlib. + if (ptype != null){ + MemberList mi, mi_static, mi_instance; + + mi_static = TypeContainer.FindMembers ( + ptype, MemberTypes.Method, + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, + MethodSignature.inheritable_method_signature_filter, ms); + + mi_instance = TypeContainer.FindMembers ( + ptype, MemberTypes.Method, + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, + MethodSignature.inheritable_method_signature_filter, + ms); + + if (mi_instance.Count > 0){ + mi = mi_instance; + } else if (mi_static.Count > 0) + mi = mi_static; + else + mi = null; + + if (mi != null && mi.Count > 0){ + parent_method = (MethodInfo) mi [0]; + string name = parent_method.DeclaringType.Name + "." + + parent_method.Name; + + if (!CheckMethodAgainstBase (parent, flags, parent_method, name)) + return false; + + if ((ModFlags & Modifiers.NEW) == 0) { + Type parent_ret = TypeManager.TypeToCoreType ( + parent_method.ReturnType); + + if (parent_ret != MemberType) { + Report.Error ( + 508, parent.MakeName (Name) + ": cannot " + + "change return type when overriding " + + "inherited member " + name); + return false; + } + } + } else { + if ((ModFlags & Modifiers.NEW) != 0) + WarningNotHiding (parent); + + if ((ModFlags & Modifiers.OVERRIDE) != 0){ + Report.Error (115, Location, + parent.MakeName (Name) + + " no suitable methods found to override"); + } + } + } else if ((ModFlags & Modifiers.NEW) != 0) + WarningNotHiding (parent); + + return true; + } + + // + // Creates the type + // + public override bool Define (TypeContainer parent) + { + if (!DoDefine (parent)) + return false; + + if (!CheckBase (parent)) + return false; + + CallingConventions cc = GetCallingConvention (parent is Class); + + MethodData = new MethodData (this, null, MemberType, ParameterTypes, + ParameterInfo, cc, OptAttributes, + ModFlags, flags, true); + + if (!MethodData.Define (parent)) + return false; + + MethodBuilder = MethodData.MethodBuilder; + + // + // This is used to track the Entry Point, + // + if (Name == "Main" && + ((ModFlags & Modifiers.STATIC) != 0) && + (RootContext.MainClass == null || + RootContext.MainClass == parent.TypeBuilder.FullName)){ + if (IsEntryPoint (MethodBuilder, ParameterInfo)) { + if (RootContext.EntryPoint == null) { + RootContext.EntryPoint = MethodBuilder; + RootContext.EntryPointLocation = Location; + } else { + DuplicateEntryPoint (RootContext.EntryPoint, RootContext.EntryPointLocation); + DuplicateEntryPoint (MethodBuilder, Location); + } + } else + Report28(MethodBuilder); + } + + return true; + } + + // + // Emits the code + // + public void Emit (TypeContainer parent) + { + MethodData.Emit (parent, Block, this); + } + } + + public abstract class ConstructorInitializer { + ArrayList argument_list; + ConstructorInfo parent_constructor; + Parameters parameters; + Location loc; + + public ConstructorInitializer (ArrayList argument_list, Parameters parameters, + Location loc) + { + this.argument_list = argument_list; + this.parameters = parameters; + this.loc = loc; + } + + public ArrayList Arguments { + get { + return argument_list; + } + } + + public bool Resolve (EmitContext ec) + { + Expression parent_constructor_group; + Type t; + + ec.CurrentBlock = new Block (null, true, parameters); + + if (argument_list != null){ + foreach (Argument a in argument_list){ + if (!a.Resolve (ec, loc)) + return false; + } + } + + ec.CurrentBlock = null; + + if (this is ConstructorBaseInitializer) { + if (ec.ContainerType.BaseType == null) + return true; + + t = ec.ContainerType.BaseType; + if (ec.ContainerType.IsValueType) { + Report.Error (522, loc, + "structs cannot call base class constructors"); + return false; + } + } else + t = ec.ContainerType; + + parent_constructor_group = Expression.MemberLookup ( + ec, t, t, ".ctor", + MemberTypes.Constructor, + BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, + loc); + + if (parent_constructor_group == null){ + Report.Error (1501, loc, + "Can not find a constructor for this argument list"); + return false; + } + + parent_constructor = (ConstructorInfo) Invocation.OverloadResolve (ec, + (MethodGroupExpr) parent_constructor_group, argument_list, loc); + + if (parent_constructor == null){ + Report.Error (1501, loc, + "Can not find a constructor for this argument list"); + return false; + } + + return true; + } + + public void Emit (EmitContext ec) + { + if (parent_constructor != null){ + if (ec.IsStatic) + Invocation.EmitCall (ec, true, true, null, parent_constructor, argument_list, loc); + else + Invocation.EmitCall (ec, true, false, ec.This, parent_constructor, argument_list, loc); + } + } + } + + public class ConstructorBaseInitializer : ConstructorInitializer { + public ConstructorBaseInitializer (ArrayList argument_list, Parameters pars, Location l) : + base (argument_list, pars, l) + { + } + } + + public class ConstructorThisInitializer : ConstructorInitializer { + public ConstructorThisInitializer (ArrayList argument_list, Parameters pars, Location l) : + base (argument_list, pars, l) + { + } + } + + public class Constructor : MethodCore { + public ConstructorBuilder ConstructorBuilder; + public ConstructorInitializer Initializer; + new public Attributes OptAttributes; + + // <summary> + // Modifiers allowed for a constructor. + // </summary> + public const int AllowedModifiers = + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.STATIC | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.PRIVATE; + + // + // The spec claims that static is not permitted, but + // my very own code has static constructors. + // + public Constructor (string name, Parameters args, ConstructorInitializer init, Location l) + : base (null, 0, AllowedModifiers, name, null, args, l) + { + Initializer = init; + } + + // + // Returns true if this is a default constructor + // + public bool IsDefault () + { + if ((ModFlags & Modifiers.STATIC) != 0) + return (Parameters.FixedParameters == null ? true : Parameters.Empty) && + (Parameters.ArrayParameter == null ? true : Parameters.Empty); + + else + return (Parameters.FixedParameters == null ? true : Parameters.Empty) && + (Parameters.ArrayParameter == null ? true : Parameters.Empty) && + (Initializer is ConstructorBaseInitializer) && + (Initializer.Arguments == null); + } + + // + // Creates the ConstructorBuilder + // + public override bool Define (TypeContainer parent) + { + MethodAttributes ca = (MethodAttributes.RTSpecialName | + MethodAttributes.SpecialName); + + // Check if arguments were correct. + if (!DoDefineParameters (parent)) + return false; + + if ((ModFlags & Modifiers.STATIC) != 0) + ca |= MethodAttributes.Static; + else { + if (parent is Struct && ParameterTypes.Length == 0){ + Report.Error ( + 568, Location, + "Structs can not contain explicit parameterless " + + "constructors"); + return false; + } + ca |= MethodAttributes.HideBySig; + + if ((ModFlags & Modifiers.PUBLIC) != 0) + ca |= MethodAttributes.Public; + else if ((ModFlags & Modifiers.PROTECTED) != 0){ + if ((ModFlags & Modifiers.INTERNAL) != 0) + ca |= MethodAttributes.FamORAssem; + else + ca |= MethodAttributes.Family; + } else if ((ModFlags & Modifiers.INTERNAL) != 0) + ca |= MethodAttributes.Assembly; + else if (IsDefault ()) + ca |= MethodAttributes.Public; + else + ca |= MethodAttributes.Private; + } + + ConstructorBuilder = parent.TypeBuilder.DefineConstructor ( + ca, GetCallingConvention (parent is Class), ParameterTypes); + + // + // HACK because System.Reflection.Emit is lame + // + if (!TypeManager.RegisterMethod (ConstructorBuilder, ParameterInfo, ParameterTypes)) { + Report.Error ( + 111, Location, + "Class `" +parent.Name+ "' already contains a definition with the " + + "same return value and parameter types for constructor `" + Name + + "'"); + return false; + } + + return true; + } + + // + // Emits the code + // + public void Emit (TypeContainer parent) + { + ILGenerator ig = ConstructorBuilder.GetILGenerator (); + EmitContext ec = new EmitContext (parent, Location, ig, null, ModFlags, true); + + if ((ModFlags & Modifiers.STATIC) == 0){ + if (parent is Class && Initializer == null) + Initializer = new ConstructorBaseInitializer ( + null, Parameters.EmptyReadOnlyParameters, parent.Location); + + + // + // Spec mandates that Initializers will not have + // `this' access + // + ec.IsStatic = true; + if (Initializer != null && !Initializer.Resolve (ec)) + return; + ec.IsStatic = false; + } + + LabelParameters (ec, ParameterTypes, ConstructorBuilder); + + // + // Classes can have base initializers and instance field initializers. + // + if (parent is Class){ + if ((ModFlags & Modifiers.STATIC) == 0) + parent.EmitFieldInitializers (ec); + } + if (Initializer != null) + Initializer.Emit (ec); + + if ((ModFlags & Modifiers.STATIC) != 0) + parent.EmitFieldInitializers (ec); + + Attribute.ApplyAttributes (ec, ConstructorBuilder, this, OptAttributes, Location); + + // If this is a non-static `struct' constructor and doesn't have any + // initializer, it must initialize all of the struct's fields. + if ((parent is Struct) && ((ModFlags & Modifiers.STATIC) == 0) && + (Initializer == null)) + Block.AddThisVariable (parent, Location); + + ec.EmitTopBlock (Block, ParameterInfo, Location); + } + } + + public class MethodData { + // + // The return type of this method + // + public readonly Type ReturnType; + public readonly Type[] ParameterTypes; + public readonly InternalParameters ParameterInfo; + public readonly CallingConventions CallingConventions; + public readonly Attributes OptAttributes; + public readonly Location Location; + + // + // Are we implementing an interface ? + // + public bool IsImplementing = false; + + // + // Protected data. + // + protected MemberBase member; + protected int modifiers; + protected MethodAttributes flags; + protected bool is_method; + protected string accessor_name; + ArrayList conditionals; + + MethodBuilder builder = null; + public MethodBuilder MethodBuilder { + get { + return builder; + } + } + + public MethodData (MemberBase member, string name, Type return_type, + Type [] parameter_types, InternalParameters parameters, + CallingConventions cc, Attributes opt_attrs, + int modifiers, MethodAttributes flags, bool is_method) + { + this.member = member; + this.accessor_name = name; + this.ReturnType = return_type; + this.ParameterTypes = parameter_types; + this.ParameterInfo = parameters; + this.CallingConventions = cc; + this.OptAttributes = opt_attrs; + this.modifiers = modifiers; + this.flags = flags; + this.is_method = is_method; + this.Location = member.Location; + this.conditionals = new ArrayList (); + } + + // + // Attributes. + // + Attribute dllimport_attribute = null; + string obsolete = null; + bool obsolete_error = false; + + public virtual bool ApplyAttributes (Attributes opt_attrs, bool is_method) + { + if ((opt_attrs == null) || (opt_attrs.AttributeSections == null)) + return true; + + foreach (AttributeSection asec in opt_attrs.AttributeSections) { + if (asec.Attributes == null) + continue; + + foreach (Attribute a in asec.Attributes) { + if (a.Name == "Conditional") { + if (!ApplyConditionalAttribute (a)) + return false; + } else if (a.Name == "Obsolete") { + if (!ApplyObsoleteAttribute (a)) + return false; + } else if (a.Name.IndexOf ("DllImport") != -1) { + if (!is_method) { + a.Type = TypeManager.dllimport_type; + Attribute.Error_AttributeNotValidForElement (a, Location); + return false; + } + if (!ApplyDllImportAttribute (a)) + return false; + } + } + } + + return true; + } + + // + // Applies the `DllImport' attribute to the method. + // + protected virtual bool ApplyDllImportAttribute (Attribute a) + { + const int extern_static = Modifiers.EXTERN | Modifiers.STATIC; + if ((modifiers & extern_static) != extern_static) { + Report.Error (601, Location, + "The DllImport attribute must be specified on a method " + + "marked `static' and `extern'."); + return false; + } + + flags |= MethodAttributes.PinvokeImpl; + dllimport_attribute = a; + return true; + } + + // + // Applies the `Obsolete' attribute to the method. + // + protected virtual bool ApplyObsoleteAttribute (Attribute a) + { + if (obsolete != null) { + Report.Error (579, Location, "Duplicate `Obsolete' attribute"); + return false; + } + + obsolete = a.Obsolete_GetObsoleteMessage (out obsolete_error); + return obsolete != null; + } + + // + // Applies the `Conditional' attribute to the method. + // + protected virtual bool ApplyConditionalAttribute (Attribute a) + { + // The Conditional attribute is only valid on methods. + if (!is_method) { + Attribute.Error_AttributeNotValidForElement (a, Location); + return false; + } + + string condition = a.Conditional_GetConditionName (); + + if (condition == null) + return false; + + if (ReturnType != TypeManager.void_type) { + Report.Error (578, Location, + "Conditional not valid on `" + member.Name + "' " + + "because its return type is not void"); + return false; + } + + if ((modifiers & Modifiers.OVERRIDE) != 0) { + Report.Error (243, Location, + "Conditional not valid on `" + member.Name + "' " + + "because it is an override method"); + return false; + } + + if (member.IsExplicitImpl) { + Report.Error (577, Location, + "Conditional not valid on `" + member.Name + "' " + + "because it is an explicit interface implementation"); + return false; + } + + if (IsImplementing) { + Report.Error (623, Location, + "Conditional not valid on `" + member.Name + "' " + + "because it is an interface method"); + return false; + } + + conditionals.Add (condition); + + return true; + } + + // + // Checks whether this method should be ignored due to its Conditional attributes. + // + bool ShouldIgnore (Location loc) + { + // When we're overriding a virtual method, we implicitly inherit the + // Conditional attributes from our parent. + if (member.ParentMethod != null) { + TypeManager.MethodFlags flags = TypeManager.GetMethodFlags ( + member.ParentMethod, loc); + + if ((flags & TypeManager.MethodFlags.ShouldIgnore) != 0) + return true; + } + + foreach (string condition in conditionals) + if (RootContext.AllDefines [condition] == null) + return true; + + return false; + } + + // + // Returns the TypeManager.MethodFlags for this method. + // This emits an error 619 / warning 618 if the method is obsolete. + // In the former case, TypeManager.MethodFlags.IsObsoleteError is returned. + // + public virtual TypeManager.MethodFlags GetMethodFlags (Location loc) + { + TypeManager.MethodFlags flags = 0; + + if (obsolete != null) { + if (obsolete_error) { + Report.Error (619, loc, "Method `" + member.Name + + "' is obsolete: `" + obsolete + "'"); + return TypeManager.MethodFlags.IsObsoleteError; + } else + Report.Warning (618, loc, "Method `" + member.Name + + "' is obsolete: `" + obsolete + "'"); + + flags |= TypeManager.MethodFlags.IsObsolete; + } + + if (ShouldIgnore (loc)) + flags |= TypeManager.MethodFlags.ShouldIgnore; + + return flags; + } + + public virtual bool Define (TypeContainer parent) + { + MethodInfo implementing = null; + string method_name, name, prefix; + + if (OptAttributes != null) + if (!ApplyAttributes (OptAttributes, is_method)) + return false; + + if (member.IsExplicitImpl) + prefix = member.InterfaceType.FullName + "."; + else + prefix = ""; + + if (accessor_name != null) + name = accessor_name + "_" + member.ShortName; + else + name = member.ShortName; + method_name = prefix + name; + + if (parent.Pending != null){ + if (member is Indexer) + implementing = parent.Pending.IsInterfaceIndexer ( + member.InterfaceType, ReturnType, ParameterTypes); + else + implementing = parent.Pending.IsInterfaceMethod ( + member.InterfaceType, name, ReturnType, ParameterTypes); + + if (member.InterfaceType != null && implementing == null){ + TypeContainer.Error_ExplicitInterfaceNotMemberInterface ( + Location, name); + return false; + } + } + + // + // For implicit implementations, make sure we are public, for + // explicit implementations, make sure we are private. + // + if (implementing != null){ + // + // Setting null inside this block will trigger a more + // verbose error reporting for missing interface implementations + // + // The "candidate" function has been flagged already + // but it wont get cleared + // + if (!member.IsExplicitImpl){ + // + // We already catch different accessibility settings + // so we just need to check that we are not private + // + if ((modifiers & Modifiers.PRIVATE) != 0) + implementing = null; + + // + // Static is not allowed + // + if ((modifiers & Modifiers.STATIC) != 0) + implementing = null; + } else { + if ((modifiers & (Modifiers.PUBLIC | Modifiers.ABSTRACT | Modifiers.VIRTUAL)) != 0){ + Modifiers.Error_InvalidModifier (Location, "public, virtual or abstract"); + implementing = null; + } + } + } + + // + // If implementing is still valid, set flags + // + if (implementing != null){ + // + // When implementing interface methods, set NewSlot. + // + if (implementing.DeclaringType.IsInterface) + flags |= MethodAttributes.NewSlot; + + flags |= + MethodAttributes.Virtual | + MethodAttributes.HideBySig; + + // Get the method name from the explicit interface. + if (member.InterfaceType != null) { + name = implementing.Name; + method_name = prefix + name; + } + + IsImplementing = true; + } + + // + // Create the MethodBuilder for the method + // + if ((flags & MethodAttributes.PinvokeImpl) != 0) { + if ((modifiers & Modifiers.STATIC) == 0) { + Report.Error (601, Location, + "The DllImport attribute must be specified on " + + "a method marked 'static' and 'extern'."); + return false; + } + + EmitContext ec = new EmitContext ( + parent, Location, null, ReturnType, modifiers); + + builder = dllimport_attribute.DefinePInvokeMethod ( + ec, parent.TypeBuilder, method_name, flags, + ReturnType, ParameterTypes); + } else + builder = parent.TypeBuilder.DefineMethod ( + method_name, flags, CallingConventions, + ReturnType, ParameterTypes); + + if (builder == null) + return false; + + if (IsImplementing) { + // + // clear the pending implemntation flag + // + if (member is Indexer) { + parent.Pending.ImplementIndexer ( + member.InterfaceType, builder, ReturnType, + ParameterTypes, true); + } else + parent.Pending.ImplementMethod ( + member.InterfaceType, name, ReturnType, + ParameterTypes, member.IsExplicitImpl); + + if (member.IsExplicitImpl) + parent.TypeBuilder.DefineMethodOverride ( + builder, implementing); + } + + if (!TypeManager.RegisterMethod (builder, ParameterInfo, ParameterTypes)) { + Report.Error (111, Location, + "Class `" + parent.Name + + "' already contains a definition with the " + + "same return value and parameter types as the " + + "'get' method of property `" + member.Name + "'"); + return false; + } + + TypeManager.AddMethod (builder, this); + + return true; + } + + // + // Emits the code + // + public virtual void Emit (TypeContainer parent, Block block, object kind) + { + ILGenerator ig; + EmitContext ec; + + if ((flags & MethodAttributes.PinvokeImpl) == 0) + ig = builder.GetILGenerator (); + else + ig = null; + + ec = new EmitContext (parent, Location, ig, ReturnType, modifiers); + + if (OptAttributes != null) + Attribute.ApplyAttributes (ec, builder, kind, OptAttributes, Location); + + if (member is MethodCore) + ((MethodCore) member).LabelParameters (ec, ParameterTypes, MethodBuilder); + + // + // abstract or extern methods have no bodies + // + if ((modifiers & (Modifiers.ABSTRACT | Modifiers.EXTERN)) != 0){ + if (block == null) + return; + + // + // abstract or extern methods have no bodies. + // + if ((modifiers & Modifiers.ABSTRACT) != 0) + Report.Error ( + 500, Location, "Abstract method `" + + TypeManager.CSharpSignature (builder) + + "' can not have a body"); + + if ((modifiers & Modifiers.EXTERN) != 0) + Report.Error ( + 179, Location, "External method `" + + TypeManager.CSharpSignature (builder) + + "' can not have a body"); + + return; + } + + // + // Methods must have a body unless they're extern or abstract + // + if (block == null) { + Report.Error ( + 501, Location, "Method `" + + TypeManager.CSharpSignature (builder) + + "' must declare a body since it is not marked " + + "abstract or extern"); + return; + } + + // + // Handle destructors specially + // + // FIXME: This code generates buggy code + // + if (member.Name == "Finalize" && ReturnType == TypeManager.void_type) + EmitDestructor (ec, block); + else { + ISymbolWriter sw = CodeGen.SymbolWriter; + + if ((sw != null) && !Location.IsNull (Location) && + !Location.IsNull (block.EndLocation)) { + Location end = block.EndLocation; + MethodToken token = MethodBuilder.GetToken (); + sw.OpenMethod (new SymbolToken (token.Token)); + // Avoid error if we don't support debugging for the platform + try { + sw.SetMethodSourceRange (Location.SymbolDocument, + Location.Row, 0, + end.SymbolDocument, + end.Row, 0); + } catch (Exception) { + } + + ec.EmitTopBlock (block, member.Name, ParameterInfo, Location); + + sw.CloseMethod (); + } else + ec.EmitTopBlock (block, member.Name, ParameterInfo, Location); + } + } + + void EmitDestructor (EmitContext ec, Block block) + { + ILGenerator ig = ec.ig; + + Label finish = ig.DefineLabel (); + bool old_in_try = ec.InTry; + + ig.BeginExceptionBlock (); + ec.InTry = true; + ec.ReturnLabel = finish; + ec.HasReturnLabel = true; + ec.EmitTopBlock (block, null, Location); + ec.InTry = old_in_try; + + // ig.MarkLabel (finish); + bool old_in_finally = ec.InFinally; + ec.InFinally = true; + ig.BeginFinallyBlock (); + + if (ec.ContainerType.BaseType != null) { + Expression member_lookup = Expression.MemberLookup ( + ec, ec.ContainerType.BaseType, ec.ContainerType.BaseType, "Finalize", + MemberTypes.Method, Expression.AllBindingFlags, Location); + + if (member_lookup != null){ + MethodGroupExpr parent_destructor = ((MethodGroupExpr) member_lookup); + + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Call, (MethodInfo) parent_destructor.Methods [0]); + } + } + ec.InFinally = old_in_finally; + + ig.EndExceptionBlock (); + //ig.MarkLabel (ec.ReturnLabel); + ig.Emit (OpCodes.Ret); + } + } + + abstract public class MemberBase : MemberCore { + public Expression Type; + public readonly Attributes OptAttributes; + + protected MethodAttributes flags; + + // + // The "short" name of this property / indexer / event. This is the + // name without the explicit interface. + // + public string ShortName; + + // + // The type of this property / indexer / event + // + public Type MemberType; + + // + // If true, this is an explicit interface implementation + // + public bool IsExplicitImpl = false; + + // + // The name of the interface we are explicitly implementing + // + public string ExplicitInterfaceName = null; + + // + // If true, the interface type we are explicitly implementing + // + public Type InterfaceType = null; + + // + // The method we're overriding if this is an override method. + // + protected MethodInfo parent_method = null; + public MethodInfo ParentMethod { + get { + return parent_method; + } + } + + // + // The constructor is only exposed to our children + // + protected MemberBase (Expression type, int mod, int allowed_mod, string name, + Attributes attrs, Location loc) + : base (name, loc) + { + Type = type; + ModFlags = Modifiers.Check (allowed_mod, mod, Modifiers.PRIVATE, loc); + OptAttributes = attrs; + } + + protected virtual bool CheckBase (TypeContainer parent) + { + return true; + } + + protected virtual bool CheckParameters (TypeContainer parent, Type [] parameters) + { + bool error = false; + + foreach (Type partype in parameters){ + if (partype.IsPointer && !UnsafeOK (parent)) + error = true; + + if (parent.AsAccessible (partype, ModFlags)) + continue; + + if (this is Indexer) + Report.Error (55, Location, + "Inconsistent accessibility: parameter type `" + + TypeManager.CSharpName (partype) + "' is less " + + "accessible than indexer `" + Name + "'"); + else + Report.Error (51, Location, + "Inconsistent accessibility: parameter type `" + + TypeManager.CSharpName (partype) + "' is less " + + "accessible than method `" + Name + "'"); + error = true; + } + + return !error; + } + + protected virtual bool DoDefine (TypeContainer parent) + { + if (Name == null) + Name = "this"; + + if (!parent.MethodModifiersValid (ModFlags, Name, Location)) + return false; + + flags = Modifiers.MethodAttr (ModFlags); + + // Lookup Type, verify validity + MemberType = parent.ResolveType (Type, false, Location); + if (MemberType == null) + return false; + + // verify accessibility + if (!parent.AsAccessible (MemberType, ModFlags)) { + if (this is Property) + Report.Error (53, Location, + "Inconsistent accessibility: property type `" + + TypeManager.CSharpName (MemberType) + "' is less " + + "accessible than property `" + Name + "'"); + else if (this is Indexer) + Report.Error (54, Location, + "Inconsistent accessibility: indexer return type `" + + TypeManager.CSharpName (MemberType) + "' is less " + + "accessible than indexer `" + Name + "'"); + else if (this is Method) + Report.Error (50, Location, + "Inconsistent accessibility: return type `" + + TypeManager.CSharpName (MemberType) + "' is less " + + "accessible than method `" + Name + "'"); + else + Report.Error (52, Location, + "Inconsistent accessibility: field type `" + + TypeManager.CSharpName (MemberType) + "' is less " + + "accessible than field `" + Name + "'"); + return false; + } + + if (MemberType.IsPointer && !UnsafeOK (parent)) + return false; + + // + // Check for explicit interface implementation + // + if ((ExplicitInterfaceName == null) && (Name.IndexOf (".") != -1)){ + int pos = Name.LastIndexOf ("."); + + ExplicitInterfaceName = Name.Substring (0, pos); + ShortName = Name.Substring (pos + 1); + } else + ShortName = Name; + + if (ExplicitInterfaceName != null) { + InterfaceType = RootContext.LookupType ( + parent, ExplicitInterfaceName, false, Location); + if (InterfaceType == null) + return false; + + // Compute the full name that we need to export. + Name = InterfaceType.FullName + "." + ShortName; + + if (!parent.VerifyImplements (InterfaceType, ShortName, Name, Location)) + return false; + + IsExplicitImpl = true; + } else + IsExplicitImpl = false; + + return true; + } + } + + // + // Fields and Events both generate FieldBuilders, we use this to share + // their common bits. This is also used to flag usage of the field + // + abstract public class FieldBase : MemberBase { + public FieldBuilder FieldBuilder; + public Status status; + + [Flags] + public enum Status : byte { ASSIGNED = 1, USED = 2 } + + // + // The constructor is only exposed to our children + // + protected FieldBase (Expression type, int mod, int allowed_mod, string name, + object init, Attributes attrs, Location loc) + : base (type, mod, allowed_mod, name, attrs, loc) + { + this.init = init; + } + + // + // Whether this field has an initializer. + // + public bool HasInitializer { + get { + return init != null; + } + } + + // Private. + readonly Object init; + Expression init_expr; + bool init_expr_initialized = false; + + // + // Resolves and returns the field initializer. + // + public Expression GetInitializerExpression (EmitContext ec) + { + if (init_expr_initialized) + return init_expr; + + Expression e; + if (init is Expression) + e = (Expression) init; + else + e = new ArrayCreation (Type, "", (ArrayList)init, Location); + + ec.IsFieldInitializer = true; + e = e.DoResolve (ec); + ec.IsFieldInitializer = false; + + init_expr = e; + init_expr_initialized = true; + + return init_expr; + } + } + + // + // The Field class is used to represents class/struct fields during parsing. + // + public class Field : FieldBase { + // <summary> + // Modifiers allowed in a class declaration + // </summary> + const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.VOLATILE | + Modifiers.UNSAFE | + Modifiers.READONLY; + + public Field (Expression type, int mod, string name, Object expr_or_array_init, + Attributes attrs, Location loc) + : base (type, mod, AllowedModifiers, name, expr_or_array_init, attrs, loc) + { + } + + public override bool Define (TypeContainer parent) + { + Type t = parent.ResolveType (Type, false, Location); + + if (t == null) + return false; + + if (!parent.AsAccessible (t, ModFlags)) { + Report.Error (52, Location, + "Inconsistent accessibility: field type `" + + TypeManager.CSharpName (t) + "' is less " + + "accessible than field `" + Name + "'"); + return false; + } + + if (t.IsPointer && !UnsafeOK (parent)) + return false; + + if (RootContext.WarningLevel > 1){ + Type ptype = parent.TypeBuilder.BaseType; + + // ptype is only null for System.Object while compiling corlib. + if (ptype != null){ + TypeContainer.FindMembers ( + ptype, MemberTypes.Method, + BindingFlags.Public | + BindingFlags.Static | BindingFlags.Instance, + System.Type.FilterName, Name); + } + } + + if ((ModFlags & Modifiers.VOLATILE) != 0){ + if (!t.IsClass){ + if (TypeManager.IsEnumType (t)) + t = TypeManager.EnumToUnderlying (t); + + if (!((t == TypeManager.bool_type) || + (t == TypeManager.sbyte_type) || + (t == TypeManager.byte_type) || + (t == TypeManager.short_type) || + (t == TypeManager.ushort_type) || + (t == TypeManager.int32_type) || + (t == TypeManager.uint32_type) || + (t == TypeManager.char_type) || + (t == TypeManager.float_type))){ + Report.Error ( + 677, Location, parent.MakeName (Name) + + " A volatile field can not be of type `" + + TypeManager.CSharpName (t) + "'"); + return false; + } + } + } + + FieldAttributes fa = Modifiers.FieldAttr (ModFlags); + + if (parent is Struct && + ((fa & FieldAttributes.Static) == 0) && + t == parent.TypeBuilder && + !TypeManager.IsBuiltinType (t)){ + Report.Error (523, Location, "Struct member `" + parent.Name + "." + Name + + "' causes a cycle in the structure layout"); + return false; + } + FieldBuilder = parent.TypeBuilder.DefineField ( + Name, t, Modifiers.FieldAttr (ModFlags)); + + TypeManager.RegisterFieldBase (FieldBuilder, this); + return true; + } + + public void Emit (TypeContainer tc) + { + EmitContext ec = new EmitContext (tc, Location, null, + FieldBuilder.FieldType, ModFlags); + + Attribute.ApplyAttributes (ec, FieldBuilder, this, OptAttributes, Location); + } + } + + // + // `set' and `get' accessors are represented with an Accessor. + // + public class Accessor { + // + // Null if the accessor is empty, or a Block if not + // + public Block Block; + public Attributes OptAttributes; + + public Accessor (Block b, Attributes attrs) + { + Block = b; + OptAttributes = attrs; + } + } + + // + // Properties and Indexers both generate PropertyBuilders, we use this to share + // their common bits. + // + abstract public class PropertyBase : MethodCore { + public Accessor Get, Set; + public PropertyBuilder PropertyBuilder; + public MethodBuilder GetBuilder, SetBuilder; + public MethodData GetData, SetData; + + protected EmitContext ec; + + public PropertyBase (Expression type, string name, int mod_flags, int allowed_mod, + Parameters parameters, Accessor get_block, Accessor set_block, + Attributes attrs, Location loc) + : base (type, mod_flags, allowed_mod, name, attrs, parameters, loc) + { + Get = get_block; + Set = set_block; + } + + protected override bool DoDefine (TypeContainer parent) + { + if (!base.DoDefine (parent)) + return false; + + ec = new EmitContext (parent, Location, null, MemberType, ModFlags); + + return true; + } + + // + // Checks our base implementation if any + // + protected override bool CheckBase (TypeContainer parent) + { + // Check whether arguments were correct. + if (!DoDefineParameters (parent)) + return false; + + if (IsExplicitImpl) + return true; + + string report_name; + MethodSignature ms, base_ms; + if (this is Indexer) { + string name, base_name; + + report_name = "this"; + name = TypeManager.IndexerPropertyName (parent.TypeBuilder); + ms = new MethodSignature (name, null, ParameterTypes); + base_name = TypeManager.IndexerPropertyName (parent.TypeBuilder.BaseType); + base_ms = new MethodSignature (base_name, null, ParameterTypes); + } else { + report_name = Name; + ms = base_ms = new MethodSignature (Name, null, ParameterTypes); + } + + MemberList props_this; + + props_this = TypeContainer.FindMembers ( + parent.TypeBuilder, MemberTypes.Property, + BindingFlags.NonPublic | BindingFlags.Public | + BindingFlags.Static | BindingFlags.Instance | + BindingFlags.DeclaredOnly, + MethodSignature.method_signature_filter, ms); + + if (props_this.Count > 0) { + Report.Error (111, Location, "Class `" + parent.Name + "' " + + "already defines a member called `" + report_name + "' " + + "with the same parameter types"); + return false; + } + + // + // Find properties with the same name on the base class + // + MemberList props; + MemberList props_static = TypeContainer.FindMembers ( + parent.TypeBuilder.BaseType, MemberTypes.Property, + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, + MethodSignature.inheritable_property_signature_filter, base_ms); + + MemberList props_instance = TypeContainer.FindMembers ( + parent.TypeBuilder.BaseType, MemberTypes.Property, + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, + MethodSignature.inheritable_property_signature_filter, + base_ms); + + // + // Find if we have anything + // + if (props_static.Count > 0) + props = props_static; + else if (props_instance.Count > 0) + props = props_instance; + else + props = null; + + // + // If we have something on the base. + if (props != null && props.Count > 0){ + PropertyInfo pi = (PropertyInfo) props [0]; + + MethodInfo inherited_get = TypeManager.GetPropertyGetter (pi); + MethodInfo inherited_set = TypeManager.GetPropertySetter (pi); + + MethodInfo reference = inherited_get == null ? + inherited_set : inherited_get; + + if (reference != null) { + string name = reference.DeclaringType.Name + "." + report_name; + + if (!CheckMethodAgainstBase (parent, flags, reference, name)) + return false; + } + + if (((ModFlags & Modifiers.NEW) == 0) && (pi.PropertyType != MemberType)) { + Report.Error (508, parent.MakeName (Name) + ": cannot " + + "change return type when overriding inherited " + + "member `" + pi.DeclaringType + "." + pi.Name + "'"); + return false; + } + } else { + if ((ModFlags & Modifiers.NEW) != 0) + WarningNotHiding (parent); + + if ((ModFlags & Modifiers.OVERRIDE) != 0){ + if (this is Indexer) + Report.Error (115, Location, + parent.MakeName (Name) + + " no suitable indexers found to override"); + else + Report.Error (115, Location, + parent.MakeName (Name) + + " no suitable properties found to override"); + return false; + } + } + return true; + } + + public void Emit (TypeContainer tc) + { + // + // The PropertyBuilder can be null for explicit implementations, in that + // case, we do not actually emit the ".property", so there is nowhere to + // put the attribute + // + if (PropertyBuilder != null) + Attribute.ApplyAttributes (ec, PropertyBuilder, this, OptAttributes, Location); +/* + if (GetData != null) + GetData.Emit (tc, Get.Block, Get); + + if (SetData != null) + SetData.Emit (tc, Set.Block, Set); +*/ + } + } + + public class Property : PropertyBase { + const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.SEALED | + Modifiers.OVERRIDE | + Modifiers.ABSTRACT | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.VIRTUAL; + + string set_parameter_name; + Parameters get_params; + Parameters set_params; + + public Property (Expression type, string name, int mod_flags, + Accessor get_block, Accessor set_block, + Attributes attrs, Location loc, string set_name, + Parameters p_get, Parameters p_set) + : base (type, name, mod_flags, AllowedModifiers, + p_set, // FIXME ??????????? + get_block, set_block, attrs, loc) + { + set_parameter_name = set_name; + get_params = p_get; + set_params = p_set; + } + + public Property (Expression type, string name, int mod_flags, + Accessor get_block, Accessor set_block, + Attributes attrs, Location loc) + : base (type, name, mod_flags, AllowedModifiers, + Parameters.EmptyReadOnlyParameters, + get_block, set_block, attrs, loc) + { + set_parameter_name = "Value"; + get_params = Parameters.EmptyReadOnlyParameters; + set_params = Parameters.EmptyReadOnlyParameters; + } + + public override bool Define (TypeContainer parent) + { + Type [] g_parameters=null, s_parameters=null; + Parameter [] g_parms, s_parms; + InternalParameters g_ip=null, s_ip=null; + + if (!DoDefine (parent)) + return false; + + if (!CheckBase (parent)) + return false; + + flags |= MethodAttributes.HideBySig | MethodAttributes.SpecialName; + + if (Get != null) { + if (get_params == Parameters.EmptyReadOnlyParameters) + { + g_parameters = TypeManager.NoTypes; + g_ip = new InternalParameters ( + parent, Parameters.EmptyReadOnlyParameters); + } + else + { + g_parameters = new Type [get_params.FixedParameters.Length]; + for (int i = 0; i < get_params.FixedParameters.Length; i ++) + { + g_parameters[i] = get_params.FixedParameters[i].ParameterType; + } + g_parms = new Parameter [get_params.FixedParameters.Length]; + for (int i = 0; i < get_params.FixedParameters.Length; i ++) + { + Parameter tp = get_params.FixedParameters[i]; + g_parms[i] = new Parameter (tp.TypeName, tp.Name, + Parameter.Modifier.NONE, null); + } + g_ip = new InternalParameters ( + parent, new Parameters (g_parms, null, Location)); + } + + GetData = new MethodData (this, "get", MemberType, + g_parameters, g_ip, CallingConventions.Standard, + Get.OptAttributes, ModFlags, flags, false); + + if (!GetData.Define (parent)) + return false; + + GetBuilder = GetData.MethodBuilder; + } + + if (Set != null) { + if (set_params == Parameters.EmptyReadOnlyParameters) + { + s_parameters = new Type [1]; + s_parameters [0] = MemberType; + + s_parms = new Parameter [1]; + s_parms [0] = new Parameter (Type, /* was "value" */ set_parameter_name, + Parameter.Modifier.NONE, null); + } + else + { + s_parameters = new Type [set_params.FixedParameters.Length]; + for (int i = 0; i < set_params.FixedParameters.Length; i ++) + { + s_parameters[i] = set_params.FixedParameters[i].ParameterType; + } + + s_parms = new Parameter [set_params.FixedParameters.Length]; + for (int i = 0; i < set_params.FixedParameters.Length; i ++) + { + Parameter tp = set_params.FixedParameters[i]; + s_parms[i] = new Parameter (tp.TypeName, tp.Name, + Parameter.Modifier.NONE, null); + } + } + + s_ip = new InternalParameters ( + parent, new Parameters (s_parms, null, Location)); + + + SetData = new MethodData (this, "set", TypeManager.void_type, + s_parameters, s_ip, CallingConventions.Standard, + Set.OptAttributes, ModFlags, flags, false); + + if (!SetData.Define (parent)) + return false; + + SetBuilder = SetData.MethodBuilder; + SetBuilder.DefineParameter (1, ParameterAttributes.None, + /* was "value" */ set_parameter_name); + } + + // FIXME - PropertyAttributes.HasDefault ? + + PropertyAttributes prop_attr = + PropertyAttributes.RTSpecialName | + PropertyAttributes.SpecialName; + + if (!IsExplicitImpl){ + PropertyBuilder = parent.TypeBuilder.DefineProperty ( + Name, prop_attr, MemberType, null); + + if (Get != null) + PropertyBuilder.SetGetMethod (GetBuilder); + + if (Set != null) + PropertyBuilder.SetSetMethod (SetBuilder); + + // + // HACK for the reasons exposed above + // + if (!TypeManager.RegisterProperty (PropertyBuilder, GetBuilder, SetBuilder)) { + Report.Error ( + 111, Location, + "Class `" + parent.Name + + "' already contains a definition for the property `" + + Name + "'"); + return false; + } + } + return true; + } + + public void Emit (TypeContainer tc) + { + base.Emit (tc); + + if (GetData != null) + { + Parameters = get_params; + GetData.Emit (tc, Get.Block, Get); + } + + if (SetData != null) + { + Parameters = set_params; + SetData.Emit (tc, Set.Block, Set); + } + + } + } + + /// </summary> + /// Gigantic workaround for lameness in SRE follows : + /// This class derives from EventInfo and attempts to basically + /// wrap around the EventBuilder so that FindMembers can quickly + /// return this in it search for members + /// </summary> + public class MyEventBuilder : EventInfo { + + // + // We use this to "point" to our Builder which is + // not really a MemberInfo + // + EventBuilder MyBuilder; + + // + // We "catch" and wrap these methods + // + MethodInfo raise, remove, add; + + EventAttributes attributes; + Type declaring_type, reflected_type, event_type; + string name; + + public MyEventBuilder (TypeBuilder type_builder, string name, EventAttributes event_attr, Type event_type) + { + MyBuilder = type_builder.DefineEvent (name, event_attr, event_type); + + // And now store the values in our own fields. + + declaring_type = type_builder; + + reflected_type = type_builder; + + attributes = event_attr; + this.name = name; + this.event_type = event_type; + } + + // + // Methods that you have to override. Note that you only need + // to "implement" the variants that take the argument (those are + // the "abstract" methods, the others (GetAddMethod()) are + // regular. + // + public override MethodInfo GetAddMethod (bool nonPublic) + { + return add; + } + + public override MethodInfo GetRemoveMethod (bool nonPublic) + { + return remove; + } + + public override MethodInfo GetRaiseMethod (bool nonPublic) + { + return raise; + } + + // + // These methods make "MyEventInfo" look like a Builder + // + public void SetRaiseMethod (MethodBuilder raiseMethod) + { + raise = raiseMethod; + MyBuilder.SetRaiseMethod (raiseMethod); + } + + public void SetRemoveOnMethod (MethodBuilder removeMethod) + { + remove = removeMethod; + MyBuilder.SetRemoveOnMethod (removeMethod); + } + + public void SetAddOnMethod (MethodBuilder addMethod) + { + add = addMethod; + MyBuilder.SetAddOnMethod (addMethod); + } + + public void SetCustomAttribute (CustomAttributeBuilder cb) + { + MyBuilder.SetCustomAttribute (cb); + } + + public override object [] GetCustomAttributes (bool inherit) + { + // FIXME : There's nothing which can be seemingly done here because + // we have no way of getting at the custom attribute objects of the + // EventBuilder ! + return null; + } + + public override object [] GetCustomAttributes (Type t, bool inherit) + { + // FIXME : Same here ! + return null; + } + + public override bool IsDefined (Type t, bool b) + { + return true; + } + + public override EventAttributes Attributes { + get { + return attributes; + } + } + + public override string Name { + get { + return name; + } + } + + public override Type DeclaringType { + get { + return declaring_type; + } + } + + public override Type ReflectedType { + get { + return reflected_type; + } + } + + public Type EventType { + get { + return event_type; + } + } + } + + public class Event : FieldBase { + const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.VIRTUAL | + Modifiers.SEALED | + Modifiers.OVERRIDE | + Modifiers.UNSAFE | + Modifiers.ABSTRACT; + + public readonly Accessor Add; + public readonly Accessor Remove; + public MyEventBuilder EventBuilder; + + MethodBuilder AddBuilder, RemoveBuilder; + MethodData AddData, RemoveData; + + public Event (Expression type, string name, Object init, int mod, Accessor add, + Accessor remove, Attributes attrs, Location loc) + : base (type, mod, AllowedModifiers, name, init, attrs, loc) + { + Add = add; + Remove = remove; + } + + public override bool Define (TypeContainer parent) + { + EventAttributes e_attr = EventAttributes.RTSpecialName | EventAttributes.SpecialName; + + if (!DoDefine (parent)) + return false; + + if (!MemberType.IsSubclassOf (TypeManager.delegate_type)) { + Report.Error (66, Location, "'" + parent.Name + "." + Name + + "' : event must be of a delegate type"); + return false; + } + + Type [] parameter_types = new Type [1]; + parameter_types [0] = MemberType; + + Parameter [] parms = new Parameter [1]; + parms [0] = new Parameter (Type, /* was "value" */ this.Name, Parameter.Modifier.NONE, null); + InternalParameters ip = new InternalParameters ( + parent, new Parameters (parms, null, Location)); + + if (!CheckBase (parent)) + return false; + + // + // Now define the accessors + // + AddData = new MethodData (this, "add", TypeManager.void_type, + parameter_types, ip, CallingConventions.Standard, + (Add != null) ? Add.OptAttributes : null, + ModFlags, flags, false); + + if (!AddData.Define (parent)) + return false; + + AddBuilder = AddData.MethodBuilder; + AddBuilder.DefineParameter (1, ParameterAttributes.None, /* was "value" */ this.Name); + + RemoveData = new MethodData (this, "remove", TypeManager.void_type, + parameter_types, ip, CallingConventions.Standard, + (Remove != null) ? Remove.OptAttributes : null, + ModFlags, flags, false); + + if (!RemoveData.Define (parent)) + return false; + + RemoveBuilder = RemoveData.MethodBuilder; + RemoveBuilder.DefineParameter (1, ParameterAttributes.None, /* was "value" */ this.Name); + + if (!IsExplicitImpl){ + EventBuilder = new MyEventBuilder ( + parent.TypeBuilder, Name, e_attr, MemberType); + + if (Add == null && Remove == null) { + FieldBuilder = parent.TypeBuilder.DefineField ( + Name, MemberType, + FieldAttributes.FamANDAssem | ((ModFlags & Modifiers.STATIC) != 0 ? FieldAttributes.Static : 0)); + TypeManager.RegisterPrivateFieldOfEvent ( + (EventInfo) EventBuilder, FieldBuilder); + TypeManager.RegisterFieldBase (FieldBuilder, this); + } + + EventBuilder.SetAddOnMethod (AddBuilder); + EventBuilder.SetRemoveOnMethod (RemoveBuilder); + + if (!TypeManager.RegisterEvent (EventBuilder, AddBuilder, RemoveBuilder)) { + Report.Error (111, Location, + "Class `" + parent.Name + + "' already contains a definition for the event `" + + Name + "'"); + return false; + } + } + + return true; + } + + void EmitDefaultMethod (EmitContext ec, bool is_add) + { + ILGenerator ig = ec.ig; + MethodInfo method = null; + + if (is_add) + method = TypeManager.delegate_combine_delegate_delegate; + else + method = TypeManager.delegate_remove_delegate_delegate; + + if ((ModFlags & Modifiers.STATIC) != 0) { + ig.Emit (OpCodes.Ldsfld, (FieldInfo) FieldBuilder); + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Call, method); + ig.Emit (OpCodes.Castclass, MemberType); + ig.Emit (OpCodes.Stsfld, (FieldInfo) FieldBuilder); + } else { + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Ldfld, (FieldInfo) FieldBuilder); + ig.Emit (OpCodes.Ldarg_1); + ig.Emit (OpCodes.Call, method); + ig.Emit (OpCodes.Castclass, MemberType); + ig.Emit (OpCodes.Stfld, (FieldInfo) FieldBuilder); + } + ig.Emit (OpCodes.Ret); + } + + public void Emit (TypeContainer tc) + { + EmitContext ec; + + ec = new EmitContext (tc, Location, null, MemberType, ModFlags); + Attribute.ApplyAttributes (ec, EventBuilder, this, OptAttributes, Location); + + if (Add != null) + AddData.Emit (tc, Add.Block, Add); + else { + ILGenerator ig = AddData.MethodBuilder.GetILGenerator (); + ec = new EmitContext (tc, Location, ig, TypeManager.void_type, ModFlags); + EmitDefaultMethod (ec, true); + } + + if (Remove != null) + RemoveData.Emit (tc, Remove.Block, Remove); + else { + ILGenerator ig = RemoveData.MethodBuilder.GetILGenerator (); + ec = new EmitContext (tc, Location, ig, TypeManager.void_type, ModFlags); + EmitDefaultMethod (ec, false); + } + } + + } + + // + // FIXME: This does not handle: + // + // int INTERFACENAME [ args ] + // Does not + // + // Only: + // + // int this [ args ] + + public class Indexer : PropertyBase { + + const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.VIRTUAL | + Modifiers.SEALED | + Modifiers.OVERRIDE | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.ABSTRACT; + + public string IndexerName; + public string InterfaceIndexerName; + + // + // Are we implementing an interface ? + // + bool IsImplementing = false; + + public Indexer (Expression type, string int_type, int flags, Parameters parameters, + Accessor get_block, Accessor set_block, Attributes attrs, Location loc) + : base (type, "", flags, AllowedModifiers, parameters, get_block, set_block, + attrs, loc) + { + ExplicitInterfaceName = int_type; + } + + public override bool Define (TypeContainer parent) + { + PropertyAttributes prop_attr = + PropertyAttributes.RTSpecialName | + PropertyAttributes.SpecialName; + + if (!DoDefine (parent)) + return false; + + IndexerName = Attribute.ScanForIndexerName (ec, OptAttributes); + if (IndexerName == null) + IndexerName = "Item"; + else if (IsExplicitImpl) + Report.Error (592, Location, + "Attribute 'IndexerName' is not valid on this declaration " + + "type. It is valid on `property' declarations only."); + + ShortName = IndexerName; + if (IsExplicitImpl) { + InterfaceIndexerName = TypeManager.IndexerPropertyName (InterfaceType); + Name = InterfaceType.FullName + "." + IndexerName; + } else { + InterfaceIndexerName = IndexerName; + Name = ShortName; + } + + if (!CheckBase (parent)) + return false; + + if (Get != null){ + InternalParameters ip = new InternalParameters (parent, Parameters); + + GetData = new MethodData (this, "get", MemberType, + ParameterTypes, ip, CallingConventions.Standard, + Get.OptAttributes, ModFlags, flags, false); + + if (!GetData.Define (parent)) + return false; + + GetBuilder = GetData.MethodBuilder; + } + + if (Set != null){ + int top = ParameterTypes.Length; + Type [] set_pars = new Type [top + 1]; + ParameterTypes.CopyTo (set_pars, 0); + set_pars [top] = MemberType; + + Parameter [] fixed_parms = Parameters.FixedParameters; + + if (fixed_parms == null){ + throw new Exception ("We currently do not support only array arguments in an indexer"); + // BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG + // BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG + // + // Here is the problem: the `value' parameter has + // to come *after* the array parameter in the declaration + // like this: + // X (object [] x, Type value) + // .param [0] + // + // BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG + // BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG + + } + + Parameter [] tmp = new Parameter [fixed_parms.Length + 1]; + + + fixed_parms.CopyTo (tmp, 0); + tmp [fixed_parms.Length] = new Parameter ( + Type, /* was "value" */ this.Name, Parameter.Modifier.NONE, null); + + Parameters set_formal_params = new Parameters (tmp, null, Location); + + InternalParameters ip = new InternalParameters (parent, set_formal_params); + + SetData = new MethodData (this, "set", TypeManager.void_type, + set_pars, ip, CallingConventions.Standard, + Set.OptAttributes, ModFlags, flags, false); + + if (!SetData.Define (parent)) + return false; + + SetBuilder = SetData.MethodBuilder; + } + + // + // Now name the parameters + // + Parameter [] p = Parameters.FixedParameters; + if (p != null) { + int i; + + for (i = 0; i < p.Length; ++i) { + if (Get != null) + GetBuilder.DefineParameter ( + i + 1, p [i].Attributes, p [i].Name); + + if (Set != null) + SetBuilder.DefineParameter ( + i + 1, p [i].Attributes, p [i].Name); + } + + + if (Set != null) + SetBuilder.DefineParameter ( + i + 1, ParameterAttributes.None, /* was "value" */ this.Name); + + if (i != ParameterTypes.Length) { + Parameter array_param = Parameters.ArrayParameter; + SetBuilder.DefineParameter ( + i + 1, array_param.Attributes, array_param.Name); + } + } + + if (GetData != null) + IsImplementing = GetData.IsImplementing; + else if (SetData != null) + IsImplementing = SetData.IsImplementing; + + // + // Define the PropertyBuilder if one of the following conditions are met: + // a) we're not implementing an interface indexer. + // b) the indexer has a different IndexerName and this is no + // explicit interface implementation. + // + if (!IsExplicitImpl) { + PropertyBuilder = parent.TypeBuilder.DefineProperty ( + IndexerName, prop_attr, MemberType, ParameterTypes); + + if (GetData != null) + PropertyBuilder.SetGetMethod (GetBuilder); + + if (SetData != null) + PropertyBuilder.SetSetMethod (SetBuilder); + + TypeManager.RegisterIndexer (PropertyBuilder, GetBuilder, SetBuilder, + ParameterTypes); + } + + return true; + } + } + + public class Operator : MemberCore { + + const int AllowedModifiers = + Modifiers.PUBLIC | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.STATIC; + + const int RequiredModifiers = + Modifiers.PUBLIC | + Modifiers.STATIC; + + public enum OpType : byte { + + // Unary operators + LogicalNot, + OnesComplement, + Increment, + Decrement, + True, + False, + + // Unary and Binary operators + Addition, + Subtraction, + + UnaryPlus, + UnaryNegation, + + // Binary operators + Multiply, + Division, + Modulus, + BitwiseAnd, + BitwiseOr, + ExclusiveOr, + LeftShift, + RightShift, + Equality, + Inequality, + GreaterThan, + LessThan, + GreaterThanOrEqual, + LessThanOrEqual, + + // Implicit and Explicit + Implicit, + Explicit + }; + + public readonly OpType OperatorType; + public readonly Expression ReturnType; + public readonly Expression FirstArgType, SecondArgType; + public readonly string FirstArgName, SecondArgName; + public readonly Block Block; + public Attributes OptAttributes; + public MethodBuilder OperatorMethodBuilder; + + public string MethodName; + public Method OperatorMethod; + + public Operator (OpType type, Expression ret_type, int flags, + Expression arg1type, string arg1name, + Expression arg2type, string arg2name, + Block block, Attributes attrs, Location loc) + : base ("", loc) + { + OperatorType = type; + ReturnType = ret_type; + ModFlags = Modifiers.Check (AllowedModifiers, flags, Modifiers.PUBLIC, loc); + FirstArgType = arg1type; + FirstArgName = arg1name; + SecondArgType = arg2type; + SecondArgName = arg2name; + Block = block; + OptAttributes = attrs; + } + + string Prototype (TypeContainer parent) + { + return parent.Name + ".operator " + OperatorType + " (" + FirstArgType + "," + + SecondArgType + ")"; + } + + public override bool Define (TypeContainer parent) + { + int length = 1; + MethodName = "op_" + OperatorType; + + if (SecondArgType != null) + length = 2; + + Parameter [] param_list = new Parameter [length]; + + if ((ModFlags & RequiredModifiers) != RequiredModifiers){ + Report.Error ( + 558, Location, + "User defined operators `" + + Prototype (parent) + + "' must be declared static and public"); + return false; + } + + param_list[0] = new Parameter (FirstArgType, FirstArgName, + Parameter.Modifier.NONE, null); + if (SecondArgType != null) + param_list[1] = new Parameter (SecondArgType, SecondArgName, + Parameter.Modifier.NONE, null); + + OperatorMethod = new Method (ReturnType, ModFlags, MethodName, + new Parameters (param_list, null, Location), + OptAttributes, Mono.CSharp.Location.Null); + + OperatorMethod.IsOperator = true; + OperatorMethod.Define (parent); + + if (OperatorMethod.MethodBuilder == null) + return false; + + OperatorMethodBuilder = OperatorMethod.MethodBuilder; + + Type [] param_types = OperatorMethod.ParameterTypes; + Type declaring_type = OperatorMethodBuilder.DeclaringType; + Type return_type = OperatorMethod.GetReturnType (); + Type first_arg_type = param_types [0]; + + // Rules for conversion operators + + if (OperatorType == OpType.Implicit || OperatorType == OpType.Explicit) { + if (first_arg_type == return_type && first_arg_type == declaring_type){ + Report.Error ( + 555, Location, + "User-defined conversion cannot take an object of the " + + "enclosing type and convert to an object of the enclosing" + + " type"); + return false; + } + + if (first_arg_type != declaring_type && return_type != declaring_type){ + Report.Error ( + 556, Location, + "User-defined conversion must convert to or from the " + + "enclosing type"); + return false; + } + + if (first_arg_type == TypeManager.object_type || + return_type == TypeManager.object_type){ + Report.Error ( + -8, Location, + "User-defined conversion cannot convert to or from " + + "object type"); + return false; + } + + if (first_arg_type.IsInterface || return_type.IsInterface){ + Report.Error ( + 552, Location, + "User-defined conversion cannot convert to or from an " + + "interface type"); + return false; + } + + if (first_arg_type.IsSubclassOf (return_type) || + return_type.IsSubclassOf (first_arg_type)){ + Report.Error ( + -10, Location, + "User-defined conversion cannot convert between types " + + "that derive from each other"); + return false; + } + } else if (SecondArgType == null) { + // Checks for Unary operators + + if (first_arg_type != declaring_type){ + Report.Error ( + 562, Location, + "The parameter of a unary operator must be the " + + "containing type"); + return false; + } + + if (OperatorType == OpType.Increment || OperatorType == OpType.Decrement) { + if (return_type != declaring_type){ + Report.Error ( + 559, Location, + "The parameter and return type for ++ and -- " + + "must be the containing type"); + return false; + } + + } + + if (OperatorType == OpType.True || OperatorType == OpType.False) { + if (return_type != TypeManager.bool_type){ + Report.Error ( + 215, Location, + "The return type of operator True or False " + + "must be bool"); + return false; + } + } + + } else { + // Checks for Binary operators + + if (first_arg_type != declaring_type && + param_types [1] != declaring_type){ + Report.Error ( + 563, Location, + "One of the parameters of a binary operator must " + + "be the containing type"); + return false; + } + } + + return true; + } + + public void Emit (TypeContainer parent) + { + EmitContext ec = new EmitContext (parent, Location, null, null, ModFlags); + Attribute.ApplyAttributes (ec, OperatorMethodBuilder, this, OptAttributes, Location); + + // + // abstract or extern methods have no bodies + // + if ((ModFlags & (Modifiers.ABSTRACT | Modifiers.EXTERN)) != 0) + return; + + OperatorMethod.Block = Block; + OperatorMethod.Emit (parent); + } + + public static string GetName (OpType ot) + { + switch (ot){ + case OpType.LogicalNot: + return "!"; + case OpType.OnesComplement: + return "~"; + case OpType.Increment: + return "++"; + case OpType.Decrement: + return "--"; + case OpType.True: + return "true"; + case OpType.False: + return "false"; + case OpType.Addition: + return "+"; + case OpType.Subtraction: + return "-"; + case OpType.UnaryPlus: + return "+"; + case OpType.UnaryNegation: + return "-"; + case OpType.Multiply: + return "*"; + case OpType.Division: + return "/"; + case OpType.Modulus: + return "%"; + case OpType.BitwiseAnd: + return "&"; + case OpType.BitwiseOr: + return "|"; + case OpType.ExclusiveOr: + return "^"; + case OpType.LeftShift: + return "<<"; + case OpType.RightShift: + return ">>"; + case OpType.Equality: + return "=="; + case OpType.Inequality: + return "!="; + case OpType.GreaterThan: + return ">"; + case OpType.LessThan: + return "<"; + case OpType.GreaterThanOrEqual: + return ">="; + case OpType.LessThanOrEqual: + return "<="; + case OpType.Implicit: + return "implicit"; + case OpType.Explicit: + return "explicit"; + default: return ""; + } + } + + public override string ToString () + { + Type return_type = OperatorMethod.GetReturnType(); + Type [] param_types = OperatorMethod.ParameterTypes; + + if (SecondArgType == null) + return String.Format ( + "{0} operator {1}({2})", + TypeManager.CSharpName (return_type), + GetName (OperatorType), + param_types [0]); + else + return String.Format ( + "{0} operator {1}({2}, {3})", + TypeManager.CSharpName (return_type), + GetName (OperatorType), + param_types [0], param_types [1]); + } + } + + // + // This is used to compare method signatures + // + struct MethodSignature { + public string Name; + public Type RetType; + public Type [] Parameters; + + /// <summary> + /// This delegate is used to extract methods which have the + /// same signature as the argument + /// </summary> + public static MemberFilter method_signature_filter; + + /// <summary> + /// This delegate is used to extract inheritable methods which + /// have the same signature as the argument. By inheritable, + /// this means that we have permissions to override the method + /// from the current assembly and class + /// </summary> + public static MemberFilter inheritable_method_signature_filter; + + /// <summary> + /// This delegate is used to extract inheritable methods which + /// have the same signature as the argument. By inheritable, + /// this means that we have permissions to override the method + /// from the current assembly and class + /// </summary> + public static MemberFilter inheritable_property_signature_filter; + + static MethodSignature () + { + method_signature_filter = new MemberFilter (MemberSignatureCompare); + inheritable_method_signature_filter = new MemberFilter ( + InheritableMemberSignatureCompare); + inheritable_property_signature_filter = new MemberFilter ( + InheritablePropertySignatureCompare); + } + + public MethodSignature (string name, Type ret_type, Type [] parameters) + { + Name = name; + RetType = ret_type; + + if (parameters == null) + Parameters = TypeManager.NoTypes; + else + Parameters = parameters; + } + + public override int GetHashCode () + { + return Name.GetHashCode (); + } + + public override bool Equals (Object o) + { + MethodSignature other = (MethodSignature) o; + + if (other.Name != Name) + return false; + + if (other.RetType != RetType) + return false; + + if (Parameters == null){ + if (other.Parameters == null) + return true; + return false; + } + + if (other.Parameters == null) + return false; + + int c = Parameters.Length; + if (other.Parameters.Length != c) + return false; + + for (int i = 0; i < c; i++) + if (other.Parameters [i] != Parameters [i]) + return false; + + return true; + } + + static bool MemberSignatureCompare (MemberInfo m, object filter_criteria) + { + MethodSignature sig = (MethodSignature) filter_criteria; + + if (m.Name != sig.Name) + return false; + + Type ReturnType; + MethodInfo mi = m as MethodInfo; + PropertyInfo pi = m as PropertyInfo; + + if (mi != null) + ReturnType = mi.ReturnType; + else if (pi != null) + ReturnType = pi.PropertyType; + else + return false; + + // + // we use sig.RetType == null to mean `do not check the + // method return value. + // + if (sig.RetType != null) + if (ReturnType != sig.RetType) + return false; + + Type [] args; + if (mi != null) + args = TypeManager.GetArgumentTypes (mi); + else + args = TypeManager.GetArgumentTypes (pi); + Type [] sigp = sig.Parameters; + + if (args.Length != sigp.Length) + return false; + + for (int i = args.Length; i > 0; ){ + i--; + if (args [i] != sigp [i]) + return false; + } + return true; + } + + // + // This filter should be used when we are requesting methods that + // we want to override. + // + // This makes a number of assumptions, for example + // that the methods being extracted are of a parent + // class (this means we know implicitly that we are + // being called to find out about members by a derived + // class). + // + static bool InheritableMemberSignatureCompare (MemberInfo m, object filter_criteria) + { + if (MemberSignatureCompare (m, filter_criteria)){ + MethodInfo mi = (MethodInfo) m; + MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask; + + // If only accessible to the current class. + if (prot == MethodAttributes.Private) + return false; + + // If only accessible to the defining assembly or + if (prot == MethodAttributes.FamANDAssem || + prot == MethodAttributes.Assembly){ + if (m.DeclaringType.Assembly == CodeGen.AssemblyBuilder) + return true; + else + return false; + } + + // Anything else (FamOrAssembly and Public) is fine + return true; + } + return false; + } + + // + // This filter should be used when we are requesting properties that + // we want to override. + // + // This makes a number of assumptions, for example + // that the methods being extracted are of a parent + // class (this means we know implicitly that we are + // being called to find out about members by a derived + // class). + // + static bool InheritablePropertySignatureCompare (MemberInfo m, object filter_criteria) + { + if (MemberSignatureCompare (m, filter_criteria)){ + PropertyInfo pi = (PropertyInfo) m; + + MethodInfo inherited_get = TypeManager.GetPropertyGetter (pi); + MethodInfo inherited_set = TypeManager.GetPropertySetter (pi); + + MethodInfo mi = inherited_get == null ? inherited_set : inherited_get; + + MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask; + + // If only accessible to the current class. + if (prot == MethodAttributes.Private) + return false; + + // If only accessible to the defining assembly or + if (prot == MethodAttributes.FamANDAssem || + prot == MethodAttributes.Assembly){ + if (m.DeclaringType.Assembly == CodeGen.AssemblyBuilder) + return true; + else + return false; + } + + // Anything else (FamOrAssembly and Public) is fine + return true; + } + return false; + } + } +} diff --git a/mcs/mbas/codegen.cs b/mcs/mbas/codegen.cs new file mode 100644 index 00000000000..0ffad11aad5 --- /dev/null +++ b/mcs/mbas/codegen.cs @@ -0,0 +1,683 @@ +// +// codegen.cs: The code generator +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. +// + +using System; +using System.IO; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics.SymbolStore; + +namespace Mono.CSharp { + + /// <summary> + /// Code generator class. + /// </summary> + public class CodeGen { + static AppDomain current_domain; + public static AssemblyBuilder AssemblyBuilder; + public static ModuleBuilder ModuleBuilder; + + static public ISymbolWriter SymbolWriter; + + public static string Basename (string name) + { + int pos = name.LastIndexOf ("/"); + + if (pos != -1) + return name.Substring (pos + 1); + + pos = name.LastIndexOf ("\\"); + if (pos != -1) + return name.Substring (pos + 1); + + return name; + } + + public static string Dirname (string name) + { + int pos = name.LastIndexOf ("/"); + + if (pos != -1) + return name.Substring (0, pos); + + pos = name.LastIndexOf ("\\"); + if (pos != -1) + return name.Substring (0, pos); + + return "."; + } + + static string TrimExt (string name) + { + int pos = name.LastIndexOf ("."); + + return name.Substring (0, pos); + } + + static public string FileName; + + // + // This routine initializes the Mono runtime SymbolWriter. + // + static bool InitMonoSymbolWriter (string basename, string symbol_output, + string exe_output_file, string[] debug_args) + { + Type itype = SymbolWriter.GetType (); + if (itype == null) + return false; + + Type[] arg_types = new Type [3]; + arg_types [0] = typeof (string); + arg_types [1] = typeof (string); + arg_types [2] = typeof (string[]); + + MethodInfo initialize = itype.GetMethod ("Initialize", arg_types); + if (initialize == null) + return false; + + object[] args = new object [3]; + args [0] = exe_output_file; + args [1] = symbol_output; + args [2] = debug_args; + + initialize.Invoke (SymbolWriter, args); + return true; + } + + // + // Initializes the symbol writer + // + static void InitializeSymbolWriter (string basename, string symbol_output, + string exe_output_file, string[] args) + { + SymbolWriter = ModuleBuilder.GetSymWriter (); + + // + // If we got an ISymbolWriter instance, initialize it. + // + if (SymbolWriter == null) { + Report.Warning ( + -18, "Cannot find any symbol writer"); + return; + } + + // + // Due to lacking documentation about the first argument of the + // Initialize method, we cannot use Microsoft's default symbol + // writer yet. + // + // If we're using the mono symbol writer, the SymbolWriter object + // is of type MonoSymbolWriter - but that's defined in a dynamically + // loaded DLL - that's why we're doing a comparision based on the type + // name here instead of using `SymbolWriter is MonoSymbolWriter'. + // + Type sym_type = ((object) SymbolWriter).GetType (); + + switch (sym_type.Name){ + case "MonoSymbolWriter": + if (!InitMonoSymbolWriter (basename, symbol_output, + exe_output_file, args)) + Report.Warning ( + -18, "Cannot initialize the symbol writer"); + break; + + default: + Report.Warning ( + -18, "Cannot generate debugging information on this platform"); + break; + } + } + + // + // Initializes the code generator variables + // + static public void Init (string name, string output, bool want_debugging_support, + string[] debug_args) + { + AssemblyName an; + + FileName = output; + an = new AssemblyName (); + an.Name = TrimExt (Basename (name)); + current_domain = AppDomain.CurrentDomain; + AssemblyBuilder = current_domain.DefineDynamicAssembly ( + an, AssemblyBuilderAccess.RunAndSave, Dirname (name)); + + // + // Pass a path-less name to DefineDynamicModule. Wonder how + // this copes with output in different directories then. + // FIXME: figure out how this copes with --output /tmp/blah + // + // If the third argument is true, the ModuleBuilder will dynamically + // load the default symbol writer. + // + ModuleBuilder = AssemblyBuilder.DefineDynamicModule ( + Basename (name), Basename (output), want_debugging_support); + + int pos = output.LastIndexOf ("."); + + string basename; + if (pos > 0) + basename = output.Substring (0, pos); + else + basename = output; + + string symbol_output = basename + ".dbg"; + + if (want_debugging_support) + InitializeSymbolWriter (basename, symbol_output, output, debug_args); + else { + try { + File.Delete (symbol_output); + } catch { + // Ignore errors. + } + } + } + + static public void Save (string name) + { + try { + AssemblyBuilder.Save (Basename (name)); + } catch (System.IO.IOException io){ + Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message); + } + } + + static public void SaveSymbols () + { + if (SymbolWriter != null) { + // If we have a symbol writer, call its Close() method to write + // the symbol file to disk. + // + // When using Mono's default symbol writer, the Close() method must + // be called after the assembly has already been written to disk since + // it opens the assembly and reads its metadata. + SymbolWriter.Close (); + } + } + } + + /// <summary> + /// An Emit Context is created for each body of code (from methods, + /// properties bodies, indexer bodies or constructor bodies) + /// </summary> + public class EmitContext { + public DeclSpace DeclSpace; + public TypeContainer TypeContainer; + public ILGenerator ig; + + /// <summary> + /// This variable tracks the `checked' state of the compilation, + /// it controls whether we should generate code that does overflow + /// checking, or if we generate code that ignores overflows. + /// + /// The default setting comes from the command line option to generate + /// checked or unchecked code plus any source code changes using the + /// checked/unchecked statements or expressions. Contrast this with + /// the ConstantCheckState flag. + /// </summary> + + public bool CheckState; + + /// <summary> + /// The constant check state is always set to `true' and cant be changed + /// from the command line. The source code can change this setting with + /// the `checked' and `unchecked' statements and expressions. + /// </summary> + public bool ConstantCheckState; + + /// <summary> + /// Whether we are emitting code inside a static or instance method + /// </summary> + public bool IsStatic; + + /// <summary> + /// Whether we are emitting a field initializer + /// </summary> + public bool IsFieldInitializer; + + /// <summary> + /// The value that is allowed to be returned or NULL if there is no + /// return type. + /// </summary> + public Type ReturnType; + + /// <summary> + /// Points to the Type (extracted from the TypeContainer) that + /// declares this body of code + /// </summary> + public Type ContainerType; + + /// <summary> + /// Whether this is generating code for a constructor + /// </summary> + public bool IsConstructor; + + /// <summary> + /// Whether we're control flow analysis enabled + /// </summary> + public bool DoFlowAnalysis; + + /// <summary> + /// Keeps track of the Type to LocalBuilder temporary storage created + /// to store structures (used to compute the address of the structure + /// value on structure method invocations) + /// </summary> + public Hashtable temporary_storage; + + public Block CurrentBlock; + + /// <summary> + /// The location where we store the return value. + /// </summary> + LocalBuilder return_value; + + /// <summary> + /// The location where return has to jump to return the + /// value + /// </summary> + public Label ReturnLabel; + + /// <summary> + /// If we already defined the ReturnLabel + /// </summary> + public bool HasReturnLabel; + + /// <summary> + /// Whether we are in a Finally block + /// </summary> + public bool InFinally; + + /// <summary> + /// Whether we are in a Try block + /// </summary> + public bool InTry; + + /// <summary> + /// Whether we are in a Catch block + /// </summary> + public bool InCatch; + + /// <summary> + /// Whether we are inside an unsafe block + /// </summary> + public bool InUnsafe; + + /// <summary> + /// Location for this EmitContext + /// </summary> + public Location loc; + + /// <summary> + /// Used to flag that it is ok to define types recursively, as the + /// expressions are being evaluated as part of the type lookup + /// during the type resolution process + /// </summary> + public bool ResolvingTypeTree; + + /// <summary> + /// Inside an enum definition, we do not resolve enumeration values + /// to their enumerations, but rather to the underlying type/value + /// This is so EnumVal + EnumValB can be evaluated. + /// + /// There is no "E operator + (E x, E y)", so during an enum evaluation + /// we relax the rules + /// </summary> + public bool InEnumContext; + + protected Stack FlowStack; + + public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig, + Type return_type, int code_flags, bool is_constructor) + { + this.ig = ig; + + TypeContainer = parent; + DeclSpace = ds; + CheckState = RootContext.Checked; + ConstantCheckState = true; + + IsStatic = (code_flags & Modifiers.STATIC) != 0; + ReturnType = return_type; + IsConstructor = is_constructor; + CurrentBlock = null; + + if (parent != null){ + // Can only be null for the ResolveType contexts. + ContainerType = parent.TypeBuilder; + if (parent.UnsafeContext) + InUnsafe = true; + else + InUnsafe = (code_flags & Modifiers.UNSAFE) != 0; + } + loc = l; + + FlowStack = new Stack (); + + if (ReturnType == TypeManager.void_type) + ReturnType = null; + } + + public EmitContext (TypeContainer tc, Location l, ILGenerator ig, + Type return_type, int code_flags, bool is_constructor) + : this (tc, tc, l, ig, return_type, code_flags, is_constructor) + { + } + + public EmitContext (TypeContainer tc, Location l, ILGenerator ig, + Type return_type, int code_flags) + : this (tc, tc, l, ig, return_type, code_flags, false) + { + } + + public FlowBranching CurrentBranching { + get { + return (FlowBranching) FlowStack.Peek (); + } + } + + // <summary> + // Starts a new code branching. This inherits the state of all local + // variables and parameters from the current branching. + // </summary> + public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc) + { + FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc); + + FlowStack.Push (cfb); + + return cfb; + } + + // <summary> + // Starts a new code branching for block `block'. + // </summary> + public FlowBranching StartFlowBranching (Block block) + { + FlowBranching cfb; + FlowBranchingType type; + + if (CurrentBranching.Type == FlowBranchingType.SWITCH) + type = FlowBranchingType.SWITCH_SECTION; + else + type = FlowBranchingType.BLOCK; + + cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation); + + FlowStack.Push (cfb); + + return cfb; + } + + // <summary> + // Ends a code branching. Merges the state of locals and parameters + // from all the children of the ending branching. + // </summary> + public FlowReturns EndFlowBranching () + { + FlowBranching cfb = (FlowBranching) FlowStack.Pop (); + + return CurrentBranching.MergeChild (cfb); + } + + // <summary> + // Kills the current code branching. This throws away any changed state + // information and should only be used in case of an error. + // </summary> + public void KillFlowBranching () + { + FlowBranching cfb = (FlowBranching) FlowStack.Pop (); + } + + // <summary> + // Checks whether the local variable `vi' is already initialized + // at the current point of the method's control flow. + // If this method returns false, the caller must report an + // error 165. + // </summary> + public bool IsVariableAssigned (VariableInfo vi) + { + if (DoFlowAnalysis) + return CurrentBranching.IsVariableAssigned (vi); + else + return true; + } + + // <summary> + // Marks the local variable `vi' as being initialized at the current + // current point of the method's control flow. + // </summary> + public void SetVariableAssigned (VariableInfo vi) + { + if (DoFlowAnalysis) + CurrentBranching.SetVariableAssigned (vi); + } + + // <summary> + // Checks whether the parameter `number' is already initialized + // at the current point of the method's control flow. + // If this method returns false, the caller must report an + // error 165. This is only necessary for `out' parameters and the + // call will always succeed for non-`out' parameters. + // </summary> + public bool IsParameterAssigned (int number) + { + if (DoFlowAnalysis) + return CurrentBranching.IsParameterAssigned (number); + else + return true; + } + + // <summary> + // Marks the parameter `number' as being initialized at the current + // current point of the method's control flow. This is only necessary + // for `out' parameters. + // </summary> + public void SetParameterAssigned (int number) + { + if (DoFlowAnalysis) + CurrentBranching.SetParameterAssigned (number); + } + + // These are two overloaded methods for EmitTopBlock + // since in MonoBasic functions we need the Function name + // along with its top block, in order to be able to + // retrieve the return value when there is no explicit + // 'Return' statement + public void EmitTopBlock (Block block, InternalParameters ip, Location loc) + { + EmitTopBlock (block, "", ip, loc); + } + + public void EmitTopBlock (Block block, string bname, InternalParameters ip, Location loc) + { + bool has_ret = false; + +// Console.WriteLine ("Emitting: " + loc); + + if (CodeGen.SymbolWriter != null) + Mark (loc); + + if (block != null){ + int errors = Report.Errors; + + block.EmitMeta (this, block); + + if (Report.Errors == errors){ + bool old_do_flow_analysis = DoFlowAnalysis; + DoFlowAnalysis = true; + + FlowBranching cfb = new FlowBranching (block, ip, loc); + FlowStack.Push (cfb); + + if (!block.Resolve (this)) { + FlowStack.Pop (); + DoFlowAnalysis = old_do_flow_analysis; + return; + } + + cfb = (FlowBranching) FlowStack.Pop (); + FlowReturns returns = cfb.MergeTopBlock (); + + DoFlowAnalysis = old_do_flow_analysis; + + has_ret = block.Emit (this); + + if ((returns == FlowReturns.ALWAYS) || + (returns == FlowReturns.EXCEPTION) || + (returns == FlowReturns.UNREACHABLE)) + has_ret = true; + + if (Report.Errors == errors){ + if (RootContext.WarningLevel >= 3) + block.UsageWarning (); + } + } + } + + if (ReturnType != null && !has_ret){ + // + // mcs here would report an error (and justly so), but functions without + // an explicit return value are perfectly legal in MonoBasic + // + + VariableInfo vi = block.GetVariableInfo (bname); + if (vi != null) + { + ig.Emit (OpCodes.Ldloc, vi.LocalBuilder); + ig.Emit (OpCodes.Ret); + } + else + Report.Error (-200, "This is not supposed to happen !"); + return; + } + + if (HasReturnLabel) + ig.MarkLabel (ReturnLabel); + if (return_value != null){ + ig.Emit (OpCodes.Ldloc, return_value); + ig.Emit (OpCodes.Ret); + } else { + if (!InTry){ + if (!has_ret || HasReturnLabel) + ig.Emit (OpCodes.Ret); + } + } + } + + /// <summary> + /// This is called immediately before emitting an IL opcode to tell the symbol + /// writer to which source line this opcode belongs. + /// </summary> + public void Mark (Location loc) + { + if ((CodeGen.SymbolWriter != null) && !Location.IsNull (loc)) { + ISymbolDocumentWriter doc = loc.SymbolDocument; + + if (doc != null) + ig.MarkSequencePoint (doc, loc.Row, 0, loc.Row, 0); + } + } + + /// <summary> + /// Returns a temporary storage for a variable of type t as + /// a local variable in the current body. + /// </summary> + public LocalBuilder GetTemporaryStorage (Type t) + { + LocalBuilder location; + + if (temporary_storage != null){ + location = (LocalBuilder) temporary_storage [t]; + if (location != null) + return location; + } + + location = ig.DeclareLocal (t); + + return location; + } + + public void FreeTemporaryStorage (LocalBuilder b) + { + // Empty for now. + } + + /// <summary> + /// Current loop begin and end labels. + /// </summary> + public Label LoopBegin, LoopEnd; + + /// <summary> + /// Whether we are inside a loop and break/continue are possible. + /// </summary> + public bool InLoop; + + /// <summary> + /// This is incremented each time we enter a try/catch block and + /// decremented if we leave it. + /// </summary> + public int TryCatchLevel; + + /// <summary> + /// The TryCatchLevel at the begin of the current loop. + /// </summary> + public int LoopBeginTryCatchLevel; + + /// <summary> + /// Default target in a switch statement. Only valid if + /// InSwitch is true + /// </summary> + public Label DefaultTarget; + + /// <summary> + /// If this is non-null, points to the current switch statement + /// </summary> + public Switch Switch; + + /// <summary> + /// ReturnValue creates on demand the LocalBuilder for the + /// return value from the function. By default this is not + /// used. This is only required when returns are found inside + /// Try or Catch statements. + /// </summary> + public LocalBuilder TemporaryReturn () + { + if (return_value == null){ + return_value = ig.DeclareLocal (ReturnType); + ReturnLabel = ig.DefineLabel (); + HasReturnLabel = true; + } + + return return_value; + } + + /// <summary> + /// A dynamic This that is shared by all variables in a emitcontext. + /// Created on demand. + /// </summary> + public Expression my_this; + public Expression This { + get { + if (my_this == null) { + if (CurrentBlock != null) + my_this = new This (CurrentBlock, loc); + else + my_this = new This (loc); + + my_this = my_this.Resolve (this); + } + + return my_this; + } + } + } +} diff --git a/mcs/mbas/const.cs b/mcs/mbas/const.cs new file mode 100644 index 00000000000..2708592a86e --- /dev/null +++ b/mcs/mbas/const.cs @@ -0,0 +1,232 @@ +// +// const.cs: Constant declarations. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. +// +// + +// +// This is needed because the following situation arises: +// +// The FieldBuilder is declared with the real type for an enumeration +// +// When we attempt to set the value for the constant, the FieldBuilder.SetConstant +// function aborts because it requires its argument to be of the same type +// + +namespace Mono.CSharp { + + using System; + using System.Reflection; + using System.Reflection.Emit; + using System.Collections; + + public class Const : MemberCore { + public Expression ConstantType; + public Expression Expr; + public Attributes OptAttributes; + public FieldBuilder FieldBuilder; + + object ConstantValue = null; + Type type; + + bool in_transit = false; + + public const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE; + + public Const (Expression constant_type, string name, Expression expr, int mod_flags, + Attributes attrs, Location loc) + : base (name, loc) + { + ConstantType = constant_type; + Name = name; + Expr = expr; + ModFlags = Modifiers.Check (AllowedModifiers, mod_flags, Modifiers.PRIVATE, loc); + OptAttributes = attrs; + } + + public FieldAttributes FieldAttr { + get { + return FieldAttributes.Literal | FieldAttributes.Static | + Modifiers.FieldAttr (ModFlags) ; + } + } + +#if DEBUG + void dump_tree (Type t) + { + Console.WriteLine ("Dumping hierarchy"); + while (t != null){ + Console.WriteLine (" " + t.FullName + " " + + (t.GetType ().IsEnum ? "yes" : "no")); + t = t.BaseType; + } + } +#endif + + /// <summary> + /// Defines the constant in the @parent + /// </summary> + public override bool Define (TypeContainer parent) + { + type = parent.ResolveType (ConstantType, false, Location); + + if (type == null) + return false; + + if (!TypeManager.IsBuiltinType (type) && + (!type.IsSubclassOf (TypeManager.enum_type))) { + Report.Error ( + -3, Location, + "Constant type is not valid (only system types are allowed)"); + return false; + } + + Type ptype = parent.TypeBuilder.BaseType; + + if (ptype != null) { + MemberList list = TypeContainer.FindMembers ( + ptype, MemberTypes.Field, BindingFlags.Public, + Type.FilterName, Name); + + if (list.Count == 0) + if ((ModFlags & Modifiers.NEW) != 0) + WarningNotHiding (parent); + + } else if ((ModFlags & Modifiers.NEW) != 0) + WarningNotHiding (parent); + + FieldBuilder = parent.TypeBuilder.DefineField (Name, type, FieldAttr); + + TypeManager.RegisterConstant (FieldBuilder, this); + + return true; + } + + /// <summary> + /// Looks up the value of a constant field. Defines it if it hasn't + /// already been. Similar to LookupEnumValue in spirit. + /// </summary> + public object LookupConstantValue (EmitContext ec) + { + if (ConstantValue != null) + return ConstantValue; + + if (in_transit) { + Report.Error (110, Location, + "The evaluation of the constant value for `" + + Name + "' involves a circular definition."); + return null; + } + + in_transit = true; + int errors = Report.Errors; + + Expr = Expr.Resolve (ec); + + in_transit = false; + + if (Expr == null) { + if (errors == Report.Errors) + Report.Error (150, Location, "A constant value is expected"); + return null; + } + + if (!(Expr is Constant)) { + UnCheckedExpr un_expr = Expr as UnCheckedExpr; + CheckedExpr ch_expr = Expr as CheckedExpr; + + if ((un_expr != null) && (un_expr.Expr is Constant)) + Expr = un_expr.Expr; + else if ((ch_expr != null) && (ch_expr.Expr is Constant)) + Expr = ch_expr.Expr; + else { + Report.Error (150, Location, "A constant value is expected"); + return null; + } + } + + ConstantValue = ((Constant) Expr).GetValue (); + + if (type != Expr.Type) { + try { + ConstantValue = TypeManager.ChangeType (ConstantValue, type); + } catch { + Expression.Error_CannotConvertImplicit (Location, Expr.Type, type); + return null; + } + + if (type == TypeManager.int32_type) + Expr = new IntConstant ((int) ConstantValue); + else if (type == TypeManager.uint32_type) + Expr = new UIntConstant ((uint) ConstantValue); + else if (type == TypeManager.int64_type) + Expr = new LongConstant ((long) ConstantValue); + else if (type == TypeManager.uint64_type) + Expr = new ULongConstant ((ulong) ConstantValue); + else if (type == TypeManager.float_type) + Expr = new FloatConstant ((float) ConstantValue); + else if (type == TypeManager.double_type) + Expr = new DoubleConstant ((double) ConstantValue); + else if (type == TypeManager.string_type) + Expr = new StringConstant ((string) ConstantValue); + else if (type == TypeManager.short_type) + Expr = new ShortConstant ((short) ConstantValue); + else if (type == TypeManager.ushort_type) + Expr = new UShortConstant ((ushort) ConstantValue); + else if (type == TypeManager.sbyte_type) + Expr = new SByteConstant ((sbyte) ConstantValue); + else if (type == TypeManager.byte_type) + Expr = new ByteConstant ((byte) ConstantValue); + else if (type == TypeManager.char_type) + Expr = new CharConstant ((char) ConstantValue); + else if (type == TypeManager.bool_type) + Expr = new BoolConstant ((bool) ConstantValue); + } + + if (type.IsEnum){ + // + // This sadly does not work for our user-defined enumerations types ;-( + // + try { + ConstantValue = System.Enum.ToObject ( + type, ConstantValue); + } catch (ArgumentException){ + Report.Error ( + -16, Location, + ".NET SDK 1.0 does not permit to create the constant "+ + " field from a user-defined enumeration"); + } + } + + FieldBuilder.SetConstant (ConstantValue); + + if (!TypeManager.RegisterFieldValue (FieldBuilder, ConstantValue)) + return null; + + return ConstantValue; + } + + + /// <summary> + /// Emits the field value by evaluating the expression + /// </summary> + public void EmitConstant (TypeContainer parent) + { + EmitContext ec = new EmitContext (parent, Location, null, type, ModFlags); + LookupConstantValue (ec); + + return; + } + } +} + + diff --git a/mcs/mbas/constant.cs b/mcs/mbas/constant.cs new file mode 100644 index 00000000000..4030080a0b2 --- /dev/null +++ b/mcs/mbas/constant.cs @@ -0,0 +1,974 @@ +// +// constant.cs: Constants. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. +// +// + +namespace Mono.CSharp { + + using System; + using System.Reflection.Emit; + + /// <summary> + /// Base class for constants and literals. + /// </summary> + public abstract class Constant : Expression { + /// <remarks> + /// This is different from ToString in that ToString + /// is supposed to be there for debugging purposes, + /// and is not guaranteed to be useful for anything else, + /// AsString() will provide something that can be used + /// for round-tripping C# code. Maybe it can be used + /// for IL assembly as well. + /// </remarks> + public abstract string AsString (); + + override public string ToString () + { + return this.GetType ().Name + " (" + AsString () + ")"; + } + + /// <summary> + /// This is used to obtain the actual value of the literal + /// cast into an object. + /// </summary> + public abstract object GetValue (); + + /// <summary> + /// Constants are always born in a fully resolved state + /// </summary> + public override Expression DoResolve (EmitContext ec) + { + return this; + } + + // + // The various ToXXXX conversion functions are used by the constant + // folding evaluator. A null value is returned if the conversion is + // not possible. + // + // Note: not all the patterns for catching `implicit_conv' are the same. + // some implicit conversions can never be performed between two types + // even if the conversion would be lossless (for example short to uint), + // but some conversions are explicitly permitted by the standard provided + // that there will be no loss of information (for example, int to uint). + // + public DoubleConstant ToDouble (Location loc) + { + DoubleConstant c = ConvertToDouble (); + + if (c == null) + Error_CannotConvertImplicit (loc, Type, TypeManager.double_type); + + return c; + } + + public FloatConstant ToFloat (Location loc) + { + FloatConstant c = ConvertToFloat (); + + if (c == null) + Error_CannotConvertImplicit (loc, Type, TypeManager.float_type); + + return c; + } + + public ULongConstant ToULong (Location loc) + { + ULongConstant c = ConvertToULong (); + + if (c == null) + Error_CannotConvertImplicit (loc, Type, TypeManager.uint64_type); + + return c; + } + + public LongConstant ToLong (Location loc) + { + LongConstant c = ConvertToLong (); + + if (c == null) + Error_CannotConvertImplicit (loc, Type, TypeManager.int64_type); + + return c; + } + + public UIntConstant ToUInt (Location loc) + { + UIntConstant c = ConvertToUInt (); + + if (c == null) + Error_CannotConvertImplicit (loc, Type, TypeManager.uint32_type); + + return c; + } + + public IntConstant ToInt (Location loc) + { + IntConstant c = ConvertToInt (); + + if (c == null) + Error_CannotConvertImplicit (loc, Type, TypeManager.int32_type); + + return c; + } + + public virtual DoubleConstant ConvertToDouble () + { + return null; + } + + public virtual FloatConstant ConvertToFloat () + { + return null; + } + + public virtual ULongConstant ConvertToULong () + { + return null; + } + + public virtual LongConstant ConvertToLong () + { + return null; + } + + public virtual UIntConstant ConvertToUInt () + { + return null; + } + + public virtual IntConstant ConvertToInt () + { + return null; + } + } + + public class BoolConstant : Constant { + public readonly bool Value; + + public BoolConstant (bool val) + { + type = TypeManager.bool_type; + eclass = ExprClass.Value; + + Value = val; + } + + override public string AsString () + { + return Value ? "true" : "false"; + } + + public override object GetValue () + { + return (object) Value; + } + + + public override void Emit (EmitContext ec) + { + if (Value) + ec.ig.Emit (OpCodes.Ldc_I4_1); + else + ec.ig.Emit (OpCodes.Ldc_I4_0); + } + } + + public class ByteConstant : Constant { + public readonly byte Value; + + public ByteConstant (byte v) + { + type = TypeManager.byte_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + IntLiteral.EmitInt (ec.ig, Value); + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return new FloatConstant (Value); + } + + public override ULongConstant ConvertToULong () + { + return new ULongConstant (Value); + } + + public override LongConstant ConvertToLong () + { + return new LongConstant (Value); + } + + public override UIntConstant ConvertToUInt () + { + return new UIntConstant (Value); + } + + public override IntConstant ConvertToInt () + { + return new IntConstant (Value); + } + } + + public class CharConstant : Constant { + public readonly char Value; + + public CharConstant (char v) + { + type = TypeManager.char_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + IntLiteral.EmitInt (ec.ig, Value); + } + + static public string descape (char c) + { + switch (c){ + case '\a': + return "\\a"; + case '\b': + return "\\b"; + case '\n': + return "\\n"; + case '\t': + return "\\t"; + case '\v': + return "\\v"; + case '\r': + return "\\r"; + case '\\': + return "\\\\"; + case '\f': + return "\\f"; + case '\0': + return "\\0"; + case '"': + return "\\\""; + case '\'': + return "\\\'"; + } + return c.ToString (); + } + + public override string AsString () + { + return "\"" + descape (Value) + "\""; + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return new FloatConstant (Value); + } + + public override ULongConstant ConvertToULong () + { + return new ULongConstant (Value); + } + + public override LongConstant ConvertToLong () + { + return new LongConstant (Value); + } + + public override UIntConstant ConvertToUInt () + { + return new UIntConstant (Value); + } + + public override IntConstant ConvertToInt () + { + return new IntConstant (Value); + } + } + + public class SByteConstant : Constant { + public readonly sbyte Value; + + public SByteConstant (sbyte v) + { + type = TypeManager.sbyte_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + IntLiteral.EmitInt (ec.ig, Value); + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return new FloatConstant (Value); + } + + public override ULongConstant ConvertToULong () + { + if (Value >= 0) + return new ULongConstant ((ulong) Value); + + return null; + } + + public override LongConstant ConvertToLong () + { + return new LongConstant (Value); + } + + public override UIntConstant ConvertToUInt () + { + return null; + } + + public override IntConstant ConvertToInt () + { + return new IntConstant (Value); + } + } + + public class ShortConstant : Constant { + public readonly short Value; + + public ShortConstant (short v) + { + type = TypeManager.short_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + IntLiteral.EmitInt (ec.ig, Value); + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return new FloatConstant (Value); + } + + public override ULongConstant ConvertToULong () + { + return null; + } + + public override LongConstant ConvertToLong () + { + return new LongConstant (Value); + } + + public override UIntConstant ConvertToUInt () + { + return null; + } + + public override IntConstant ConvertToInt () + { + return new IntConstant (Value); + } + } + + public class UShortConstant : Constant { + public readonly ushort Value; + + public UShortConstant (ushort v) + { + type = TypeManager.ushort_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + IntLiteral.EmitInt (ec.ig, Value); + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return new FloatConstant (Value); + } + + public override ULongConstant ConvertToULong () + { + return new ULongConstant (Value); + } + + public override LongConstant ConvertToLong () + { + return new LongConstant (Value); + } + + public override UIntConstant ConvertToUInt () + { + return new UIntConstant (Value); + } + + public override IntConstant ConvertToInt () + { + return new IntConstant (Value); + } + } + + public class IntConstant : Constant { + public readonly int Value; + + public IntConstant (int v) + { + type = TypeManager.int32_type; + eclass = ExprClass.Value; + Value = v; + } + + static public void EmitInt (ILGenerator ig, int i) + { + switch (i){ + case -1: + ig.Emit (OpCodes.Ldc_I4_M1); + break; + + case 0: + ig.Emit (OpCodes.Ldc_I4_0); + break; + + case 1: + ig.Emit (OpCodes.Ldc_I4_1); + break; + + case 2: + ig.Emit (OpCodes.Ldc_I4_2); + break; + + case 3: + ig.Emit (OpCodes.Ldc_I4_3); + break; + + case 4: + ig.Emit (OpCodes.Ldc_I4_4); + break; + + case 5: + ig.Emit (OpCodes.Ldc_I4_5); + break; + + case 6: + ig.Emit (OpCodes.Ldc_I4_6); + break; + + case 7: + ig.Emit (OpCodes.Ldc_I4_7); + break; + + case 8: + ig.Emit (OpCodes.Ldc_I4_8); + break; + + default: + if (i >= -128 && i <= 127){ + ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i); + } else + ig.Emit (OpCodes.Ldc_I4, i); + break; + } + } + + public override void Emit (EmitContext ec) + { + EmitInt (ec.ig, Value); + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return new FloatConstant (Value); + } + + public override ULongConstant ConvertToULong () + { + if (Value < 0) + return null; + + return new ULongConstant ((ulong) Value); + } + + public override LongConstant ConvertToLong () + { + return new LongConstant (Value); + } + + public override UIntConstant ConvertToUInt () + { + if (Value < 0) + return null; + + return new UIntConstant ((uint) Value); + } + + public override IntConstant ConvertToInt () + { + return this; + } + } + + public class UIntConstant : Constant { + public readonly uint Value; + + public UIntConstant (uint v) + { + type = TypeManager.uint32_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + IntLiteral.EmitInt (ec.ig, unchecked ((int) Value)); + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return new FloatConstant (Value); + } + + public override ULongConstant ConvertToULong () + { + return new ULongConstant (Value); + } + + public override LongConstant ConvertToLong () + { + return new LongConstant (Value); + } + + public override UIntConstant ConvertToUInt () + { + return this; + } + + public override IntConstant ConvertToInt () + { + return null; + } + } + + public class LongConstant : Constant { + public readonly long Value; + + public LongConstant (long v) + { + type = TypeManager.int64_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + EmitLong (ig, Value); + } + + static public void EmitLong (ILGenerator ig, long l) + { + if ((l >> 32) == 0){ + IntLiteral.EmitInt (ig, unchecked ((int) l)); + ig.Emit (OpCodes.Conv_U8); + } else { + ig.Emit (OpCodes.Ldc_I8, l); + } + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return new FloatConstant (Value); + } + + public override ULongConstant ConvertToULong () + { + if (Value < 0) + return null; + + return new ULongConstant ((ulong) Value); + } + + public override LongConstant ConvertToLong () + { + return this; + } + + public override UIntConstant ConvertToUInt () + { + return null; + } + + public override IntConstant ConvertToInt () + { + return null; + } + } + + public class ULongConstant : Constant { + public readonly ulong Value; + + public ULongConstant (ulong v) + { + type = TypeManager.uint64_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + LongLiteral.EmitLong (ig, unchecked ((long) Value)); + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return new FloatConstant (Value); + } + + public override ULongConstant ConvertToULong () + { + return this; + } + + public override LongConstant ConvertToLong () + { + return null; + } + + public override UIntConstant ConvertToUInt () + { + return null; + } + + public override IntConstant ConvertToInt () + { + return null; + } + } + + public class FloatConstant : Constant { + public readonly float Value; + + public FloatConstant (float v) + { + type = TypeManager.float_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + ec.ig.Emit (OpCodes.Ldc_R4, Value); + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return new DoubleConstant (Value); + } + + public override FloatConstant ConvertToFloat () + { + return this; + } + + public override LongConstant ConvertToLong () + { + return null; + } + + public override UIntConstant ConvertToUInt () + { + return null; + } + + public override IntConstant ConvertToInt () + { + return null; + } + } + + public class DoubleConstant : Constant { + public readonly double Value; + + public DoubleConstant (double v) + { + type = TypeManager.double_type; + eclass = ExprClass.Value; + Value = v; + } + + public override void Emit (EmitContext ec) + { + ec.ig.Emit (OpCodes.Ldc_R8, Value); + } + + public override string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override DoubleConstant ConvertToDouble () + { + return this; + } + + public override FloatConstant ConvertToFloat () + { + return null; + } + + public override ULongConstant ConvertToULong () + { + return null; + } + + public override LongConstant ConvertToLong () + { + return null; + } + + public override UIntConstant ConvertToUInt () + { + return null; + } + + public override IntConstant ConvertToInt () + { + return null; + } + } + + public class DecimalConstant : Constant { + public readonly decimal Value; + + public DecimalConstant (decimal d) + { + type = TypeManager.decimal_type; + eclass = ExprClass.Value; + Value = d; + } + + override public string AsString () + { + return Value.ToString (); + } + + public override object GetValue () + { + return (object) Value; + } + + public override void Emit (EmitContext ec) + { + int [] words = Decimal.GetBits (Value); + + // + // FIXME: we could optimize this, and call a better + // constructor + // + + ILGenerator ig = ec.ig; + + IntConstant.EmitInt (ig, words [0]); + IntConstant.EmitInt (ig, words [1]); + IntConstant.EmitInt (ig, words [2]); + + // sign + IntConstant.EmitInt (ig, words [3] >> 31); + + // power + IntConstant.EmitInt (ig, (words [3] >> 16) & 0xff); + + ig.Emit (OpCodes.Newobj, TypeManager.void_decimal_ctor_five_args); + } + } + + public class StringConstant : Constant { + public readonly string Value; + + public StringConstant (string s) + { + type = TypeManager.string_type; + eclass = ExprClass.Value; + Value = s; + } + + // FIXME: Escape the string. + override public string AsString () + { + return "\"" + Value + "\""; + } + + public override object GetValue () + { + return Value; + } + + public override void Emit (EmitContext ec) + { + ec.ig.Emit (OpCodes.Ldstr, Value); + } + } + +} + + diff --git a/mcs/mbas/decl.cs b/mcs/mbas/decl.cs new file mode 100644 index 00000000000..a18ced8b8e5 --- /dev/null +++ b/mcs/mbas/decl.cs @@ -0,0 +1,1233 @@ +// +// 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 { + + /// <summary> + /// Base representation for members. This is only used to keep track + /// of Name, Location and Modifier flags. + /// </summary> + public abstract class MemberCore { + /// <summary> + /// Public name + /// </summary> + public string Name; + + /// <summary> + /// Modifier flags that the user specified in the source code + /// </summary> + public int ModFlags; + + /// <summary> + /// Location where this declaration happens + /// </summary> + 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.MakeName (Name) + " does not hide an " + + "inherited member. The keyword new is not required"); + + } + + void Error_CannotChangeAccessModifiers (TypeContainer parent, MethodInfo parent_method, + string name) + { + // + // FIXME: report the old/new permissions? + // + Report.Error ( + 507, Location, parent.MakeName (Name) + + ": can't change the access modifiers when overriding inherited " + + "member `" + 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, string name) + { + bool ok = true; + + if ((ModFlags & Modifiers.OVERRIDE) != 0){ + if (!(mb.IsAbstract || mb.IsVirtual)){ + Report.Error ( + 506, Location, parent.MakeName (Name) + + ": cannot override inherited member `" + + name + "' 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 `" + name + + "' 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, name); + ok = false; + } + } + + if (mb.IsVirtual || mb.IsAbstract){ + if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE)) == 0){ + if (Name != "Finalize"){ + Report.Warning ( + 114, 2, Location, parent.MakeName (Name) + + " hides inherited member `" + name + + "'. To make the current member override that " + + "implementation, add the override keyword, " + + "otherwise use the new keyword"); + ModFlags |= Modifiers.NEW; + } + } + } else { + if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE)) == 0){ + if (Name != "Finalize"){ + Report.Warning ( + 108, 1, Location, "The keyword new is required on " + + parent.MakeName (Name) + " because it hides " + + "inherited member `" + name + "'"); + ModFlags |= Modifiers.NEW; + } + } + } + + 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 + // + + /// <summary> + /// The result value from adding an declaration into + /// a struct or a class + /// </summary> + public enum AdditionResult { + /// <summary> + /// The declaration has been successfully + /// added to the declation space. + /// </summary> + Success, + + /// <summary> + /// The symbol has already been defined. + /// </summary> + NameExists, + + /// <summary> + /// 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 + /// </summary> + EnclosingClash, + + /// <summary> + /// 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 + /// </summary> + NotAConstructor, + + /// <summary> + /// 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. + /// </summary> + MethodExists + } + + /// <summary> + /// Base class for structs, classes, enumerations and interfaces. + /// </summary> + /// <remarks> + /// They all create new declaration spaces. This + /// provides the common foundation for managing those name + /// spaces. + /// </remarks> + public abstract class DeclSpace : MemberCore { + /// <summary> + /// this points to the actual definition that is being + /// created with System.Reflection.Emit + /// </summary> + public TypeBuilder TypeBuilder; + + /// <summary> + /// This variable tracks whether we have Closed the type + /// </summary> + 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; + + /// <summary> + /// defined_names is used for toplevel objects + /// </summary> + 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; + } + + /// <summary> + /// Returns a status code based purely on the name + /// of the member being added + /// </summary> + protected AdditionResult IsValid (string name) + { + if (name == Basename) + return AdditionResult.EnclosingClash; + + if (defined_names.Contains (name)) + return AdditionResult.NameExists; + + return AdditionResult.Success; + } + + /// <summary> + /// 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 + /// </summary> + protected void DefineName (string name, object o) + { + defined_names.Add (name, o); + } + + /// <summary> + /// Returns the object associated with a given name in the declaration + /// space. This is the inverse operation of `DefineName' + /// </summary> + public object GetDefinition (string name) + { + return defined_names [name]; + } + + bool in_transit = false; + + /// <summary> + /// This function is used to catch recursive definitions + /// in declarations. + /// </summary> + public bool InTransit { + get { + return in_transit; + } + + set { + in_transit = value; + } + } + + public TypeContainer Parent { + get { + return parent; + } + } + + /// <summary> + /// Looks up the alias for the name + /// </summary> + public string LookupAlias (string name) + { + if (Namespace != null) + return Namespace.LookupAlias (name); + else + return null; + } + + // + // 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; + } + } + + /// <remarks> + /// Should be overriten by the appropriate declaration space + /// <remarks> + public abstract TypeBuilder DefineType (); + + /// <summary> + /// Define all members, but don't apply any attributes or do anything which may + /// access not-yet-defined classes. This method also creates the MemberCache. + /// </summary> + public abstract bool DefineMembers (TypeContainer parent); + + // + // 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; + } + + EmitContext type_resolve_ec; + EmitContext GetTypeResolveEmitContext (TypeContainer parent, Location loc) + { + type_resolve_ec = new EmitContext (parent, this, loc, null, null, ModFlags, false); + type_resolve_ec.ResolvingTypeTree = true; + + return type_resolve_ec; + } + + // <summary> + // Looks up the type, as parsed into the expression `e' + // </summary> + public Type ResolveType (Expression e, bool silent, Location loc) + { + if (type_resolve_ec == null) + type_resolve_ec = GetTypeResolveEmitContext (parent, loc); + type_resolve_ec.loc = loc; + + int errors = Report.Errors; + Expression d = e.Resolve (type_resolve_ec, ResolveFlags.Type); + if (d == null || d.eclass != ExprClass.Type){ + if (!silent && errors == Report.Errors){ + Report.Error (246, loc, "Cannot find type `"+ e.ToString () +"'"); + } + return null; + } + + return d.Type; + } + + // <summary> + // Resolves the expression `e' for a type, and will recursively define + // types. + // </summary> + public Expression ResolveTypeExpr (Expression e, bool silent, Location loc) + { + if (type_resolve_ec == null) + type_resolve_ec = GetTypeResolveEmitContext (parent, loc); + + Expression d = e.Resolve (type_resolve_ec, ResolveFlags.Type); + if (d == null || d.eclass != ExprClass.Type){ + if (!silent){ + Report.Error (246, loc, "Cannot find type `"+ e +"'"); + } + return null; + } + + return d; + } + + 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, Location, "Class definition is circular: `"+name+"'"); + error = true; + return null; + } + return t; + } + + public static void Error_AmbiguousTypeReference (Location loc, string name, Type t1, Type t2) + { + Report.Error (104, loc, + String.Format ("`{0}' is an ambiguous reference ({1} or {2}) ", name, + t1.FullName, t2.FullName)); + } + + /// <summary> + /// GetType is used to resolve type names at the DeclSpace level. + /// Use this to lookup class/struct bases, interface bases or + /// delegate type references + /// </summary> + /// + /// <remarks> + /// 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 + /// </remarks> + public Type FindType (Location loc, 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; + + Type match = null; + foreach (Namespace.UsingEntry ue in using_list){ + match = LookupInterfaceOrClass (ue.Name, name, out error); + if (error) + return null; + + if (match != null){ + if (t != null){ + Error_AmbiguousTypeReference (loc, name, t, match); + return null; + } + + t = match; + ue.Used = true; + } + } + if (t != null) + return t; + } + + //Report.Error (246, Location, "Can not find type `"+name+"'"); + return null; + } + + /// <remarks> + /// This function is broken and not what you're looking for. It should only + /// be used while the type is still being created since it doesn't use the cache + /// and relies on the filter doing the member name check. + /// </remarks> + public abstract MemberList FindMembers (MemberTypes mt, BindingFlags bf, + MemberFilter filter, object criteria); + + /// <remarks> + /// If we have a MemberCache, return it. This property may return null if the + /// class doesn't have a member cache or while it's still being created. + /// </remarks> + public abstract MemberCache MemberCache { + get; + } + } + + /// <summary> + /// This is a readonly list of MemberInfo's. + /// </summary> + public class MemberList : IList { + public readonly IList List; + int count; + + /// <summary> + /// Create a new MemberList from the given IList. + /// </summary> + public MemberList (IList list) + { + if (list != null) + this.List = list; + else + this.List = new ArrayList (); + count = List.Count; + } + + /// <summary> + /// Concatenate the ILists `first' and `second' to a new MemberList. + /// </summary> + public MemberList (IList first, IList second) + { + ArrayList list = new ArrayList (); + list.AddRange (first); + list.AddRange (second); + count = list.Count; + List = list; + } + + public static readonly MemberList Empty = new MemberList (new ArrayList ()); + + /// <summary> + /// Cast the MemberList into a MemberInfo[] array. + /// </summary> + /// <remarks> + /// This is an expensive operation, only use it if it's really necessary. + /// </remarks> + public static explicit operator MemberInfo [] (MemberList list) + { + Timer.StartTimer (TimerType.MiscTimer); + MemberInfo [] result = new MemberInfo [list.Count]; + list.CopyTo (result, 0); + Timer.StopTimer (TimerType.MiscTimer); + return result; + } + + // ICollection + + public int Count { + get { + return count; + } + } + + public bool IsSynchronized { + get { + return List.IsSynchronized; + } + } + + public object SyncRoot { + get { + return List.SyncRoot; + } + } + + public void CopyTo (Array array, int index) + { + List.CopyTo (array, index); + } + + // IEnumerable + + public IEnumerator GetEnumerator () + { + return List.GetEnumerator (); + } + + // IList + + public bool IsFixedSize { + get { + return true; + } + } + + public bool IsReadOnly { + get { + return true; + } + } + + object IList.this [int index] { + get { + return List [index]; + } + + set { + throw new NotSupportedException (); + } + } + + // FIXME: try to find out whether we can avoid the cast in this indexer. + public MemberInfo this [int index] { + get { + return (MemberInfo) List [index]; + } + } + + public int Add (object value) + { + throw new NotSupportedException (); + } + + public void Clear () + { + throw new NotSupportedException (); + } + + public bool Contains (object value) + { + return List.Contains (value); + } + + public int IndexOf (object value) + { + return List.IndexOf (value); + } + + public void Insert (int index, object value) + { + throw new NotSupportedException (); + } + + public void Remove (object value) + { + throw new NotSupportedException (); + } + + public void RemoveAt (int index) + { + throw new NotSupportedException (); + } + } + + /// <summary> + /// This interface is used to get all members of a class when creating the + /// member cache. It must be implemented by all DeclSpace derivatives which + /// want to support the member cache and by TypeHandle to get caching of + /// non-dynamic types. + /// </summary> + public interface IMemberContainer { + /// <summary> + /// The name of the IMemberContainer. This is only used for + /// debugging purposes. + /// </summary> + string Name { + get; + } + + /// <summary> + /// The type of this IMemberContainer. + /// </summary> + Type Type { + get; + } + + /// <summary> + /// Returns the IMemberContainer of the parent class or null if this + /// is an interface or TypeManger.object_type. + /// This is used when creating the member cache for a class to get all + /// members from the parent class. + /// </summary> + IMemberContainer Parent { + get; + } + + /// <summary> + /// Whether this is an interface. + /// </summary> + bool IsInterface { + get; + } + + /// <summary> + /// Returns all members of this class with the corresponding MemberTypes + /// and BindingFlags. + /// </summary> + /// <remarks> + /// When implementing this method, make sure not to return any inherited + /// members and check the MemberTypes and BindingFlags properly. + /// Unfortunately, System.Reflection is lame and doesn't provide a way to + /// get the BindingFlags (static/non-static,public/non-public) in the + /// MemberInfo class, but the cache needs this information. That's why + /// this method is called multiple times with different BindingFlags. + /// </remarks> + MemberList GetMembers (MemberTypes mt, BindingFlags bf); + + /// <summary> + /// Return the container's member cache. + /// </summary> + MemberCache MemberCache { + get; + } + } + + /// <summary> + /// The MemberCache is used by dynamic and non-dynamic types to speed up + /// member lookups. It has a member name based hash table; it maps each member + /// name to a list of CacheEntry objects. Each CacheEntry contains a MemberInfo + /// and the BindingFlags that were initially used to get it. The cache contains + /// all members of the current class and all inherited members. If this cache is + /// for an interface types, it also contains all inherited members. + /// + /// There are two ways to get a MemberCache: + /// * if this is a dynamic type, lookup the corresponding DeclSpace and then + /// use the DeclSpace.MemberCache property. + /// * if this not a dynamic type, call TypeHandle.GetTypeHandle() to get a + /// TypeHandle instance for the type and then use TypeHandle.MemberCache. + /// </summary> + public class MemberCache { + public readonly IMemberContainer Container; + protected Hashtable member_hash; + protected Hashtable method_hash; + protected Hashtable interface_hash; + + /// <summary> + /// Create a new MemberCache for the given IMemberContainer `container'. + /// </summary> + public MemberCache (IMemberContainer container) + { + this.Container = container; + + Timer.IncrementCounter (CounterType.MemberCache); + Timer.StartTimer (TimerType.CacheInit); + + interface_hash = new Hashtable (); + + // If we have a parent class (we have a parent class unless we're + // TypeManager.object_type), we deep-copy its MemberCache here. + if (Container.Parent != null) + member_hash = SetupCache (Container.Parent.MemberCache); + else if (Container.IsInterface) + member_hash = SetupCacheForInterface (); + else + member_hash = new Hashtable (); + + // If this is neither a dynamic type nor an interface, create a special + // method cache with all declared and inherited methods. + Type type = container.Type; + if (!(type is TypeBuilder) && !type.IsInterface) { + method_hash = new Hashtable (); + AddMethods (type); + } + + // Add all members from the current class. + AddMembers (Container); + + Timer.StopTimer (TimerType.CacheInit); + } + + /// <summary> + /// Bootstrap this member cache by doing a deep-copy of our parent. + /// </summary> + Hashtable SetupCache (MemberCache parent) + { + Hashtable hash = new Hashtable (); + + IDictionaryEnumerator it = parent.member_hash.GetEnumerator (); + while (it.MoveNext ()) { + hash [it.Key] = ((ArrayList) it.Value).Clone (); + } + + return hash; + } + + void AddInterfaces (MemberCache parent) + { + foreach (Type iface in parent.interface_hash.Keys) { + if (!interface_hash.Contains (iface)) + interface_hash.Add (iface, true); + } + } + + /// <summary> + /// Add the contents of `new_hash' to `hash'. + /// </summary> + void AddHashtable (Hashtable hash, Hashtable new_hash) + { + IDictionaryEnumerator it = new_hash.GetEnumerator (); + while (it.MoveNext ()) { + ArrayList list = (ArrayList) hash [it.Key]; + if (list != null) + list.AddRange ((ArrayList) it.Value); + else + hash [it.Key] = ((ArrayList) it.Value).Clone (); + } + } + + /// <summary> + /// Bootstrap the member cache for an interface type. + /// Type.GetMembers() won't return any inherited members for interface types, + /// so we need to do this manually. Interfaces also inherit from System.Object. + /// </summary> + Hashtable SetupCacheForInterface () + { + Hashtable hash = SetupCache (TypeHandle.ObjectType.MemberCache); + Type [] ifaces = TypeManager.GetInterfaces (Container.Type); + + foreach (Type iface in ifaces) { + if (interface_hash.Contains (iface)) + continue; + interface_hash.Add (iface, true); + + IMemberContainer iface_container = + TypeManager.LookupMemberContainer (iface); + + MemberCache iface_cache = iface_container.MemberCache; + AddHashtable (hash, iface_cache.member_hash); + AddInterfaces (iface_cache); + } + + return hash; + } + + /// <summary> + /// Add all members from class `container' to the cache. + /// </summary> + void AddMembers (IMemberContainer container) + { + // We need to call AddMembers() with a single member type at a time + // to get the member type part of CacheEntry.EntryType right. + AddMembers (MemberTypes.Constructor, container); + AddMembers (MemberTypes.Field, container); + AddMembers (MemberTypes.Method, container); + AddMembers (MemberTypes.Property, container); + AddMembers (MemberTypes.Event, container); + // Nested types are returned by both Static and Instance searches. + AddMembers (MemberTypes.NestedType, + BindingFlags.Static | BindingFlags.Public, container); + AddMembers (MemberTypes.NestedType, + BindingFlags.Static | BindingFlags.NonPublic, container); + } + + void AddMembers (MemberTypes mt, IMemberContainer container) + { + AddMembers (mt, BindingFlags.Static | BindingFlags.Public, container); + AddMembers (mt, BindingFlags.Static | BindingFlags.NonPublic, container); + AddMembers (mt, BindingFlags.Instance | BindingFlags.Public, container); + AddMembers (mt, BindingFlags.Instance | BindingFlags.NonPublic, container); + } + + /// <summary> + /// Add all members from class `container' with the requested MemberTypes and + /// BindingFlags to the cache. This method is called multiple times with different + /// MemberTypes and BindingFlags. + /// </summary> + void AddMembers (MemberTypes mt, BindingFlags bf, IMemberContainer container) + { + MemberList members = container.GetMembers (mt, bf); + BindingFlags new_bf = (container == Container) ? + bf | BindingFlags.DeclaredOnly : bf; + + foreach (MemberInfo member in members) { + string name = member.Name; + + // We use a name-based hash table of ArrayList's. + ArrayList list = (ArrayList) member_hash [name]; + if (list == null) { + list = new ArrayList (); + member_hash.Add (name, list); + } + + // When this method is called for the current class, the list will + // already contain all inherited members from our parent classes. + // We cannot add new members in front of the list since this'd be an + // expensive operation, that's why the list is sorted in reverse order + // (ie. members from the current class are coming last). + list.Add (new CacheEntry (container, member, mt, bf)); + } + } + + /// <summary> + /// Add all declared and inherited methods from class `type' to the method cache. + /// </summary> + void AddMethods (Type type) + { + AddMethods (BindingFlags.Static | BindingFlags.Public, type); + AddMethods (BindingFlags.Static | BindingFlags.NonPublic, type); + AddMethods (BindingFlags.Instance | BindingFlags.Public, type); + AddMethods (BindingFlags.Instance | BindingFlags.NonPublic, type); + } + + void AddMethods (BindingFlags bf, Type type) + { + MemberInfo [] members = type.GetMethods (bf); + + foreach (MethodBase member in members) { + string name = member.Name; + + // Varargs methods aren't allowed in C# code. + if ((member.CallingConvention & CallingConventions.VarArgs) != 0) + continue; + + // We use a name-based hash table of ArrayList's. + ArrayList list = (ArrayList) method_hash [name]; + if (list == null) { + list = new ArrayList (); + method_hash.Add (name, list); + } + + // Unfortunately, the elements returned by Type.GetMethods() aren't + // sorted so we need to do this check for every member. + BindingFlags new_bf = bf; + if (member.DeclaringType == type) + new_bf |= BindingFlags.DeclaredOnly; + + list.Add (new CacheEntry (Container, member, MemberTypes.Method, new_bf)); + } + } + + /// <summary> + /// Compute and return a appropriate `EntryType' magic number for the given + /// MemberTypes and BindingFlags. + /// </summary> + protected static EntryType GetEntryType (MemberTypes mt, BindingFlags bf) + { + EntryType type = EntryType.None; + + if ((mt & MemberTypes.Constructor) != 0) + type |= EntryType.Constructor; + if ((mt & MemberTypes.Event) != 0) + type |= EntryType.Event; + if ((mt & MemberTypes.Field) != 0) + type |= EntryType.Field; + if ((mt & MemberTypes.Method) != 0) + type |= EntryType.Method; + if ((mt & MemberTypes.Property) != 0) + type |= EntryType.Property; + // Nested types are returned by static and instance searches. + if ((mt & MemberTypes.NestedType) != 0) + type |= EntryType.NestedType | EntryType.Static | EntryType.Instance; + + if ((bf & BindingFlags.Instance) != 0) + type |= EntryType.Instance; + if ((bf & BindingFlags.Static) != 0) + type |= EntryType.Static; + if ((bf & BindingFlags.Public) != 0) + type |= EntryType.Public; + if ((bf & BindingFlags.NonPublic) != 0) + type |= EntryType.NonPublic; + if ((bf & BindingFlags.DeclaredOnly) != 0) + type |= EntryType.Declared; + + return type; + } + + /// <summary> + /// The `MemberTypes' enumeration type is a [Flags] type which means that it may + /// denote multiple member types. Returns true if the given flags value denotes a + /// single member types. + /// </summary> + public static bool IsSingleMemberType (MemberTypes mt) + { + switch (mt) { + case MemberTypes.Constructor: + case MemberTypes.Event: + case MemberTypes.Field: + case MemberTypes.Method: + case MemberTypes.Property: + case MemberTypes.NestedType: + return true; + + default: + return false; + } + } + + /// <summary> + /// We encode the MemberTypes and BindingFlags of each members in a "magic" + /// number to speed up the searching process. + /// </summary> + [Flags] + protected enum EntryType { + None = 0x000, + + Instance = 0x001, + Static = 0x002, + MaskStatic = Instance|Static, + + Public = 0x004, + NonPublic = 0x008, + MaskProtection = Public|NonPublic, + + Declared = 0x010, + + Constructor = 0x020, + Event = 0x040, + Field = 0x080, + Method = 0x100, + Property = 0x200, + NestedType = 0x400, + + MaskType = Constructor|Event|Field|Method|Property|NestedType + } + + protected struct CacheEntry { + public readonly IMemberContainer Container; + public readonly EntryType EntryType; + public readonly MemberInfo Member; + + public CacheEntry (IMemberContainer container, MemberInfo member, + MemberTypes mt, BindingFlags bf) + { + this.Container = container; + this.Member = member; + this.EntryType = GetEntryType (mt, bf); + } + } + + /// <summary> + /// This is called each time we're walking up one level in the class hierarchy + /// and checks whether we can abort the search since we've already found what + /// we were looking for. + /// </summary> + protected bool DoneSearching (ArrayList list) + { + // + // We've found exactly one member in the current class and it's not + // a method or constructor. + // + if (list.Count == 1 && !(list [0] is MethodBase)) + return true; + + // + // Multiple properties: we query those just to find out the indexer + // name + // + if ((list.Count > 0) && (list [0] is PropertyInfo)) + return true; + + return false; + } + + /// <summary> + /// Looks up members with name `name'. If you provide an optional + /// filter function, it'll only be called with members matching the + /// requested member name. + /// + /// This method will try to use the cache to do the lookup if possible. + /// + /// Unlike other FindMembers implementations, this method will always + /// check all inherited members - even when called on an interface type. + /// + /// If you know that you're only looking for methods, you should use + /// MemberTypes.Method alone since this speeds up the lookup a bit. + /// When doing a method-only search, it'll try to use a special method + /// cache (unless it's a dynamic type or an interface) and the returned + /// MemberInfo's will have the correct ReflectedType for inherited methods. + /// The lookup process will automatically restart itself in method-only + /// search mode if it discovers that it's about to return methods. + /// </summary> + public MemberList FindMembers (MemberTypes mt, BindingFlags bf, string name, + MemberFilter filter, object criteria) + { + bool declared_only = (bf & BindingFlags.DeclaredOnly) != 0; + bool method_search = mt == MemberTypes.Method; + // If we have a method cache and we aren't already doing a method-only search, + // then we restart a method search if the first match is a method. + bool do_method_search = !method_search && (method_hash != null); + + ArrayList applicable; + + // If this is a method-only search, we try to use the method cache if + // possible; a lookup in the method cache will return a MemberInfo with + // the correct ReflectedType for inherited methods. + if (method_search && (method_hash != null)) + applicable = (ArrayList) method_hash [name]; + else + applicable = (ArrayList) member_hash [name]; + + if (applicable == null) + return MemberList.Empty; + + ArrayList list = new ArrayList (); + + Timer.StartTimer (TimerType.CachedLookup); + + EntryType type = GetEntryType (mt, bf); + + IMemberContainer current = Container; + + // `applicable' is a list of all members with the given member name `name' + // in the current class and all its parent classes. The list is sorted in + // reverse order due to the way how the cache is initialy created (to speed + // things up, we're doing a deep-copy of our parent). + + for (int i = applicable.Count-1; i >= 0; i--) { + CacheEntry entry = (CacheEntry) applicable [i]; + + // This happens each time we're walking one level up in the class + // hierarchy. If we're doing a DeclaredOnly search, we must abort + // the first time this happens (this may already happen in the first + // iteration of this loop if there are no members with the name we're + // looking for in the current class). + if (entry.Container != current) { + if (declared_only || DoneSearching (list)) + break; + + current = entry.Container; + } + + // Is the member of the correct type ? + if ((entry.EntryType & type & EntryType.MaskType) == 0) + continue; + + // Is the member static/non-static ? + if ((entry.EntryType & type & EntryType.MaskStatic) == 0) + continue; + + // Apply the filter to it. + if (filter (entry.Member, criteria)) { + if ((entry.EntryType & EntryType.MaskType) != EntryType.Method) + do_method_search = false; + list.Add (entry.Member); + } + } + + Timer.StopTimer (TimerType.CachedLookup); + + // If we have a method cache and we aren't already doing a method-only + // search, we restart in method-only search mode if the first match is + // a method. This ensures that we return a MemberInfo with the correct + // ReflectedType for inherited methods. + if (do_method_search && (list.Count > 0)) + return FindMembers (MemberTypes.Method, bf, name, filter, criteria); + + return new MemberList (list); + } + } +} diff --git a/mcs/mbas/delegate.cs b/mcs/mbas/delegate.cs new file mode 100644 index 00000000000..add8e2579b1 --- /dev/null +++ b/mcs/mbas/delegate.cs @@ -0,0 +1,771 @@ +// +// delegate.cs: Delegate Handler +// +// Author: Ravi Pratap (ravi@ximian.com) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// +// + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; + +namespace Mono.CSharp { + + /// <summary> + /// Holds Delegates + /// </summary> + public class Delegate : DeclSpace { + public Expression ReturnType; + public Parameters Parameters; + public Attributes OptAttributes; + + public ConstructorBuilder ConstructorBuilder; + public MethodBuilder InvokeBuilder; + public MethodBuilder BeginInvokeBuilder; + public MethodBuilder EndInvokeBuilder; + + Type [] param_types; + Type ret_type; + + Expression instance_expr; + MethodBase delegate_method; + + const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.UNSAFE | + Modifiers.PRIVATE; + + public Delegate (TypeContainer parent, Expression type, int mod_flags, + string name, Parameters param_list, + Attributes attrs, Location l) + : base (parent, name, l) + { + this.ReturnType = type; + ModFlags = Modifiers.Check (AllowedModifiers, mod_flags, + IsTopLevel ? Modifiers.INTERNAL : + Modifiers.PRIVATE, l); + Parameters = param_list; + OptAttributes = attrs; + } + + public override TypeBuilder DefineType () + { + TypeAttributes attr; + + if (TypeBuilder != null) + return TypeBuilder; + + if (IsTopLevel) { + ModuleBuilder builder = CodeGen.ModuleBuilder; + attr = TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed; + + TypeBuilder = builder.DefineType ( + Name, attr, TypeManager.multicast_delegate_type); + } else { + TypeBuilder builder = Parent.TypeBuilder; + attr = TypeAttributes.NestedPublic | TypeAttributes.Class | + TypeAttributes.Sealed; + + string name = Name.Substring (1 + Name.LastIndexOf ('.')); + TypeBuilder = builder.DefineNestedType ( + name, attr, TypeManager.multicast_delegate_type); + } + + TypeManager.AddDelegateType (Name, TypeBuilder, this); + + return TypeBuilder; + } + + public override bool DefineMembers (TypeContainer container) + { + return true; + } + + public override bool Define (TypeContainer container) + { + MethodAttributes mattr; + int i; + + // FIXME: POSSIBLY make this static, as it is always constant + // + Type [] const_arg_types = new Type [2]; + const_arg_types [0] = TypeManager.object_type; + const_arg_types [1] = TypeManager.intptr_type; + + mattr = MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | + MethodAttributes.HideBySig | MethodAttributes.Public; + + ConstructorBuilder = TypeBuilder.DefineConstructor (mattr, + CallingConventions.Standard, + const_arg_types); + + ConstructorBuilder.DefineParameter (1, ParameterAttributes.None, "object"); + ConstructorBuilder.DefineParameter (2, ParameterAttributes.None, "method"); + // + // HACK because System.Reflection.Emit is lame + // + // + // FIXME: POSSIBLY make these static, as they are always the same + Parameter [] fixed_pars = new Parameter [2]; + fixed_pars [0] = new Parameter (null, null, Parameter.Modifier.NONE, null); + fixed_pars [1] = new Parameter (null, null, Parameter.Modifier.NONE, null); + Parameters const_parameters = new Parameters (fixed_pars, null, Location); + + TypeManager.RegisterMethod ( + ConstructorBuilder, + new InternalParameters (const_arg_types, const_parameters), + const_arg_types); + + + ConstructorBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + + // + // Here the various methods like Invoke, BeginInvoke etc are defined + // + // First, call the `out of band' special method for + // defining recursively any types we need: + + if (!Parameters.ComputeAndDefineParameterTypes (this)) + return false; + + param_types = Parameters.GetParameterInfo (this); + if (param_types == null) + return false; + + // + // Invoke method + // + + // Check accessibility + foreach (Type partype in param_types) + if (!container.AsAccessible (partype, ModFlags)) { + Report.Error (59, Location, + "Inconsistent accessibility: parameter type `" + + TypeManager.CSharpName (partype) + "` is less " + + "accessible than delegate `" + Name + "'"); + return false; + } + + ReturnType = ResolveTypeExpr (ReturnType, false, Location); + ret_type = ReturnType.Type; + if (ret_type == null) + return false; + + if (!container.AsAccessible (ret_type, ModFlags)) { + Report.Error (58, Location, + "Inconsistent accessibility: return type `" + + TypeManager.CSharpName (ret_type) + "` is less " + + "accessible than delegate `" + Name + "'"); + return false; + } + + // + // We don't have to check any others because they are all + // guaranteed to be accessible - they are standard types. + // + + CallingConventions cc = Parameters.GetCallingConvention (); + + mattr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual; + + InvokeBuilder = TypeBuilder.DefineMethod ("Invoke", + mattr, + cc, + ret_type, + param_types); + + i = 0; + if (Parameters.FixedParameters != null){ + int top = Parameters.FixedParameters.Length; + Parameter p; + + for (; i < top; i++) { + p = Parameters.FixedParameters [i]; + + InvokeBuilder.DefineParameter ( + i+1, p.Attributes, p.Name); + } + } + if (Parameters.ArrayParameter != null){ + Parameter p = Parameters.ArrayParameter; + + InvokeBuilder.DefineParameter ( + i+1, p.Attributes, p.Name); + } + + InvokeBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + + TypeManager.RegisterMethod (InvokeBuilder, + new InternalParameters (container, Parameters), + param_types); + + // + // BeginInvoke + // + int params_num = param_types.Length; + Type [] async_param_types = new Type [params_num + 2]; + + param_types.CopyTo (async_param_types, 0); + + async_param_types [params_num] = TypeManager.asynccallback_type; + async_param_types [params_num + 1] = TypeManager.object_type; + + mattr = MethodAttributes.Public | MethodAttributes.HideBySig | + MethodAttributes.Virtual | MethodAttributes.NewSlot; + + BeginInvokeBuilder = TypeBuilder.DefineMethod ("BeginInvoke", + mattr, + cc, + TypeManager.iasyncresult_type, + async_param_types); + + i = 0; + if (Parameters.FixedParameters != null){ + int top = Parameters.FixedParameters.Length; + Parameter p; + + for (i = 0 ; i < top; i++) { + p = Parameters.FixedParameters [i]; + + BeginInvokeBuilder.DefineParameter ( + i+1, p.Attributes, p.Name); + } + } + if (Parameters.ArrayParameter != null){ + Parameter p = Parameters.ArrayParameter; + + BeginInvokeBuilder.DefineParameter ( + i+1, p.Attributes, p.Name); + i++; + } + + BeginInvokeBuilder.DefineParameter (i + 1, ParameterAttributes.None, "callback"); + BeginInvokeBuilder.DefineParameter (i + 2, ParameterAttributes.None, "object"); + + BeginInvokeBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + + Parameter [] async_params = new Parameter [params_num + 2]; + int n = 0; + if (Parameters.FixedParameters != null){ + Parameters.FixedParameters.CopyTo (async_params, 0); + n = Parameters.FixedParameters.Length; + } + if (Parameters.ArrayParameter != null) + async_params [n] = Parameters.ArrayParameter; + + async_params [params_num] = new Parameter ( + TypeManager.system_asynccallback_expr, "callback", + Parameter.Modifier.NONE, null); + async_params [params_num + 1] = new Parameter ( + TypeManager.system_object_expr, "object", + Parameter.Modifier.NONE, null); + + Parameters async_parameters = new Parameters (async_params, null, Location); + + async_parameters.ComputeAndDefineParameterTypes (this); + TypeManager.RegisterMethod (BeginInvokeBuilder, + new InternalParameters (container, async_parameters), + async_param_types); + + // + // EndInvoke + // + Type [] end_param_types = new Type [1]; + end_param_types [0] = TypeManager.iasyncresult_type; + + EndInvokeBuilder = TypeBuilder.DefineMethod ("EndInvoke", + mattr, + cc, + ret_type, + end_param_types); + EndInvokeBuilder.DefineParameter (1, ParameterAttributes.None, "result"); + + EndInvokeBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + + Parameter [] end_params = new Parameter [1]; + end_params [0] = new Parameter ( + TypeManager.system_iasyncresult_expr, "result", + Parameter.Modifier.NONE, null); + + TypeManager.RegisterMethod ( + EndInvokeBuilder, new InternalParameters ( + container, + new Parameters ( + end_params, null, Location)), + end_param_types); + + return true; + } + + /// <summary> + /// Verifies whether the method in question is compatible with the delegate + /// Returns the method itself if okay and null if not. + /// </summary> + public static MethodBase VerifyMethod (EmitContext ec, Type delegate_type, MethodBase mb, + Location loc) + { + ParameterData pd = Invocation.GetParameterData (mb); + + int pd_count = pd.Count; + + Expression ml = Expression.MemberLookup ( + ec, delegate_type, "Invoke", loc); + + if (!(ml is MethodGroupExpr)) { + Report.Error (-100, loc, "Internal error: could not find Invoke method!"); + return null; + } + + MethodBase invoke_mb = ((MethodGroupExpr) ml).Methods [0]; + + ParameterData invoke_pd = Invocation.GetParameterData (invoke_mb); + + if (invoke_pd.Count != pd_count) + return null; + + for (int i = pd_count; i > 0; ) { + i--; + + if (invoke_pd.ParameterType (i) == pd.ParameterType (i)) + continue; + else + return null; + } + + if (((MethodInfo) invoke_mb).ReturnType == ((MethodInfo) mb).ReturnType) + return mb; + else + return null; + } + + // <summary> + // Verifies whether the invocation arguments are compatible with the + // delegate's target method + // </summary> + public static bool VerifyApplicability (EmitContext ec, + Type delegate_type, + ArrayList args, + Location loc) + { + int arg_count; + + if (args == null) + arg_count = 0; + else + arg_count = args.Count; + + Expression ml = Expression.MemberLookup ( + ec, delegate_type, "Invoke", loc); + + if (!(ml is MethodGroupExpr)) { + Report.Error (-100, loc, "Internal error: could not find Invoke method!" + delegate_type); + return false; + } + + MethodBase mb = ((MethodGroupExpr) ml).Methods [0]; + ParameterData pd = Invocation.GetParameterData (mb); + + int pd_count = pd.Count; + + bool not_params_method = (pd_count == 0) || + (pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS); + + if (not_params_method && pd_count != arg_count) { + Report.Error (1593, loc, + "Delegate '" + delegate_type.ToString () + + "' does not take '" + arg_count + "' arguments"); + return false; + } + + return Invocation.VerifyArgumentsCompat (ec, args, arg_count, mb, !not_params_method, + delegate_type, loc); + } + + /// <summary> + /// Verifies whether the delegate in question is compatible with this one in + /// order to determine if instantiation from the same is possible. + /// </summary> + public static bool VerifyDelegate (EmitContext ec, Type delegate_type, Type probe_type, Location loc) + { + Expression ml = Expression.MemberLookup ( + ec, delegate_type, "Invoke", loc); + + if (!(ml is MethodGroupExpr)) { + Report.Error (-100, loc, "Internal error: could not find Invoke method!"); + return false; + } + + MethodBase mb = ((MethodGroupExpr) ml).Methods [0]; + ParameterData pd = Invocation.GetParameterData (mb); + + Expression probe_ml = Expression.MemberLookup ( + ec, delegate_type, "Invoke", loc); + + if (!(probe_ml is MethodGroupExpr)) { + Report.Error (-100, loc, "Internal error: could not find Invoke method!"); + return false; + } + + MethodBase probe_mb = ((MethodGroupExpr) probe_ml).Methods [0]; + ParameterData probe_pd = Invocation.GetParameterData (probe_mb); + + if (((MethodInfo) mb).ReturnType != ((MethodInfo) probe_mb).ReturnType) + return false; + + if (pd.Count != probe_pd.Count) + return false; + + for (int i = pd.Count; i > 0; ) { + i--; + + if (pd.ParameterType (i) != probe_pd.ParameterType (i) || + pd.ParameterModifier (i) != probe_pd.ParameterModifier (i)) + return false; + } + + return true; + } + + public static string FullDelegateDesc (Type del_type, MethodBase mb, ParameterData pd) + { + StringBuilder sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType)); + + sb.Append (" " + del_type.ToString ()); + sb.Append (" ("); + + int length = pd.Count; + + for (int i = length; i > 0; ) { + i--; + + sb.Append (TypeManager.CSharpName (pd.ParameterType (length - i - 1))); + if (i != 0) + sb.Append (", "); + } + + sb.Append (")"); + return sb.ToString (); + + } + + // Hack around System.Reflection as found everywhere else + public override MemberList FindMembers (MemberTypes mt, BindingFlags bf, + MemberFilter filter, object criteria) + { + ArrayList members = new ArrayList (); + + if ((mt & MemberTypes.Method) != 0) { + if (ConstructorBuilder != null) + if (filter (ConstructorBuilder, criteria)) + members.Add (ConstructorBuilder); + + if (InvokeBuilder != null) + if (filter (InvokeBuilder, criteria)) + members.Add (InvokeBuilder); + + if (BeginInvokeBuilder != null) + if (filter (BeginInvokeBuilder, criteria)) + members.Add (BeginInvokeBuilder); + + if (EndInvokeBuilder != null) + if (filter (EndInvokeBuilder, criteria)) + members.Add (EndInvokeBuilder); + } + + return new MemberList (members); + } + + public override MemberCache MemberCache { + get { + return null; + } + } + + public Expression InstanceExpression { + get { + return instance_expr; + } + set { + instance_expr = value; + } + } + + public MethodBase TargetMethod { + get { + return delegate_method; + } + set { + delegate_method = value; + } + } + + public Type TargetReturnType { + get { + return ret_type; + } + } + + public Type [] ParameterTypes { + get { + return param_types; + } + } + + } + + public class NewDelegate : Expression { + + public ArrayList Arguments; + + MethodBase constructor_method; + MethodBase delegate_method; + Expression delegate_instance_expr; + + public NewDelegate (Type type, ArrayList Arguments, Location loc) + { + this.type = type; + this.Arguments = Arguments; + this.loc = loc; + } + + public override Expression DoResolve (EmitContext ec) + { + if (Arguments == null) { + Report.Error (-11, loc, + "Delegate creation expression takes only one argument"); + return null; + } + + if (Arguments.Count != 1) { + Report.Error (-11, loc, + "Delegate creation expression takes only one argument"); + return null; + } + + Expression ml = Expression.MemberLookup ( + ec, type, ".ctor", loc); + + if (!(ml is MethodGroupExpr)) { + Report.Error (-100, loc, "Internal error: Could not find delegate constructor!"); + return null; + } + + constructor_method = ((MethodGroupExpr) ml).Methods [0]; + Argument a = (Argument) Arguments [0]; + + if (!a.ResolveMethodGroup (ec, Location)) + return null; + + Expression e = a.Expr; + + Expression invoke_method = Expression.MemberLookup ( + ec, type, "Invoke", MemberTypes.Method, + Expression.AllBindingFlags, loc); + + if (invoke_method == null) { + Report.Error (-200, loc, "Internal error ! Could not find Invoke method!"); + return null; + } + + if (e is MethodGroupExpr) { + MethodGroupExpr mg = (MethodGroupExpr) e; + + foreach (MethodInfo mi in mg.Methods){ + delegate_method = Delegate.VerifyMethod (ec, type, mi, loc); + + if (delegate_method != null) + break; + } + + if (delegate_method == null) { + string method_desc; + if (mg.Methods.Length > 1) + method_desc = mg.Methods [0].Name; + else + method_desc = Invocation.FullMethodDesc (mg.Methods [0]); + + MethodBase dm = ((MethodGroupExpr) invoke_method).Methods [0]; + ParameterData param = Invocation.GetParameterData (dm); + string delegate_desc = Delegate.FullDelegateDesc (type, dm, param); + + Report.Error (123, loc, "Method '" + method_desc + "' does not " + + "match delegate '" + delegate_desc + "'"); + + return null; + } + + // + // Check safe/unsafe of the delegate + // + if (!ec.InUnsafe){ + ParameterData param = Invocation.GetParameterData (delegate_method); + int count = param.Count; + + for (int i = 0; i < count; i++){ + if (param.ParameterType (i).IsPointer){ + Expression.UnsafeError (loc); + return null; + } + } + } + + if (mg.InstanceExpression != null) + delegate_instance_expr = mg.InstanceExpression.Resolve (ec); + else { + if (!ec.IsStatic) + delegate_instance_expr = ec.This; + else + delegate_instance_expr = null; + } + + if (delegate_instance_expr != null) + if (delegate_instance_expr.Type.IsValueType) + delegate_instance_expr = new BoxedCast (delegate_instance_expr); + + eclass = ExprClass.Value; + return this; + } + + Type e_type = e.Type; + + if (!TypeManager.IsDelegateType (e_type)) { + Report.Error (-12, loc, "Cannot create a delegate from something " + + "not a delegate or a method."); + return null; + } + + // This is what MS' compiler reports. We could always choose + // to be more verbose and actually give delegate-level specifics + + if (!Delegate.VerifyDelegate (ec, type, e_type, loc)) { + Report.Error (29, loc, "Cannot implicitly convert type '" + e_type + "' " + + "to type '" + type + "'"); + return null; + } + + delegate_instance_expr = e; + delegate_method = ((MethodGroupExpr) invoke_method).Methods [0]; + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + if (delegate_instance_expr == null || + delegate_method.IsStatic) + ec.ig.Emit (OpCodes.Ldnull); + else + delegate_instance_expr.Emit (ec); + + if (delegate_method.IsVirtual) { + ec.ig.Emit (OpCodes.Dup); + ec.ig.Emit (OpCodes.Ldvirtftn, (MethodInfo) delegate_method); + } else + ec.ig.Emit (OpCodes.Ldftn, (MethodInfo) delegate_method); + ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) constructor_method); + } + } + + public class DelegateInvocation : ExpressionStatement { + + public Expression InstanceExpr; + public ArrayList Arguments; + + MethodBase method; + + public DelegateInvocation (Expression instance_expr, ArrayList args, Location loc) + { + this.InstanceExpr = instance_expr; + this.Arguments = args; + this.loc = loc; + } + + public override Expression DoResolve (EmitContext ec) + { + if (InstanceExpr is EventExpr) { + + EventInfo ei = ((EventExpr) InstanceExpr).EventInfo; + + Expression ml = MemberLookup ( + ec, ec.ContainerType, ei.Name, + MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc); + + if (ml == null) { + // + // If this is the case, then the Event does not belong + // to this Type and so, according to the spec + // cannot be accessed directly + // + // Note that target will not appear as an EventExpr + // in the case it is being referenced within the same type container; + // it will appear as a FieldExpr in that case. + // + + Assign.error70 (ei, loc); + return null; + } + } + + + Type del_type = InstanceExpr.Type; + if (del_type == null) + return null; + + if (Arguments != null){ + foreach (Argument a in Arguments){ + if (!a.Resolve (ec, loc)) + return null; + } + } + + if (!Delegate.VerifyApplicability (ec, del_type, Arguments, loc)) + return null; + + Expression lookup = Expression.MemberLookup (ec, del_type, "Invoke", loc); + if (!(lookup is MethodGroupExpr)) { + Report.Error (-100, loc, "Internal error: could not find Invoke method!"); + return null; + } + + method = ((MethodGroupExpr) lookup).Methods [0]; + type = ((MethodInfo) method).ReturnType; + eclass = ExprClass.Value; + + return this; + } + + public override void Emit (EmitContext ec) + { + Delegate del = TypeManager.LookupDelegate (InstanceExpr.Type); + + // + // Invocation on delegates call the virtual Invoke member + // so we are always `instance' calls + // + Invocation.EmitCall (ec, false, false, InstanceExpr, method, Arguments, loc); + } + + public override void EmitStatement (EmitContext ec) + { + Emit (ec); + // + // Pop the return value if there is one + // + if (method is MethodInfo){ + if (((MethodInfo) method).ReturnType != TypeManager.void_type) + ec.ig.Emit (OpCodes.Pop); + } + } + + } +} diff --git a/mcs/mbas/driver.cs b/mcs/mbas/driver.cs new file mode 100644 index 00000000000..d0b5ac74d7e --- /dev/null +++ b/mcs/mbas/driver.cs @@ -0,0 +1,661 @@ +// +// driver.cs: The compiler command line driver. +// +// Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com) +// Based on mcs by : Miguel de Icaza (miguel@gnu.org) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2002 Rafael Teixeira +// + +namespace Mono.Languages +{ + using System; + using System.Reflection; + using System.Reflection.Emit; + using System.Collections; + using System.IO; + using System.Globalization; + using Mono.CSharp; + using Mono.GetOptions; + + enum Target + { + Library, Exe, Module, WinExe + }; + + /// <summary> + /// The compiler driver. + /// </summary> + public class Driver : Options + { + + [Option("Verbose parsing (for debugging the parser)",'v')] + public bool verbose { set { GenericParser.yacc_verbose_flag = value; } } + + [Option("Specifies PARAM as main (starting) class", 'm')] + public string main { set { RootContext.MainClass = value; } } + + [Option("About the MonoBASIC compiler", "about")] + public override WhatToDoNext DoAbout() + { + return base.DoAbout(); + } + + [Option("Adds PARAM to the assembly link path", 'L')] + public string[] LinkPaths = null; + + [Option("Defines the symbol PARAM", "define")] + public string[] Defines = null; + + [Option("Only parses the source file (for debugging the tokenizer)", "parse")] + public bool parse_only = false; + + + [Option("Disables implicit references to assemblies", "noconfig")] + public bool NoConfig { set { load_default_config = !value; } } + + [Option("Allows unsafe code", "unsafe")] + public bool AllowUnsafeCode { set { RootContext.Unsafe = value; } } + + [Option("Specifies output file", 'o', "output")] + public string output_file = null; + + [Option("Only tokenizes source files", "tokenize")] + public bool tokenize = false; + + [Option("Set default context to checked", "checked")] + public bool Checked { set { RootContext.Checked = value; } } + + [Option("Shows stack trace at Error location", "Stacktrace")] + public bool Stacktrace { set { Report.Stacktrace = value; } } + + [Option(-1, "References an assembly", 'r')] + public string reference { set { references.Add(value); } } + + [Option("Adds PARAM as a resource", "resource")] + public string[] resources; + + [Option("Set default context to checked", "nostdlib")] + public bool nostdlib { set { RootContext.StdLib = !value; } } + + [Option("Makes errors fatal", "fatal")] + public bool Fatal { set { Report.Fatal = value; } } + + [Option("Treat warnings as errors", "werror")] + public bool WarningsAreErrors { set { Report.WarningsAreErrors = value; } } + + [Option("Ignores warning number PARAM", "nowarn")] + public WhatToDoNext SetIgnoreWarning(int warn) + { + Report.SetIgnoreWarning(warn); + return WhatToDoNext.GoAhead; + } + + [Option("Recursively compiles the files in PARAM ([dir]/file)", "recurse")] + public WhatToDoNext recurse(string DirName) + { + AddFiles (DirName, true); + return WhatToDoNext.GoAhead; + } + + + [Option("Write symbolic debugging information to FILE-debug.s", 'g', "debug")] + public bool want_debugging_support = false; + + [Option("Debugger arguments", "debug-args")] + public WhatToDoNext SetDebugArgs(string args) + { + char[] sep = { ',' }; + debug_arglist.AddRange (args.Split (sep)); + return WhatToDoNext.GoAhead; + } + + [Option("Specifies the target (PARAM is one of: exe, winexe, library, module)", "target")] + public WhatToDoNext SetTarget(string type) + { + switch (type) + { + case "library": + target = Target.Library; + target_ext = ".dll"; + break; + + case "exe": + target = Target.Exe; + break; + + case "winexe": + target = Target.WinExe; + break; + + case "module": + target = Target.Module; + target_ext = ".dll"; + break; + } + return WhatToDoNext.GoAhead; + } + + [Option("Sets warning level (the highest is 4, the default)", "wlevel")] + public int wlevel { set { RootContext.WarningLevel = value; } } + + [Option("Displays time stamps of various compiler events")] + public bool timestamp + { + set + { + timestamps = true; + last_time = DateTime.Now; + debug_arglist.Add("timestamp"); + } + } + + // TODO : response file support + + + ArrayList defines = new ArrayList(); + ArrayList references = new ArrayList(); + ArrayList soft_references = new ArrayList(); + string first_source = null; + Target target = Target.Exe; + string target_ext = ".exe"; + ArrayList debug_arglist = new ArrayList (); + bool timestamps = false; + Hashtable source_files = new Hashtable (); + bool load_default_config = true; + + // + // Last time we took the time + // + DateTime last_time; + void ShowTime (string msg) + { + DateTime now = DateTime.Now; + TimeSpan span = now - last_time; + last_time = now; + + Console.WriteLine ( + "[{0:00}:{1:000}] {2}", + (int) span.TotalSeconds, span.Milliseconds, msg); + } + + public static int Main (string[] args) + { + Driver Exec = new Driver(); + + Exec.MainDriver(args); + + if (Report.Errors == 0) + { + Console.Write("Compilation succeeded"); + if (Report.Warnings > 0) + { + Console.Write(" - {0} warning(s)", Report.Warnings); + } + Console.WriteLine(); + return 0; + } + else + { + Console.WriteLine("Compilation failed: {0} Error(s), {1} warnings", + Report.Errors, Report.Warnings); + return 1; + } + } + + public int LoadAssembly (string assembly, bool soft) + { + Assembly a; + string total_log = ""; + + try { + char[] path_chars = { '/', '\\' }; + + if (assembly.IndexOfAny (path_chars) != -1) + a = Assembly.LoadFrom(assembly); + else + a = Assembly.Load(assembly); + TypeManager.AddAssembly (a); + return 0; + } catch (FileNotFoundException){ + foreach (string dir in LinkPaths){ + string full_path = dir + "/" + assembly + ".dll"; + + try { + a = Assembly.LoadFrom (full_path); + TypeManager.AddAssembly (a); + return 0; + } catch (FileNotFoundException ff) { + total_log += ff.FusionLog; + continue; + } + } + if (soft) + return 0; + } catch (BadImageFormatException f) { + Error ("// Bad file format while loading assembly"); + Error ("Log: " + f.FusionLog); + return 1; + } catch (FileLoadException f){ + Error ("File Load Exception: " + assembly); + Error ("Log: " + f.FusionLog); + return 1; + } catch (ArgumentNullException){ + Error ("// Argument Null exception "); + return 1; + } + + Report.Error (6, "Can not find assembly `" + assembly + "'" ); + Console.WriteLine ("Log: \n" + total_log); + + return 0; + } + + void Error(string message) + { + Console.WriteLine(message); + } + + /// <summary> + /// Loads all assemblies referenced on the command line + /// </summary> + public int LoadReferences () + { + int errors = 0; + + foreach (string r in references) + errors += LoadAssembly (r, false); + + foreach (string r in soft_references) + errors += LoadAssembly (r, true); + + return errors; + } + + void SetupDefaultDefines () + { + defines = new ArrayList (); + defines.Add ("__MonoBASIC__"); + } + + + // + // Returns the directory where the system assemblies are installed + // + string GetSystemDir () + { + Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies (); + + foreach (Assembly a in assemblies){ + string codebase = a.CodeBase; + if (codebase.EndsWith ("corlib.dll")){ + return codebase.Substring (0, codebase.LastIndexOf ("/")); + } + } + + Report.Error (-15, "Can not compute my system path"); + return ""; + } + + // + // Given a path specification, splits the path from the file/pattern + // + void SplitPathAndPattern (string spec, out string path, out string pattern) + { + int p = spec.LastIndexOf ("/"); + if (p != -1){ + // + // Windows does not like /file.cs, switch that to: + // "\", "file.cs" + // + if (p == 0){ + path = "\\"; + pattern = spec.Substring (1); + } else { + path = spec.Substring (0, p); + pattern = spec.Substring (p + 1); + } + return; + } + + p = spec.LastIndexOf ("\\"); + if (p != -1){ + path = spec.Substring (0, p); + pattern = spec.Substring (p + 1); + return; + } + + path = "."; + pattern = spec; + } + + bool AddFiles (string spec, bool recurse) + { + string path, pattern; + + SplitPathAndPattern(spec, out path, out pattern); + if (pattern.IndexOf("*") == -1) + { + AddFile(spec); + return true; + } + + string [] files = null; + try { + files = Directory.GetFiles(path, pattern); + } catch (System.IO.DirectoryNotFoundException) { + Report.Error (2001, "Source file `" + spec + "' could not be found"); + return false; + } catch (System.IO.IOException){ + Report.Error (2001, "Source file `" + spec + "' could not be found"); + return false; + } + foreach (string f in files) + AddFile (f); + + if (!recurse) + return true; + + string [] dirs = null; + + try { + dirs = Directory.GetDirectories(path); + } catch { + } + + foreach (string d in dirs) { + + // Don't include path in this string, as each + // directory entry already does + AddFiles (d + "/" + pattern, true); + } + + return true; + } + + void DefineDefaultConfig () + { + // + // For now the "default config" is harcoded into the compiler + // we can move this outside later + // + string [] default_config = + { + "System", + "System.Data", + "System.Xml", + "Microsoft.VisualBasic" , +#if EXTRA_DEFAULT_REFS + // + // Is it worth pre-loading all this stuff? + // + "Accessibility", + "System.Configuration.Install", + "System.Design", + "System.DirectoryServices", + "System.Drawing.Design", + "System.Drawing", + "System.EnterpriseServices", + "System.Management", + "System.Messaging", + "System.Runtime.Remoting", + "System.Runtime.Serialization.Formatters.Soap", + "System.Security", + "System.ServiceProcess", + "System.Web", + "System.Web.RegularExpressions", + "System.Web.Services" , + "System.Windows.Forms" +#endif + }; + + foreach (string def in default_config) + soft_references.Add(def); + } + + [ArgumentProcessor] + public void AddFile(string fileName) + { + string f = fileName; + if (first_source == null) + first_source = f; + + if (source_files.Contains(f)) + Report.Error(1516, "Source file '" + f + "' specified multiple times"); + else + source_files.Add(f, f); + } + + void ProcessSourceFile(string filename) + { + if (tokenize) + GenericParser.Tokenize(filename); + else + GenericParser.Parse(filename); + } + + string outputFile_Name = null; + + string outputFileName + { + get + { + if (outputFile_Name == null) + { + if (output_file == null) + { + int pos = first_source.LastIndexOf("."); + + if (pos > 0) + output_file = first_source.Substring(0, pos); + else + output_file = first_source; + } + string bname = CodeGen.Basename(output_file); + if (bname.IndexOf(".") == -1) + output_file += target_ext; + outputFile_Name = output_file; + } + return outputFile_Name; + } + } + + /// <summary> + /// Parses the arguments, and calls the compilation process. + /// </summary> + void MainDriver(string [] args) + { + ProcessArgs(args); + CompileAll(); + } + + public Driver() + { + SetupDefaultDefines(); + } + + bool ParseAll() // Phase 1 + { + if (first_source == null) + { + Report.Error(2008, "No files to compile were specified"); + return false; + } + + foreach(string filename in source_files.Values) + ProcessSourceFile(filename); + + if (tokenize || parse_only || (Report.Errors > 0)) + return false; + + return true; // everything went well go ahead + } + + void InitializeDebuggingSupport() + { + string[] debug_args = new string [debug_arglist.Count]; + debug_arglist.CopyTo(debug_args); + CodeGen.Init(outputFileName, outputFileName, want_debugging_support, debug_args); + TypeManager.AddModule(CodeGen.ModuleBuilder); + } + + public bool ResolveAllTypes() // Phase 2 + { + // Load Core Library for default compilation + if (RootContext.StdLib) + references.Insert(0, "mscorlib"); + + if (load_default_config) + DefineDefaultConfig(); + + if (timestamps) + ShowTime("Loading references"); + + // Load assemblies required + if (LoadReferences() > 0) + { + Error ("Could not load one or more assemblies"); + return false; + } + + if (timestamps) + ShowTime("References loaded"); + + InitializeDebuggingSupport(); + + // + // Before emitting, we need to get the core + // types emitted from the user defined types + // or from the system ones. + // + if (timestamps) + ShowTime ("Initializing Core Types"); + + if (!RootContext.StdLib) + RootContext.ResolveCore (); + if (Report.Errors > 0) + return false; + + TypeManager.InitCoreTypes(); + if (Report.Errors > 0) + return false; + + if (timestamps) + ShowTime (" Core Types done"); + + if (timestamps) + ShowTime ("Resolving tree"); + + // The second pass of the compiler + RootContext.ResolveTree (); + if (Report.Errors > 0) + return false; + + if (timestamps) + ShowTime ("Populate tree"); + + if (!RootContext.StdLib) + RootContext.BootCorlib_PopulateCoreTypes(); + if (Report.Errors > 0) + return false; + + RootContext.PopulateTypes(); + if (Report.Errors > 0) + return false; + + TypeManager.InitCodeHelpers(); + if (Report.Errors > 0) + return false; + + return true; + } + + bool GenerateAssembly() + { + // + // The code generator + // + if (timestamps) + ShowTime ("Emitting code"); + + RootContext.EmitCode(); + if (Report.Errors > 0) + return false; + + if (timestamps) + ShowTime (" done"); + + + if (timestamps) + ShowTime ("Closing types"); + + RootContext.CloseTypes (); + if (Report.Errors > 0) + return false; + + if (timestamps) + ShowTime (" done"); + + PEFileKinds k = PEFileKinds.ConsoleApplication; + + if (target == Target.Library || target == Target.Module) + k = PEFileKinds.Dll; + else if (target == Target.Exe) + k = PEFileKinds.ConsoleApplication; + else if (target == Target.WinExe) + k = PEFileKinds.WindowApplication; + + if (target == Target.Exe || target == Target.WinExe) + { + MethodInfo ep = RootContext.EntryPoint; + + if (ep == null) + { + Report.Error (5001, "Program " + outputFileName + + " does not have an entry point defined"); + return false; + } + + CodeGen.AssemblyBuilder.SetEntryPoint (ep, k); + } + + // Add the resources + if (resources != null) + foreach (string file in resources) + CodeGen.AssemblyBuilder.AddResourceFile (file, file); + + CodeGen.Save(outputFileName); + + if (timestamps) + ShowTime ("Saved output"); + + + if (want_debugging_support) + { + CodeGen.SaveSymbols (); + if (timestamps) + ShowTime ("Saved symbols"); + } + + return true; + } + + public void CompileAll() + { + if (!ParseAll()) // Phase 1 + return; + + if (!ResolveAllTypes()) // Phase 2 + return; + + if (!GenerateAssembly()) // Phase 3 + return; + + if (Report.ExpectedError != 0) + Error("Failed to report expected Error " + Report.ExpectedError); + } + + } +} diff --git a/mcs/mbas/ecore.cs b/mcs/mbas/ecore.cs new file mode 100644 index 00000000000..98ae385087f --- /dev/null +++ b/mcs/mbas/ecore.cs @@ -0,0 +1,4387 @@ +// +// ecore.cs: Core of the Expression representation for the intermediate tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. +// +// + +namespace Mono.CSharp { + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Reflection.Emit; + using System.Text; + + /// <remarks> + /// The ExprClass class contains the is used to pass the + /// classification of an expression (value, variable, namespace, + /// type, method group, property access, event access, indexer access, + /// nothing). + /// </remarks> + public enum ExprClass : byte { + Invalid, + + Value, + Variable, + Namespace, + Type, + MethodGroup, + PropertyAccess, + EventAccess, + IndexerAccess, + Nothing, + } + + /// <remarks> + /// This is used to tell Resolve in which types of expressions we're + /// interested. + /// </remarks> + [Flags] + public enum ResolveFlags { + // Returns Value, Variable, PropertyAccess, EventAccess or IndexerAccess. + VariableOrValue = 1, + + // Returns a type expression. + Type = 2, + + // Returns a method group. + MethodGroup = 4, + + // Allows SimpleNames to be returned. + // This is used by MemberAccess to construct long names that can not be + // partially resolved (namespace-qualified names for example). + SimpleName = 8, + + // Mask of all the expression class flags. + MaskExprClass = 15, + + // Disable control flow analysis while resolving the expression. + // This is used when resolving the instance expression of a field expression. + DisableFlowAnalysis = 16 + } + + // + // This is just as a hint to AddressOf of what will be done with the + // address. + [Flags] + public enum AddressOp { + Store = 1, + Load = 2, + LoadStore = 3 + }; + + /// <summary> + /// This interface is implemented by variables + /// </summary> + public interface IMemoryLocation { + /// <summary> + /// The AddressOf method should generate code that loads + /// the address of the object and leaves it on the stack. + /// + /// The `mode' argument is used to notify the expression + /// of whether this will be used to read from the address or + /// write to the address. + /// + /// This is just a hint that can be used to provide good error + /// reporting, and should have no other side effects. + /// </summary> + void AddressOf (EmitContext ec, AddressOp mode); + } + + /// <summary> + /// This interface is implemented by variables + /// </summary> + public interface IVariable { + /// <summary> + /// Checks whether the variable has already been assigned at + /// the current position of the method's control flow and + /// reports an appropriate error message if not. + /// + /// If the variable is a struct, then this call checks whether + /// all of its fields (including all private ones) have been + /// assigned. + /// </summary> + bool IsAssigned (EmitContext ec, Location loc); + + /// <summary> + /// Checks whether field `name' in this struct has been assigned. + /// </summary> + bool IsFieldAssigned (EmitContext ec, string name, Location loc); + + /// <summary> + /// Tells the flow analysis code that the variable has already + /// been assigned at the current code position. + /// + /// If the variable is a struct, this call marks all its fields + /// (including private fields) as being assigned. + /// </summary> + void SetAssigned (EmitContext ec); + + /// <summary> + /// Tells the flow analysis code that field `name' in this struct + /// has already been assigned atthe current code position. + /// </summary> + void SetFieldAssigned (EmitContext ec, string name); + } + + /// <summary> + /// This interface denotes an expression which evaluates to a member + /// of a struct or a class. + /// </summary> + public interface IMemberExpr + { + /// <summary> + /// The name of this member. + /// </summary> + string Name { + get; + } + + /// <summary> + /// Whether this is an instance member. + /// </summary> + bool IsInstance { + get; + } + + /// <summary> + /// Whether this is a static member. + /// </summary> + bool IsStatic { + get; + } + + /// <summary> + /// The type which declares this member. + /// </summary> + Type DeclaringType { + get; + } + + /// <summary> + /// The instance expression associated with this member, if it's a + /// non-static member. + /// </summary> + Expression InstanceExpression { + get; set; + } + } + + /// <summary> + /// Expression which resolves to a type. + /// </summary> + public interface ITypeExpression + { + /// <summary> + /// Resolve the expression, but only lookup types. + /// </summary> + Expression DoResolveType (EmitContext ec); + } + + /// <remarks> + /// Base class for expressions + /// </remarks> + public abstract class Expression { + public ExprClass eclass; + protected Type type; + protected Location loc; + + public Type Type { + get { + return type; + } + + set { + type = value; + } + } + + public Location Location { + get { + return loc; + } + } + + /// <summary> + /// Utility wrapper routine for Error, just to beautify the code + /// </summary> + public void Error (int error, string s) + { + if (!Location.IsNull (loc)) + Report.Error (error, loc, s); + else + Report.Error (error, s); + } + + /// <summary> + /// Utility wrapper routine for Warning, just to beautify the code + /// </summary> + public void Warning (int warning, string s) + { + if (!Location.IsNull (loc)) + Report.Warning (warning, loc, s); + else + Report.Warning (warning, s); + } + + /// <summary> + /// Utility wrapper routine for Warning, only prints the warning if + /// warnings of level `level' are enabled. + /// </summary> + public void Warning (int warning, int level, string s) + { + if (level <= RootContext.WarningLevel) + Warning (warning, s); + } + + static public void Error_CannotConvertType (Location loc, Type source, Type target) + { + Report.Error (30, loc, "Cannot convert type '" + + TypeManager.CSharpName (source) + "' to '" + + TypeManager.CSharpName (target) + "'"); + } + + /// <summary> + /// Performs semantic analysis on the Expression + /// </summary> + /// + /// <remarks> + /// The Resolve method is invoked to perform the semantic analysis + /// on the node. + /// + /// The return value is an expression (it can be the + /// same expression in some cases) or a new + /// expression that better represents this node. + /// + /// For example, optimizations of Unary (LiteralInt) + /// would return a new LiteralInt with a negated + /// value. + /// + /// If there is an error during semantic analysis, + /// then an error should be reported (using Report) + /// and a null value should be returned. + /// + /// There are two side effects expected from calling + /// Resolve(): the the field variable "eclass" should + /// be set to any value of the enumeration + /// `ExprClass' and the type variable should be set + /// to a valid type (this is the type of the + /// expression). + /// </remarks> + public abstract Expression DoResolve (EmitContext ec); + + public virtual Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + return DoResolve (ec); + } + + /// <summary> + /// Resolves an expression and performs semantic analysis on it. + /// </summary> + /// + /// <remarks> + /// Currently Resolve wraps DoResolve to perform sanity + /// checking and assertion checking on what we expect from Resolve. + /// </remarks> + public Expression Resolve (EmitContext ec, ResolveFlags flags) + { + // Are we doing a types-only search ? + if ((flags & ResolveFlags.MaskExprClass) == ResolveFlags.Type) { + ITypeExpression type_expr = this as ITypeExpression; + + if (type_expr == null) + return null; + + return type_expr.DoResolveType (ec); + } + + bool old_do_flow_analysis = ec.DoFlowAnalysis; + if ((flags & ResolveFlags.DisableFlowAnalysis) != 0) + ec.DoFlowAnalysis = false; + + Expression e; + if (this is SimpleName) + e = ((SimpleName) this).DoResolveAllowStatic (ec); + else + e = DoResolve (ec); + + ec.DoFlowAnalysis = old_do_flow_analysis; + + if (e == null) + return null; + + if (e is SimpleName){ + SimpleName s = (SimpleName) e; + + if ((flags & ResolveFlags.SimpleName) == 0) { + + object lookup = TypeManager.MemberLookup ( + ec.ContainerType, ec.ContainerType, AllMemberTypes, + AllBindingFlags | BindingFlags.NonPublic, s.Name); + if (lookup != null) + Error (122, "`" + s.Name + "' " + + "is inaccessible because of its protection level"); + else + Error (103, "The name `" + s.Name + "' could not be " + + "found in `" + ec.DeclSpace.Name + "'"); + return null; + } + + return s; + } + + if ((e is TypeExpr) || (e is ComposedCast)) { + if ((flags & ResolveFlags.Type) == 0) { + e.Error118 (flags); + return null; + } + + return e; + } + + switch (e.eclass) { + case ExprClass.Type: + if ((flags & ResolveFlags.VariableOrValue) == 0) { + e.Error118 (flags); + return null; + } + break; + + case ExprClass.MethodGroup: + if ((flags & ResolveFlags.MethodGroup) == 0) { + ((MethodGroupExpr) e).ReportUsageError (); + return null; + } + break; + + case ExprClass.Value: + case ExprClass.Variable: + case ExprClass.PropertyAccess: + case ExprClass.EventAccess: + case ExprClass.IndexerAccess: + if ((flags & ResolveFlags.VariableOrValue) == 0) { + e.Error118 (flags); + return null; + } + break; + + default: + throw new Exception ("Expression " + e.GetType () + + " ExprClass is Invalid after resolve"); + } + + if (e.type == null) + throw new Exception ( + "Expression " + e.GetType () + + " did not set its type after Resolve\n" + + "called from: " + this.GetType ()); + + return e; + } + + /// <summary> + /// Resolves an expression and performs semantic analysis on it. + /// </summary> + public Expression Resolve (EmitContext ec) + { + return Resolve (ec, ResolveFlags.VariableOrValue); + } + + /// <summary> + /// Resolves an expression for LValue assignment + /// </summary> + /// + /// <remarks> + /// Currently ResolveLValue wraps DoResolveLValue to perform sanity + /// checking and assertion checking on what we expect from Resolve + /// </remarks> + public Expression ResolveLValue (EmitContext ec, Expression right_side) + { + Expression e = DoResolveLValue (ec, right_side); + + if (e != null){ + if (e is SimpleName){ + SimpleName s = (SimpleName) e; + + Report.Error ( + 103, loc, + "The name `" + s.Name + "' could not be found in `" + + ec.DeclSpace.Name + "'"); + return null; + } + + if (e.eclass == ExprClass.Invalid) + throw new Exception ("Expression " + e + + " ExprClass is Invalid after resolve"); + + if (e.eclass == ExprClass.MethodGroup) { + ((MethodGroupExpr) e).ReportUsageError (); + return null; + } + + if (e.type == null) + throw new Exception ("Expression " + e + + " did not set its type after Resolve"); + } + + return e; + } + + /// <summary> + /// Emits the code for the expression + /// </summary> + /// + /// <remarks> + /// The Emit method is invoked to generate the code + /// for the expression. + /// </remarks> + public abstract void Emit (EmitContext ec); + + /// <summary> + /// Protected constructor. Only derivate types should + /// be able to be created + /// </summary> + + protected Expression () + { + eclass = ExprClass.Invalid; + type = null; + } + + /// <summary> + /// Returns a literalized version of a literal FieldInfo + /// </summary> + /// + /// <remarks> + /// The possible return values are: + /// IntConstant, UIntConstant + /// LongLiteral, ULongConstant + /// FloatConstant, DoubleConstant + /// StringConstant + /// + /// The value returned is already resolved. + /// </remarks> + public static Constant Constantify (object v, Type t) + { + if (t == TypeManager.int32_type) + return new IntConstant ((int) v); + else if (t == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + else if (t == TypeManager.int64_type) + return new LongConstant ((long) v); + else if (t == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + else if (t == TypeManager.float_type) + return new FloatConstant ((float) v); + else if (t == TypeManager.double_type) + return new DoubleConstant ((double) v); + else if (t == TypeManager.string_type) + return new StringConstant ((string) v); + else if (t == TypeManager.short_type) + return new ShortConstant ((short)v); + else if (t == TypeManager.ushort_type) + return new UShortConstant ((ushort)v); + else if (t == TypeManager.sbyte_type) + return new SByteConstant (((sbyte)v)); + else if (t == TypeManager.byte_type) + return new ByteConstant ((byte)v); + else if (t == TypeManager.char_type) + return new CharConstant ((char)v); + else if (t == TypeManager.bool_type) + return new BoolConstant ((bool) v); + else if (TypeManager.IsEnumType (t)){ + Constant e = Constantify (v, TypeManager.TypeToCoreType (v.GetType ())); + + return new EnumConstant (e, t); + } else + throw new Exception ("Unknown type for constant (" + t + + "), details: " + v); + } + + /// <summary> + /// Returns a fully formed expression after a MemberLookup + /// </summary> + public static Expression ExprClassFromMemberInfo (EmitContext ec, MemberInfo mi, Location loc) + { + if (mi is EventInfo) + return new EventExpr ((EventInfo) mi, loc); + else if (mi is FieldInfo) + return new FieldExpr ((FieldInfo) mi, loc); + else if (mi is PropertyInfo) + return new PropertyExpr (ec, (PropertyInfo) mi, loc); + else if (mi is Type){ + return new TypeExpr ((System.Type) mi, loc); + } + + return null; + } + + // + // FIXME: Probably implement a cache for (t,name,current_access_set)? + // + // This code could use some optimizations, but we need to do some + // measurements. For example, we could use a delegate to `flag' when + // something can not any longer be a method-group (because it is something + // else). + // + // Return values: + // If the return value is an Array, then it is an array of + // MethodBases + // + // If the return value is an MemberInfo, it is anything, but a Method + // + // null on error. + // + // FIXME: When calling MemberLookup inside an `Invocation', we should pass + // the arguments here and have MemberLookup return only the methods that + // match the argument count/type, unlike we are doing now (we delay this + // decision). + // + // This is so we can catch correctly attempts to invoke instance methods + // from a static body (scan for error 120 in ResolveSimpleName). + // + // + // FIXME: Potential optimization, have a static ArrayList + // + + public static Expression MemberLookup (EmitContext ec, Type t, string name, + MemberTypes mt, BindingFlags bf, Location loc) + { + return MemberLookup (ec, ec.ContainerType, t, name, mt, bf, loc); + } + + // + // Lookup type `t' for code in class `invocation_type'. Note that it's important + // to set `invocation_type' correctly since this method also checks whether the + // invoking class is allowed to access the member in class `t'. When you want to + // explicitly do a lookup in the base class, you must set both `t' and `invocation_type' + // to the base class (although a derived class can access protected members of its base + // class it cannot do so through an instance of the base class (error CS1540)). + // + + public static Expression MemberLookup (EmitContext ec, Type invocation_type, Type t, + string name, MemberTypes mt, BindingFlags bf, + Location loc) + { + MemberInfo [] mi = TypeManager.MemberLookup (invocation_type, t, mt, bf, name); + + if (mi == null) + return null; + + int count = mi.Length; + + if (count > 1) + return new MethodGroupExpr (mi, loc); + + if (mi [0] is MethodBase) + return new MethodGroupExpr (mi, loc); + + return ExprClassFromMemberInfo (ec, mi [0], loc); + } + + public const MemberTypes AllMemberTypes = + MemberTypes.Constructor | + MemberTypes.Event | + MemberTypes.Field | + MemberTypes.Method | + MemberTypes.NestedType | + MemberTypes.Property; + + public const BindingFlags AllBindingFlags = + BindingFlags.Public | + BindingFlags.Static | + BindingFlags.Instance; + + public static Expression MemberLookup (EmitContext ec, Type t, string name, Location loc) + { + return MemberLookup (ec, ec.ContainerType, t, name, + AllMemberTypes, AllBindingFlags, loc); + } + + public static Expression MethodLookup (EmitContext ec, Type t, string name, Location loc) + { + return MemberLookup (ec, ec.ContainerType, t, name, + MemberTypes.Method, AllBindingFlags, loc); + } + + /// <summary> + /// This is a wrapper for MemberLookup that is not used to "probe", but + /// to find a final definition. If the final definition is not found, we + /// look for private members and display a useful debugging message if we + /// find it. + /// </summary> + public static Expression MemberLookupFinal (EmitContext ec, Type t, string name, + Location loc) + { + return MemberLookupFinal (ec, t, name, MemberTypes.Method, AllBindingFlags, loc); + } + + public static Expression MemberLookupFinal (EmitContext ec, Type t, string name, + MemberTypes mt, BindingFlags bf, Location loc) + { + Expression e; + + int errors = Report.Errors; + + e = MemberLookup (ec, ec.ContainerType, t, name, mt, bf, loc); + + if (e != null) + return e; + + // Error has already been reported. + if (errors < Report.Errors) + return null; + + e = MemberLookup (ec, t, name, AllMemberTypes, + AllBindingFlags | BindingFlags.NonPublic, loc); + if (e == null){ + Report.Error ( + 117, loc, "`" + t + "' does not contain a definition " + + "for `" + name + "'"); + } else { + Report.Error ( + 122, loc, "`" + t + "." + name + + "' is inaccessible due to its protection level"); + } + + return null; + } + + static public MemberInfo GetFieldFromEvent (EventExpr event_expr) + { + EventInfo ei = event_expr.EventInfo; + + return TypeManager.GetPrivateFieldOfEvent (ei); + } + + static EmptyExpression MyEmptyExpr; + static public Expression ImplicitReferenceConversion (Expression expr, Type target_type) + { + Type expr_type = expr.Type; + + if (expr_type == null && expr.eclass == ExprClass.MethodGroup){ + // if we are a method group, emit a warning + + expr.Emit (null); + } + + // + // notice that it is possible to write "ValueType v = 1", the ValueType here + // is an abstract class, and not really a value type, so we apply the same rules. + // + if (target_type == TypeManager.object_type || target_type == TypeManager.value_type) { + // + // A pointer type cannot be converted to object + // + if (expr_type.IsPointer) + return null; + + if (expr_type.IsValueType) + return new BoxedCast (expr); + if (expr_type.IsClass || expr_type.IsInterface) + return new EmptyCast (expr, target_type); + } else if (expr_type.IsSubclassOf (target_type)) { + // + // Special case: enumeration to System.Enum. + // System.Enum is not a value type, it is a class, so we need + // a boxing conversion + // + if (expr_type.IsEnum) + return new BoxedCast (expr); + + return new EmptyCast (expr, target_type); + } else { + + // This code is kind of mirrored inside StandardConversionExists + // with the small distinction that we only probe there + // + // Always ensure that the code here and there is in sync + + // from the null type to any reference-type. + if (expr is NullLiteral && !target_type.IsValueType) + return new EmptyCast (expr, target_type); + + // from any class-type S to any interface-type T. + if (target_type.IsInterface) { + if (TypeManager.ImplementsInterface (expr_type, target_type)){ + if (expr_type.IsClass) + return new EmptyCast (expr, target_type); + else if (expr_type.IsValueType) + return new BoxedCast (expr); + } + } + + // from any interface type S to interface-type T. + if (expr_type.IsInterface && target_type.IsInterface) { + if (TypeManager.ImplementsInterface (expr_type, target_type)) + return new EmptyCast (expr, target_type); + else + return null; + } + + // from an array-type S to an array-type of type T + if (expr_type.IsArray && target_type.IsArray) { + if (expr_type.GetArrayRank () == target_type.GetArrayRank ()) { + + Type expr_element_type = expr_type.GetElementType (); + + if (MyEmptyExpr == null) + MyEmptyExpr = new EmptyExpression (); + + MyEmptyExpr.SetType (expr_element_type); + Type target_element_type = target_type.GetElementType (); + + if (!expr_element_type.IsValueType && !target_element_type.IsValueType) + if (StandardConversionExists (MyEmptyExpr, + target_element_type)) + return new EmptyCast (expr, target_type); + } + } + + + // from an array-type to System.Array + if (expr_type.IsArray && target_type == TypeManager.array_type) + return new EmptyCast (expr, target_type); + + // from any delegate type to System.Delegate + if (expr_type.IsSubclassOf (TypeManager.delegate_type) && + target_type == TypeManager.delegate_type) + return new EmptyCast (expr, target_type); + + // from any array-type or delegate type into System.ICloneable. + if (expr_type.IsArray || expr_type.IsSubclassOf (TypeManager.delegate_type)) + if (target_type == TypeManager.icloneable_type) + return new EmptyCast (expr, target_type); + + return null; + + } + + return null; + } + + /// <summary> + /// Implicit Numeric Conversions. + /// + /// expr is the expression to convert, returns a new expression of type + /// target_type or null if an implicit conversion is not possible. + /// </summary> + static public Expression ImplicitNumericConversion (EmitContext ec, Expression expr, + Type target_type, Location loc) + { + Type expr_type = expr.Type; + + // + // Attempt to do the implicit constant expression conversions + + if (expr is IntConstant){ + Expression e; + + e = TryImplicitIntConversion (target_type, (IntConstant) expr); + + if (e != null) + return e; + } else if (expr is LongConstant && target_type == TypeManager.uint64_type){ + // + // Try the implicit constant expression conversion + // from long to ulong, instead of a nice routine, + // we just inline it + // + long v = ((LongConstant) expr).Value; + if (v > 0) + return new ULongConstant ((ulong) v); + } + + Type real_target_type = target_type; + + if (expr_type == TypeManager.sbyte_type){ + // + // From sbyte to short, int, long, float, double. + // + if (real_target_type == TypeManager.int32_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_I4); + if (real_target_type == TypeManager.int64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + if (real_target_type == TypeManager.float_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + if (real_target_type == TypeManager.short_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_I2); + } else if (expr_type == TypeManager.byte_type){ + // + // From byte to short, ushort, int, uint, long, ulong, float, double + // + if ((real_target_type == TypeManager.short_type) || + (real_target_type == TypeManager.ushort_type) || + (real_target_type == TypeManager.int32_type) || + (real_target_type == TypeManager.uint32_type)) + return new EmptyCast (expr, target_type); + + if (real_target_type == TypeManager.uint64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + if (real_target_type == TypeManager.int64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + if (real_target_type == TypeManager.float_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + } else if (expr_type == TypeManager.short_type){ + // + // From short to int, long, float, double + // + if (real_target_type == TypeManager.int32_type) + return new EmptyCast (expr, target_type); + if (real_target_type == TypeManager.int64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + if (real_target_type == TypeManager.float_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + } else if (expr_type == TypeManager.ushort_type){ + // + // From ushort to int, uint, long, ulong, float, double + // + if (real_target_type == TypeManager.uint32_type) + return new EmptyCast (expr, target_type); + + if (real_target_type == TypeManager.uint64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + if (real_target_type == TypeManager.int32_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_I4); + if (real_target_type == TypeManager.int64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + if (real_target_type == TypeManager.float_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + } else if (expr_type == TypeManager.int32_type){ + // + // From int to long, float, double + // + if (real_target_type == TypeManager.int64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + if (real_target_type == TypeManager.float_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + } else if (expr_type == TypeManager.uint32_type){ + // + // From uint to long, ulong, float, double + // + if (real_target_type == TypeManager.int64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + if (real_target_type == TypeManager.uint64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un, + OpCodes.Conv_R8); + if (real_target_type == TypeManager.float_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un, + OpCodes.Conv_R4); + } else if (expr_type == TypeManager.int64_type){ + // + // From long/ulong to float, double + // + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + if (real_target_type == TypeManager.float_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + } else if (expr_type == TypeManager.uint64_type){ + // + // From ulong to float, double + // + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un, + OpCodes.Conv_R8); + if (real_target_type == TypeManager.float_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un, + OpCodes.Conv_R4); + } else if (expr_type == TypeManager.char_type){ + // + // From char to ushort, int, uint, long, ulong, float, double + // + if ((real_target_type == TypeManager.ushort_type) || + (real_target_type == TypeManager.int32_type) || + (real_target_type == TypeManager.uint32_type)) + return new EmptyCast (expr, target_type); + if (real_target_type == TypeManager.uint64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + if (real_target_type == TypeManager.int64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + if (real_target_type == TypeManager.float_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + } else if (expr_type == TypeManager.float_type){ + // + // float to double + // + if (real_target_type == TypeManager.double_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + } + + return null; + } + + // + // Tests whether an implicit reference conversion exists between expr_type + // and target_type + // + public static bool ImplicitReferenceConversionExists (Expression expr, Type target_type) + { + Type expr_type = expr.Type; + + // + // This is the boxed case. + // + if (target_type == TypeManager.object_type) { + if ((expr_type.IsClass) || + (expr_type.IsValueType) || + (expr_type.IsInterface)) + return true; + + } else if (expr_type.IsSubclassOf (target_type)) { + return true; + } else { + // Please remember that all code below actually comes + // from ImplicitReferenceConversion so make sure code remains in sync + + // from any class-type S to any interface-type T. + if (target_type.IsInterface) { + if (TypeManager.ImplementsInterface (expr_type, target_type)) + return true; + } + + // from any interface type S to interface-type T. + if (expr_type.IsInterface && target_type.IsInterface) + if (TypeManager.ImplementsInterface (expr_type, target_type)) + return true; + + // from an array-type S to an array-type of type T + if (expr_type.IsArray && target_type.IsArray) { + if (expr_type.GetArrayRank () == target_type.GetArrayRank ()) { + + Type expr_element_type = expr_type.GetElementType (); + + if (MyEmptyExpr == null) + MyEmptyExpr = new EmptyExpression (); + + MyEmptyExpr.SetType (expr_element_type); + Type target_element_type = target_type.GetElementType (); + + if (!expr_element_type.IsValueType && !target_element_type.IsValueType) + if (StandardConversionExists (MyEmptyExpr, + target_element_type)) + return true; + } + } + + // from an array-type to System.Array + if (expr_type.IsArray && (target_type == TypeManager.array_type)) + return true; + + // from any delegate type to System.Delegate + if (expr_type.IsSubclassOf (TypeManager.delegate_type) && + target_type == TypeManager.delegate_type) + if (target_type.IsAssignableFrom (expr_type)) + return true; + + // from any array-type or delegate type into System.ICloneable. + if (expr_type.IsArray || expr_type.IsSubclassOf (TypeManager.delegate_type)) + if (target_type == TypeManager.icloneable_type) + return true; + + // from the null type to any reference-type. + if (expr is NullLiteral && !target_type.IsValueType && + !TypeManager.IsEnumType (target_type)) + return true; + + } + + return false; + } + + /// <summary> + /// Same as StandardConversionExists except that it also looks at + /// implicit user defined conversions - needed for overload resolution + /// </summary> + public static bool ImplicitConversionExists (EmitContext ec, Expression expr, Type target_type) + { + if (StandardConversionExists (expr, target_type) == true) + return true; + + Expression dummy = ImplicitUserConversion (ec, expr, target_type, Location.Null); + + if (dummy != null) + return true; + + return false; + } + + /// <summary> + /// Determines if a standard implicit conversion exists from + /// expr_type to target_type + /// </summary> + public static bool StandardConversionExists (Expression expr, Type target_type) + { + Type expr_type = expr.Type; + + if (expr_type == TypeManager.void_type) + return false; + + if (expr_type == target_type) + return true; + + // First numeric conversions + + if (expr_type == TypeManager.sbyte_type){ + // + // From sbyte to short, int, long, float, double. + // + if ((target_type == TypeManager.int32_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.short_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.byte_type){ + // + // From byte to short, ushort, int, uint, long, ulong, float, double + // + if ((target_type == TypeManager.short_type) || + (target_type == TypeManager.ushort_type) || + (target_type == TypeManager.int32_type) || + (target_type == TypeManager.uint32_type) || + (target_type == TypeManager.uint64_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.short_type){ + // + // From short to int, long, float, double + // + if ((target_type == TypeManager.int32_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.ushort_type){ + // + // From ushort to int, uint, long, ulong, float, double + // + if ((target_type == TypeManager.uint32_type) || + (target_type == TypeManager.uint64_type) || + (target_type == TypeManager.int32_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.int32_type){ + // + // From int to long, float, double + // + if ((target_type == TypeManager.int64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.uint32_type){ + // + // From uint to long, ulong, float, double + // + if ((target_type == TypeManager.int64_type) || + (target_type == TypeManager.uint64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if ((expr_type == TypeManager.uint64_type) || + (expr_type == TypeManager.int64_type)) { + // + // From long/ulong to float, double + // + if ((target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.char_type){ + // + // From char to ushort, int, uint, long, ulong, float, double + // + if ((target_type == TypeManager.ushort_type) || + (target_type == TypeManager.int32_type) || + (target_type == TypeManager.uint32_type) || + (target_type == TypeManager.uint64_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.float_type){ + // + // float to double + // + if (target_type == TypeManager.double_type) + return true; + } + + if (ImplicitReferenceConversionExists (expr, target_type)) + return true; + + if (expr is IntConstant){ + int value = ((IntConstant) expr).Value; + + if (target_type == TypeManager.sbyte_type){ + if (value >= SByte.MinValue && value <= SByte.MaxValue) + return true; + } else if (target_type == TypeManager.byte_type){ + if (Byte.MinValue >= 0 && value <= Byte.MaxValue) + return true; + } else if (target_type == TypeManager.short_type){ + if (value >= Int16.MinValue && value <= Int16.MaxValue) + return true; + } else if (target_type == TypeManager.ushort_type){ + if (value >= UInt16.MinValue && value <= UInt16.MaxValue) + return true; + } else if (target_type == TypeManager.uint32_type){ + if (value >= 0) + return true; + } else if (target_type == TypeManager.uint64_type){ + // + // we can optimize this case: a positive int32 + // always fits on a uint64. But we need an opcode + // to do it. + // + if (value >= 0) + return true; + } + + if (value == 0 && expr is IntLiteral && TypeManager.IsEnumType (target_type)) + return true; + } + + if (expr is LongConstant && target_type == TypeManager.uint64_type){ + // + // Try the implicit constant expression conversion + // from long to ulong, instead of a nice routine, + // we just inline it + // + long v = ((LongConstant) expr).Value; + if (v > 0) + return true; + } + + if (target_type.IsSubclassOf (TypeManager.enum_type) && expr is IntLiteral){ + IntLiteral i = (IntLiteral) expr; + + if (i.Value == 0) + return true; + } + + if (target_type == TypeManager.void_ptr_type && expr_type.IsPointer) + return true; + + return false; + } + + // + // Used internally by FindMostEncompassedType, this is used + // to avoid creating lots of objects in the tight loop inside + // FindMostEncompassedType + // + static EmptyExpression priv_fmet_param; + + /// <summary> + /// Finds "most encompassed type" according to the spec (13.4.2) + /// amongst the methods in the MethodGroupExpr + /// </summary> + static Type FindMostEncompassedType (ArrayList types) + { + Type best = null; + + if (priv_fmet_param == null) + priv_fmet_param = new EmptyExpression (); + + foreach (Type t in types){ + priv_fmet_param.SetType (t); + + if (best == null) { + best = t; + continue; + } + + if (StandardConversionExists (priv_fmet_param, best)) + best = t; + } + + return best; + } + + // + // Used internally by FindMostEncompassingType, this is used + // to avoid creating lots of objects in the tight loop inside + // FindMostEncompassingType + // + static EmptyExpression priv_fmee_ret; + + /// <summary> + /// Finds "most encompassing type" according to the spec (13.4.2) + /// amongst the types in the given set + /// </summary> + static Type FindMostEncompassingType (ArrayList types) + { + Type best = null; + + if (priv_fmee_ret == null) + priv_fmee_ret = new EmptyExpression (); + + foreach (Type t in types){ + priv_fmee_ret.SetType (best); + + if (best == null) { + best = t; + continue; + } + + if (StandardConversionExists (priv_fmee_ret, t)) + best = t; + } + + return best; + } + + // + // Used to avoid creating too many objects + // + static EmptyExpression priv_fms_expr; + + /// <summary> + /// Finds the most specific source Sx according to the rules of the spec (13.4.4) + /// by making use of FindMostEncomp* methods. Applies the correct rules separately + /// for explicit and implicit conversion operators. + /// </summary> + static public Type FindMostSpecificSource (MethodGroupExpr me, Expression source, + bool apply_explicit_conv_rules, + Location loc) + { + ArrayList src_types_set = new ArrayList (); + + if (priv_fms_expr == null) + priv_fms_expr = new EmptyExpression (); + + // + // If any operator converts from S then Sx = S + // + Type source_type = source.Type; + foreach (MethodBase mb in me.Methods){ + ParameterData pd = Invocation.GetParameterData (mb); + Type param_type = pd.ParameterType (0); + + if (param_type == source_type) + return param_type; + + if (apply_explicit_conv_rules) { + // + // From the spec : + // Find the set of applicable user-defined conversion operators, U. This set + // consists of the + // user-defined implicit or explicit conversion operators declared by + // the classes or structs in D that convert from a type encompassing + // or encompassed by S to a type encompassing or encompassed by T + // + priv_fms_expr.SetType (param_type); + if (StandardConversionExists (priv_fms_expr, source_type)) + src_types_set.Add (param_type); + else { + if (StandardConversionExists (source, param_type)) + src_types_set.Add (param_type); + } + } else { + // + // Only if S is encompassed by param_type + // + if (StandardConversionExists (source, param_type)) + src_types_set.Add (param_type); + } + } + + // + // Explicit Conv rules + // + if (apply_explicit_conv_rules) { + ArrayList candidate_set = new ArrayList (); + + foreach (Type param_type in src_types_set){ + if (StandardConversionExists (source, param_type)) + candidate_set.Add (param_type); + } + + if (candidate_set.Count != 0) + return FindMostEncompassedType (candidate_set); + } + + // + // Final case + // + if (apply_explicit_conv_rules) + return FindMostEncompassingType (src_types_set); + else + return FindMostEncompassedType (src_types_set); + } + + // + // Useful in avoiding proliferation of objects + // + static EmptyExpression priv_fmt_expr; + + /// <summary> + /// Finds the most specific target Tx according to section 13.4.4 + /// </summary> + static public Type FindMostSpecificTarget (MethodGroupExpr me, Type target, + bool apply_explicit_conv_rules, + Location loc) + { + ArrayList tgt_types_set = new ArrayList (); + + if (priv_fmt_expr == null) + priv_fmt_expr = new EmptyExpression (); + + // + // If any operator converts to T then Tx = T + // + foreach (MethodInfo mi in me.Methods){ + Type ret_type = mi.ReturnType; + + if (ret_type == target) + return ret_type; + + if (apply_explicit_conv_rules) { + // + // From the spec : + // Find the set of applicable user-defined conversion operators, U. + // + // This set consists of the + // user-defined implicit or explicit conversion operators declared by + // the classes or structs in D that convert from a type encompassing + // or encompassed by S to a type encompassing or encompassed by T + // + priv_fms_expr.SetType (ret_type); + if (StandardConversionExists (priv_fms_expr, target)) + tgt_types_set.Add (ret_type); + else { + priv_fms_expr.SetType (target); + if (StandardConversionExists (priv_fms_expr, ret_type)) + tgt_types_set.Add (ret_type); + } + } else { + // + // Only if T is encompassed by param_type + // + priv_fms_expr.SetType (ret_type); + if (StandardConversionExists (priv_fms_expr, target)) + tgt_types_set.Add (ret_type); + } + } + + // + // Explicit conv rules + // + if (apply_explicit_conv_rules) { + ArrayList candidate_set = new ArrayList (); + + foreach (Type ret_type in tgt_types_set){ + priv_fmt_expr.SetType (ret_type); + + if (StandardConversionExists (priv_fmt_expr, target)) + candidate_set.Add (ret_type); + } + + if (candidate_set.Count != 0) + return FindMostEncompassingType (candidate_set); + } + + // + // Okay, final case ! + // + if (apply_explicit_conv_rules) + return FindMostEncompassedType (tgt_types_set); + else + return FindMostEncompassingType (tgt_types_set); + } + + /// <summary> + /// User-defined Implicit conversions + /// </summary> + static public Expression ImplicitUserConversion (EmitContext ec, Expression source, + Type target, Location loc) + { + return UserDefinedConversion (ec, source, target, loc, false); + } + + /// <summary> + /// User-defined Explicit conversions + /// </summary> + static public Expression ExplicitUserConversion (EmitContext ec, Expression source, + Type target, Location loc) + { + return UserDefinedConversion (ec, source, target, loc, true); + } + + /// <summary> + /// Computes the MethodGroup for the user-defined conversion + /// operators from source_type to target_type. `look_for_explicit' + /// controls whether we should also include the list of explicit + /// operators + /// </summary> + static MethodGroupExpr GetConversionOperators (EmitContext ec, + Type source_type, Type target_type, + Location loc, bool look_for_explicit) + { + Expression mg1 = null, mg2 = null; + Expression mg5 = null, mg6 = null, mg7 = null, mg8 = null; + string op_name; + + // + // FIXME : How does the False operator come into the picture ? + // This doesn't look complete and very correct ! + // + if (target_type == TypeManager.bool_type && !look_for_explicit) + op_name = "op_True"; + else + op_name = "op_Implicit"; + + MethodGroupExpr union3; + + mg1 = MethodLookup (ec, source_type, op_name, loc); + if (source_type.BaseType != null) + mg2 = MethodLookup (ec, source_type.BaseType, op_name, loc); + + if (mg1 == null) + union3 = (MethodGroupExpr) mg2; + else if (mg2 == null) + union3 = (MethodGroupExpr) mg1; + else + union3 = Invocation.MakeUnionSet (mg1, mg2, loc); + + mg1 = MethodLookup (ec, target_type, op_name, loc); + if (mg1 != null){ + if (union3 != null) + union3 = Invocation.MakeUnionSet (union3, mg1, loc); + else + union3 = (MethodGroupExpr) mg1; + } + + if (target_type.BaseType != null) + mg1 = MethodLookup (ec, target_type.BaseType, op_name, loc); + + if (mg1 != null){ + if (union3 != null) + union3 = Invocation.MakeUnionSet (union3, mg1, loc); + else + union3 = (MethodGroupExpr) mg1; + } + + MethodGroupExpr union4 = null; + + if (look_for_explicit) { + op_name = "op_Explicit"; + + mg5 = MemberLookup (ec, source_type, op_name, loc); + if (source_type.BaseType != null) + mg6 = MethodLookup (ec, source_type.BaseType, op_name, loc); + + mg7 = MemberLookup (ec, target_type, op_name, loc); + if (target_type.BaseType != null) + mg8 = MethodLookup (ec, target_type.BaseType, op_name, loc); + + MethodGroupExpr union5 = Invocation.MakeUnionSet (mg5, mg6, loc); + MethodGroupExpr union6 = Invocation.MakeUnionSet (mg7, mg8, loc); + + union4 = Invocation.MakeUnionSet (union5, union6, loc); + } + + return Invocation.MakeUnionSet (union3, union4, loc); + } + + /// <summary> + /// User-defined conversions + /// </summary> + static public Expression UserDefinedConversion (EmitContext ec, Expression source, + Type target, Location loc, + bool look_for_explicit) + { + MethodGroupExpr union; + Type source_type = source.Type; + MethodBase method = null; + + union = GetConversionOperators (ec, source_type, target, loc, look_for_explicit); + if (union == null) + return null; + + Type most_specific_source, most_specific_target; + +#if BLAH + foreach (MethodBase m in union.Methods){ + Console.WriteLine ("Name: " + m.Name); + Console.WriteLine (" : " + ((MethodInfo)m).ReturnType); + } +#endif + + most_specific_source = FindMostSpecificSource (union, source, look_for_explicit, loc); + if (most_specific_source == null) + return null; + + most_specific_target = FindMostSpecificTarget (union, target, look_for_explicit, loc); + if (most_specific_target == null) + return null; + + int count = 0; + + foreach (MethodBase mb in union.Methods){ + ParameterData pd = Invocation.GetParameterData (mb); + MethodInfo mi = (MethodInfo) mb; + + if (pd.ParameterType (0) == most_specific_source && + mi.ReturnType == most_specific_target) { + method = mb; + count++; + } + } + + if (method == null || count > 1) + return null; + + + // + // This will do the conversion to the best match that we + // found. Now we need to perform an implict standard conversion + // if the best match was not the type that we were requested + // by target. + // + if (look_for_explicit) + source = ConvertExplicitStandard (ec, source, most_specific_source, loc); + else + source = ConvertImplicitStandard (ec, source, most_specific_source, loc); + + if (source == null) + return null; + + Expression e; + e = new UserCast ((MethodInfo) method, source, loc); + if (e.Type != target){ + if (!look_for_explicit) + e = ConvertImplicitStandard (ec, e, target, loc); + else + e = ConvertExplicitStandard (ec, e, target, loc); + } + return e; + } + + /// <summary> + /// Converts implicitly the resolved expression `expr' into the + /// `target_type'. It returns a new expression that can be used + /// in a context that expects a `target_type'. + /// </summary> + static public Expression ConvertImplicit (EmitContext ec, Expression expr, + Type target_type, Location loc) + { + Type expr_type = expr.Type; + Expression e; + + if (expr_type == target_type) + return expr; + + if (target_type == null) + throw new Exception ("Target type is null"); + + e = ConvertImplicitStandard (ec, expr, target_type, loc); + if (e != null) + return e; + + e = ImplicitUserConversion (ec, expr, target_type, loc); + if (e != null) + return e; + + return null; + } + + + /// <summary> + /// Attempts to apply the `Standard Implicit + /// Conversion' rules to the expression `expr' into + /// the `target_type'. It returns a new expression + /// that can be used in a context that expects a + /// `target_type'. + /// + /// This is different from `ConvertImplicit' in that the + /// user defined implicit conversions are excluded. + /// </summary> + static public Expression ConvertImplicitStandard (EmitContext ec, Expression expr, + Type target_type, Location loc) + { + Type expr_type = expr.Type; + Expression e; + + if (expr_type == target_type) + return expr; + + e = ImplicitNumericConversion (ec, expr, target_type, loc); + if (e != null) + return e; + + e = ImplicitReferenceConversion (expr, target_type); + if (e != null) + return e; + + if (target_type.IsSubclassOf (TypeManager.enum_type) && expr is IntLiteral){ + IntLiteral i = (IntLiteral) expr; + + if (i.Value == 0) + return new EmptyCast (expr, target_type); + } + + if (ec.InUnsafe) { + if (expr_type.IsPointer){ + if (target_type == TypeManager.void_ptr_type) + return new EmptyCast (expr, target_type); + + // + // yep, comparing pointer types cant be done with + // t1 == t2, we have to compare their element types. + // + if (target_type.IsPointer){ + if (target_type.GetElementType()==expr_type.GetElementType()) + return expr; + } + } + + if (target_type.IsPointer){ + if (expr is NullLiteral) + return new EmptyCast (expr, target_type); + } + } + + return null; + } + + /// <summary> + /// Attemps to perform an implict constant conversion of the IntConstant + /// into a different data type using casts (See Implicit Constant + /// Expression Conversions) + /// </summary> + static protected Expression TryImplicitIntConversion (Type target_type, IntConstant ic) + { + int value = ic.Value; + + // + // FIXME: This could return constants instead of EmptyCasts + // + if (target_type == TypeManager.sbyte_type){ + if (value >= SByte.MinValue && value <= SByte.MaxValue) + return new SByteConstant ((sbyte) value); + } else if (target_type == TypeManager.byte_type){ + if (Byte.MinValue >= 0 && value <= Byte.MaxValue) + return new ByteConstant ((byte) value); + } else if (target_type == TypeManager.short_type){ + if (value >= Int16.MinValue && value <= Int16.MaxValue) + return new ShortConstant ((short) value); + } else if (target_type == TypeManager.ushort_type){ + if (value >= UInt16.MinValue && value <= UInt16.MaxValue) + return new UShortConstant ((ushort) value); + } else if (target_type == TypeManager.uint32_type){ + if (value >= 0) + return new UIntConstant ((uint) value); + } else if (target_type == TypeManager.uint64_type){ + // + // we can optimize this case: a positive int32 + // always fits on a uint64. But we need an opcode + // to do it. + // + if (value >= 0) + return new ULongConstant ((ulong) value); + } + + if (value == 0 && ic is IntLiteral && TypeManager.IsEnumType (target_type)){ + Type underlying = TypeManager.EnumToUnderlying (target_type); + Constant e = (Constant) ic; + + // + // Possibly, we need to create a different 0 literal before passing + // to EnumConstant + //n + if (underlying == TypeManager.int64_type) + e = new LongLiteral (0); + else if (underlying == TypeManager.uint64_type) + e = new ULongLiteral (0); + + return new EnumConstant (e, target_type); + } + return null; + } + + static public void Error_CannotConvertImplicit (Location loc, Type source, Type target) + { + string msg = "Cannot convert implicitly from `"+ + TypeManager.CSharpName (source) + "' to `" + + TypeManager.CSharpName (target) + "'"; + + Report.Error (29, loc, msg); + } + + /// <summary> + /// Attemptes to implicityly convert `target' into `type', using + /// ConvertImplicit. If there is no implicit conversion, then + /// an error is signaled + /// </summary> + static public Expression ConvertImplicitRequired (EmitContext ec, Expression source, + Type target_type, Location loc) + { + Expression e; + + e = ConvertImplicit (ec, source, target_type, loc); + if (e != null) + return e; + + if (source is DoubleLiteral && target_type == TypeManager.float_type){ + Report.Error (664, loc, + "Double literal cannot be implicitly converted to " + + "float type, use F suffix to create a float literal"); + } + + Error_CannotConvertImplicit (loc, source.Type, target_type); + + return null; + } + + /// <summary> + /// Performs the explicit numeric conversions + /// </summary> + static Expression ConvertNumericExplicit (EmitContext ec, Expression expr, Type target_type, Location loc) + { + Type expr_type = expr.Type; + + // + // If we have an enumeration, extract the underlying type, + // use this during the comparison, but wrap around the original + // target_type + // + Type real_target_type = target_type; + + if (TypeManager.IsEnumType (real_target_type)) + real_target_type = TypeManager.EnumToUnderlying (real_target_type); + + if (StandardConversionExists (expr, real_target_type)){ + Expression ce = ConvertImplicitStandard (ec, expr, real_target_type, loc); + + if (real_target_type != target_type) + return new EmptyCast (ce, target_type); + return ce; + } + + if (expr_type == TypeManager.sbyte_type){ + // + // From sbyte to byte, ushort, uint, ulong, char + // + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I1_U1); + if (real_target_type == TypeManager.ushort_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I1_U2); + if (real_target_type == TypeManager.uint32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I1_U4); + if (real_target_type == TypeManager.uint64_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I1_U8); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I1_CH); + } else if (expr_type == TypeManager.byte_type){ + // + // From byte to sbyte and char + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U1_I1); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U1_CH); + } else if (expr_type == TypeManager.short_type){ + // + // From short to sbyte, byte, ushort, uint, ulong, char + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I2_I1); + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I2_U1); + if (real_target_type == TypeManager.ushort_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I2_U2); + if (real_target_type == TypeManager.uint32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I2_U4); + if (real_target_type == TypeManager.uint64_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I2_U8); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I2_CH); + } else if (expr_type == TypeManager.ushort_type){ + // + // From ushort to sbyte, byte, short, char + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U2_I1); + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U2_U1); + if (real_target_type == TypeManager.short_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U2_I2); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U2_CH); + } else if (expr_type == TypeManager.int32_type){ + // + // From int to sbyte, byte, short, ushort, uint, ulong, char + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I4_I1); + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I4_U1); + if (real_target_type == TypeManager.short_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I4_I2); + if (real_target_type == TypeManager.ushort_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I4_U2); + if (real_target_type == TypeManager.uint32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I4_U4); + if (real_target_type == TypeManager.uint64_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I4_U8); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I4_CH); + } else if (expr_type == TypeManager.uint32_type){ + // + // From uint to sbyte, byte, short, ushort, int, char + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U4_I1); + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U4_U1); + if (real_target_type == TypeManager.short_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U4_I2); + if (real_target_type == TypeManager.ushort_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U4_U2); + if (real_target_type == TypeManager.int32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U4_I4); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U4_CH); + } else if (expr_type == TypeManager.int64_type){ + // + // From long to sbyte, byte, short, ushort, int, uint, ulong, char + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I8_I1); + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I8_U1); + if (real_target_type == TypeManager.short_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I8_I2); + if (real_target_type == TypeManager.ushort_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I8_U2); + if (real_target_type == TypeManager.int32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I8_I4); + if (real_target_type == TypeManager.uint32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I8_U4); + if (real_target_type == TypeManager.uint64_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I8_U8); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.I8_CH); + } else if (expr_type == TypeManager.uint64_type){ + // + // From ulong to sbyte, byte, short, ushort, int, uint, long, char + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U8_I1); + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U8_U1); + if (real_target_type == TypeManager.short_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U8_I2); + if (real_target_type == TypeManager.ushort_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U8_U2); + if (real_target_type == TypeManager.int32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U8_I4); + if (real_target_type == TypeManager.uint32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U8_U4); + if (real_target_type == TypeManager.int64_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U8_I8); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.U8_CH); + } else if (expr_type == TypeManager.char_type){ + // + // From char to sbyte, byte, short + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.CH_I1); + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.CH_U1); + if (real_target_type == TypeManager.short_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.CH_I2); + } else if (expr_type == TypeManager.float_type){ + // + // From float to sbyte, byte, short, + // ushort, int, uint, long, ulong, char + // or decimal + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R4_I1); + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R4_U1); + if (real_target_type == TypeManager.short_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R4_I2); + if (real_target_type == TypeManager.ushort_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R4_U2); + if (real_target_type == TypeManager.int32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R4_I4); + if (real_target_type == TypeManager.uint32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R4_U4); + if (real_target_type == TypeManager.int64_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R4_I8); + if (real_target_type == TypeManager.uint64_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R4_U8); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R4_CH); + } else if (expr_type == TypeManager.double_type){ + // + // From double to byte, byte, short, + // ushort, int, uint, long, ulong, + // char, float or decimal + // + if (real_target_type == TypeManager.sbyte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_I1); + if (real_target_type == TypeManager.byte_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_U1); + if (real_target_type == TypeManager.short_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_I2); + if (real_target_type == TypeManager.ushort_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_U2); + if (real_target_type == TypeManager.int32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_I4); + if (real_target_type == TypeManager.uint32_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_U4); + if (real_target_type == TypeManager.int64_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_I8); + if (real_target_type == TypeManager.uint64_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_U8); + if (real_target_type == TypeManager.char_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_CH); + if (real_target_type == TypeManager.float_type) + return new ConvCast (ec, expr, target_type, ConvCast.Mode.R8_R4); + } + + // decimal is taken care of by the op_Explicit methods. + + return null; + } + + /// <summary> + /// Returns whether an explicit reference conversion can be performed + /// from source_type to target_type + /// </summary> + public static bool ExplicitReferenceConversionExists (Type source_type, Type target_type) + { + bool target_is_value_type = target_type.IsValueType; + + if (source_type == target_type) + return true; + + // + // From object to any reference type + // + if (source_type == TypeManager.object_type && !target_is_value_type) + return true; + + // + // From any class S to any class-type T, provided S is a base class of T + // + if (target_type.IsSubclassOf (source_type)) + return true; + + // + // From any interface type S to any interface T provided S is not derived from T + // + if (source_type.IsInterface && target_type.IsInterface){ + if (!target_type.IsSubclassOf (source_type)) + return true; + } + + // + // From any class type S to any interface T, provided S is not sealed + // and provided S does not implement T. + // + if (target_type.IsInterface && !source_type.IsSealed && + !TypeManager.ImplementsInterface (source_type, target_type)) + return true; + + // + // From any interface-type S to to any class type T, provided T is not + // sealed, or provided T implements S. + // + if (source_type.IsInterface && + (!target_type.IsSealed || TypeManager.ImplementsInterface (target_type, source_type))) + return true; + + + // From an array type S with an element type Se to an array type T with an + // element type Te provided all the following are true: + // * S and T differe only in element type, in other words, S and T + // have the same number of dimensions. + // * Both Se and Te are reference types + // * An explicit referenc conversions exist from Se to Te + // + if (source_type.IsArray && target_type.IsArray) { + if (source_type.GetArrayRank () == target_type.GetArrayRank ()) { + + Type source_element_type = source_type.GetElementType (); + Type target_element_type = target_type.GetElementType (); + + if (!source_element_type.IsValueType && !target_element_type.IsValueType) + if (ExplicitReferenceConversionExists (source_element_type, + target_element_type)) + return true; + } + } + + + // From System.Array to any array-type + if (source_type == TypeManager.array_type && + target_type.IsArray){ + return true; + } + + // + // From System delegate to any delegate-type + // + if (source_type == TypeManager.delegate_type && + target_type.IsSubclassOf (TypeManager.delegate_type)) + return true; + + // + // From ICloneable to Array or Delegate types + // + if (source_type == TypeManager.icloneable_type && + (target_type == TypeManager.array_type || + target_type == TypeManager.delegate_type)) + return true; + + return false; + } + + /// <summary> + /// Implements Explicit Reference conversions + /// </summary> + static Expression ConvertReferenceExplicit (Expression source, Type target_type) + { + Type source_type = source.Type; + bool target_is_value_type = target_type.IsValueType; + + // + // From object to any reference type + // + if (source_type == TypeManager.object_type && !target_is_value_type) + return new ClassCast (source, target_type); + + + // + // From any class S to any class-type T, provided S is a base class of T + // + if (target_type.IsSubclassOf (source_type)) + return new ClassCast (source, target_type); + + // + // From any interface type S to any interface T provided S is not derived from T + // + if (source_type.IsInterface && target_type.IsInterface){ + if (TypeManager.ImplementsInterface (source_type, target_type)) + return null; + else + return new ClassCast (source, target_type); + } + + // + // From any class type S to any interface T, provides S is not sealed + // and provided S does not implement T. + // + if (target_type.IsInterface && !source_type.IsSealed) { + if (TypeManager.ImplementsInterface (source_type, target_type)) + return null; + else + return new ClassCast (source, target_type); + + } + + // + // From any interface-type S to to any class type T, provided T is not + // sealed, or provided T implements S. + // + if (source_type.IsInterface) { + if (!target_type.IsSealed || TypeManager.ImplementsInterface (target_type, source_type)) + return new ClassCast (source, target_type); + else + return null; + } + + // From an array type S with an element type Se to an array type T with an + // element type Te provided all the following are true: + // * S and T differe only in element type, in other words, S and T + // have the same number of dimensions. + // * Both Se and Te are reference types + // * An explicit referenc conversions exist from Se to Te + // + if (source_type.IsArray && target_type.IsArray) { + if (source_type.GetArrayRank () == target_type.GetArrayRank ()) { + + Type source_element_type = source_type.GetElementType (); + Type target_element_type = target_type.GetElementType (); + + if (!source_element_type.IsValueType && !target_element_type.IsValueType) + if (ExplicitReferenceConversionExists (source_element_type, + target_element_type)) + return new ClassCast (source, target_type); + } + } + + + // From System.Array to any array-type + if (source_type == TypeManager.array_type && + target_type.IsArray) { + return new ClassCast (source, target_type); + } + + // + // From System delegate to any delegate-type + // + if (source_type == TypeManager.delegate_type && + target_type.IsSubclassOf (TypeManager.delegate_type)) + return new ClassCast (source, target_type); + + // + // From ICloneable to Array or Delegate types + // + if (source_type == TypeManager.icloneable_type && + (target_type == TypeManager.array_type || + target_type == TypeManager.delegate_type)) + return new ClassCast (source, target_type); + + return null; + } + + /// <summary> + /// Performs an explicit conversion of the expression `expr' whose + /// type is expr.Type to `target_type'. + /// </summary> + static public Expression ConvertExplicit (EmitContext ec, Expression expr, + Type target_type, Location loc) + { + Type expr_type = expr.Type; + Expression ne = ConvertImplicitStandard (ec, expr, target_type, loc); + + if (ne != null) + return ne; + + ne = ConvertNumericExplicit (ec, expr, target_type, loc); + if (ne != null) + return ne; + + // + // Unboxing conversion. + // + if (expr_type == TypeManager.object_type && target_type.IsValueType) + return new UnboxCast (expr, target_type); + + // + // Enum types + // + if (expr_type.IsSubclassOf (TypeManager.enum_type)) { + Expression e; + + // + // FIXME: Is there any reason we should have EnumConstant + // dealt with here instead of just using always the + // UnderlyingSystemType to wrap the type? + // + if (expr is EnumConstant) + e = ((EnumConstant) expr).Child; + else { + e = new EmptyCast (expr, TypeManager.EnumToUnderlying (expr_type)); + } + + Expression t = ConvertImplicit (ec, e, target_type, loc); + if (t != null) + return t; + + t = ConvertNumericExplicit (ec, e, target_type, loc); + if (t != null) + return t; + + Error_CannotConvertType (loc, expr_type, target_type); + return null; + } + + ne = ConvertReferenceExplicit (expr, target_type); + if (ne != null) + return ne; + + if (ec.InUnsafe){ + if (target_type.IsPointer){ + if (expr_type.IsPointer) + return new EmptyCast (expr, target_type); + + if (expr_type == TypeManager.sbyte_type || + expr_type == TypeManager.byte_type || + expr_type == TypeManager.short_type || + expr_type == TypeManager.ushort_type || + expr_type == TypeManager.int32_type || + expr_type == TypeManager.uint32_type || + expr_type == TypeManager.uint64_type || + expr_type == TypeManager.int64_type) + return new OpcodeCast (expr, target_type, OpCodes.Conv_U); + } + if (expr_type.IsPointer){ + if (target_type == TypeManager.sbyte_type || + target_type == TypeManager.byte_type || + target_type == TypeManager.short_type || + target_type == TypeManager.ushort_type || + target_type == TypeManager.int32_type || + target_type == TypeManager.uint32_type || + target_type == TypeManager.uint64_type || + target_type == TypeManager.int64_type){ + Expression e = new EmptyCast (expr, TypeManager.uint32_type); + Expression ci, ce; + + ci = ConvertImplicitStandard (ec, e, target_type, loc); + + if (ci != null) + return ci; + + ce = ConvertNumericExplicit (ec, e, target_type, loc); + if (ce != null) + return ce; + // + // We should always be able to go from an uint32 + // implicitly or explicitly to the other integral + // types + // + throw new Exception ("Internal compiler error"); + } + } + } + + ne = ExplicitUserConversion (ec, expr, target_type, loc); + if (ne != null) + return ne; + + Error_CannotConvertType (loc, expr_type, target_type); + return null; + } + + /// <summary> + /// Same as ConvertExplicit, only it doesn't include user defined conversions + /// </summary> + static public Expression ConvertExplicitStandard (EmitContext ec, Expression expr, + Type target_type, Location l) + { + Expression ne = ConvertImplicitStandard (ec, expr, target_type, l); + + if (ne != null) + return ne; + + ne = ConvertNumericExplicit (ec, expr, target_type, l); + if (ne != null) + return ne; + + ne = ConvertReferenceExplicit (expr, target_type); + if (ne != null) + return ne; + + Error_CannotConvertType (l, expr.Type, target_type); + return null; + } + + static string ExprClassName (ExprClass c) + { + switch (c){ + case ExprClass.Invalid: + return "Invalid"; + case ExprClass.Value: + return "value"; + case ExprClass.Variable: + return "variable"; + case ExprClass.Namespace: + return "namespace"; + case ExprClass.Type: + return "type"; + case ExprClass.MethodGroup: + return "method group"; + case ExprClass.PropertyAccess: + return "property access"; + case ExprClass.EventAccess: + return "event access"; + case ExprClass.IndexerAccess: + return "indexer access"; + case ExprClass.Nothing: + return "null"; + } + throw new Exception ("Should not happen"); + } + + /// <summary> + /// Reports that we were expecting `expr' to be of class `expected' + /// </summary> + public void Error118 (string expected) + { + string kind = "Unknown"; + + kind = ExprClassName (eclass); + + Error (118, "Expression denotes a `" + kind + + "' where a `" + expected + "' was expected"); + } + + public void Error118 (ResolveFlags flags) + { + ArrayList valid = new ArrayList (10); + + if ((flags & ResolveFlags.VariableOrValue) != 0) { + valid.Add ("variable"); + valid.Add ("value"); + } + + if ((flags & ResolveFlags.Type) != 0) + valid.Add ("type"); + + if ((flags & ResolveFlags.MethodGroup) != 0) + valid.Add ("method group"); + + if ((flags & ResolveFlags.SimpleName) != 0) + valid.Add ("simple name"); + + if (valid.Count == 0) + valid.Add ("unknown"); + + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < valid.Count; i++) { + if (i > 0) + sb.Append (", "); + else if (i == valid.Count) + sb.Append (" or "); + sb.Append (valid [i]); + } + + string kind = ExprClassName (eclass); + + Error (119, "Expression denotes a `" + kind + "' where " + + "a `" + sb.ToString () + "' was expected"); + } + + static void Error_ConstantValueCannotBeConverted (Location l, string val, Type t) + { + Report.Error (31, l, "Constant value `" + val + "' cannot be converted to " + + TypeManager.CSharpName (t)); + } + + public static void UnsafeError (Location loc) + { + Report.Error (214, loc, "Pointers may only be used in an unsafe context"); + } + + /// <summary> + /// Converts the IntConstant, UIntConstant, LongConstant or + /// ULongConstant into the integral target_type. Notice + /// that we do not return an `Expression' we do return + /// a boxed integral type. + /// + /// FIXME: Since I added the new constants, we need to + /// also support conversions from CharConstant, ByteConstant, + /// SByteConstant, UShortConstant, ShortConstant + /// + /// This is used by the switch statement, so the domain + /// of work is restricted to the literals above, and the + /// targets are int32, uint32, char, byte, sbyte, ushort, + /// short, uint64 and int64 + /// </summary> + public static object ConvertIntLiteral (Constant c, Type target_type, Location loc) + { + string s = ""; + + if (c.Type == target_type) + return ((Constant) c).GetValue (); + + // + // Make into one of the literals we handle, we dont really care + // about this value as we will just return a few limited types + // + if (c is EnumConstant) + c = ((EnumConstant)c).WidenToCompilerConstant (); + + if (c is IntConstant){ + int v = ((IntConstant) c).Value; + + if (target_type == TypeManager.uint32_type){ + if (v >= 0) + return (uint) v; + } else if (target_type == TypeManager.char_type){ + if (v >= Char.MinValue && v <= Char.MaxValue) + return (char) v; + } else if (target_type == TypeManager.byte_type){ + if (v >= Byte.MinValue && v <= Byte.MaxValue) + return (byte) v; + } else if (target_type == TypeManager.sbyte_type){ + if (v >= SByte.MinValue && v <= SByte.MaxValue) + return (sbyte) v; + } else if (target_type == TypeManager.short_type){ + if (v >= Int16.MinValue && v <= UInt16.MaxValue) + return (short) v; + } else if (target_type == TypeManager.ushort_type){ + if (v >= UInt16.MinValue && v <= UInt16.MaxValue) + return (ushort) v; + } else if (target_type == TypeManager.int64_type) + return (long) v; + else if (target_type == TypeManager.uint64_type){ + if (v > 0) + return (ulong) v; + } + + s = v.ToString (); + } else if (c is UIntConstant){ + uint v = ((UIntConstant) c).Value; + + if (target_type == TypeManager.int32_type){ + if (v <= Int32.MaxValue) + return (int) v; + } else if (target_type == TypeManager.char_type){ + if (v >= Char.MinValue && v <= Char.MaxValue) + return (char) v; + } else if (target_type == TypeManager.byte_type){ + if (v <= Byte.MaxValue) + return (byte) v; + } else if (target_type == TypeManager.sbyte_type){ + if (v <= SByte.MaxValue) + return (sbyte) v; + } else if (target_type == TypeManager.short_type){ + if (v <= UInt16.MaxValue) + return (short) v; + } else if (target_type == TypeManager.ushort_type){ + if (v <= UInt16.MaxValue) + return (ushort) v; + } else if (target_type == TypeManager.int64_type) + return (long) v; + else if (target_type == TypeManager.uint64_type) + return (ulong) v; + s = v.ToString (); + } else if (c is LongConstant){ + long v = ((LongConstant) c).Value; + + if (target_type == TypeManager.int32_type){ + if (v >= UInt32.MinValue && v <= UInt32.MaxValue) + return (int) v; + } else if (target_type == TypeManager.uint32_type){ + if (v >= 0 && v <= UInt32.MaxValue) + return (uint) v; + } else if (target_type == TypeManager.char_type){ + if (v >= Char.MinValue && v <= Char.MaxValue) + return (char) v; + } else if (target_type == TypeManager.byte_type){ + if (v >= Byte.MinValue && v <= Byte.MaxValue) + return (byte) v; + } else if (target_type == TypeManager.sbyte_type){ + if (v >= SByte.MinValue && v <= SByte.MaxValue) + return (sbyte) v; + } else if (target_type == TypeManager.short_type){ + if (v >= Int16.MinValue && v <= UInt16.MaxValue) + return (short) v; + } else if (target_type == TypeManager.ushort_type){ + if (v >= UInt16.MinValue && v <= UInt16.MaxValue) + return (ushort) v; + } else if (target_type == TypeManager.uint64_type){ + if (v > 0) + return (ulong) v; + } + s = v.ToString (); + } else if (c is ULongConstant){ + ulong v = ((ULongConstant) c).Value; + + if (target_type == TypeManager.int32_type){ + if (v <= Int32.MaxValue) + return (int) v; + } else if (target_type == TypeManager.uint32_type){ + if (v <= UInt32.MaxValue) + return (uint) v; + } else if (target_type == TypeManager.char_type){ + if (v >= Char.MinValue && v <= Char.MaxValue) + return (char) v; + } else if (target_type == TypeManager.byte_type){ + if (v >= Byte.MinValue && v <= Byte.MaxValue) + return (byte) v; + } else if (target_type == TypeManager.sbyte_type){ + if (v <= (int) SByte.MaxValue) + return (sbyte) v; + } else if (target_type == TypeManager.short_type){ + if (v <= UInt16.MaxValue) + return (short) v; + } else if (target_type == TypeManager.ushort_type){ + if (v <= UInt16.MaxValue) + return (ushort) v; + } else if (target_type == TypeManager.int64_type){ + if (v <= Int64.MaxValue) + return (long) v; + } + s = v.ToString (); + } else if (c is ByteConstant){ + byte v = ((ByteConstant) c).Value; + + if (target_type == TypeManager.int32_type) + return (int) v; + else if (target_type == TypeManager.uint32_type) + return (uint) v; + else if (target_type == TypeManager.char_type) + return (char) v; + else if (target_type == TypeManager.sbyte_type){ + if (v <= SByte.MaxValue) + return (sbyte) v; + } else if (target_type == TypeManager.short_type) + return (short) v; + else if (target_type == TypeManager.ushort_type) + return (ushort) v; + else if (target_type == TypeManager.int64_type) + return (long) v; + else if (target_type == TypeManager.uint64_type) + return (ulong) v; + s = v.ToString (); + } else if (c is SByteConstant){ + sbyte v = ((SByteConstant) c).Value; + + if (target_type == TypeManager.int32_type) + return (int) v; + else if (target_type == TypeManager.uint32_type){ + if (v >= 0) + return (uint) v; + } else if (target_type == TypeManager.char_type){ + if (v >= 0) + return (char) v; + } else if (target_type == TypeManager.byte_type){ + if (v >= 0) + return (byte) v; + } else if (target_type == TypeManager.short_type) + return (short) v; + else if (target_type == TypeManager.ushort_type){ + if (v >= 0) + return (ushort) v; + } else if (target_type == TypeManager.int64_type) + return (long) v; + else if (target_type == TypeManager.uint64_type){ + if (v >= 0) + return (ulong) v; + } + s = v.ToString (); + } else if (c is ShortConstant){ + short v = ((ShortConstant) c).Value; + + if (target_type == TypeManager.int32_type){ + return (int) v; + } else if (target_type == TypeManager.uint32_type){ + if (v >= 0) + return (uint) v; + } else if (target_type == TypeManager.char_type){ + if (v >= 0) + return (char) v; + } else if (target_type == TypeManager.byte_type){ + if (v >= Byte.MinValue && v <= Byte.MaxValue) + return (byte) v; + } else if (target_type == TypeManager.sbyte_type){ + if (v >= SByte.MinValue && v <= SByte.MaxValue) + return (sbyte) v; + } else if (target_type == TypeManager.ushort_type){ + if (v >= 0) + return (ushort) v; + } else if (target_type == TypeManager.int64_type) + return (long) v; + else if (target_type == TypeManager.uint64_type) + return (ulong) v; + + s = v.ToString (); + } else if (c is UShortConstant){ + ushort v = ((UShortConstant) c).Value; + + if (target_type == TypeManager.int32_type) + return (int) v; + else if (target_type == TypeManager.uint32_type) + return (uint) v; + else if (target_type == TypeManager.char_type){ + if (v >= Char.MinValue && v <= Char.MaxValue) + return (char) v; + } else if (target_type == TypeManager.byte_type){ + if (v >= Byte.MinValue && v <= Byte.MaxValue) + return (byte) v; + } else if (target_type == TypeManager.sbyte_type){ + if (v <= SByte.MaxValue) + return (byte) v; + } else if (target_type == TypeManager.short_type){ + if (v <= Int16.MaxValue) + return (short) v; + } else if (target_type == TypeManager.int64_type) + return (long) v; + else if (target_type == TypeManager.uint64_type) + return (ulong) v; + + s = v.ToString (); + } else if (c is CharConstant){ + char v = ((CharConstant) c).Value; + + if (target_type == TypeManager.int32_type) + return (int) v; + else if (target_type == TypeManager.uint32_type) + return (uint) v; + else if (target_type == TypeManager.byte_type){ + if (v >= Byte.MinValue && v <= Byte.MaxValue) + return (byte) v; + } else if (target_type == TypeManager.sbyte_type){ + if (v <= SByte.MaxValue) + return (sbyte) v; + } else if (target_type == TypeManager.short_type){ + if (v <= Int16.MaxValue) + return (short) v; + } else if (target_type == TypeManager.ushort_type) + return (short) v; + else if (target_type == TypeManager.int64_type) + return (long) v; + else if (target_type == TypeManager.uint64_type) + return (ulong) v; + + s = v.ToString (); + } + Error_ConstantValueCannotBeConverted (loc, s, target_type); + return null; + } + + // + // Load the object from the pointer. + // + public static void LoadFromPtr (ILGenerator ig, Type t) + { + if (t == TypeManager.int32_type) + ig.Emit (OpCodes.Ldind_I4); + else if (t == TypeManager.uint32_type) + ig.Emit (OpCodes.Ldind_U4); + else if (t == TypeManager.short_type) + ig.Emit (OpCodes.Ldind_I2); + else if (t == TypeManager.ushort_type) + ig.Emit (OpCodes.Ldind_U2); + else if (t == TypeManager.char_type) + ig.Emit (OpCodes.Ldind_U2); + else if (t == TypeManager.byte_type) + ig.Emit (OpCodes.Ldind_U1); + else if (t == TypeManager.sbyte_type) + ig.Emit (OpCodes.Ldind_I1); + else if (t == TypeManager.uint64_type) + ig.Emit (OpCodes.Ldind_I8); + else if (t == TypeManager.int64_type) + ig.Emit (OpCodes.Ldind_I8); + else if (t == TypeManager.float_type) + ig.Emit (OpCodes.Ldind_R4); + else if (t == TypeManager.double_type) + ig.Emit (OpCodes.Ldind_R8); + else if (t == TypeManager.bool_type) + ig.Emit (OpCodes.Ldind_I1); + else if (t == TypeManager.intptr_type) + ig.Emit (OpCodes.Ldind_I); + else if (TypeManager.IsEnumType (t)) { + if (t == TypeManager.enum_type) + ig.Emit (OpCodes.Ldind_Ref); + else + LoadFromPtr (ig, TypeManager.EnumToUnderlying (t)); + } else if (t.IsValueType) + ig.Emit (OpCodes.Ldobj, t); + else + ig.Emit (OpCodes.Ldind_Ref); + } + + // + // The stack contains the pointer and the value of type `type' + // + public static void StoreFromPtr (ILGenerator ig, Type type) + { + if (TypeManager.IsEnumType (type)) + type = TypeManager.EnumToUnderlying (type); + if (type == TypeManager.int32_type || type == TypeManager.uint32_type) + ig.Emit (OpCodes.Stind_I4); + else if (type == TypeManager.int64_type || type == TypeManager.uint64_type) + ig.Emit (OpCodes.Stind_I8); + else if (type == TypeManager.char_type || type == TypeManager.short_type || + type == TypeManager.ushort_type) + ig.Emit (OpCodes.Stind_I2); + else if (type == TypeManager.float_type) + ig.Emit (OpCodes.Stind_R4); + else if (type == TypeManager.double_type) + ig.Emit (OpCodes.Stind_R8); + else if (type == TypeManager.byte_type || type == TypeManager.sbyte_type || + type == TypeManager.bool_type) + ig.Emit (OpCodes.Stind_I1); + else if (type == TypeManager.intptr_type) + ig.Emit (OpCodes.Stind_I); + else if (type.IsValueType) + ig.Emit (OpCodes.Stobj, type); + else + ig.Emit (OpCodes.Stind_Ref); + } + + // + // Returns the size of type `t' if known, otherwise, 0 + // + public static int GetTypeSize (Type t) + { + t = TypeManager.TypeToCoreType (t); + if (t == TypeManager.int32_type || + t == TypeManager.uint32_type || + t == TypeManager.float_type) + return 4; + else if (t == TypeManager.int64_type || + t == TypeManager.uint64_type || + t == TypeManager.double_type) + return 8; + else if (t == TypeManager.byte_type || + t == TypeManager.sbyte_type || + t == TypeManager.bool_type) + return 1; + else if (t == TypeManager.short_type || + t == TypeManager.char_type || + t == TypeManager.ushort_type) + return 2; + else if (t == TypeManager.decimal_type) + return 16; + else + return 0; + } + + // + // Default implementation of IAssignMethod.CacheTemporaries + // + public void CacheTemporaries (EmitContext ec) + { + } + + static void Error_NegativeArrayIndex (Location loc) + { + Report.Error (284, loc, "Can not create array with a negative size"); + } + + // + // Converts `source' to an int, uint, long or ulong. + // + public Expression ExpressionToArrayArgument (EmitContext ec, Expression source, Location loc) + { + Expression target; + + bool old_checked = ec.CheckState; + ec.CheckState = true; + + target = ConvertImplicit (ec, source, TypeManager.int32_type, loc); + if (target == null){ + target = ConvertImplicit (ec, source, TypeManager.uint32_type, loc); + if (target == null){ + target = ConvertImplicit (ec, source, TypeManager.int64_type, loc); + if (target == null){ + target = ConvertImplicit (ec, source, TypeManager.uint64_type, loc); + if (target == null) + Expression.Error_CannotConvertImplicit (loc, source.Type, TypeManager.int32_type); + } + } + } + ec.CheckState = old_checked; + + // + // Only positive constants are allowed at compile time + // + if (target is Constant){ + if (target is IntConstant){ + if (((IntConstant) target).Value < 0){ + Error_NegativeArrayIndex (loc); + return null; + } + } + + if (target is LongConstant){ + if (((LongConstant) target).Value < 0){ + Error_NegativeArrayIndex (loc); + return null; + } + } + + } + + return target; + } + + } + + /// <summary> + /// This is just a base class for expressions that can + /// appear on statements (invocations, object creation, + /// assignments, post/pre increment and decrement). The idea + /// being that they would support an extra Emition interface that + /// does not leave a result on the stack. + /// </summary> + public abstract class ExpressionStatement : Expression { + + /// <summary> + /// Requests the expression to be emitted in a `statement' + /// context. This means that no new value is left on the + /// stack after invoking this method (constrasted with + /// Emit that will always leave a value on the stack). + /// </summary> + public abstract void EmitStatement (EmitContext ec); + } + + /// <summary> + /// This kind of cast is used to encapsulate the child + /// whose type is child.Type into an expression that is + /// reported to return "return_type". This is used to encapsulate + /// expressions which have compatible types, but need to be dealt + /// at higher levels with. + /// + /// For example, a "byte" expression could be encapsulated in one + /// of these as an "unsigned int". The type for the expression + /// would be "unsigned int". + /// + /// </summary> + public class EmptyCast : Expression { + protected Expression child; + + public EmptyCast (Expression child, Type return_type) + { + eclass = child.eclass; + type = return_type; + this.child = child; + } + + public override Expression DoResolve (EmitContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + } + } + + /// <summary> + /// This class is used to wrap literals which belong inside Enums + /// </summary> + public class EnumConstant : Constant { + public Constant Child; + + public EnumConstant (Constant child, Type enum_type) + { + eclass = child.eclass; + this.Child = child; + type = enum_type; + } + + public override Expression DoResolve (EmitContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + Child.Emit (ec); + } + + public override object GetValue () + { + return Child.GetValue (); + } + + // + // Converts from one of the valid underlying types for an enumeration + // (int32, uint32, int64, uint64, short, ushort, byte, sbyte) to + // one of the internal compiler literals: Int/UInt/Long/ULong Literals. + // + public Constant WidenToCompilerConstant () + { + Type t = TypeManager.EnumToUnderlying (Child.Type); + object v = ((Constant) Child).GetValue ();; + + if (t == TypeManager.int32_type) + return new IntConstant ((int) v); + if (t == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (t == TypeManager.int64_type) + return new LongConstant ((long) v); + if (t == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (t == TypeManager.short_type) + return new ShortConstant ((short) v); + if (t == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (t == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (t == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + + throw new Exception ("Invalid enumeration underlying type: " + t); + } + + // + // Extracts the value in the enumeration on its native representation + // + public object GetPlainValue () + { + Type t = TypeManager.EnumToUnderlying (Child.Type); + object v = ((Constant) Child).GetValue ();; + + if (t == TypeManager.int32_type) + return (int) v; + if (t == TypeManager.uint32_type) + return (uint) v; + if (t == TypeManager.int64_type) + return (long) v; + if (t == TypeManager.uint64_type) + return (ulong) v; + if (t == TypeManager.short_type) + return (short) v; + if (t == TypeManager.ushort_type) + return (ushort) v; + if (t == TypeManager.byte_type) + return (byte) v; + if (t == TypeManager.sbyte_type) + return (sbyte) v; + + return null; + } + + public override string AsString () + { + return Child.AsString (); + } + + public override DoubleConstant ConvertToDouble () + { + return Child.ConvertToDouble (); + } + + public override FloatConstant ConvertToFloat () + { + return Child.ConvertToFloat (); + } + + public override ULongConstant ConvertToULong () + { + return Child.ConvertToULong (); + } + + public override LongConstant ConvertToLong () + { + return Child.ConvertToLong (); + } + + public override UIntConstant ConvertToUInt () + { + return Child.ConvertToUInt (); + } + + public override IntConstant ConvertToInt () + { + return Child.ConvertToInt (); + } + } + + /// <summary> + /// This kind of cast is used to encapsulate Value Types in objects. + /// + /// The effect of it is to box the value type emitted by the previous + /// operation. + /// </summary> + public class BoxedCast : EmptyCast { + + public BoxedCast (Expression expr) + : base (expr, TypeManager.object_type) + { + } + + public override Expression DoResolve (EmitContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + ec.ig.Emit (OpCodes.Box, child.Type); + } + } + + public class UnboxCast : EmptyCast { + public UnboxCast (Expression expr, Type return_type) + : base (expr, return_type) + { + } + + public override Expression DoResolve (EmitContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + Type t = type; + ILGenerator ig = ec.ig; + + base.Emit (ec); + ig.Emit (OpCodes.Unbox, t); + + LoadFromPtr (ig, t); + } + } + + /// <summary> + /// This is used to perform explicit numeric conversions. + /// + /// Explicit numeric conversions might trigger exceptions in a checked + /// context, so they should generate the conv.ovf opcodes instead of + /// conv opcodes. + /// </summary> + public class ConvCast : EmptyCast { + public enum Mode : byte { + I1_U1, I1_U2, I1_U4, I1_U8, I1_CH, + U1_I1, U1_CH, + I2_I1, I2_U1, I2_U2, I2_U4, I2_U8, I2_CH, + U2_I1, U2_U1, U2_I2, U2_CH, + I4_I1, I4_U1, I4_I2, I4_U2, I4_U4, I4_U8, I4_CH, + U4_I1, U4_U1, U4_I2, U4_U2, U4_I4, U4_CH, + I8_I1, I8_U1, I8_I2, I8_U2, I8_I4, I8_U4, I8_U8, I8_CH, + U8_I1, U8_U1, U8_I2, U8_U2, U8_I4, U8_U4, U8_I8, U8_CH, + CH_I1, CH_U1, CH_I2, + R4_I1, R4_U1, R4_I2, R4_U2, R4_I4, R4_U4, R4_I8, R4_U8, R4_CH, + R8_I1, R8_U1, R8_I2, R8_U2, R8_I4, R8_U4, R8_I8, R8_U8, R8_CH, R8_R4 + } + + Mode mode; + bool checked_state; + + public ConvCast (EmitContext ec, Expression child, Type return_type, Mode m) + : base (child, return_type) + { + checked_state = ec.CheckState; + mode = m; + } + + public override Expression DoResolve (EmitContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + base.Emit (ec); + + if (checked_state){ + switch (mode){ + case Mode.I1_U1: ig.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I1_U2: ig.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I1_U4: ig.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I1_U8: ig.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I1_CH: ig.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U1_I1: ig.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U1_CH: /* nothing */ break; + + case Mode.I2_I1: ig.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.I2_U1: ig.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I2_U2: ig.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I2_U4: ig.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I2_U8: ig.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I2_CH: ig.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U2_I1: ig.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U2_U1: ig.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.U2_I2: ig.Emit (OpCodes.Conv_Ovf_I2_Un); break; + case Mode.U2_CH: /* nothing */ break; + + case Mode.I4_I1: ig.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.I4_U1: ig.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I4_I2: ig.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.I4_U4: ig.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I4_U2: ig.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I4_U8: ig.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I4_CH: ig.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U4_I1: ig.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U4_U1: ig.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.U4_I2: ig.Emit (OpCodes.Conv_Ovf_I2_Un); break; + case Mode.U4_U2: ig.Emit (OpCodes.Conv_Ovf_U2_Un); break; + case Mode.U4_I4: ig.Emit (OpCodes.Conv_Ovf_I4_Un); break; + case Mode.U4_CH: ig.Emit (OpCodes.Conv_Ovf_U2_Un); break; + + case Mode.I8_I1: ig.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.I8_U1: ig.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I8_I2: ig.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.I8_U2: ig.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I8_I4: ig.Emit (OpCodes.Conv_Ovf_I4); break; + case Mode.I8_U4: ig.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I8_U8: ig.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I8_CH: ig.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U8_I1: ig.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U8_U1: ig.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.U8_I2: ig.Emit (OpCodes.Conv_Ovf_I2_Un); break; + case Mode.U8_U2: ig.Emit (OpCodes.Conv_Ovf_U2_Un); break; + case Mode.U8_I4: ig.Emit (OpCodes.Conv_Ovf_I4_Un); break; + case Mode.U8_U4: ig.Emit (OpCodes.Conv_Ovf_U4_Un); break; + case Mode.U8_I8: ig.Emit (OpCodes.Conv_Ovf_I8_Un); break; + case Mode.U8_CH: ig.Emit (OpCodes.Conv_Ovf_U2_Un); break; + + case Mode.CH_I1: ig.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.CH_U1: ig.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.CH_I2: ig.Emit (OpCodes.Conv_Ovf_I2_Un); break; + + case Mode.R4_I1: ig.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.R4_U1: ig.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.R4_I2: ig.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.R4_U2: ig.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.R4_I4: ig.Emit (OpCodes.Conv_Ovf_I4); break; + case Mode.R4_U4: ig.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.R4_I8: ig.Emit (OpCodes.Conv_Ovf_I8); break; + case Mode.R4_U8: ig.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.R4_CH: ig.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.R8_I1: ig.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.R8_U1: ig.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.R8_I2: ig.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.R8_U2: ig.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.R8_I4: ig.Emit (OpCodes.Conv_Ovf_I4); break; + case Mode.R8_U4: ig.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.R8_I8: ig.Emit (OpCodes.Conv_Ovf_I8); break; + case Mode.R8_U8: ig.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.R8_CH: ig.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.R8_R4: ig.Emit (OpCodes.Conv_R4); break; + } + } else { + switch (mode){ + case Mode.I1_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.I1_U2: ig.Emit (OpCodes.Conv_U2); break; + case Mode.I1_U4: ig.Emit (OpCodes.Conv_U4); break; + case Mode.I1_U8: ig.Emit (OpCodes.Conv_I8); break; + case Mode.I1_CH: ig.Emit (OpCodes.Conv_U2); break; + + case Mode.U1_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.U1_CH: ig.Emit (OpCodes.Conv_U2); break; + + case Mode.I2_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.I2_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.I2_U2: ig.Emit (OpCodes.Conv_U2); break; + case Mode.I2_U4: ig.Emit (OpCodes.Conv_U4); break; + case Mode.I2_U8: ig.Emit (OpCodes.Conv_I8); break; + case Mode.I2_CH: ig.Emit (OpCodes.Conv_U2); break; + + case Mode.U2_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.U2_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.U2_I2: ig.Emit (OpCodes.Conv_I2); break; + case Mode.U2_CH: /* nothing */ break; + + case Mode.I4_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.I4_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.I4_I2: ig.Emit (OpCodes.Conv_I2); break; + case Mode.I4_U4: /* nothing */ break; + case Mode.I4_U2: ig.Emit (OpCodes.Conv_U2); break; + case Mode.I4_U8: ig.Emit (OpCodes.Conv_I8); break; + case Mode.I4_CH: ig.Emit (OpCodes.Conv_U2); break; + + case Mode.U4_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.U4_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.U4_I2: ig.Emit (OpCodes.Conv_I2); break; + case Mode.U4_U2: ig.Emit (OpCodes.Conv_U2); break; + case Mode.U4_I4: /* nothing */ break; + case Mode.U4_CH: ig.Emit (OpCodes.Conv_U2); break; + + case Mode.I8_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.I8_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.I8_I2: ig.Emit (OpCodes.Conv_I2); break; + case Mode.I8_U2: ig.Emit (OpCodes.Conv_U2); break; + case Mode.I8_I4: ig.Emit (OpCodes.Conv_I4); break; + case Mode.I8_U4: ig.Emit (OpCodes.Conv_U4); break; + case Mode.I8_U8: /* nothing */ break; + case Mode.I8_CH: ig.Emit (OpCodes.Conv_U2); break; + + case Mode.U8_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.U8_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.U8_I2: ig.Emit (OpCodes.Conv_I2); break; + case Mode.U8_U2: ig.Emit (OpCodes.Conv_U2); break; + case Mode.U8_I4: ig.Emit (OpCodes.Conv_I4); break; + case Mode.U8_U4: ig.Emit (OpCodes.Conv_U4); break; + case Mode.U8_I8: /* nothing */ break; + case Mode.U8_CH: ig.Emit (OpCodes.Conv_U2); break; + + case Mode.CH_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.CH_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.CH_I2: ig.Emit (OpCodes.Conv_I2); break; + + case Mode.R4_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.R4_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.R4_I2: ig.Emit (OpCodes.Conv_I2); break; + case Mode.R4_U2: ig.Emit (OpCodes.Conv_U2); break; + case Mode.R4_I4: ig.Emit (OpCodes.Conv_I4); break; + case Mode.R4_U4: ig.Emit (OpCodes.Conv_U4); break; + case Mode.R4_I8: ig.Emit (OpCodes.Conv_I8); break; + case Mode.R4_U8: ig.Emit (OpCodes.Conv_U8); break; + case Mode.R4_CH: ig.Emit (OpCodes.Conv_U2); break; + + case Mode.R8_I1: ig.Emit (OpCodes.Conv_I1); break; + case Mode.R8_U1: ig.Emit (OpCodes.Conv_U1); break; + case Mode.R8_I2: ig.Emit (OpCodes.Conv_I2); break; + case Mode.R8_U2: ig.Emit (OpCodes.Conv_U2); break; + case Mode.R8_I4: ig.Emit (OpCodes.Conv_I4); break; + case Mode.R8_U4: ig.Emit (OpCodes.Conv_U4); break; + case Mode.R8_I8: ig.Emit (OpCodes.Conv_I8); break; + case Mode.R8_U8: ig.Emit (OpCodes.Conv_U8); break; + case Mode.R8_CH: ig.Emit (OpCodes.Conv_U2); break; + case Mode.R8_R4: ig.Emit (OpCodes.Conv_R4); break; + } + } + } + } + + public class OpcodeCast : EmptyCast { + OpCode op, op2; + bool second_valid; + + public OpcodeCast (Expression child, Type return_type, OpCode op) + : base (child, return_type) + + { + this.op = op; + second_valid = false; + } + + public OpcodeCast (Expression child, Type return_type, OpCode op, OpCode op2) + : base (child, return_type) + + { + this.op = op; + this.op2 = op2; + second_valid = true; + } + + public override Expression DoResolve (EmitContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + ec.ig.Emit (op); + + if (second_valid) + ec.ig.Emit (op2); + } + } + + /// <summary> + /// This kind of cast is used to encapsulate a child and cast it + /// to the class requested + /// </summary> + public class ClassCast : EmptyCast { + public ClassCast (Expression child, Type return_type) + : base (child, return_type) + + { + } + + public override Expression DoResolve (EmitContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + ec.ig.Emit (OpCodes.Castclass, type); + } + + } + + /// <summary> + /// SimpleName expressions are initially formed of a single + /// word and it only happens at the beginning of the expression. + /// </summary> + /// + /// <remarks> + /// The expression will try to be bound to a Field, a Method + /// group or a Property. If those fail we pass the name to our + /// caller and the SimpleName is compounded to perform a type + /// lookup. The idea behind this process is that we want to avoid + /// creating a namespace map from the assemblies, as that requires + /// the GetExportedTypes function to be called and a hashtable to + /// be constructed which reduces startup time. If later we find + /// that this is slower, we should create a `NamespaceExpr' expression + /// that fully participates in the resolution process. + /// + /// For example `System.Console.WriteLine' is decomposed into + /// MemberAccess (MemberAccess (SimpleName ("System"), "Console"), "WriteLine") + /// + /// The first SimpleName wont produce a match on its own, so it will + /// be turned into: + /// MemberAccess (SimpleName ("System.Console"), "WriteLine"). + /// + /// System.Console will produce a TypeExpr match. + /// + /// The downside of this is that we might be hitting `LookupType' too many + /// times with this scheme. + /// </remarks> + public class SimpleName : Expression, ITypeExpression { + public readonly string Name; + + public SimpleName (string name, Location l) + { + Name = name; + loc = l; + } + + public static void Error_ObjectRefRequired (EmitContext ec, Location l, string name) + { + if (ec.IsFieldInitializer) + Report.Error ( + 236, l, + "A field initializer cannot reference the non-static field, " + + "method or property `"+name+"'"); + else + Report.Error ( + 120, l, + "An object reference is required " + + "for the non-static field `"+name+"'"); + } + + // + // Checks whether we are trying to access an instance + // property, method or field from a static body. + // + Expression MemberStaticCheck (EmitContext ec, Expression e) + { + if (e is IMemberExpr){ + IMemberExpr member = (IMemberExpr) e; + + if (!member.IsStatic){ + Error_ObjectRefRequired (ec, loc, Name); + return null; + } + } + + return e; + } + + public override Expression DoResolve (EmitContext ec) + { + return SimpleNameResolve (ec, null, false); + } + + public override Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + return SimpleNameResolve (ec, right_side, false); + } + + + public Expression DoResolveAllowStatic (EmitContext ec) + { + return SimpleNameResolve (ec, null, true); + } + + public Expression DoResolveType (EmitContext ec) + { + // + // Stage 3: Lookup symbol in the various namespaces. + // + DeclSpace ds = ec.DeclSpace; + Type t; + string alias_value; + + if (ec.ResolvingTypeTree){ + int errors = Report.Errors; + Type dt = ec.DeclSpace.FindType (loc, Name); + if (Report.Errors != errors) + return null; + + if (dt != null) + return new TypeExpr (dt, loc); + } + + if ((t = RootContext.LookupType (ds, Name, true, loc)) != null) + return new TypeExpr (t, loc); + + + // + // Stage 2 part b: Lookup up if we are an alias to a type + // or a namespace. + // + // Since we are cheating: we only do the Alias lookup for + // namespaces if the name does not include any dots in it + // + + alias_value = ec.DeclSpace.LookupAlias (Name); + + if (Name.IndexOf ('.') == -1 && alias_value != null) { + if ((t = RootContext.LookupType (ds, alias_value, true, loc)) != null) + return new TypeExpr (t, loc); + + // we have alias value, but it isn't Type, so try if it's namespace + return new SimpleName (alias_value, loc); + } + + // No match, maybe our parent can compose us + // into something meaningful. + return this; + } + + /// <remarks> + /// 7.5.2: Simple Names. + /// + /// Local Variables and Parameters are handled at + /// parse time, so they never occur as SimpleNames. + /// + /// The `allow_static' flag is used by MemberAccess only + /// and it is used to inform us that it is ok for us to + /// avoid the static check, because MemberAccess might end + /// up resolving the Name as a Type name and the access as + /// a static type access. + /// + /// ie: Type Type; .... { Type.GetType (""); } + /// + /// Type is both an instance variable and a Type; Type.GetType + /// is the static method not an instance method of type. + /// </remarks> + Expression SimpleNameResolve (EmitContext ec, Expression right_side, bool allow_static) + { + Expression e = null; + + // + // Stage 1: Performed by the parser (binding to locals or parameters). + // + Block current_block = ec.CurrentBlock; + if (current_block != null && current_block.IsVariableDefined (Name)){ + LocalVariableReference var; + + var = new LocalVariableReference (ec.CurrentBlock, Name, loc); + + if (right_side != null) + return var.ResolveLValue (ec, right_side); + else + return var.Resolve (ec); + } + + if (current_block != null){ + int idx = -1; + Parameter par = null; + Parameters pars = current_block.Parameters; + if (pars != null) + par = pars.GetParameterByName (Name, out idx); + + if (par != null) { + ParameterReference param; + + param = new ParameterReference (pars, idx, Name, loc); + + if (right_side != null) + return param.ResolveLValue (ec, right_side); + else + return param.Resolve (ec); + } + } + + // + // Stage 2: Lookup members + // + + // + // For enums, the TypeBuilder is not ec.DeclSpace.TypeBuilder + // Hence we have two different cases + // + + DeclSpace lookup_ds = ec.DeclSpace; + do { + if (lookup_ds.TypeBuilder == null) + break; + + e = MemberLookup (ec, lookup_ds.TypeBuilder, Name, loc); + if (e != null) + break; + + // + // Classes/structs keep looking, enums break + // + if (lookup_ds is TypeContainer) + lookup_ds = ((TypeContainer) lookup_ds).Parent; + else + break; + } while (lookup_ds != null); + + if (e == null && ec.ContainerType != null) + e = MemberLookup (ec, ec.ContainerType, Name, loc); + + if (e == null) + return DoResolveType (ec); + + if (e is TypeExpr) + return e; + + if (e is IMemberExpr) { + e = MemberAccess.ResolveMemberAccess (ec, e, null, loc, this); + if (e == null) + return null; + + IMemberExpr me = e as IMemberExpr; + if (me == null) + return e; + + // This fails if ResolveMemberAccess() was unable to decide whether + // it's a field or a type of the same name. + if (!me.IsStatic && (me.InstanceExpression == null)) + return e; + +/* FIXME If this is not commented out, it seems that it's not possible to reach class members in mBas. + Maybe a grammar-related problem? + + if (!me.IsStatic && + TypeManager.IsNestedChildOf (me.InstanceExpression.Type, me.DeclaringType)) { + Error (38, "Cannot access nonstatic member `" + me.Name + "' of " + + "outer type `" + me.DeclaringType + "' via nested type `" + + me.InstanceExpression.Type + "'"); + return null; + } +*/ + if (right_side != null) + e = e.DoResolveLValue (ec, right_side); + else + e = e.DoResolve (ec); + + return e; + } + + if (ec.IsStatic || ec.IsFieldInitializer){ + if (allow_static) + return e; + + return MemberStaticCheck (ec, e); + } else + return e; + } + + public override void Emit (EmitContext ec) + { + // + // If this is ever reached, then we failed to + // find the name as a namespace + // + + Error (103, "The name `" + Name + + "' does not exist in the class `" + + ec.DeclSpace.Name + "'"); + } + + public override string ToString () + { + return Name; + } + } + + /// <summary> + /// Fully resolved expression that evaluates to a type + /// </summary> + public class TypeExpr : Expression, ITypeExpression { + public TypeExpr (Type t, Location l) + { + Type = t; + eclass = ExprClass.Type; + loc = l; + } + + public virtual Expression DoResolveType (EmitContext ec) + { + return this; + } + + override public Expression DoResolve (EmitContext ec) + { + return this; + } + + override public void Emit (EmitContext ec) + { + throw new Exception ("Should never be called"); + } + + public override string ToString () + { + return Type.ToString (); + } + } + + /// <summary> + /// Used to create types from a fully qualified name. These are just used + /// by the parser to setup the core types. A TypeLookupExpression is always + /// classified as a type. + /// </summary> + public class TypeLookupExpression : TypeExpr { + string name; + + public TypeLookupExpression (string name) : base (null, Location.Null) + { + this.name = name; + } + + public override Expression DoResolveType (EmitContext ec) + { + if (type == null) + type = RootContext.LookupType (ec.DeclSpace, name, false, Location.Null); + return this; + } + + public override Expression DoResolve (EmitContext ec) + { + return DoResolveType (ec); + } + + public override void Emit (EmitContext ec) + { + throw new Exception ("Should never be called"); + } + + public override string ToString () + { + return name; + } + } + + /// <summary> + /// MethodGroup Expression. + /// + /// This is a fully resolved expression that evaluates to a type + /// </summary> + public class MethodGroupExpr : Expression, IMemberExpr { + public MethodBase [] Methods; + Expression instance_expression = null; + bool is_explicit_impl = false; + + public MethodGroupExpr (MemberInfo [] mi, Location l) + { + Methods = new MethodBase [mi.Length]; + mi.CopyTo (Methods, 0); + eclass = ExprClass.MethodGroup; + type = TypeManager.object_type; + loc = l; + } + + public MethodGroupExpr (ArrayList list, Location l) + { + Methods = new MethodBase [list.Count]; + + try { + list.CopyTo (Methods, 0); + } catch { + foreach (MemberInfo m in list){ + if (!(m is MethodBase)){ + Console.WriteLine ("Name " + m.Name); + Console.WriteLine ("Found a: " + m.GetType ().FullName); + } + } + throw; + } + loc = l; + eclass = ExprClass.MethodGroup; + type = TypeManager.object_type; + } + + public Type DeclaringType { + get { + return Methods [0].DeclaringType; + } + } + + // + // `A method group may have associated an instance expression' + // + public Expression InstanceExpression { + get { + return instance_expression; + } + + set { + instance_expression = value; + } + } + + public bool IsExplicitImpl { + get { + return is_explicit_impl; + } + + set { + is_explicit_impl = value; + } + } + + public string Name { + get { + return Methods [0].Name; + } + } + + public bool IsInstance { + get { + foreach (MethodBase mb in Methods) + if (!mb.IsStatic) + return true; + + return false; + } + } + + public bool IsStatic { + get { + foreach (MethodBase mb in Methods) + if (mb.IsStatic) + return true; + + return false; + } + } + + override public Expression DoResolve (EmitContext ec) + { + if (instance_expression != null) { + instance_expression = instance_expression.DoResolve (ec); + if (instance_expression == null) + return null; + } + + return this; + } + + public void ReportUsageError () + { + Report.Error (654, loc, "Method `" + Methods [0].DeclaringType + "." + + Methods [0].Name + "()' is referenced without parentheses"); + } + + override public void Emit (EmitContext ec) + { + ReportUsageError (); + } + + bool RemoveMethods (bool keep_static) + { + ArrayList smethods = new ArrayList (); + + foreach (MethodBase mb in Methods){ + if (mb.IsStatic == keep_static) + smethods.Add (mb); + } + + if (smethods.Count == 0) + return false; + + Methods = new MethodBase [smethods.Count]; + smethods.CopyTo (Methods, 0); + + return true; + } + + /// <summary> + /// Removes any instance methods from the MethodGroup, returns + /// false if the resulting set is empty. + /// </summary> + public bool RemoveInstanceMethods () + { + return RemoveMethods (true); + } + + /// <summary> + /// Removes any static methods from the MethodGroup, returns + /// false if the resulting set is empty. + /// </summary> + public bool RemoveStaticMethods () + { + return RemoveMethods (false); + } + } + + /// <summary> + /// Fully resolved expression that evaluates to a Field + /// </summary> + public class FieldExpr : Expression, IAssignMethod, IMemoryLocation, IMemberExpr { + public readonly FieldInfo FieldInfo; + Expression instance_expr; + + public FieldExpr (FieldInfo fi, Location l) + { + FieldInfo = fi; + eclass = ExprClass.Variable; + type = fi.FieldType; + loc = l; + } + + public string Name { + get { + return FieldInfo.Name; + } + } + + public bool IsInstance { + get { + return !FieldInfo.IsStatic; + } + } + + public bool IsStatic { + get { + return FieldInfo.IsStatic; + } + } + + public Type DeclaringType { + get { + return FieldInfo.DeclaringType; + } + } + + public Expression InstanceExpression { + get { + return instance_expr; + } + + set { + instance_expr = value; + } + } + + override public Expression DoResolve (EmitContext ec) + { + if (!FieldInfo.IsStatic){ + if (instance_expr == null){ + throw new Exception ("non-static FieldExpr without instance var\n" + + "You have to assign the Instance variable\n" + + "Of the FieldExpr to set this\n"); + } + + // Resolve the field's instance expression while flow analysis is turned + // off: when accessing a field "a.b", we must check whether the field + // "a.b" is initialized, not whether the whole struct "a" is initialized. + instance_expr = instance_expr.Resolve (ec, ResolveFlags.VariableOrValue | + ResolveFlags.DisableFlowAnalysis); + if (instance_expr == null) + return null; + } + + // If the instance expression is a local variable or parameter. + IVariable var = instance_expr as IVariable; + if ((var != null) && !var.IsFieldAssigned (ec, FieldInfo.Name, loc)) + return null; + + return this; + } + + void Report_AssignToReadonly (bool is_instance) + { + string msg; + + if (is_instance) + msg = "Readonly field can not be assigned outside " + + "of constructor or variable initializer"; + else + msg = "A static readonly field can only be assigned in " + + "a static constructor"; + + Report.Error (is_instance ? 191 : 198, loc, msg); + } + + override public Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + IVariable var = instance_expr as IVariable; + if (var != null) + var.SetFieldAssigned (ec, FieldInfo.Name); + + Expression e = DoResolve (ec); + + if (e == null) + return null; + + if (!FieldInfo.IsInitOnly) + return this; + + // + // InitOnly fields can only be assigned in constructors + // + + if (ec.IsConstructor) + return this; + + Report_AssignToReadonly (true); + + return null; + } + + override public void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + bool is_volatile = false; + + if (FieldInfo is FieldBuilder){ + FieldBase f = TypeManager.GetField (FieldInfo); + + if ((f.ModFlags & Modifiers.VOLATILE) != 0) + is_volatile = true; + + f.status |= Field.Status.USED; + } + + if (FieldInfo.IsStatic){ + if (is_volatile) + ig.Emit (OpCodes.Volatile); + + ig.Emit (OpCodes.Ldsfld, FieldInfo); + } else { + if (instance_expr.Type.IsValueType){ + IMemoryLocation ml; + LocalTemporary tempo = null; + + if (!(instance_expr is IMemoryLocation)){ + tempo = new LocalTemporary ( + ec, instance_expr.Type); + + InstanceExpression.Emit (ec); + tempo.Store (ec); + ml = tempo; + } else + ml = (IMemoryLocation) instance_expr; + + ml.AddressOf (ec, AddressOp.Load); + } else + instance_expr.Emit (ec); + + if (is_volatile) + ig.Emit (OpCodes.Volatile); + + ig.Emit (OpCodes.Ldfld, FieldInfo); + } + } + + public void EmitAssign (EmitContext ec, Expression source) + { + FieldAttributes fa = FieldInfo.Attributes; + bool is_static = (fa & FieldAttributes.Static) != 0; + bool is_readonly = (fa & FieldAttributes.InitOnly) != 0; + ILGenerator ig = ec.ig; + + if (is_readonly && !ec.IsConstructor){ + Report_AssignToReadonly (!is_static); + return; + } + + if (!is_static){ + Expression instance = instance_expr; + + if (instance.Type.IsValueType){ + if (instance is IMemoryLocation){ + IMemoryLocation ml = (IMemoryLocation) instance; + + ml.AddressOf (ec, AddressOp.Store); + } else + throw new Exception ("The " + instance + " of type " + + instance.Type + + " represents a ValueType and does " + + "not implement IMemoryLocation"); + } else + instance.Emit (ec); + } + source.Emit (ec); + + if (FieldInfo is FieldBuilder){ + FieldBase f = TypeManager.GetField (FieldInfo); + + if ((f.ModFlags & Modifiers.VOLATILE) != 0) + ig.Emit (OpCodes.Volatile); + } + + if (is_static) + ig.Emit (OpCodes.Stsfld, FieldInfo); + else + ig.Emit (OpCodes.Stfld, FieldInfo); + + if (FieldInfo is FieldBuilder){ + FieldBase f = TypeManager.GetField (FieldInfo); + + f.status |= Field.Status.ASSIGNED; + } + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + ILGenerator ig = ec.ig; + + if (FieldInfo is FieldBuilder){ + FieldBase f = TypeManager.GetField (FieldInfo); + if ((f.ModFlags & Modifiers.VOLATILE) != 0) + ig.Emit (OpCodes.Volatile); + } + + if (FieldInfo is FieldBuilder){ + FieldBase f = TypeManager.GetField (FieldInfo); + + if ((mode & AddressOp.Store) != 0) + f.status |= Field.Status.ASSIGNED; + if ((mode & AddressOp.Load) != 0) + f.status |= Field.Status.USED; + } + + // + // Handle initonly fields specially: make a copy and then + // get the address of the copy. + // + if (FieldInfo.IsInitOnly && !ec.IsConstructor){ + LocalBuilder local; + + Emit (ec); + local = ig.DeclareLocal (type); + ig.Emit (OpCodes.Stloc, local); + ig.Emit (OpCodes.Ldloca, local); + return; + } + + if (FieldInfo.IsStatic) + ig.Emit (OpCodes.Ldsflda, FieldInfo); + else { + if (instance_expr is IMemoryLocation) + ((IMemoryLocation)instance_expr).AddressOf (ec, AddressOp.LoadStore); + else + instance_expr.Emit (ec); + ig.Emit (OpCodes.Ldflda, FieldInfo); + } + } + } + + /// <summary> + /// Expression that evaluates to a Property. The Assign class + /// might set the `Value' expression if we are in an assignment. + /// + /// This is not an LValue because we need to re-write the expression, we + /// can not take data from the stack and store it. + /// </summary> + public class PropertyExpr : ExpressionStatement, IAssignMethod, IMemberExpr { + public readonly PropertyInfo PropertyInfo; + public bool IsBase; + MethodInfo getter, setter; + bool is_static; + public ArrayList PropertyArgs; + + Expression instance_expr; + + public PropertyExpr (EmitContext ec, PropertyInfo pi, Location l) + { + PropertyInfo = pi; + eclass = ExprClass.PropertyAccess; + PropertyArgs = new ArrayList(); + is_static = false; + loc = l; + + type = TypeManager.TypeToCoreType (pi.PropertyType); + + ResolveAccessors (ec); + } + + public string Name { + get { + return PropertyInfo.Name; + } + } + + public bool IsInstance { + get { + return !is_static; + } + } + + public bool IsStatic { + get { + return is_static; + } + } + + public Type DeclaringType { + get { + return PropertyInfo.DeclaringType; + } + } + + // + // The instance expression associated with this expression + // + public Expression InstanceExpression { + set { + instance_expr = value; + } + + get { + return instance_expr; + } + } + + public bool VerifyAssignable () + { + if (!PropertyInfo.CanWrite){ + Report.Error (200, loc, + "The property `" + PropertyInfo.Name + + "' can not be assigned to, as it has not set accessor"); + return false; + } + + return true; + } + + void ResolveAccessors (EmitContext ec) + { + BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; + MemberInfo [] group; + + group = TypeManager.MemberLookup (ec.ContainerType, PropertyInfo.DeclaringType, + MemberTypes.Method, flags, "get_" + PropertyInfo.Name); + + // + // The first method is the closest to us + // + if (group != null && group.Length > 0){ + getter = (MethodInfo) group [0]; + + if (getter.IsStatic) + is_static = true; + } + + // + // The first method is the closest to us + // + group = TypeManager.MemberLookup (ec.ContainerType, PropertyInfo.DeclaringType, + MemberTypes.Method, flags, "set_" + PropertyInfo.Name); + if (group != null && group.Length > 0){ + setter = (MethodInfo) group [0]; + if (setter.IsStatic) + is_static = true; + } + } + + override public Expression DoResolve (EmitContext ec) + { + if (getter == null){ + Report.Error (154, loc, + "The property `" + PropertyInfo.Name + + "' can not be used in " + + "this context because it lacks a get accessor"); + return null; + } + + if ((instance_expr == null) && ec.IsStatic && !is_static) { + SimpleName.Error_ObjectRefRequired (ec, loc, PropertyInfo.Name); + return null; + } + + if (instance_expr != null) { + instance_expr = instance_expr.DoResolve (ec); + if (instance_expr == null) + return null; + } + + return this; + } + + override public Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + if (setter == null){ + Report.Error (154, loc, + "The property `" + PropertyInfo.Name + + "' can not be used in " + + "this context because it lacks a set accessor"); + return null; + } + + if (instance_expr != null) { + instance_expr = instance_expr.DoResolve (ec); + if (instance_expr == null) + return null; + } + + return this; + } + + override public void Emit (EmitContext ec) + { + // + // Special case: length of single dimension array property is turned into ldlen + // + if ((getter == TypeManager.system_int_array_get_length) || + (getter == TypeManager.int_array_get_length)){ + Type iet = instance_expr.Type; + + // + // System.Array.Length can be called, but the Type does not + // support invoking GetArrayRank, so test for that case first + // + if (iet != TypeManager.array_type && (iet.GetArrayRank () == 1)){ + instance_expr.Emit (ec); + ec.ig.Emit (OpCodes.Ldlen); + return; + } + } + Invocation.EmitCall (ec, IsBase, IsStatic, instance_expr, getter, null, PropertyArgs, loc); + } + + // + // Implements the IAssignMethod interface for assignments + // + public void EmitAssign (EmitContext ec, Expression source) + { + Argument arg = new Argument (source, Argument.AType.Expression); + ArrayList args = new ArrayList (); +//HERE + args.Add (arg); + Invocation.EmitCall (ec, IsBase, IsStatic, instance_expr, setter, args, PropertyArgs,loc); + } + + override public void EmitStatement (EmitContext ec) + { + Emit (ec); + ec.ig.Emit (OpCodes.Pop); + } + } + + /// <summary> + /// Fully resolved expression that evaluates to an Event + /// </summary> + public class EventExpr : Expression, IMemberExpr { + public readonly EventInfo EventInfo; + public Expression instance_expr; + + bool is_static; + MethodInfo add_accessor, remove_accessor; + + public EventExpr (EventInfo ei, Location loc) + { + EventInfo = ei; + this.loc = loc; + eclass = ExprClass.EventAccess; + + add_accessor = TypeManager.GetAddMethod (ei); + remove_accessor = TypeManager.GetRemoveMethod (ei); + + if (add_accessor.IsStatic || remove_accessor.IsStatic) + is_static = true; + + if (EventInfo is MyEventBuilder) + type = ((MyEventBuilder) EventInfo).EventType; + else + type = EventInfo.EventHandlerType; + } + + public string Name { + get { + return EventInfo.Name; + } + } + + public bool IsInstance { + get { + return !is_static; + } + } + + public bool IsStatic { + get { + return is_static; + } + } + + public Type DeclaringType { + get { + return EventInfo.DeclaringType; + } + } + + public Expression InstanceExpression { + get { + return instance_expr; + } + + set { + instance_expr = value; + } + } + + public override Expression DoResolve (EmitContext ec) + { + if (instance_expr != null) { + instance_expr = instance_expr.DoResolve (ec); + if (instance_expr == null) + return null; + } + + return this; + } + + public override void Emit (EmitContext ec) + { + Report.Error (70, loc, "The event `" + Name + "' can only appear on the left hand side of += or -= (except on the defining type)"); + } + + public void EmitAddOrRemove (EmitContext ec, Expression source) + { + Expression handler = ((Binary) source).Right; + + Argument arg = new Argument (handler, Argument.AType.Expression); + ArrayList args = new ArrayList (); + + args.Add (arg); + + if (((Binary) source).Oper == Binary.Operator.Addition) + Invocation.EmitCall ( + ec, false, IsStatic, instance_expr, add_accessor, args, loc); + else + Invocation.EmitCall ( + ec, false, IsStatic, instance_expr, remove_accessor, args, loc); + } + } +} diff --git a/mcs/mbas/enum.cs b/mcs/mbas/enum.cs new file mode 100644 index 00000000000..691fcbe6b12 --- /dev/null +++ b/mcs/mbas/enum.cs @@ -0,0 +1,613 @@ +// +// enum.cs: Enum handling. +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Ravi Pratap (ravi@ximian.com) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +namespace Mono.CSharp { + + /// <summary> + /// Enumeration container + /// </summary> + public class Enum : DeclSpace { + ArrayList ordered_enums; + + public Expression BaseType; + public Attributes OptAttributes; + + public Type UnderlyingType; + + Hashtable member_to_location; + Hashtable member_to_attributes; + + // + // This is for members that have been defined + // + Hashtable member_to_value; + + // + // This is used to mark members we're currently defining + // + Hashtable in_transit; + + ArrayList field_builders; + + public const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE; + + public Enum (TypeContainer parent, Expression type, int mod_flags, string name, Attributes attrs, Location l) + : base (parent, name, l) + { + this.BaseType = type; + ModFlags = Modifiers.Check (AllowedModifiers, mod_flags, + IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE, l); + OptAttributes = attrs; + + ordered_enums = new ArrayList (); + member_to_location = new Hashtable (); + member_to_value = new Hashtable (); + in_transit = new Hashtable (); + field_builders = new ArrayList (); + } + + /// <summary> + /// Adds @name to the enumeration space, with @expr + /// being its definition. + /// </summary> + public AdditionResult AddEnumMember (string name, Expression expr, Location loc, + Attributes opt_attrs) + { + if (defined_names.Contains (name)) + return AdditionResult.NameExists; + + DefineName (name, expr); + + ordered_enums.Add (name); + member_to_location.Add (name, loc); + + if (member_to_attributes == null) + member_to_attributes = new Hashtable (); + + member_to_attributes.Add (name, opt_attrs); + + return AdditionResult.Success; + } + + // + // This is used by corlib compilation: we map from our + // type to a type that is consumable by the DefineField + // + Type MapToInternalType (Type t) + { + if (t == TypeManager.int32_type) + return typeof (int); + if (t == TypeManager.int64_type) + return typeof (long); + if (t == TypeManager.uint32_type) + return typeof (uint); + if (t == TypeManager.uint64_type) + return typeof (ulong); + if (t == TypeManager.float_type) + return typeof (float); + if (t == TypeManager.double_type) + return typeof (double); + if (t == TypeManager.byte_type) + return typeof (byte); + if (t == TypeManager.sbyte_type) + return typeof (sbyte); + if (t == TypeManager.char_type) + return typeof (char); + if (t == TypeManager.short_type) + return typeof (short); + if (t == TypeManager.ushort_type) + return typeof (ushort); + + throw new Exception (); + } + + public override TypeBuilder DefineType () + { + if (TypeBuilder != null) + return TypeBuilder; + + TypeAttributes attr = Modifiers.TypeAttr (ModFlags, IsTopLevel); + + attr |= TypeAttributes.Class | TypeAttributes.Sealed; + + UnderlyingType = ResolveType (BaseType, false, Location); + + if (UnderlyingType != TypeManager.int32_type && + UnderlyingType != TypeManager.uint32_type && + UnderlyingType != TypeManager.int64_type && + UnderlyingType != TypeManager.uint64_type && + UnderlyingType != TypeManager.short_type && + UnderlyingType != TypeManager.ushort_type && + UnderlyingType != TypeManager.byte_type && + UnderlyingType != TypeManager.sbyte_type) { + Report.Error (1008, Location, + "Type byte, sbyte, short, ushort, int, uint, " + + "long, or ulong expected (got: " + + TypeManager.CSharpName (UnderlyingType) + ")"); + return null; + } + + if (IsTopLevel) { + ModuleBuilder builder = CodeGen.ModuleBuilder; + + TypeBuilder = builder.DefineType (Name, attr, TypeManager.enum_type); + } else { + TypeBuilder builder = Parent.TypeBuilder; + + TypeBuilder = builder.DefineNestedType ( + Basename, attr, TypeManager.enum_type); + } + + // + // Call MapToInternalType for corlib + // + TypeBuilder.DefineField ("value__", UnderlyingType, + FieldAttributes.Public | FieldAttributes.SpecialName + | FieldAttributes.RTSpecialName); + + TypeManager.AddEnumType (Name, TypeBuilder, this); + + return TypeBuilder; + } + + bool IsValidEnumConstant (Expression e) + { + if (!(e is Constant)) + return false; + + if (e is IntConstant || e is UIntConstant || e is LongConstant || + e is ByteConstant || e is SByteConstant || e is ShortConstant || + e is UShortConstant || e is ULongConstant || e is EnumConstant) + return true; + else + return false; + } + + object GetNextDefaultValue (object default_value) + { + if (UnderlyingType == TypeManager.int32_type) { + int i = (int) default_value; + + if (i < System.Int32.MaxValue) + return ++i; + else + return null; + } else if (UnderlyingType == TypeManager.uint32_type) { + uint i = (uint) default_value; + + if (i < System.UInt32.MaxValue) + return ++i; + else + return null; + } else if (UnderlyingType == TypeManager.int64_type) { + long i = (long) default_value; + + if (i < System.Int64.MaxValue) + return ++i; + else + return null; + } else if (UnderlyingType == TypeManager.uint64_type) { + ulong i = (ulong) default_value; + + if (i < System.UInt64.MaxValue) + return ++i; + else + return null; + } else if (UnderlyingType == TypeManager.short_type) { + short i = (short) default_value; + + if (i < System.Int16.MaxValue) + return ++i; + else + return null; + } else if (UnderlyingType == TypeManager.ushort_type) { + ushort i = (ushort) default_value; + + if (i < System.UInt16.MaxValue) + return ++i; + else + return null; + } else if (UnderlyingType == TypeManager.byte_type) { + byte i = (byte) default_value; + + if (i < System.Byte.MaxValue) + return ++i; + else + return null; + } else if (UnderlyingType == TypeManager.sbyte_type) { + sbyte i = (sbyte) default_value; + + if (i < System.SByte.MaxValue) + return ++i; + else + return null; + } + + return null; + } + + void Error_ConstantValueCannotBeConverted (object val, Location loc) + { + if (val is Constant) + Report.Error (31, loc, "Constant value '" + ((Constant) val).AsString () + + "' cannot be converted" + + " to a " + TypeManager.CSharpName (UnderlyingType)); + else + Report.Error (31, loc, "Constant value '" + val + + "' cannot be converted" + + " to a " + TypeManager.CSharpName (UnderlyingType)); + return; + } + + /// <summary> + /// Determines if a standard implicit conversion exists from + /// expr_type to target_type + /// </summary> + public static bool ImplicitConversionExists (Type expr_type, Type target_type) + { + expr_type = TypeManager.TypeToCoreType (expr_type); + + if (expr_type == TypeManager.void_type) + return false; + + if (expr_type == target_type) + return true; + + // First numeric conversions + + if (expr_type == TypeManager.sbyte_type){ + // + // From sbyte to short, int, long, float, double. + // + if ((target_type == TypeManager.int32_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.short_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.byte_type){ + // + // From byte to short, ushort, int, uint, long, ulong, float, double + // + if ((target_type == TypeManager.short_type) || + (target_type == TypeManager.ushort_type) || + (target_type == TypeManager.int32_type) || + (target_type == TypeManager.uint32_type) || + (target_type == TypeManager.uint64_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.short_type){ + // + // From short to int, long, float, double + // + if ((target_type == TypeManager.int32_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.ushort_type){ + // + // From ushort to int, uint, long, ulong, float, double + // + if ((target_type == TypeManager.uint32_type) || + (target_type == TypeManager.uint64_type) || + (target_type == TypeManager.int32_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.int32_type){ + // + // From int to long, float, double + // + if ((target_type == TypeManager.int64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.uint32_type){ + // + // From uint to long, ulong, float, double + // + if ((target_type == TypeManager.int64_type) || + (target_type == TypeManager.uint64_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if ((expr_type == TypeManager.uint64_type) || + (expr_type == TypeManager.int64_type)) { + // + // From long/ulong to float, double + // + if ((target_type == TypeManager.double_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.char_type){ + // + // From char to ushort, int, uint, long, ulong, float, double + // + if ((target_type == TypeManager.ushort_type) || + (target_type == TypeManager.int32_type) || + (target_type == TypeManager.uint32_type) || + (target_type == TypeManager.uint64_type) || + (target_type == TypeManager.int64_type) || + (target_type == TypeManager.float_type) || + (target_type == TypeManager.double_type) || + (target_type == TypeManager.decimal_type)) + return true; + + } else if (expr_type == TypeManager.float_type){ + // + // float to double + // + if (target_type == TypeManager.double_type) + return true; + } + + return false; + } + + /// <summary> + /// This is used to lookup the value of an enum member. If the member is undefined, + /// it attempts to define it and return its value + /// </summary> + public object LookupEnumValue (EmitContext ec, string name, Location loc) + { + object default_value = null; + Constant c = null; + + default_value = member_to_value [name]; + + if (default_value != null) + return default_value; + + // + // This may happen if we're calling a method in System.Enum, for instance + // Enum.IsDefined(). + // + if (!defined_names.Contains (name)) + return null; + + if (in_transit.Contains (name)) { + Report.Error (110, loc, "The evaluation of the constant value for `" + + Name + "." + name + "' involves a circular definition."); + return null; + } + + // + // So if the above doesn't happen, we have a member that is undefined + // We now proceed to define it + // + Expression val = this [name]; + + if (val == null) { + + int idx = ordered_enums.IndexOf (name); + + if (idx == 0) + default_value = 0; + else { + for (int i = 0; i < idx; ++i) { + string n = (string) ordered_enums [i]; + Location m_loc = (Mono.CSharp.Location) + member_to_location [n]; + in_transit.Add (name, true); + default_value = LookupEnumValue (ec, n, m_loc); + in_transit.Remove (name); + if (default_value == null) + return null; + } + + default_value = GetNextDefaultValue (default_value); + } + + } else { + bool old = ec.InEnumContext; + ec.InEnumContext = true; + in_transit.Add (name, true); + val = val.Resolve (ec); + in_transit.Remove (name); + ec.InEnumContext = old; + + if (val == null) + return null; + + if (!IsValidEnumConstant (val)) { + Report.Error ( + 1008, loc, + "Type byte, sbyte, short, ushort, int, uint, long, or " + + "ulong expected (have: " + val + ")"); + return null; + } + + c = (Constant) val; + default_value = c.GetValue (); + + if (default_value == null) { + Error_ConstantValueCannotBeConverted (c, loc); + return null; + } + + if (val is EnumConstant){ + Type etype = TypeManager.EnumToUnderlying (c.Type); + + if (!ImplicitConversionExists (etype, UnderlyingType)){ + Expression.Error_CannotConvertImplicit ( + loc, c.Type, UnderlyingType); + return null; + } + } + } + + FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static + | FieldAttributes.Literal; + + FieldBuilder fb = TypeBuilder.DefineField (name, UnderlyingType, attr); + + try { + default_value = TypeManager.ChangeType (default_value, UnderlyingType); + } catch { + Error_ConstantValueCannotBeConverted (c, loc); + return null; + } + + fb.SetConstant (default_value); + field_builders.Add (fb); + member_to_value [name] = default_value; + + if (!TypeManager.RegisterFieldValue (fb, default_value)) + return null; + + // + // Now apply attributes + // + Attribute.ApplyAttributes (ec, fb, fb, (Attributes) member_to_attributes [name], loc); + + return default_value; + } + + public override bool DefineMembers (TypeContainer parent) + { + return true; + } + + public override bool Define (TypeContainer parent) + { + // + // If there was an error during DefineEnum, return + // + if (TypeBuilder == null) + return false; + + EmitContext ec = new EmitContext (parent, this, Location, null, + UnderlyingType, ModFlags, false); + + object default_value = 0; + + FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static + | FieldAttributes.Literal; + + + foreach (string name in ordered_enums) { + // + // Have we already been defined, thanks to some cross-referencing ? + // + if (member_to_value.Contains (name)) + continue; + + Location loc = (Mono.CSharp.Location) member_to_location [name]; + + if (this [name] != null) { + default_value = LookupEnumValue (ec, name, loc); + + if (default_value == null) + return true; + + } else { + FieldBuilder fb = TypeBuilder.DefineField ( + name, UnderlyingType, attr); + + if (default_value == null) { + Report.Error (543, loc, "Enumerator value for '" + name + "' is too large to " + + "fit in its type"); + return false; + } + + try { + default_value = TypeManager.ChangeType (default_value, UnderlyingType); + } catch { + Error_ConstantValueCannotBeConverted (default_value, loc); + return false; + } + + fb.SetConstant (default_value); + field_builders.Add (fb); + member_to_value [name] = default_value; + + if (!TypeManager.RegisterFieldValue (fb, default_value)) + return false; + + // + // Apply attributes on the enum member + // + Attribute.ApplyAttributes (ec, fb, fb, (Attributes) member_to_attributes [name], loc); + } + + default_value = GetNextDefaultValue (default_value); + } + + Attribute.ApplyAttributes (ec, TypeBuilder, this, OptAttributes, Location); + + return true; + } + + // + // IMemberFinder + // + public override MemberList FindMembers (MemberTypes mt, BindingFlags bf, + MemberFilter filter, object criteria) + { + ArrayList members = new ArrayList (); + + if ((mt & MemberTypes.Field) != 0) { + foreach (FieldBuilder fb in field_builders) + if (filter (fb, criteria) == true) + members.Add (fb); + } + + return new MemberList (members); + } + + public override MemberCache MemberCache { + get { + return null; + } + } + + public ArrayList ValueNames { + get { + return ordered_enums; + } + } + + // indexer + public Expression this [string name] { + get { + return (Expression) defined_names [name]; + } + } + } +} diff --git a/mcs/mbas/expression.cs b/mcs/mbas/expression.cs new file mode 100644 index 00000000000..f0097687fae --- /dev/null +++ b/mcs/mbas/expression.cs @@ -0,0 +1,7119 @@ +// +// expression.cs: Expression representation for the IL tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. +// +// +#define USE_OLD + +namespace Mono.CSharp { + using System; + using System.Collections; + using System.Reflection; + using System.Reflection.Emit; + using System.Text; + + /// <summary> + /// This is just a helper class, it is generated by Unary, UnaryMutator + /// when an overloaded method has been found. It just emits the code for a + /// static call. + /// </summary> + public class StaticCallExpr : ExpressionStatement { + ArrayList args; + MethodInfo mi; + + StaticCallExpr (MethodInfo m, ArrayList a, Location l) + { + mi = m; + args = a; + + type = m.ReturnType; + eclass = ExprClass.Value; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + // + // We are born fully resolved + // + return this; + } + + public override void Emit (EmitContext ec) + { + if (args != null) + Invocation.EmitArguments (ec, mi, args); + + ec.ig.Emit (OpCodes.Call, mi); + return; + } + + static public Expression MakeSimpleCall (EmitContext ec, MethodGroupExpr mg, + Expression e, Location loc) + { + ArrayList args; + MethodBase method; + + args = new ArrayList (1); + args.Add (new Argument (e, Argument.AType.Expression)); + method = Invocation.OverloadResolve (ec, (MethodGroupExpr) mg, args, loc); + + if (method == null) + return null; + + return new StaticCallExpr ((MethodInfo) method, args, loc); + } + + public override void EmitStatement (EmitContext ec) + { + Emit (ec); + if (TypeManager.TypeToCoreType (type) != TypeManager.void_type) + ec.ig.Emit (OpCodes.Pop); + } + } + + /// <summary> + /// Unary expressions. + /// </summary> + /// + /// <remarks> + /// Unary implements unary expressions. It derives from + /// ExpressionStatement becuase the pre/post increment/decrement + /// operators can be used in a statement context. + /// </remarks> + public class Unary : Expression { + public enum Operator : byte { + UnaryPlus, UnaryNegation, LogicalNot, OnesComplement, + Indirection, AddressOf, TOP + } + + public Operator Oper; + public Expression Expr; + + public Unary (Operator op, Expression expr, Location loc) + { + this.Oper = op; + this.Expr = expr; + this.loc = loc; + } + + /// <summary> + /// Returns a stringified representation of the Operator + /// </summary> + static public string OperName (Operator oper) + { + switch (oper){ + case Operator.UnaryPlus: + return "+"; + case Operator.UnaryNegation: + return "-"; + case Operator.LogicalNot: + return "!"; + case Operator.OnesComplement: + return "~"; + case Operator.AddressOf: + return "&"; + case Operator.Indirection: + return "*"; + } + + return oper.ToString (); + } + + static string [] oper_names; + + static Unary () + { + oper_names = new string [(int)Operator.TOP]; + + oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus"; + oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation"; + oper_names [(int) Operator.LogicalNot] = "op_LogicalNot"; + oper_names [(int) Operator.OnesComplement] = "op_OnesComplement"; + oper_names [(int) Operator.Indirection] = "op_Indirection"; + oper_names [(int) Operator.AddressOf] = "op_AddressOf"; + } + + void Error23 (Type t) + { + Error ( + 23, "Operator " + OperName (Oper) + + " cannot be applied to operand of type `" + + TypeManager.CSharpName (t) + "'"); + } + + /// <remarks> + /// The result has been already resolved: + /// + /// FIXME: a minus constant -128 sbyte cant be turned into a + /// constant byte. + /// </remarks> + static Expression TryReduceNegative (Constant expr) + { + Expression e = null; + + if (expr is IntConstant) + e = new IntConstant (-((IntConstant) expr).Value); + else if (expr is UIntConstant){ + uint value = ((UIntConstant) expr).Value; + + if (value < 2147483649) + return new IntConstant (-(int)value); + else + e = new LongConstant (value); + } + else if (expr is LongConstant) + e = new LongConstant (-((LongConstant) expr).Value); + else if (expr is ULongConstant){ + ulong value = ((ULongConstant) expr).Value; + + if (value < 9223372036854775809) + return new LongConstant(-(long)value); + } + else if (expr is FloatConstant) + e = new FloatConstant (-((FloatConstant) expr).Value); + else if (expr is DoubleConstant) + e = new DoubleConstant (-((DoubleConstant) expr).Value); + else if (expr is DecimalConstant) + e = new DecimalConstant (-((DecimalConstant) expr).Value); + else if (expr is ShortConstant) + e = new IntConstant (-((ShortConstant) expr).Value); + else if (expr is UShortConstant) + e = new IntConstant (-((UShortConstant) expr).Value); + return e; + } + + // <summary> + // This routine will attempt to simplify the unary expression when the + // argument is a constant. The result is returned in `result' and the + // function returns true or false depending on whether a reduction + // was performed or not + // </summary> + bool Reduce (EmitContext ec, Constant e, out Expression result) + { + Type expr_type = e.Type; + + switch (Oper){ + case Operator.UnaryPlus: + result = e; + return true; + + case Operator.UnaryNegation: + result = TryReduceNegative (e); + return true; + + case Operator.LogicalNot: + if (expr_type != TypeManager.bool_type) { + result = null; + Error23 (expr_type); + return false; + } + + BoolConstant b = (BoolConstant) e; + result = new BoolConstant (!(b.Value)); + return true; + + case Operator.OnesComplement: + if (!((expr_type == TypeManager.int32_type) || + (expr_type == TypeManager.uint32_type) || + (expr_type == TypeManager.int64_type) || + (expr_type == TypeManager.uint64_type) || + (expr_type.IsSubclassOf (TypeManager.enum_type)))){ + result = null; + Error23 (expr_type); + return false; + } + + if (e is EnumConstant){ + EnumConstant enum_constant = (EnumConstant) e; + Expression reduced; + + if (Reduce (ec, enum_constant.Child, out reduced)){ + result = new EnumConstant ((Constant) reduced, enum_constant.Type); + return true; + } else { + result = null; + return false; + } + } + + if (expr_type == TypeManager.int32_type){ + result = new IntConstant (~ ((IntConstant) e).Value); + } else if (expr_type == TypeManager.uint32_type){ + result = new UIntConstant (~ ((UIntConstant) e).Value); + } else if (expr_type == TypeManager.int64_type){ + result = new LongConstant (~ ((LongConstant) e).Value); + } else if (expr_type == TypeManager.uint64_type){ + result = new ULongConstant (~ ((ULongConstant) e).Value); + } else { + result = null; + Error23 (expr_type); + return false; + } + return true; + + case Operator.AddressOf: + result = this; + return false; + + case Operator.Indirection: + result = this; + return false; + } + throw new Exception ("Can not constant fold: " + Oper.ToString()); + } + + Expression ResolveOperator (EmitContext ec) + { + Type expr_type = Expr.Type; + + // + // Step 1: Perform Operator Overload location + // + Expression mg; + string op_name; + + op_name = oper_names [(int) Oper]; + + mg = MemberLookup (ec, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc); + + if (mg != null) { + Expression e = StaticCallExpr.MakeSimpleCall ( + ec, (MethodGroupExpr) mg, Expr, loc); + + if (e == null){ + Error23 (expr_type); + return null; + } + + return e; + } + + // Only perform numeric promotions on: + // +, - + + if (expr_type == null) + return null; + + // + // Step 2: Default operations on CLI native types. + // + + // Attempt to use a constant folding operation. + if (Expr is Constant){ + Expression result; + + if (Reduce (ec, (Constant) Expr, out result)) + return result; + } + + switch (Oper){ + case Operator.LogicalNot: + if (expr_type != TypeManager.bool_type) { + Error23 (Expr.Type); + return null; + } + + type = TypeManager.bool_type; + return this; + + case Operator.OnesComplement: + if (!((expr_type == TypeManager.int32_type) || + (expr_type == TypeManager.uint32_type) || + (expr_type == TypeManager.int64_type) || + (expr_type == TypeManager.uint64_type) || + (expr_type.IsSubclassOf (TypeManager.enum_type)))){ + Expression e; + + e = ConvertImplicit (ec, Expr, TypeManager.int32_type, loc); + if (e != null){ + type = TypeManager.int32_type; + return this; + } + e = ConvertImplicit (ec, Expr, TypeManager.uint32_type, loc); + if (e != null){ + type = TypeManager.uint32_type; + return this; + } + e = ConvertImplicit (ec, Expr, TypeManager.int64_type, loc); + if (e != null){ + type = TypeManager.int64_type; + return this; + } + e = ConvertImplicit (ec, Expr, TypeManager.uint64_type, loc); + if (e != null){ + type = TypeManager.uint64_type; + return this; + } + Error23 (expr_type); + return null; + } + type = expr_type; + return this; + + case Operator.AddressOf: + if (Expr.eclass != ExprClass.Variable){ + Error (211, "Cannot take the address of non-variables"); + return null; + } + + if (!ec.InUnsafe) { + UnsafeError (loc); + return null; + } + + if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){ + return null; + } + + string ptr_type_name = Expr.Type.FullName + "*"; + type = TypeManager.LookupType (ptr_type_name); + + return this; + + case Operator.Indirection: + if (!ec.InUnsafe){ + UnsafeError (loc); + return null; + } + + if (!expr_type.IsPointer){ + Error ( + 193, + "The * or -> operator can only be applied to pointers"); + return null; + } + + // + // We create an Indirection expression, because + // it can implement the IMemoryLocation. + // + return new Indirection (Expr, loc); + + case Operator.UnaryPlus: + // + // A plus in front of something is just a no-op, so return the child. + // + return Expr; + + case Operator.UnaryNegation: + // + // Deals with -literals + // int operator- (int x) + // long operator- (long x) + // float operator- (float f) + // double operator- (double d) + // decimal operator- (decimal d) + // + Expression expr = null; + + // + // transform - - expr into expr + // + if (Expr is Unary){ + Unary unary = (Unary) Expr; + + if (unary.Oper == Operator.UnaryNegation) + return unary.Expr; + } + + // + // perform numeric promotions to int, + // long, double. + // + // + // The following is inneficient, because we call + // ConvertImplicit too many times. + // + // It is also not clear if we should convert to Float + // or Double initially. + // + if (expr_type == TypeManager.uint32_type){ + // + // FIXME: handle exception to this rule that + // permits the int value -2147483648 (-2^31) to + // bt wrote as a decimal interger literal + // + type = TypeManager.int64_type; + Expr = ConvertImplicit (ec, Expr, type, loc); + return this; + } + + if (expr_type == TypeManager.uint64_type){ + // + // FIXME: Handle exception of `long value' + // -92233720368547758087 (-2^63) to be wrote as + // decimal integer literal. + // + Error23 (expr_type); + return null; + } + + if (expr_type == TypeManager.float_type){ + type = expr_type; + return this; + } + + expr = ConvertImplicit (ec, Expr, TypeManager.int32_type, loc); + if (expr != null){ + Expr = expr; + type = expr.Type; + return this; + } + + expr = ConvertImplicit (ec, Expr, TypeManager.int64_type, loc); + if (expr != null){ + Expr = expr; + type = expr.Type; + return this; + } + + expr = ConvertImplicit (ec, Expr, TypeManager.double_type, loc); + if (expr != null){ + Expr = expr; + type = expr.Type; + return this; + } + + Error23 (expr_type); + return null; + } + + Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" + + TypeManager.CSharpName (expr_type) + "'"); + return null; + } + + public override Expression DoResolve (EmitContext ec) + { + if (Oper == Operator.AddressOf) + Expr = Expr.ResolveLValue (ec, new EmptyExpression ()); + else + Expr = Expr.Resolve (ec); + + if (Expr == null) + return null; + + eclass = ExprClass.Value; + return ResolveOperator (ec); + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + Type expr_type = Expr.Type; + + switch (Oper) { + case Operator.UnaryPlus: + throw new Exception ("This should be caught by Resolve"); + + case Operator.UnaryNegation: + Expr.Emit (ec); + ig.Emit (OpCodes.Neg); + break; + + case Operator.LogicalNot: + Expr.Emit (ec); + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Ceq); + break; + + case Operator.OnesComplement: + Expr.Emit (ec); + ig.Emit (OpCodes.Not); + break; + + case Operator.AddressOf: + ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore); + break; + + default: + throw new Exception ("This should not happen: Operator = " + + Oper.ToString ()); + } + } + + /// <summary> + /// This will emit the child expression for `ec' avoiding the logical + /// not. The parent will take care of changing brfalse/brtrue + /// </summary> + public void EmitLogicalNot (EmitContext ec) + { + if (Oper != Operator.LogicalNot) + throw new Exception ("EmitLogicalNot can only be called with !expr"); + + Expr.Emit (ec); + } + + public override string ToString () + { + return "Unary (" + Oper + ", " + Expr + ")"; + } + + } + + // + // Unary operators are turned into Indirection expressions + // after semantic analysis (this is so we can take the address + // of an indirection). + // + public class Indirection : Expression, IMemoryLocation, IAssignMethod { + Expression expr; + LocalTemporary temporary; + bool have_temporary; + + public Indirection (Expression expr, Location l) + { + this.expr = expr; + this.type = TypeManager.TypeToCoreType (expr.Type.GetElementType ()); + eclass = ExprClass.Variable; + loc = l; + } + + void LoadExprValue (EmitContext ec) + { + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + if (temporary != null){ + if (have_temporary){ + temporary.Emit (ec); + return; + } + expr.Emit (ec); + ec.ig.Emit (OpCodes.Dup); + temporary.Store (ec); + have_temporary = true; + } else + expr.Emit (ec); + + LoadFromPtr (ig, Type); + } + + public void EmitAssign (EmitContext ec, Expression source) + { + if (temporary != null){ + if (have_temporary){ + temporary.Emit (ec); + return; + } + expr.Emit (ec); + ec.ig.Emit (OpCodes.Dup); + temporary.Store (ec); + have_temporary = true; + } else + expr.Emit (ec); + + source.Emit (ec); + StoreFromPtr (ec.ig, type); + } + + public void AddressOf (EmitContext ec, AddressOp Mode) + { + if (temporary != null){ + if (have_temporary){ + temporary.Emit (ec); + return; + } + expr.Emit (ec); + ec.ig.Emit (OpCodes.Dup); + temporary.Store (ec); + have_temporary = true; + } else + expr.Emit (ec); + } + + public override Expression DoResolve (EmitContext ec) + { + // + // Born fully resolved + // + return this; + } + + public new void CacheTemporaries (EmitContext ec) + { + temporary = new LocalTemporary (ec, type); + } + } + + /// <summary> + /// Unary Mutator expressions (pre and post ++ and --) + /// </summary> + /// + /// <remarks> + /// UnaryMutator implements ++ and -- expressions. It derives from + /// ExpressionStatement becuase the pre/post increment/decrement + /// operators can be used in a statement context. + /// + /// FIXME: Idea, we could split this up in two classes, one simpler + /// for the common case, and one with the extra fields for more complex + /// classes (indexers require temporary access; overloaded require method) + /// + /// Maybe we should have classes PreIncrement, PostIncrement, PreDecrement, + /// PostDecrement, that way we could save the `Mode' byte as well. + /// </remarks> + public class UnaryMutator : ExpressionStatement { + public enum Mode : byte { + PreIncrement, PreDecrement, PostIncrement, PostDecrement + } + + Mode mode; + Expression expr; + LocalTemporary temp_storage; + + // + // This is expensive for the simplest case. + // + Expression method; + + public UnaryMutator (Mode m, Expression e, Location l) + { + mode = m; + loc = l; + expr = e; + } + + static string OperName (Mode mode) + { + return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ? + "++" : "--"; + } + + void Error23 (Type t) + { + Error ( + 23, "Operator " + OperName (mode) + + " cannot be applied to operand of type `" + + TypeManager.CSharpName (t) + "'"); + } + + /// <summary> + /// Returns whether an object of type `t' can be incremented + /// or decremented with add/sub (ie, basically whether we can + /// use pre-post incr-decr operations on it, but it is not a + /// System.Decimal, which we require operator overloading to catch) + /// </summary> + static bool IsIncrementableNumber (Type t) + { + return (t == TypeManager.sbyte_type) || + (t == TypeManager.byte_type) || + (t == TypeManager.short_type) || + (t == TypeManager.ushort_type) || + (t == TypeManager.int32_type) || + (t == TypeManager.uint32_type) || + (t == TypeManager.int64_type) || + (t == TypeManager.uint64_type) || + (t == TypeManager.char_type) || + (t.IsSubclassOf (TypeManager.enum_type)) || + (t == TypeManager.float_type) || + (t == TypeManager.double_type) || + (t.IsPointer && t != TypeManager.void_ptr_type); + } + + Expression ResolveOperator (EmitContext ec) + { + Type expr_type = expr.Type; + + // + // Step 1: Perform Operator Overload location + // + Expression mg; + string op_name; + + if (mode == Mode.PreIncrement || mode == Mode.PostIncrement) + op_name = "op_Increment"; + else + op_name = "op_Decrement"; + + mg = MemberLookup (ec, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc); + + if (mg == null && expr_type.BaseType != null) + mg = MemberLookup (ec, expr_type.BaseType, op_name, + MemberTypes.Method, AllBindingFlags, loc); + + if (mg != null) { + method = StaticCallExpr.MakeSimpleCall ( + ec, (MethodGroupExpr) mg, expr, loc); + + type = method.Type; + return this; + } + + // + // The operand of the prefix/postfix increment decrement operators + // should be an expression that is classified as a variable, + // a property access or an indexer access + // + type = expr_type; + if (expr.eclass == ExprClass.Variable){ + if (IsIncrementableNumber (expr_type) || + expr_type == TypeManager.decimal_type){ + return this; + } + } else if (expr.eclass == ExprClass.IndexerAccess){ + IndexerAccess ia = (IndexerAccess) expr; + + temp_storage = new LocalTemporary (ec, expr.Type); + + expr = ia.ResolveLValue (ec, temp_storage); + if (expr == null) + return null; + + return this; + } else if (expr.eclass == ExprClass.PropertyAccess){ + PropertyExpr pe = (PropertyExpr) expr; + + if (pe.VerifyAssignable ()) + return this; + + return null; + } else { + expr.Error118 ("variable, indexer or property access"); + return null; + } + + Error (187, "No such operator '" + OperName (mode) + "' defined for type '" + + TypeManager.CSharpName (expr_type) + "'"); + return null; + } + + public override Expression DoResolve (EmitContext ec) + { + expr = expr.Resolve (ec); + + if (expr == null) + return null; + + eclass = ExprClass.Value; + return ResolveOperator (ec); + } + + static int PtrTypeSize (Type t) + { + return GetTypeSize (t.GetElementType ()); + } + + // + // Loads the proper "1" into the stack based on the type + // + static void LoadOne (ILGenerator ig, Type t) + { + if (t == TypeManager.uint64_type || t == TypeManager.int64_type) + ig.Emit (OpCodes.Ldc_I8, 1L); + else if (t == TypeManager.double_type) + ig.Emit (OpCodes.Ldc_R8, 1.0); + else if (t == TypeManager.float_type) + ig.Emit (OpCodes.Ldc_R4, 1.0F); + else if (t.IsPointer){ + int n = PtrTypeSize (t); + + if (n == 0) + ig.Emit (OpCodes.Sizeof, t); + else + IntConstant.EmitInt (ig, n); + } else + ig.Emit (OpCodes.Ldc_I4_1); + } + + + // + // FIXME: We need some way of avoiding the use of temp_storage + // for some types of storage (parameters, local variables, + // static fields) and single-dimension array access. + // + void EmitCode (EmitContext ec, bool is_expr) + { + ILGenerator ig = ec.ig; + IAssignMethod ia = (IAssignMethod) expr; + Type expr_type = expr.Type; + + if (temp_storage == null) + temp_storage = new LocalTemporary (ec, expr_type); + + ia.CacheTemporaries (ec); + ig.Emit (OpCodes.Nop); + switch (mode){ + case Mode.PreIncrement: + case Mode.PreDecrement: + if (method == null){ + expr.Emit (ec); + + LoadOne (ig, expr_type); + + // + // Select the opcode based on the check state (then the type) + // and the actual operation + // + if (ec.CheckState){ + if (expr_type == TypeManager.int32_type || + expr_type == TypeManager.int64_type){ + if (mode == Mode.PreDecrement) + ig.Emit (OpCodes.Sub_Ovf); + else + ig.Emit (OpCodes.Add_Ovf); + } else if (expr_type == TypeManager.uint32_type || + expr_type == TypeManager.uint64_type){ + if (mode == Mode.PreDecrement) + ig.Emit (OpCodes.Sub_Ovf_Un); + else + ig.Emit (OpCodes.Add_Ovf_Un); + } else { + if (mode == Mode.PreDecrement) + ig.Emit (OpCodes.Sub_Ovf); + else + ig.Emit (OpCodes.Add_Ovf); + } + } else { + if (mode == Mode.PreDecrement) + ig.Emit (OpCodes.Sub); + else + ig.Emit (OpCodes.Add); + } + } else + method.Emit (ec); + + temp_storage.Store (ec); + ia.EmitAssign (ec, temp_storage); + if (is_expr) + temp_storage.Emit (ec); + break; + + case Mode.PostIncrement: + case Mode.PostDecrement: + if (is_expr) + expr.Emit (ec); + + if (method == null){ + if (!is_expr) + expr.Emit (ec); + else + ig.Emit (OpCodes.Dup); + + LoadOne (ig, expr_type); + + if (ec.CheckState){ + if (expr_type == TypeManager.int32_type || + expr_type == TypeManager.int64_type){ + if (mode == Mode.PostDecrement) + ig.Emit (OpCodes.Sub_Ovf); + else + ig.Emit (OpCodes.Add_Ovf); + } else if (expr_type == TypeManager.uint32_type || + expr_type == TypeManager.uint64_type){ + if (mode == Mode.PostDecrement) + ig.Emit (OpCodes.Sub_Ovf_Un); + else + ig.Emit (OpCodes.Add_Ovf_Un); + } else { + if (mode == Mode.PostDecrement) + ig.Emit (OpCodes.Sub_Ovf); + else + ig.Emit (OpCodes.Add_Ovf); + } + } else { + if (mode == Mode.PostDecrement) + ig.Emit (OpCodes.Sub); + else + ig.Emit (OpCodes.Add); + } + } else { + method.Emit (ec); + } + + temp_storage.Store (ec); + ia.EmitAssign (ec, temp_storage); + break; + } + } + + public override void Emit (EmitContext ec) + { + EmitCode (ec, true); + + } + + public override void EmitStatement (EmitContext ec) + { + EmitCode (ec, false); + } + + } + + /// <summary> + /// Base class for the `Is' and `As' classes. + /// </summary> + /// + /// <remarks> + /// FIXME: Split this in two, and we get to save the `Operator' Oper + /// size. + /// </remarks> + public abstract class Probe : Expression { + public readonly Expression ProbeType; + protected Expression expr; + protected Type probe_type; + + public Probe (Expression expr, Expression probe_type, Location l) + { + ProbeType = probe_type; + loc = l; + this.expr = expr; + } + + public Expression Expr { + get { + return expr; + } + } + + public override Expression DoResolve (EmitContext ec) + { + probe_type = ec.DeclSpace.ResolveType (ProbeType, false, loc); + + if (probe_type == null) + return null; + + expr = expr.Resolve (ec); + + return this; + } + } + + /// <summary> + /// Implementation of the `is' operator. + /// </summary> + public class Is : Probe { + public Is (Expression expr, Expression probe_type, Location l) + : base (expr, probe_type, l) + { + } + + enum Action { + AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe + } + + Action action; + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + expr.Emit (ec); + + switch (action){ + case Action.AlwaysFalse: + ig.Emit (OpCodes.Pop); + IntConstant.EmitInt (ig, 0); + return; + case Action.AlwaysTrue: + ig.Emit (OpCodes.Pop); + ig.Emit (OpCodes.Nop); + IntConstant.EmitInt (ig, 1); + return; + case Action.LeaveOnStack: + // the `e != null' rule. + return; + case Action.Probe: + ig.Emit (OpCodes.Isinst, probe_type); + ig.Emit (OpCodes.Ldnull); + ig.Emit (OpCodes.Cgt_Un); + return; + } + throw new Exception ("never reached"); + } + + public override Expression DoResolve (EmitContext ec) + { + Expression e = base.DoResolve (ec); + + if ((e == null) || (expr == null)) + return null; + + Type etype = expr.Type; + bool warning_always_matches = false; + bool warning_never_matches = false; + + type = TypeManager.bool_type; + eclass = ExprClass.Value; + + // + // First case, if at compile time, there is an implicit conversion + // then e != null (objects) or true (value types) + // + e = ConvertImplicitStandard (ec, expr, probe_type, loc); + if (e != null){ + expr = e; + if (etype.IsValueType) + action = Action.AlwaysTrue; + else + action = Action.LeaveOnStack; + + warning_always_matches = true; + } else if (ExplicitReferenceConversionExists (etype, probe_type)){ + // + // Second case: explicit reference convresion + // + if (expr is NullLiteral) + action = Action.AlwaysFalse; + else + action = Action.Probe; + } else { + action = Action.AlwaysFalse; + warning_never_matches = true; + } + + if (RootContext.WarningLevel >= 1){ + if (warning_always_matches) + Warning ( + 183, + "The expression is always of type `" + + TypeManager.CSharpName (probe_type) + "'"); + else if (warning_never_matches){ + if (!(probe_type.IsInterface || expr.Type.IsInterface)) + Warning ( + 184, + "The expression is never of type `" + + TypeManager.CSharpName (probe_type) + "'"); + } + } + + return this; + } + } + + /// <summary> + /// Implementation of the `as' operator. + /// </summary> + public class As : Probe { + public As (Expression expr, Expression probe_type, Location l) + : base (expr, probe_type, l) + { + } + + bool do_isinst = false; + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + expr.Emit (ec); + + if (do_isinst) + ig.Emit (OpCodes.Isinst, probe_type); + } + + static void Error_CannotConvertType (Type source, Type target, Location loc) + { + Report.Error ( + 39, loc, "as operator can not convert from `" + + TypeManager.CSharpName (source) + "' to `" + + TypeManager.CSharpName (target) + "'"); + } + + public override Expression DoResolve (EmitContext ec) + { + Expression e = base.DoResolve (ec); + + if (e == null) + return null; + + type = probe_type; + eclass = ExprClass.Value; + Type etype = expr.Type; + + if (TypeManager.IsValueType (probe_type)){ + Report.Error (77, loc, "The as operator should be used with a reference type only (" + + TypeManager.CSharpName (probe_type) + " is a value type"); + return null; + + } + + e = ConvertImplicit (ec, expr, probe_type, loc); + if (e != null){ + expr = e; + do_isinst = false; + return this; + } + + if (ExplicitReferenceConversionExists (etype, probe_type)){ + do_isinst = true; + return this; + } + + Error_CannotConvertType (etype, probe_type, loc); + return null; + } + } + + /// <summary> + /// This represents a typecast in the source language. + /// + /// FIXME: Cast expressions have an unusual set of parsing + /// rules, we need to figure those out. + /// </summary> + public class Cast : Expression { + Expression target_type; + Expression expr; + + public Cast (Expression cast_type, Expression expr, Location loc) + { + this.target_type = cast_type; + this.expr = expr; + this.loc = loc; + } + + public Expression TargetType { + get { + return target_type; + } + } + + public Expression Expr { + get { + return expr; + } + set { + expr = value; + } + } + + /// <summary> + /// Attempts to do a compile-time folding of a constant cast. + /// </summary> + Expression TryReduce (EmitContext ec, Type target_type) + { + if (expr is ByteConstant){ + byte v = ((ByteConstant) expr).Value; + + if (target_type == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + if (target_type == TypeManager.short_type) + return new ShortConstant ((short) v); + if (target_type == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (target_type == TypeManager.int32_type) + return new IntConstant ((int) v); + if (target_type == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (target_type == TypeManager.int64_type) + return new LongConstant ((long) v); + if (target_type == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (target_type == TypeManager.float_type) + return new FloatConstant ((float) v); + if (target_type == TypeManager.double_type) + return new DoubleConstant ((double) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + if (expr is SByteConstant){ + sbyte v = ((SByteConstant) expr).Value; + + if (target_type == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (target_type == TypeManager.short_type) + return new ShortConstant ((short) v); + if (target_type == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (target_type == TypeManager.int32_type) + return new IntConstant ((int) v); + if (target_type == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (target_type == TypeManager.int64_type) + return new LongConstant ((long) v); + if (target_type == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (target_type == TypeManager.float_type) + return new FloatConstant ((float) v); + if (target_type == TypeManager.double_type) + return new DoubleConstant ((double) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + if (expr is ShortConstant){ + short v = ((ShortConstant) expr).Value; + + if (target_type == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (target_type == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + if (target_type == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (target_type == TypeManager.int32_type) + return new IntConstant ((int) v); + if (target_type == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (target_type == TypeManager.int64_type) + return new LongConstant ((long) v); + if (target_type == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (target_type == TypeManager.float_type) + return new FloatConstant ((float) v); + if (target_type == TypeManager.double_type) + return new DoubleConstant ((double) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + if (expr is UShortConstant){ + ushort v = ((UShortConstant) expr).Value; + + if (target_type == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (target_type == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + if (target_type == TypeManager.short_type) + return new ShortConstant ((short) v); + if (target_type == TypeManager.int32_type) + return new IntConstant ((int) v); + if (target_type == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (target_type == TypeManager.int64_type) + return new LongConstant ((long) v); + if (target_type == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (target_type == TypeManager.float_type) + return new FloatConstant ((float) v); + if (target_type == TypeManager.double_type) + return new DoubleConstant ((double) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + if (expr is IntConstant){ + int v = ((IntConstant) expr).Value; + + if (target_type == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (target_type == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + if (target_type == TypeManager.short_type) + return new ShortConstant ((short) v); + if (target_type == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (target_type == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (target_type == TypeManager.int64_type) + return new LongConstant ((long) v); + if (target_type == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (target_type == TypeManager.float_type) + return new FloatConstant ((float) v); + if (target_type == TypeManager.double_type) + return new DoubleConstant ((double) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + if (expr is UIntConstant){ + uint v = ((UIntConstant) expr).Value; + + if (target_type == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (target_type == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + if (target_type == TypeManager.short_type) + return new ShortConstant ((short) v); + if (target_type == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (target_type == TypeManager.int32_type) + return new IntConstant ((int) v); + if (target_type == TypeManager.int64_type) + return new LongConstant ((long) v); + if (target_type == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (target_type == TypeManager.float_type) + return new FloatConstant ((float) v); + if (target_type == TypeManager.double_type) + return new DoubleConstant ((double) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + if (expr is LongConstant){ + long v = ((LongConstant) expr).Value; + + if (target_type == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (target_type == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + if (target_type == TypeManager.short_type) + return new ShortConstant ((short) v); + if (target_type == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (target_type == TypeManager.int32_type) + return new IntConstant ((int) v); + if (target_type == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (target_type == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (target_type == TypeManager.float_type) + return new FloatConstant ((float) v); + if (target_type == TypeManager.double_type) + return new DoubleConstant ((double) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + if (expr is ULongConstant){ + ulong v = ((ULongConstant) expr).Value; + + if (target_type == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (target_type == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + if (target_type == TypeManager.short_type) + return new ShortConstant ((short) v); + if (target_type == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (target_type == TypeManager.int32_type) + return new IntConstant ((int) v); + if (target_type == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (target_type == TypeManager.int64_type) + return new LongConstant ((long) v); + if (target_type == TypeManager.float_type) + return new FloatConstant ((float) v); + if (target_type == TypeManager.double_type) + return new DoubleConstant ((double) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + if (expr is FloatConstant){ + float v = ((FloatConstant) expr).Value; + + if (target_type == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (target_type == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + if (target_type == TypeManager.short_type) + return new ShortConstant ((short) v); + if (target_type == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (target_type == TypeManager.int32_type) + return new IntConstant ((int) v); + if (target_type == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (target_type == TypeManager.int64_type) + return new LongConstant ((long) v); + if (target_type == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (target_type == TypeManager.double_type) + return new DoubleConstant ((double) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + if (expr is DoubleConstant){ + double v = ((DoubleConstant) expr).Value; + + if (target_type == TypeManager.byte_type) + return new ByteConstant ((byte) v); + if (target_type == TypeManager.sbyte_type) + return new SByteConstant ((sbyte) v); + if (target_type == TypeManager.short_type) + return new ShortConstant ((short) v); + if (target_type == TypeManager.ushort_type) + return new UShortConstant ((ushort) v); + if (target_type == TypeManager.int32_type) + return new IntConstant ((int) v); + if (target_type == TypeManager.uint32_type) + return new UIntConstant ((uint) v); + if (target_type == TypeManager.int64_type) + return new LongConstant ((long) v); + if (target_type == TypeManager.uint64_type) + return new ULongConstant ((ulong) v); + if (target_type == TypeManager.float_type) + return new FloatConstant ((float) v); + if (target_type == TypeManager.char_type) + return new CharConstant ((char) v); + if (target_type == TypeManager.decimal_type) + return new DecimalConstant ((decimal) v); + } + + return null; + } + + public override Expression DoResolve (EmitContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + int errors = Report.Errors; + + type = ec.DeclSpace.ResolveType (target_type, false, Location); + + if (type == null) + return null; + + eclass = ExprClass.Value; + + if (expr is Constant){ + Expression e = TryReduce (ec, type); + + if (e != null) + return e; + } + + expr = ConvertExplicit (ec, expr, type, loc); + return expr; + } + + public override void Emit (EmitContext ec) + { + // + // This one will never happen + // + throw new Exception ("Should not happen"); + } + } + + /// <summary> + /// Binary operators + /// </summary> + public class Binary : Expression { + public enum Operator : byte { + Multiply, Division, Modulus, + Addition, Subtraction, + LeftShift, RightShift, + LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual, + Equality, Inequality, + BitwiseAnd, + ExclusiveOr, + BitwiseOr, + LogicalAnd, + LogicalOr, + TOP + } + + Operator oper; + Expression left, right; + + // + // After resolution, method might contain the operator overload + // method. + // + protected MethodBase method; + ArrayList Arguments; + + bool DelegateOperation; + + // This must be kept in sync with Operator!!! + static string [] oper_names; + + static Binary () + { + oper_names = new string [(int) Operator.TOP]; + + oper_names [(int) Operator.Multiply] = "op_Multiply"; + oper_names [(int) Operator.Division] = "op_Division"; + oper_names [(int) Operator.Modulus] = "op_Modulus"; + oper_names [(int) Operator.Addition] = "op_Addition"; + oper_names [(int) Operator.Subtraction] = "op_Subtraction"; + oper_names [(int) Operator.LeftShift] = "op_LeftShift"; + oper_names [(int) Operator.RightShift] = "op_RightShift"; + oper_names [(int) Operator.LessThan] = "op_LessThan"; + oper_names [(int) Operator.GreaterThan] = "op_GreaterThan"; + oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual"; + oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual"; + oper_names [(int) Operator.Equality] = "op_Equality"; + oper_names [(int) Operator.Inequality] = "op_Inequality"; + oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd"; + oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr"; + oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr"; + oper_names [(int) Operator.LogicalOr] = "op_LogicalOr"; + oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd"; + } + + public Binary (Operator oper, Expression left, Expression right, Location loc) + { + this.oper = oper; + this.left = left; + this.right = right; + this.loc = loc; + } + + public Operator Oper { + get { + return oper; + } + set { + oper = value; + } + } + + public Expression Left { + get { + return left; + } + set { + left = value; + } + } + + public Expression Right { + get { + return right; + } + set { + right = value; + } + } + + + /// <summary> + /// Returns a stringified representation of the Operator + /// </summary> + static string OperName (Operator oper) + { + switch (oper){ + case Operator.Multiply: + return "*"; + case Operator.Division: + return "/"; + case Operator.Modulus: + return "%"; + case Operator.Addition: + return "+"; + case Operator.Subtraction: + return "-"; + case Operator.LeftShift: + return "<<"; + case Operator.RightShift: + return ">>"; + case Operator.LessThan: + return "<"; + case Operator.GreaterThan: + return ">"; + case Operator.LessThanOrEqual: + return "<="; + case Operator.GreaterThanOrEqual: + return ">="; + case Operator.Equality: + return "=="; + case Operator.Inequality: + return "!="; + case Operator.BitwiseAnd: + return "&"; + case Operator.BitwiseOr: + return "|"; + case Operator.ExclusiveOr: + return "^"; + case Operator.LogicalOr: + return "||"; + case Operator.LogicalAnd: + return "&&"; + } + + return oper.ToString (); + } + + public override string ToString () + { + return "operator " + OperName (oper) + "(" + left.ToString () + ", " + + right.ToString () + ")"; + } + + Expression ForceConversion (EmitContext ec, Expression expr, Type target_type) + { + if (expr.Type == target_type) + return expr; + + return ConvertImplicit (ec, expr, target_type, new Location (-1)); + } + + public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r) + { + Report.Error ( + 34, loc, "Operator `" + OperName (oper) + + "' is ambiguous on operands of type `" + + TypeManager.CSharpName (l) + "' " + + "and `" + TypeManager.CSharpName (r) + + "'"); + } + + // + // Note that handling the case l == Decimal || r == Decimal + // is taken care of by the Step 1 Operator Overload resolution. + // + bool DoNumericPromotions (EmitContext ec, Type l, Type r) + { + if (l == TypeManager.double_type || r == TypeManager.double_type){ + // + // If either operand is of type double, the other operand is + // conveted to type double. + // + if (r != TypeManager.double_type) + right = ConvertImplicit (ec, right, TypeManager.double_type, loc); + if (l != TypeManager.double_type) + left = ConvertImplicit (ec, left, TypeManager.double_type, loc); + + type = TypeManager.double_type; + } else if (l == TypeManager.float_type || r == TypeManager.float_type){ + // + // if either operand is of type float, the other operand is + // converted to type float. + // + if (r != TypeManager.double_type) + right = ConvertImplicit (ec, right, TypeManager.float_type, loc); + if (l != TypeManager.double_type) + left = ConvertImplicit (ec, left, TypeManager.float_type, loc); + type = TypeManager.float_type; + } else if (l == TypeManager.uint64_type || r == TypeManager.uint64_type){ + Expression e; + Type other; + // + // If either operand is of type ulong, the other operand is + // converted to type ulong. or an error ocurrs if the other + // operand is of type sbyte, short, int or long + // + if (l == TypeManager.uint64_type){ + if (r != TypeManager.uint64_type){ + if (right is IntConstant){ + IntConstant ic = (IntConstant) right; + + e = TryImplicitIntConversion (l, ic); + if (e != null) + right = e; + } else if (right is LongConstant){ + long ll = ((LongConstant) right).Value; + + if (ll > 0) + right = new ULongConstant ((ulong) ll); + } else { + e = ImplicitNumericConversion (ec, right, l, loc); + if (e != null) + right = e; + } + } + other = right.Type; + } else { + if (left is IntConstant){ + e = TryImplicitIntConversion (r, (IntConstant) left); + if (e != null) + left = e; + } else if (left is LongConstant){ + long ll = ((LongConstant) left).Value; + + if (ll > 0) + left = new ULongConstant ((ulong) ll); + } else { + e = ImplicitNumericConversion (ec, left, r, loc); + if (e != null) + left = e; + } + other = left.Type; + } + + if ((other == TypeManager.sbyte_type) || + (other == TypeManager.short_type) || + (other == TypeManager.int32_type) || + (other == TypeManager.int64_type)) + Error_OperatorAmbiguous (loc, oper, l, r); + type = TypeManager.uint64_type; + } else if (l == TypeManager.int64_type || r == TypeManager.int64_type){ + // + // If either operand is of type long, the other operand is converted + // to type long. + // + if (l != TypeManager.int64_type) + left = ConvertImplicit (ec, left, TypeManager.int64_type, loc); + if (r != TypeManager.int64_type) + right = ConvertImplicit (ec, right, TypeManager.int64_type, loc); + + type = TypeManager.int64_type; + } else if (l == TypeManager.uint32_type || r == TypeManager.uint32_type){ + // + // If either operand is of type uint, and the other + // operand is of type sbyte, short or int, othe operands are + // converted to type long. + // + Type other = null; + + if (l == TypeManager.uint32_type){ + if (right is IntConstant){ + IntConstant ic = (IntConstant) right; + int val = ic.Value; + + if (val >= 0) + right = new UIntConstant ((uint) val); + + type = l; + return true; + } + other = r; + } + else if (r == TypeManager.uint32_type){ + if (left is IntConstant){ + IntConstant ic = (IntConstant) left; + int val = ic.Value; + + if (val >= 0) + left = new UIntConstant ((uint) val); + + type = r; + return true; + } + + other = l; + } + + if ((other == TypeManager.sbyte_type) || + (other == TypeManager.short_type) || + (other == TypeManager.int32_type)){ + left = ForceConversion (ec, left, TypeManager.int64_type); + right = ForceConversion (ec, right, TypeManager.int64_type); + type = TypeManager.int64_type; + } else { + // + // if either operand is of type uint, the other + // operand is converd to type uint + // + left = ForceConversion (ec, left, TypeManager.uint32_type); + right = ForceConversion (ec, right, TypeManager.uint32_type); + type = TypeManager.uint32_type; + } + } else if (l == TypeManager.decimal_type || r == TypeManager.decimal_type){ + if (l != TypeManager.decimal_type) + left = ConvertImplicit (ec, left, TypeManager.decimal_type, loc); + + if (r != TypeManager.decimal_type) + right = ConvertImplicit (ec, right, TypeManager.decimal_type, loc); + type = TypeManager.decimal_type; + } else { + left = ForceConversion (ec, left, TypeManager.int32_type); + right = ForceConversion (ec, right, TypeManager.int32_type); + + type = TypeManager.int32_type; + } + + return (left != null) && (right != null); + } + + static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r) + { + Report.Error (19, loc, + "Operator " + name + " cannot be applied to operands of type `" + + TypeManager.CSharpName (l) + "' and `" + + TypeManager.CSharpName (r) + "'"); + } + + void Error_OperatorCannotBeApplied () + { + Error_OperatorCannotBeApplied (loc, OperName (oper), left.Type, right.Type); + } + + static bool is_32_or_64 (Type t) + { + return (t == TypeManager.int32_type || t == TypeManager.uint32_type || + t == TypeManager.int64_type || t == TypeManager.uint64_type); + } + + static bool is_unsigned (Type t) + { + return (t == TypeManager.uint32_type || t == TypeManager.uint64_type || + t == TypeManager.short_type || t == TypeManager.byte_type); + } + + Expression CheckShiftArguments (EmitContext ec) + { + Expression e; + Type l = left.Type; + Type r = right.Type; + + e = ForceConversion (ec, right, TypeManager.int32_type); + if (e == null){ + Error_OperatorCannotBeApplied (); + return null; + } + right = e; + + if (((e = ConvertImplicit (ec, left, TypeManager.int32_type, loc)) != null) || + ((e = ConvertImplicit (ec, left, TypeManager.uint32_type, loc)) != null) || + ((e = ConvertImplicit (ec, left, TypeManager.int64_type, loc)) != null) || + ((e = ConvertImplicit (ec, left, TypeManager.uint64_type, loc)) != null)){ + left = e; + type = e.Type; + + return this; + } + Error_OperatorCannotBeApplied (); + return null; + } + + Expression ResolveOperator (EmitContext ec) + { + Type l = left.Type; + Type r = right.Type; + + bool overload_failed = false; + + // + // Step 1: Perform Operator Overload location + // + Expression left_expr, right_expr; + + string op = oper_names [(int) oper]; + + MethodGroupExpr union; + left_expr = MemberLookup (ec, l, op, MemberTypes.Method, AllBindingFlags, loc); + if (r != l){ + right_expr = MemberLookup ( + ec, r, op, MemberTypes.Method, AllBindingFlags, loc); + union = Invocation.MakeUnionSet (left_expr, right_expr, loc); + } else + union = (MethodGroupExpr) left_expr; + + if (union != null) { + Arguments = new ArrayList (); + Arguments.Add (new Argument (left, Argument.AType.Expression)); + Arguments.Add (new Argument (right, Argument.AType.Expression)); + + method = Invocation.OverloadResolve (ec, union, Arguments, Location.Null); + if (method != null) { + MethodInfo mi = (MethodInfo) method; + + type = mi.ReturnType; + return this; + } else { + overload_failed = true; + } + } + + // + // Step 2: Default operations on CLI native types. + // + + // + // Step 0: String concatenation (because overloading will get this wrong) + // + if (oper == Operator.Addition){ + // + // If any of the arguments is a string, cast to string + // + + if (l == TypeManager.string_type){ + + if (r == TypeManager.void_type) { + Error_OperatorCannotBeApplied (); + return null; + } + + if (r == TypeManager.string_type){ + if (left is Constant && right is Constant){ + StringConstant ls = (StringConstant) left; + StringConstant rs = (StringConstant) right; + + return new StringConstant ( + ls.Value + rs.Value); + } + + // string + string + method = TypeManager.string_concat_string_string; + } else { + // string + object + method = TypeManager.string_concat_object_object; + right = ConvertImplicit (ec, right, + TypeManager.object_type, loc); + if (right == null){ + Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); + return null; + } + } + type = TypeManager.string_type; + + Arguments = new ArrayList (); + Arguments.Add (new Argument (left, Argument.AType.Expression)); + Arguments.Add (new Argument (right, Argument.AType.Expression)); + + return this; + + } else if (r == TypeManager.string_type){ + // object + string + + if (l == TypeManager.void_type) { + Error_OperatorCannotBeApplied (); + return null; + } + + method = TypeManager.string_concat_object_object; + left = ConvertImplicit (ec, left, TypeManager.object_type, loc); + if (left == null){ + Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); + return null; + } + Arguments = new ArrayList (); + Arguments.Add (new Argument (left, Argument.AType.Expression)); + Arguments.Add (new Argument (right, Argument.AType.Expression)); + + type = TypeManager.string_type; + + return this; + } + + // + // Transform a + ( - b) into a - b + // + if (right is Unary){ + Unary right_unary = (Unary) right; + + if (right_unary.Oper == Unary.Operator.UnaryNegation){ + oper = Operator.Subtraction; + right = right_unary.Expr; + r = right.Type; + } + } + } + + if (oper == Operator.Equality || oper == Operator.Inequality){ + if (l == TypeManager.bool_type || r == TypeManager.bool_type){ + if (r != TypeManager.bool_type || l != TypeManager.bool_type){ + Error_OperatorCannotBeApplied (); + return null; + } + + type = TypeManager.bool_type; + return this; + } + + // + // operator != (object a, object b) + // operator == (object a, object b) + // + // For this to be used, both arguments have to be reference-types. + // Read the rationale on the spec (14.9.6) + // + // Also, if at compile time we know that the classes do not inherit + // one from the other, then we catch the error there. + // + if (!(l.IsValueType || r.IsValueType)){ + type = TypeManager.bool_type; + + if (l == r) + return this; + + if (l.IsSubclassOf (r) || r.IsSubclassOf (l)) + return this; + + // + // Also, a standard conversion must exist from either one + // + if (!(StandardConversionExists (left, r) || + StandardConversionExists (right, l))){ + Error_OperatorCannotBeApplied (); + return null; + } + // + // We are going to have to convert to an object to compare + // + if (l != TypeManager.object_type) + left = new EmptyCast (left, TypeManager.object_type); + if (r != TypeManager.object_type) + right = new EmptyCast (right, TypeManager.object_type); + + // + // FIXME: CSC here catches errors cs254 and cs252 + // + return this; + } + + // + // One of them is a valuetype, but the other one is not. + // + if (!l.IsValueType || !r.IsValueType) { + Error_OperatorCannotBeApplied (); + return null; + } + } + + // Only perform numeric promotions on: + // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >= + // + if (oper == Operator.Addition || oper == Operator.Subtraction) { + if (l.IsSubclassOf (TypeManager.delegate_type) && + r.IsSubclassOf (TypeManager.delegate_type)) { + + Arguments = new ArrayList (); + Arguments.Add (new Argument (left, Argument.AType.Expression)); + Arguments.Add (new Argument (right, Argument.AType.Expression)); + + if (oper == Operator.Addition) + method = TypeManager.delegate_combine_delegate_delegate; + else + method = TypeManager.delegate_remove_delegate_delegate; + + if (l != r) { + Error_OperatorCannotBeApplied (); + return null; + } + + DelegateOperation = true; + type = l; + return this; + } + + // + // Pointer arithmetic: + // + // T* operator + (T* x, int y); + // T* operator + (T* x, uint y); + // T* operator + (T* x, long y); + // T* operator + (T* x, ulong y); + // + // T* operator + (int y, T* x); + // T* operator + (uint y, T *x); + // T* operator + (long y, T *x); + // T* operator + (ulong y, T *x); + // + // T* operator - (T* x, int y); + // T* operator - (T* x, uint y); + // T* operator - (T* x, long y); + // T* operator - (T* x, ulong y); + // + // long operator - (T* x, T *y) + // + if (l.IsPointer){ + if (r.IsPointer && oper == Operator.Subtraction){ + if (r == l) + return new PointerArithmetic ( + false, left, right, TypeManager.int64_type, + loc); + } else if (is_32_or_64 (r)) + return new PointerArithmetic ( + oper == Operator.Addition, left, right, l, loc); + } else if (r.IsPointer && is_32_or_64 (l) && oper == Operator.Addition) + return new PointerArithmetic ( + true, right, left, r, loc); + } + + // + // Enumeration operators + // + bool lie = TypeManager.IsEnumType (l); + bool rie = TypeManager.IsEnumType (r); + if (lie || rie){ + Expression temp; + + // U operator - (E e, E f) + if (lie && rie && oper == Operator.Subtraction){ + if (l == r){ + type = TypeManager.EnumToUnderlying (l); + return this; + } + Error_OperatorCannotBeApplied (); + return null; + } + + // + // operator + (E e, U x) + // operator - (E e, U x) + // + if (oper == Operator.Addition || oper == Operator.Subtraction){ + Type enum_type = lie ? l : r; + Type other_type = lie ? r : l; + Type underlying_type = TypeManager.EnumToUnderlying (enum_type); +; + + if (underlying_type != other_type){ + Error_OperatorCannotBeApplied (); + return null; + } + + type = enum_type; + return this; + } + + if (!rie){ + temp = ConvertImplicit (ec, right, l, loc); + if (temp != null) + right = temp; + else { + Error_OperatorCannotBeApplied (); + return null; + } + } if (!lie){ + temp = ConvertImplicit (ec, left, r, loc); + if (temp != null){ + left = temp; + l = r; + } else { + Error_OperatorCannotBeApplied (); + return null; + } + } + + if (oper == Operator.Equality || oper == Operator.Inequality || + oper == Operator.LessThanOrEqual || oper == Operator.LessThan || + oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){ + type = TypeManager.bool_type; + return this; + } + + if (oper == Operator.BitwiseAnd || + oper == Operator.BitwiseOr || + oper == Operator.ExclusiveOr){ + type = l; + return this; + } + Error_OperatorCannotBeApplied (); + return null; + } + + if (oper == Operator.LeftShift || oper == Operator.RightShift) + return CheckShiftArguments (ec); + + if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){ + if (l != TypeManager.bool_type || r != TypeManager.bool_type){ + Error_OperatorCannotBeApplied (); + return null; + } + + type = TypeManager.bool_type; + return this; + } + + // + // operator & (bool x, bool y) + // operator | (bool x, bool y) + // operator ^ (bool x, bool y) + // + if (l == TypeManager.bool_type && r == TypeManager.bool_type){ + if (oper == Operator.BitwiseAnd || + oper == Operator.BitwiseOr || + oper == Operator.ExclusiveOr){ + type = l; + return this; + } + } + + // + // Pointer comparison + // + if (l.IsPointer && r.IsPointer){ + if (oper == Operator.Equality || oper == Operator.Inequality || + oper == Operator.LessThan || oper == Operator.LessThanOrEqual || + oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){ + type = TypeManager.bool_type; + return this; + } + } + + // + // We are dealing with numbers + // + if (overload_failed){ + Error_OperatorCannotBeApplied (); + return null; + } + + // + // This will leave left or right set to null if there is an error + // + DoNumericPromotions (ec, l, r); + if (left == null || right == null){ + Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); + return null; + } + + // + // reload our cached types if required + // + l = left.Type; + r = right.Type; + + if (oper == Operator.BitwiseAnd || + oper == Operator.BitwiseOr || + oper == Operator.ExclusiveOr){ + if (l == r){ + if (!((l == TypeManager.int32_type) || + (l == TypeManager.uint32_type) || + (l == TypeManager.int64_type) || + (l == TypeManager.uint64_type))) + type = l; + } else { + Error_OperatorCannotBeApplied (); + return null; + } + } + + if (oper == Operator.Equality || + oper == Operator.Inequality || + oper == Operator.LessThanOrEqual || + oper == Operator.LessThan || + oper == Operator.GreaterThanOrEqual || + oper == Operator.GreaterThan){ + type = TypeManager.bool_type; + } + + return this; + } + + public override Expression DoResolve (EmitContext ec) + { + left = left.Resolve (ec); + right = right.Resolve (ec); + + if (left == null || right == null) + return null; + + if (left.Type == null) + throw new Exception ( + "Resolve returned non null, but did not set the type! (" + + left + ") at Line: " + loc.Row); + if (right.Type == null) + throw new Exception ( + "Resolve returned non null, but did not set the type! (" + + right + ") at Line: "+ loc.Row); + + eclass = ExprClass.Value; + + if (left is Constant && right is Constant){ + Expression e = ConstantFold.BinaryFold ( + ec, oper, (Constant) left, (Constant) right, loc); + if (e != null) + return e; + } + + return ResolveOperator (ec); + } + + /// <remarks> + /// EmitBranchable is called from Statement.EmitBoolExpression in the + /// context of a conditional bool expression. This function will return + /// false if it is was possible to use EmitBranchable, or true if it was. + /// + /// The expression's code is generated, and we will generate a branch to `target' + /// if the resulting expression value is equal to isTrue + /// </remarks> + public bool EmitBranchable (EmitContext ec, Label target, bool onTrue) + { + if (method != null) + return false; + + ILGenerator ig = ec.ig; + + // + // This is more complicated than it looks, but its just to avoid + // duplicated tests: basically, we allow ==, !=, >, <, >= and <= + // but on top of that we want for == and != to use a special path + // if we are comparing against null + // + if (oper == Operator.Equality || oper == Operator.Inequality){ + bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue; + + if (left is NullLiteral){ + right.Emit (ec); + if (my_on_true) + ig.Emit (OpCodes.Brtrue, target); + else + ig.Emit (OpCodes.Brfalse, target); + return true; + } else if (right is NullLiteral){ + left.Emit (ec); + if (my_on_true) + ig.Emit (OpCodes.Brtrue, target); + else + ig.Emit (OpCodes.Brfalse, target); + return true; + } + } else if (!(oper == Operator.LessThan || + oper == Operator.GreaterThan || + oper == Operator.LessThanOrEqual || + oper == Operator.GreaterThanOrEqual)) + return false; + + + + left.Emit (ec); + right.Emit (ec); + + bool isUnsigned = is_unsigned (left.Type); + + switch (oper){ + case Operator.Equality: + if (onTrue) + ig.Emit (OpCodes.Beq, target); + else + ig.Emit (OpCodes.Bne_Un, target); + break; + + case Operator.Inequality: + if (onTrue) + ig.Emit (OpCodes.Bne_Un, target); + else + ig.Emit (OpCodes.Beq, target); + break; + + case Operator.LessThan: + if (onTrue) + if (isUnsigned) + ig.Emit (OpCodes.Blt_Un, target); + else + ig.Emit (OpCodes.Blt, target); + else + if (isUnsigned) + ig.Emit (OpCodes.Bge_Un, target); + else + ig.Emit (OpCodes.Bge, target); + break; + + case Operator.GreaterThan: + if (onTrue) + if (isUnsigned) + ig.Emit (OpCodes.Bgt_Un, target); + else + ig.Emit (OpCodes.Bgt, target); + else + if (isUnsigned) + ig.Emit (OpCodes.Ble_Un, target); + else + ig.Emit (OpCodes.Ble, target); + break; + + case Operator.LessThanOrEqual: + if (onTrue) + if (isUnsigned) + ig.Emit (OpCodes.Ble_Un, target); + else + ig.Emit (OpCodes.Ble, target); + else + if (isUnsigned) + ig.Emit (OpCodes.Bgt_Un, target); + else + ig.Emit (OpCodes.Bgt, target); + break; + + + case Operator.GreaterThanOrEqual: + if (onTrue) + if (isUnsigned) + ig.Emit (OpCodes.Bge_Un, target); + else + ig.Emit (OpCodes.Bge, target); + else + if (isUnsigned) + ig.Emit (OpCodes.Blt_Un, target); + else + ig.Emit (OpCodes.Blt, target); + break; + + default: + return false; + } + + return true; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + Type l = left.Type; + Type r = right.Type; + OpCode opcode; + + if (method != null) { + + // Note that operators are static anyway + + if (Arguments != null) + Invocation.EmitArguments (ec, method, Arguments); + + if (method is MethodInfo) + ig.Emit (OpCodes.Call, (MethodInfo) method); + else + ig.Emit (OpCodes.Call, (ConstructorInfo) method); + + if (DelegateOperation) + ig.Emit (OpCodes.Castclass, type); + + return; + } + + // + // Handle short-circuit operators differently + // than the rest + // + if (oper == Operator.LogicalAnd){ + Label load_zero = ig.DefineLabel (); + Label end = ig.DefineLabel (); + + left.Emit (ec); + ig.Emit (OpCodes.Brfalse, load_zero); + right.Emit (ec); + ig.Emit (OpCodes.Br, end); + ig.MarkLabel (load_zero); + ig.Emit (OpCodes.Ldc_I4_0); + ig.MarkLabel (end); + return; + } else if (oper == Operator.LogicalOr){ + Label load_one = ig.DefineLabel (); + Label end = ig.DefineLabel (); + + left.Emit (ec); + ig.Emit (OpCodes.Brtrue, load_one); + right.Emit (ec); + ig.Emit (OpCodes.Br, end); + ig.MarkLabel (load_one); + ig.Emit (OpCodes.Ldc_I4_1); + ig.MarkLabel (end); + return; + } + + left.Emit (ec); + right.Emit (ec); + + switch (oper){ + case Operator.Multiply: + if (ec.CheckState){ + if (l == TypeManager.int32_type || l == TypeManager.int64_type) + opcode = OpCodes.Mul_Ovf; + else if (l==TypeManager.uint32_type || l==TypeManager.uint64_type) + opcode = OpCodes.Mul_Ovf_Un; + else + opcode = OpCodes.Mul; + } else + opcode = OpCodes.Mul; + + break; + + case Operator.Division: + if (l == TypeManager.uint32_type || l == TypeManager.uint64_type) + opcode = OpCodes.Div_Un; + else + opcode = OpCodes.Div; + break; + + case Operator.Modulus: + if (l == TypeManager.uint32_type || l == TypeManager.uint64_type) + opcode = OpCodes.Rem_Un; + else + opcode = OpCodes.Rem; + break; + + case Operator.Addition: + if (ec.CheckState){ + if (l == TypeManager.int32_type || l == TypeManager.int64_type) + opcode = OpCodes.Add_Ovf; + else if (l==TypeManager.uint32_type || l==TypeManager.uint64_type) + opcode = OpCodes.Add_Ovf_Un; + else + opcode = OpCodes.Add; + } else + opcode = OpCodes.Add; + break; + + case Operator.Subtraction: + if (ec.CheckState){ + if (l == TypeManager.int32_type || l == TypeManager.int64_type) + opcode = OpCodes.Sub_Ovf; + else if (l==TypeManager.uint32_type || l==TypeManager.uint64_type) + opcode = OpCodes.Sub_Ovf_Un; + else + opcode = OpCodes.Sub; + } else + opcode = OpCodes.Sub; + break; + + case Operator.RightShift: + if (l == TypeManager.uint32_type || l == TypeManager.uint64_type) + opcode = OpCodes.Shr_Un; + else + opcode = OpCodes.Shr; + break; + + case Operator.LeftShift: + opcode = OpCodes.Shl; + break; + + case Operator.Equality: + opcode = OpCodes.Ceq; + break; + + case Operator.Inequality: + ec.ig.Emit (OpCodes.Ceq); + ec.ig.Emit (OpCodes.Ldc_I4_0); + + opcode = OpCodes.Ceq; + break; + + case Operator.LessThan: + opcode = OpCodes.Clt; + break; + + case Operator.GreaterThan: + opcode = OpCodes.Cgt; + break; + + case Operator.LessThanOrEqual: + ec.ig.Emit (OpCodes.Cgt); + ec.ig.Emit (OpCodes.Ldc_I4_0); + + opcode = OpCodes.Ceq; + break; + + case Operator.GreaterThanOrEqual: + ec.ig.Emit (OpCodes.Clt); + ec.ig.Emit (OpCodes.Ldc_I4_1); + + opcode = OpCodes.Sub; + break; + + case Operator.BitwiseOr: + opcode = OpCodes.Or; + break; + + case Operator.BitwiseAnd: + opcode = OpCodes.And; + break; + + case Operator.ExclusiveOr: + opcode = OpCodes.Xor; + break; + + default: + throw new Exception ("This should not happen: Operator = " + + oper.ToString ()); + } + + ig.Emit (opcode); + } + + public bool IsBuiltinOperator { + get { + return method == null; + } + } + } + + public class PointerArithmetic : Expression { + Expression left, right; + bool is_add; + + // + // We assume that `l' is always a pointer + // + public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, + Location loc) + { + type = t; + eclass = ExprClass.Variable; + this.loc = loc; + left = l; + right = r; + is_add = is_addition; + } + + public override Expression DoResolve (EmitContext ec) + { + // + // We are born fully resolved + // + return this; + } + + public override void Emit (EmitContext ec) + { + Type op_type = left.Type; + ILGenerator ig = ec.ig; + int size = GetTypeSize (op_type.GetElementType ()); + + if (right.Type.IsPointer){ + // + // handle (pointer - pointer) + // + left.Emit (ec); + right.Emit (ec); + ig.Emit (OpCodes.Sub); + + if (size != 1){ + if (size == 0) + ig.Emit (OpCodes.Sizeof, op_type); + else + IntLiteral.EmitInt (ig, size); + ig.Emit (OpCodes.Div); + } + ig.Emit (OpCodes.Conv_I8); + } else { + // + // handle + and - on (pointer op int) + // + left.Emit (ec); + ig.Emit (OpCodes.Conv_I); + right.Emit (ec); + if (size != 1){ + if (size == 0) + ig.Emit (OpCodes.Sizeof, op_type); + else + IntLiteral.EmitInt (ig, size); + ig.Emit (OpCodes.Mul); + } + if (is_add) + ig.Emit (OpCodes.Add); + else + ig.Emit (OpCodes.Sub); + } + } + } + + /// <summary> + /// Implements the ternary conditional operator (?:) + /// </summary> + public class Conditional : Expression { + Expression expr, trueExpr, falseExpr; + + public Conditional (Expression expr, Expression trueExpr, Expression falseExpr, Location l) + { + this.expr = expr; + this.trueExpr = trueExpr; + this.falseExpr = falseExpr; + this.loc = l; + } + + public Expression Expr { + get { + return expr; + } + } + + public Expression TrueExpr { + get { + return trueExpr; + } + } + + public Expression FalseExpr { + get { + return falseExpr; + } + } + + public override Expression DoResolve (EmitContext ec) + { + expr = expr.Resolve (ec); + + if (expr == null) + return null; + + if (expr.Type != TypeManager.bool_type) + expr = Expression.ConvertImplicitRequired ( + ec, expr, TypeManager.bool_type, loc); + + trueExpr = trueExpr.Resolve (ec); + falseExpr = falseExpr.Resolve (ec); + + if (trueExpr == null || falseExpr == null) + return null; + + eclass = ExprClass.Value; + if (trueExpr.Type == falseExpr.Type) + type = trueExpr.Type; + else { + Expression conv; + Type true_type = trueExpr.Type; + Type false_type = falseExpr.Type; + + if (trueExpr is NullLiteral){ + type = false_type; + return this; + } else if (falseExpr is NullLiteral){ + type = true_type; + return this; + } + + // + // First, if an implicit conversion exists from trueExpr + // to falseExpr, then the result type is of type falseExpr.Type + // + conv = ConvertImplicit (ec, trueExpr, false_type, loc); + if (conv != null){ + // + // Check if both can convert implicitl to each other's type + // + if (ConvertImplicit (ec, falseExpr, true_type, loc) != null){ + Error (172, + "Can not compute type of conditional expression " + + "as `" + TypeManager.CSharpName (trueExpr.Type) + + "' and `" + TypeManager.CSharpName (falseExpr.Type) + + "' convert implicitly to each other"); + return null; + } + type = false_type; + trueExpr = conv; + } else if ((conv = ConvertImplicit(ec, falseExpr, true_type,loc))!= null){ + type = true_type; + falseExpr = conv; + } else { + Error (173, "The type of the conditional expression can " + + "not be computed because there is no implicit conversion" + + " from `" + TypeManager.CSharpName (trueExpr.Type) + "'" + + " and `" + TypeManager.CSharpName (falseExpr.Type) + "'"); + return null; + } + } + + if (expr is BoolConstant){ + BoolConstant bc = (BoolConstant) expr; + + if (bc.Value) + return trueExpr; + else + return falseExpr; + } + + return this; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + Label false_target = ig.DefineLabel (); + Label end_target = ig.DefineLabel (); + + Statement.EmitBoolExpression (ec, expr, false_target, false); + trueExpr.Emit (ec); + ig.Emit (OpCodes.Br, end_target); + ig.MarkLabel (false_target); + falseExpr.Emit (ec); + ig.MarkLabel (end_target); + } + + } + + /// <summary> + /// Local variables + /// </summary> + public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable { + public readonly string Name; + public readonly Block Block; + VariableInfo variable_info; + bool is_readonly; + + public LocalVariableReference (Block block, string name, Location l) + { + Block = block; + Name = name; + loc = l; + eclass = ExprClass.Variable; + } + + // Setting `is_readonly' to false will allow you to create a writable + // reference to a read-only variable. This is used by foreach and using. + public LocalVariableReference (Block block, string name, Location l, + VariableInfo variable_info, bool is_readonly) + : this (block, name, l) + { + this.variable_info = variable_info; + this.is_readonly = is_readonly; + } + + public VariableInfo VariableInfo { + get { + if (variable_info == null) { + variable_info = Block.GetVariableInfo (Name); + is_readonly = variable_info.ReadOnly; + } + return variable_info; + } + } + + public bool IsAssigned (EmitContext ec, Location loc) + { + return VariableInfo.IsAssigned (ec, loc); + } + + public bool IsFieldAssigned (EmitContext ec, string name, Location loc) + { + return VariableInfo.IsFieldAssigned (ec, name, loc); + } + + public void SetAssigned (EmitContext ec) + { + VariableInfo.SetAssigned (ec); + } + + public void SetFieldAssigned (EmitContext ec, string name) + { + VariableInfo.SetFieldAssigned (ec, name); + } + + public bool IsReadOnly { + get { + if (variable_info == null) { + variable_info = Block.GetVariableInfo (Name); + is_readonly = variable_info.ReadOnly; + } + return is_readonly; + } + } + + public override Expression DoResolve (EmitContext ec) + { + VariableInfo vi = VariableInfo; + + if (Block.IsConstant (Name)) { + Expression e = Block.GetConstantExpression (Name); + + vi.Used = true; + return e; + } + + if (ec.DoFlowAnalysis && !IsAssigned (ec, loc)) + return null; + + type = vi.VariableType; + return this; + } + + override public Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + VariableInfo vi = VariableInfo; + + if (ec.DoFlowAnalysis) + ec.SetVariableAssigned (vi); + + Expression e = DoResolve (ec); + + if (e == null) + return null; + + if (is_readonly){ + Error (1604, "cannot assign to `" + Name + "' because it is readonly"); + return null; + } + + return this; + } + + public override void Emit (EmitContext ec) + { + VariableInfo vi = VariableInfo; + ILGenerator ig = ec.ig; + + ig.Emit (OpCodes.Ldloc, vi.LocalBuilder); + vi.Used = true; + } + + public void EmitAssign (EmitContext ec, Expression source) + { + ILGenerator ig = ec.ig; + VariableInfo vi = VariableInfo; + + vi.Assigned = true; + + source.Emit (ec); + + ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + VariableInfo vi = VariableInfo; + + ec.ig.Emit (OpCodes.Ldloca, vi.LocalBuilder); + } + } + + /// <summary> + /// This represents a reference to a parameter in the intermediate + /// representation. + /// </summary> + public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable { + Parameters pars; + String name; + int idx; + public Parameter.Modifier mod; + public bool is_ref, is_out; + + public ParameterReference (Parameters pars, int idx, string name, Location loc) + { + this.pars = pars; + this.idx = idx; + this.name = name; + this.loc = loc; + eclass = ExprClass.Variable; + } + + public bool IsAssigned (EmitContext ec, Location loc) + { + if (!is_out || !ec.DoFlowAnalysis) + return true; + + if (!ec.CurrentBranching.IsParameterAssigned (idx)) { + Report.Error (165, loc, + "Use of unassigned local variable `" + name + "'"); + return false; + } + + return true; + } + + public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc) + { + if (!is_out || !ec.DoFlowAnalysis) + return true; + + if (ec.CurrentBranching.IsParameterAssigned (idx)) + return true; + + if (!ec.CurrentBranching.IsParameterAssigned (idx, field_name)) { + Report.Error (170, loc, + "Use of possibly unassigned field `" + field_name + "'"); + return false; + } + + return true; + } + + public void SetAssigned (EmitContext ec) + { + if (is_out && ec.DoFlowAnalysis) + ec.CurrentBranching.SetParameterAssigned (idx); + } + + public void SetFieldAssigned (EmitContext ec, string field_name) + { + if (is_out && ec.DoFlowAnalysis) + ec.CurrentBranching.SetParameterAssigned (idx, field_name); + } + + // + // Notice that for ref/out parameters, the type exposed is not the + // same type exposed externally. + // + // for "ref int a": + // externally we expose "int&" + // here we expose "int". + // + // We record this in "is_ref". This means that the type system can treat + // the type as it is expected, but when we generate the code, we generate + // the alternate kind of code. + // + public override Expression DoResolve (EmitContext ec) + { + type = pars.GetParameterInfo (ec.DeclSpace, idx, out mod); + is_ref = (mod & Parameter.Modifier.ISBYREF) != 0; + is_out = (mod & Parameter.Modifier.OUT) != 0; + eclass = ExprClass.Variable; + + if (is_out && ec.DoFlowAnalysis && !IsAssigned (ec, loc)) + return null; + + return this; + } + + override public Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + type = pars.GetParameterInfo (ec.DeclSpace, idx, out mod); + is_ref = (mod & Parameter.Modifier.ISBYREF) != 0; + is_out = (mod & Parameter.Modifier.OUT) != 0; + eclass = ExprClass.Variable; + + if (is_out && ec.DoFlowAnalysis) + ec.SetParameterAssigned (idx); + + return this; + } + + static void EmitLdArg (ILGenerator ig, int x) + { + if (x <= 255){ + switch (x){ + case 0: ig.Emit (OpCodes.Ldarg_0); break; + case 1: ig.Emit (OpCodes.Ldarg_1); break; + case 2: ig.Emit (OpCodes.Ldarg_2); break; + case 3: ig.Emit (OpCodes.Ldarg_3); break; + default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break; + } + } else + ig.Emit (OpCodes.Ldarg, x); + } + + // + // This method is used by parameters that are references, that are + // being passed as references: we only want to pass the pointer (that + // is already stored in the parameter, not the address of the pointer, + // and not the value of the variable). + // + public void EmitLoad (EmitContext ec) + { + ILGenerator ig = ec.ig; + int arg_idx = idx; + + if (!ec.IsStatic) + arg_idx++; + + EmitLdArg (ig, arg_idx); + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + int arg_idx = idx; + + if (!ec.IsStatic) + arg_idx++; + + EmitLdArg (ig, arg_idx); + + if (!is_ref) + return; + + // + // If we are a reference, we loaded on the stack a pointer + // Now lets load the real value + // + LoadFromPtr (ig, type); + } + + public void EmitAssign (EmitContext ec, Expression source) + { + ILGenerator ig = ec.ig; + int arg_idx = idx; + + if (!ec.IsStatic) + arg_idx++; + + if (is_ref) + EmitLdArg (ig, arg_idx); + + source.Emit (ec); + + if (is_ref) + StoreFromPtr (ig, type); + else { + if (arg_idx <= 255) + ig.Emit (OpCodes.Starg_S, (byte) arg_idx); + else + ig.Emit (OpCodes.Starg, arg_idx); + } + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + int arg_idx = idx; + + if (!ec.IsStatic) + arg_idx++; + + if (is_ref){ + if (arg_idx <= 255) + ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx); + else + ec.ig.Emit (OpCodes.Ldarg, arg_idx); + } else { + if (arg_idx <= 255) + ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx); + else + ec.ig.Emit (OpCodes.Ldarga, arg_idx); + } + } + } + + /// <summary> + /// Used for arguments to New(), Invocation() + /// </summary> + public class Argument { + public enum AType : byte { + Expression, + Ref, + Out, + NoArg + }; + + public readonly AType ArgType; + public Expression Expr; + + public Argument (Expression expr, AType type) + { + this.Expr = expr; + this.ArgType = type; + } + + public Type Type { + get { + if (ArgType == AType.Ref || ArgType == AType.Out) + return TypeManager.LookupType (Expr.Type.ToString () + "&"); + else + return Expr.Type; + } + } + + public Parameter.Modifier GetParameterModifier () + { + switch (ArgType) { + case AType.Out: + return Parameter.Modifier.OUT | Parameter.Modifier.ISBYREF; + + case AType.Ref: + return Parameter.Modifier.REF | Parameter.Modifier.ISBYREF; + + default: + return Parameter.Modifier.NONE; + } + } + + public static string FullDesc (Argument a) + { + return (a.ArgType == AType.Ref ? "ref " : + (a.ArgType == AType.Out ? "out " : "")) + + TypeManager.CSharpName (a.Expr.Type); + } + + public bool ResolveMethodGroup (EmitContext ec, Location loc) + { + // FIXME: csc doesn't report any error if you try to use `ref' or + // `out' in a delegate creation expression. + Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup); + if (Expr == null) + return false; + + return true; + } + + public bool Resolve (EmitContext ec, Location loc) + { + // Optional void arguments - MyCall (1,,2) - are resolved later + // in VerifyArgsCompat + if (ArgType == AType.NoArg) + { + return true; + } + + if (ArgType == AType.Ref) { + Expr = Expr.Resolve (ec); + if (Expr == null) + return false; + + Expr = Expr.ResolveLValue (ec, Expr); + } else if (ArgType == AType.Out) + Expr = Expr.ResolveLValue (ec, new EmptyExpression ()); + else + Expr = Expr.Resolve (ec); + + if (Expr == null) + return false; + + if (ArgType == AType.Expression) + return true; + + if (Expr.eclass != ExprClass.Variable){ + // + // We just probe to match the CSC output + // + if (Expr.eclass == ExprClass.PropertyAccess || + Expr.eclass == ExprClass.IndexerAccess){ + Report.Error ( + 206, loc, + "A property or indexer can not be passed as an out or ref " + + "parameter"); + } else { + Report.Error ( + 1510, loc, + "An lvalue is required as an argument to out or ref"); + } + return false; + } + + return true; + } + + public void Emit (EmitContext ec) + { + // + // Ref and Out parameters need to have their addresses taken. + // + // ParameterReferences might already be references, so we want + // to pass just the value + // + if (ArgType == AType.Ref || ArgType == AType.Out){ + AddressOp mode = AddressOp.Store; + + if (ArgType == AType.Ref) + mode |= AddressOp.Load; + + if (Expr is ParameterReference){ + ParameterReference pr = (ParameterReference) Expr; + + if (pr.is_ref) + pr.EmitLoad (ec); + else { + + pr.AddressOf (ec, mode); + } + } else + ((IMemoryLocation)Expr).AddressOf (ec, mode); + } else + Expr.Emit (ec); + } + } + + /// <summary> + /// Invocation of methods or delegates. + /// </summary> + public class Invocation : ExpressionStatement { + public ArrayList Arguments; + + Expression expr; + MethodBase method = null; + bool is_base; + + static Hashtable method_parameter_cache; + static MemberFilter CompareName; + + static Invocation () + { + method_parameter_cache = new PtrHashtable (); + } + + // + // arguments is an ArrayList, but we do not want to typecast, + // as it might be null. + // + // FIXME: only allow expr to be a method invocation or a + // delegate invocation (7.5.5) + // + public Invocation (Expression expr, ArrayList arguments, Location l) + { + this.expr = expr; + Arguments = arguments; + loc = l; + CompareName = new MemberFilter (compare_name_filter); + } + + public Expression Expr { + get { + return expr; + } + } + + /// <summary> + /// Returns the Parameters (a ParameterData interface) for the + /// Method `mb' + /// </summary> + public static ParameterData GetParameterData (MethodBase mb) + { + object pd = method_parameter_cache [mb]; + object ip; + + if (pd != null) + return (ParameterData) pd; + + + ip = TypeManager.LookupParametersByBuilder (mb); + if (ip != null){ + method_parameter_cache [mb] = ip; + + return (ParameterData) ip; + } else { + ParameterInfo [] pi = mb.GetParameters (); + ReflectionParameters rp = new ReflectionParameters (pi); + method_parameter_cache [mb] = rp; + + return (ParameterData) rp; + } + } + + /// <summary> + /// Determines "better conversion" as specified in 7.4.2.3 + /// Returns : 1 if a->p is better + /// 0 if a->q or neither is better + /// </summary> + static int BetterConversion (EmitContext ec, Argument a, Type p, Type q, Location loc) + { + Type argument_type = a.Type; + Expression argument_expr = a.Expr; + + if (argument_type == null) + throw new Exception ("Expression of type " + a.Expr + " does not resolve its type"); + + // + // This is a special case since csc behaves this way. I can't find + // it anywhere in the spec but oh well ... + // + if (argument_expr is NullLiteral && p == TypeManager.string_type && q == TypeManager.object_type) + return 1; + else if (argument_expr is NullLiteral && p == TypeManager.object_type && q == TypeManager.string_type) + return 0; + + if (p == q) + return 0; + + if (argument_type == p) + return 1; + + if (argument_type == q) + return 0; + + // + // Now probe whether an implicit constant expression conversion + // can be used. + // + // An implicit constant expression conversion permits the following + // conversions: + // + // * A constant-expression of type `int' can be converted to type + // sbyte, byute, short, ushort, uint, ulong provided the value of + // of the expression is withing the range of the destination type. + // + // * A constant-expression of type long can be converted to type + // ulong, provided the value of the constant expression is not negative + // + // FIXME: Note that this assumes that constant folding has + // taken place. We dont do constant folding yet. + // + + if (argument_expr is IntConstant){ + IntConstant ei = (IntConstant) argument_expr; + int value = ei.Value; + + if (p == TypeManager.sbyte_type){ + if (value >= SByte.MinValue && value <= SByte.MaxValue) + return 1; + } else if (p == TypeManager.byte_type){ + if (Byte.MinValue >= 0 && value <= Byte.MaxValue) + return 1; + } else if (p == TypeManager.short_type){ + if (value >= Int16.MinValue && value <= Int16.MaxValue) + return 1; + } else if (p == TypeManager.ushort_type){ + if (value >= UInt16.MinValue && value <= UInt16.MaxValue) + return 1; + } else if (p == TypeManager.uint32_type){ + // + // we can optimize this case: a positive int32 + // always fits on a uint32 + // + if (value >= 0) + return 1; + } else if (p == TypeManager.uint64_type){ + // + // we can optimize this case: a positive int32 + // always fits on a uint64 + // + if (value >= 0) + return 1; + } + } else if (argument_type == TypeManager.int64_type && argument_expr is LongConstant){ + LongConstant lc = (LongConstant) argument_expr; + + if (p == TypeManager.uint64_type){ + if (lc.Value > 0) + return 1; + } + } + + if (q == null) { + Expression tmp = ConvertImplicit (ec, argument_expr, p, loc); + + if (tmp != null) + return 1; + else + return 0; + } + + Expression p_tmp = new EmptyExpression (p); + Expression q_tmp = new EmptyExpression (q); + + if (StandardConversionExists (p_tmp, q) == true && + StandardConversionExists (q_tmp, p) == false) + return 1; + + if (p == TypeManager.sbyte_type) + if (q == TypeManager.byte_type || q == TypeManager.ushort_type || + q == TypeManager.uint32_type || q == TypeManager.uint64_type) + return 1; + + if (p == TypeManager.short_type) + if (q == TypeManager.ushort_type || q == TypeManager.uint32_type || + q == TypeManager.uint64_type) + return 1; + + if (p == TypeManager.int32_type) + if (q == TypeManager.uint32_type || q == TypeManager.uint64_type) + return 1; + + if (p == TypeManager.int64_type) + if (q == TypeManager.uint64_type) + return 1; + + return 0; + } + + /// <summary> + /// Determines "Better function" + /// </summary> + /// <remarks> + /// and returns an integer indicating : + /// 0 if candidate ain't better + /// 1 if candidate is better than the current best match + /// </remarks> + static int BetterFunction (EmitContext ec, ArrayList args, + MethodBase candidate, MethodBase best, + bool expanded_form, Location loc) + { + ParameterData candidate_pd = GetParameterData (candidate); + ParameterData best_pd; + int argument_count; + + if (args == null) + argument_count = 0; + else + argument_count = args.Count; + + int cand_count = candidate_pd.Count; + + if (cand_count == 0 && argument_count == 0) + return 1; + + if (candidate_pd.ParameterModifier (cand_count - 1) != Parameter.Modifier.PARAMS) + if (cand_count != argument_count) + return 0; + + if (best == null) { + int x = 0; + + if (argument_count == 0 && cand_count == 1 && + candidate_pd.ParameterModifier (cand_count - 1) == Parameter.Modifier.PARAMS) + return 1; + + for (int j = argument_count; j > 0;) { + j--; + + Argument a = (Argument) args [j]; + Type t = candidate_pd.ParameterType (j); + + if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS) + if (expanded_form) + t = t.GetElementType (); + + x = BetterConversion (ec, a, t, null, loc); + + if (x <= 0) + break; + } + + if (x > 0) + return 1; + else + return 0; + } + + best_pd = GetParameterData (best); + + int rating1 = 0, rating2 = 0; + + for (int j = 0; j < argument_count; ++j) { + int x, y; + + Argument a = (Argument) args [j]; + + Type ct = candidate_pd.ParameterType (j); + Type bt = best_pd.ParameterType (j); + + if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS) + if (expanded_form) + ct = ct.GetElementType (); + + if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS) + if (expanded_form) + bt = bt.GetElementType (); + + x = BetterConversion (ec, a, ct, bt, loc); + y = BetterConversion (ec, a, bt, ct, loc); + + if (x < y) + return 0; + + rating1 += x; + rating2 += y; + } + + if (rating1 > rating2) + return 1; + else + return 0; + } + + public static string FullMethodDesc (MethodBase mb) + { + string ret_type = ""; + + if (mb is MethodInfo) + ret_type = TypeManager.CSharpName (((MethodInfo) mb).ReturnType) + " "; + + StringBuilder sb = new StringBuilder (ret_type + mb.Name); + ParameterData pd = GetParameterData (mb); + + int count = pd.Count; + sb.Append (" ("); + + for (int i = count; i > 0; ) { + i--; + + sb.Append (pd.ParameterDesc (count - i - 1)); + if (i != 0) + sb.Append (", "); + } + + sb.Append (")"); + return sb.ToString (); + } + + public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc) + { + MemberInfo [] miset; + MethodGroupExpr union; + + if (mg1 == null){ + if (mg2 == null) + return null; + return (MethodGroupExpr) mg2; + } else { + if (mg2 == null) + return (MethodGroupExpr) mg1; + } + + MethodGroupExpr left_set = null, right_set = null; + int length1 = 0, length2 = 0; + + left_set = (MethodGroupExpr) mg1; + length1 = left_set.Methods.Length; + + right_set = (MethodGroupExpr) mg2; + length2 = right_set.Methods.Length; + + ArrayList common = new ArrayList (); + + foreach (MethodBase l in left_set.Methods){ + foreach (MethodBase r in right_set.Methods){ + if (l != r) + continue; + common.Add (r); + break; + } + } + + miset = new MemberInfo [length1 + length2 - common.Count]; + left_set.Methods.CopyTo (miset, 0); + + int k = length1; + + foreach (MemberInfo mi in right_set.Methods){ + if (!common.Contains (mi)) + miset [k++] = mi; + } + + union = new MethodGroupExpr (miset, loc); + + return union; + } + + /// <summary> + /// Determines is the candidate method, if a params method, is applicable + /// in its expanded form to the given set of arguments + /// </summary> + static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments, MethodBase candidate) + { + int arg_count; + + if (arguments == null) + arg_count = 0; + else + arg_count = arguments.Count; + + ParameterData pd = GetParameterData (candidate); + + int pd_count = pd.Count; + + if (pd_count == 0) + return false; + + if (pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS) + return false; + + if (pd_count - 1 > arg_count) + return false; + + if (pd_count == 1 && arg_count == 0) + return true; + + // + // If we have come this far, the case which remains is when the number of parameters + // is less than or equal to the argument count. + // + for (int i = 0; i < pd_count - 1; ++i) { + + Argument a = (Argument) arguments [i]; + + Parameter.Modifier a_mod = a.GetParameterModifier () & + ~(Parameter.Modifier.OUT | Parameter.Modifier.REF); + Parameter.Modifier p_mod = pd.ParameterModifier (i) & + ~(Parameter.Modifier.OUT | Parameter.Modifier.REF); + + if (a_mod == p_mod) { + + if (a_mod == Parameter.Modifier.NONE) + if (!ImplicitConversionExists (ec, a.Expr, pd.ParameterType (i))) + return false; + + if ((a_mod & Parameter.Modifier.ISBYREF) != 0) { + Type pt = pd.ParameterType (i); + + if (!pt.IsByRef) + pt = TypeManager.LookupType (pt.FullName + "&"); + + if (pt != a.Type) + return false; + } + } else + return false; + + } + + Type element_type = pd.ParameterType (pd_count - 1).GetElementType (); + + for (int i = pd_count - 1; i < arg_count; i++) { + Argument a = (Argument) arguments [i]; + + if (!StandardConversionExists (a.Expr, element_type)) + return false; + } + + return true; + } + + static bool CheckParameterAgainstArgument (EmitContext ec, ParameterData pd, int i, Argument a, Type ptype) + { + Parameter.Modifier a_mod = a.GetParameterModifier () & + ~(Parameter.Modifier.OUT | Parameter.Modifier.REF); + Parameter.Modifier p_mod = pd.ParameterModifier (i) & + ~(Parameter.Modifier.OUT | Parameter.Modifier.REF); + + if (a_mod == p_mod || + (a_mod == Parameter.Modifier.NONE && p_mod == Parameter.Modifier.PARAMS)) { + if (a_mod == Parameter.Modifier.NONE) + if (!ImplicitConversionExists (ec, a.Expr, ptype)) + return false; + + if ((a_mod & Parameter.Modifier.ISBYREF) != 0) { + Type pt = pd.ParameterType (i); + + if (!pt.IsByRef) + pt = TypeManager.LookupType (pt.FullName + "&"); + + if (pt != a.Type) + return false; + } + } else + return false; + + return true; + } + + /// <summary> + /// Determines if the candidate method is applicable (section 14.4.2.1) + /// to the given set of arguments + /// </summary> + static bool IsApplicable (EmitContext ec, ref ArrayList arguments, MethodBase candidate) + { + int arg_count, ps_count, po_count; + Type param_type; + + if (arguments == null) + arg_count = 0; + else + arg_count = arguments.Count; + + ParameterData pd = GetParameterData (candidate); + Parameters ps = GetFullParameters (candidate); + + if (ps == null) { + ps_count = 0; + po_count = 0; + } + else + { + ps_count = ps.CountStandardParams(); + po_count = ps.CountOptionalParams(); + } + int pd_count = pd.Count; + + // Validate argument count + if (po_count == 0) { + if (arg_count != pd.Count) + return false; + } + else + { + if ((arg_count < ps_count) || (arg_count > pd_count)) + return false; + } + + if (arg_count > 0) { + for (int i = arg_count; i > 0 ; ) { + i--; + + Argument a = (Argument) arguments [i]; + if (a.ArgType == Argument.AType.NoArg){ + Parameter p = (Parameter) ps.FixedParameters[i]; + a = new Argument (p.ParameterInitializer, Argument.AType.Expression); + param_type = p.ParameterInitializer.Type; + } + else + param_type = pd.ParameterType (i); + + if (!CheckParameterAgainstArgument (ec, pd, i, a, param_type)) + return (false); + } + } + else + { + // If we have no arguments AND the first parameter is optional + // we must check for a candidate (the loop above wouldn't) + if (po_count > 0) { + ArrayList arglist = new ArrayList(); + + // Since we got so far, there's no need to check if + // arguments are optional; we simply retrieve + // parameter default values and build a brand-new + // argument list. + + for (int i = 0; i < ps.FixedParameters.Length; i++) { + Parameter p = ps.FixedParameters[i]; + Argument a = new Argument (p.ParameterInitializer, Argument.AType.Expression); + a.Resolve(ec, Location.Null); + arglist.Add (a); + } + arguments = arglist; + return true; + } + } + // We've found a candidate, so we exchange the dummy NoArg arguments + // with new arguments containing the default value for that parameter + for (int i = 0; i < arg_count; i++) { + Argument a = (Argument) arguments [i]; + if (a.ArgType == Argument.AType.NoArg){ + Parameter p = (Parameter) ps.FixedParameters[i]; + a = new Argument (p.ParameterInitializer, Argument.AType.Expression); + a.Resolve(ec, Location.Null); + arguments [i] = a; + } + } + + return true; + } + + static bool compare_name_filter (MemberInfo m, object filterCriteria) + { + return (m.Name == ((string) filterCriteria)); + } + + static Parameters GetFullParameters (MethodBase mb) + { + TypeContainer tc = TypeManager.LookupTypeContainer (mb.DeclaringType); + InternalParameters ip = TypeManager.LookupParametersByBuilder(mb); + + return (ip != null) ? ip.Parameters : null; + } + + // We need an overload for OverloadResolve because Invocation.DoResolve + // must pass Arguments by reference, since a later call to IsApplicable + // can change the argument list if optional parameters are defined + // in the method declaration + public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me, + ArrayList Arguments, Location loc) + { + ArrayList a = Arguments; + return OverloadResolve (ec, me, ref a, loc); + } + + /// <summary> + /// Find the Applicable Function Members (7.4.2.1) + /// + /// me: Method Group expression with the members to select. + /// it might contain constructors or methods (or anything + /// that maps to a method). + /// + /// Arguments: ArrayList containing resolved Argument objects. + /// + /// loc: The location if we want an error to be reported, or a Null + /// location for "probing" purposes. + /// + /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo) + /// that is the best match of me on Arguments. + /// + /// </summary> + public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me, + ref ArrayList Arguments, Location loc) + { + ArrayList afm = new ArrayList (); + MethodBase method = null; + Type current_type = null; + int argument_count; + ArrayList candidates = new ArrayList (); + + foreach (MethodBase candidate in me.Methods){ + int x; + + // If we're going one level higher in the class hierarchy, abort if + // we already found an applicable method. + if (candidate.DeclaringType != current_type) { + current_type = candidate.DeclaringType; + if (method != null) + break; + } + + // Check if candidate is applicable (section 14.4.2.1) + if (!IsApplicable (ec, ref Arguments, candidate)) + continue; + + candidates.Add (candidate); + x = BetterFunction (ec, Arguments, candidate, method, false, loc); + + if (x == 0) + continue; + + method = candidate; + } + + if (Arguments == null) + argument_count = 0; + else + argument_count = Arguments.Count; + + + // + // Now we see if we can find params functions, applicable in their expanded form + // since if they were applicable in their normal form, they would have been selected + // above anyways + // + bool chose_params_expanded = false; + + if (method == null) { + candidates = new ArrayList (); + foreach (MethodBase candidate in me.Methods){ + if (!IsParamsMethodApplicable (ec, Arguments, candidate)) + continue; + + candidates.Add (candidate); + + int x = BetterFunction (ec, Arguments, candidate, method, true, loc); + if (x == 0) + continue; + + method = candidate; + chose_params_expanded = true; + } + } + + if (method == null) { + // + // Okay so we have failed to find anything so we + // return by providing info about the closest match + // + for (int i = 0; i < me.Methods.Length; ++i) { + + MethodBase c = (MethodBase) me.Methods [i]; + ParameterData pd = GetParameterData (c); + + if (pd.Count != argument_count) + continue; + + VerifyArgumentsCompat (ec, Arguments, argument_count, c, false, + null, loc); + } + + return null; + } + + // + // Now check that there are no ambiguities i.e the selected method + // should be better than all the others + // + + foreach (MethodBase candidate in candidates){ + if (candidate == method) + continue; + + // + // If a normal method is applicable in the sense that it has the same + // number of arguments, then the expanded params method is never applicable + // so we debar the params method. + // + if (IsParamsMethodApplicable (ec, Arguments, candidate) && + IsApplicable (ec, ref Arguments, method)) + continue; + + int x = BetterFunction (ec, Arguments, method, candidate, + chose_params_expanded, loc); + + if (x != 1) { + Report.Error ( + 121, loc, + "Ambiguous call when selecting function due to implicit casts"); + return null; + } + } + + // + // And now check if the arguments are all compatible, perform conversions + // if necessary etc. and return if everything is all right + // + if (VerifyArgumentsCompat (ec, Arguments, argument_count, method, + chose_params_expanded, null, loc)) + return method; + else + return null; + } + + public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments, + int argument_count, + MethodBase method, + bool chose_params_expanded, + Type delegate_type, + Location loc) + { + return (VerifyArgumentsCompat (ec, Arguments, argument_count, + method, chose_params_expanded, delegate_type, loc, null)); + } + + public static bool VerifyArgumentsCompat (EmitContext ec, + ArrayList Arguments, + int argument_count, + MethodBase method, + bool chose_params_expanded, + Type delegate_type, + Location loc, + string InvokingProperty) + { + ParameterData pd = GetParameterData (method); + int pd_count = pd.Count; + + for (int j = 0; j < argument_count; j++) { + Argument a = (Argument) Arguments [j]; + Expression a_expr = a.Expr; + Type parameter_type = pd.ParameterType (j); + + if (pd.ParameterModifier (j) == Parameter.Modifier.PARAMS && + chose_params_expanded) + parameter_type = TypeManager.TypeToCoreType (parameter_type.GetElementType ()); + + if (a.Type != parameter_type){ + Expression conv; + + conv = ConvertImplicit (ec, a_expr, parameter_type, loc); + + if (conv == null) { + if (!Location.IsNull (loc)) { + if (delegate_type == null) + if (InvokingProperty == null) + Report.Error (1502, loc, + "The best overloaded match for method '" + + FullMethodDesc (method) + + "' has some invalid arguments"); + else + Report.Error (1502, loc, + "Property '" + + InvokingProperty + + "' has some invalid arguments"); + else + Report.Error (1594, loc, + "Delegate '" + delegate_type.ToString () + + "' has some invalid arguments."); + Report.Error (1503, loc, + "Argument " + (j+1) + + ": Cannot convert from '" + Argument.FullDesc (a) + + "' to '" + pd.ParameterDesc (j) + "'"); + } + + return false; + } + + // + // Update the argument with the implicit conversion + // + if (a_expr != conv) + a.Expr = conv; + } + + Parameter.Modifier a_mod = a.GetParameterModifier () & + ~(Parameter.Modifier.OUT | Parameter.Modifier.REF); + Parameter.Modifier p_mod = pd.ParameterModifier (j) & + ~(Parameter.Modifier.OUT | Parameter.Modifier.REF); + + + if (a_mod != p_mod && + pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS) { + if (!Location.IsNull (loc)) { + Report.Error (1502, loc, + "The best overloaded match for method '" + FullMethodDesc (method)+ + "' has some invalid arguments"); + Report.Error (1503, loc, + "Argument " + (j+1) + + ": Cannot convert from '" + Argument.FullDesc (a) + + "' to '" + pd.ParameterDesc (j) + "'"); + } + + return false; + } + } + + return true; + } + + public override Expression DoResolve (EmitContext ec) + { + // + // First, resolve the expression that is used to + // trigger the invocation + // + Expression expr_to_return = null; + + if (expr is BaseAccess) + is_base = true; + + expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup); + if (expr == null) + return null; + + if (!(expr is MethodGroupExpr)) + { + Type expr_type = expr.Type; + + if (expr_type != null) + { + bool IsDelegate = TypeManager.IsDelegateType (expr_type); + if (IsDelegate) + return (new DelegateInvocation ( + this.expr, Arguments, loc)).Resolve (ec); + } + } + /* + if (!(expr is MethodGroupExpr)){ + expr.Error118 (ResolveFlags.MethodGroup); + return null; + } + */ + // + // Next, evaluate all the expressions in the argument list + // + if (Arguments != null) + { + foreach (Argument a in Arguments) + { + if ((a.ArgType == Argument.AType.NoArg) && (!(expr is MethodGroupExpr))) + Report.Error (999, "This item cannot have empty arguments"); + + if (!a.Resolve (ec, loc)) + return null; + } + } + + if (expr is MethodGroupExpr) + { + MethodGroupExpr mg = (MethodGroupExpr) expr; + method = OverloadResolve (ec, mg, ref Arguments, loc); + + if (method == null) + { + Error (-6, + "Could not find any applicable function for this argument list"); + return null; + } + + MethodInfo mi = method as MethodInfo; + if (mi != null) + { + type = TypeManager.TypeToCoreType (mi.ReturnType); + if (!mi.IsStatic && !mg.IsExplicitImpl && (mg.InstanceExpression == null)) + SimpleName.Error_ObjectRefRequired (ec, loc, mi.Name); + } + + if (type.IsPointer) + { + if (!ec.InUnsafe) + { + UnsafeError (loc); + return null; + } + } + eclass = ExprClass.Value; + expr_to_return = this; + } + + if (expr is PropertyExpr) + { + PropertyExpr pe = ((PropertyExpr) expr); + pe.PropertyArgs = (ArrayList) Arguments.Clone(); + Arguments.Clear(); + Arguments = new ArrayList(); + MethodBase mi = pe.PropertyInfo.GetGetMethod(true); + + if(VerifyArgumentsCompat (ec, pe.PropertyArgs, + pe.PropertyArgs.Count, mi, false, null, loc, pe.Name)) + { + + expr_to_return = pe.DoResolve (ec); + expr_to_return.eclass = ExprClass.PropertyAccess; + } + } + + if (expr is FieldExpr) { + // If we are here, expr must be an ArrayAccess + // FIXME: we should check dimensions, etc. + ArrayList idxs = new ArrayList(); + foreach (Argument a in Arguments) + { + idxs.Add (a.Expr); + } + ElementAccess ea = new ElementAccess (expr, idxs, expr.Location); + ArrayAccess aa = new ArrayAccess (ea, expr.Location); + expr_to_return = aa.DoResolve(ec); + expr_to_return.eclass = ExprClass.Variable; + } + + return expr_to_return; + } + + // <summary> + // Emits the list of arguments as an array + // </summary> + static void EmitParams (EmitContext ec, int idx, ArrayList arguments) + { + ILGenerator ig = ec.ig; + int count = arguments.Count - idx; + Argument a = (Argument) arguments [idx]; + Type t = a.Expr.Type; + string array_type = t.FullName + "[]"; + LocalBuilder array; + + array = ig.DeclareLocal (TypeManager.LookupType (array_type)); + IntConstant.EmitInt (ig, count); + ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t)); + ig.Emit (OpCodes.Stloc, array); + + int top = arguments.Count; + for (int j = idx; j < top; j++){ + a = (Argument) arguments [j]; + + ig.Emit (OpCodes.Ldloc, array); + IntConstant.EmitInt (ig, j - idx); + a.Emit (ec); + + ArrayAccess.EmitStoreOpcode (ig, t); + } + ig.Emit (OpCodes.Ldloc, array); + } + + /// <summary> + /// Emits a list of resolved Arguments that are in the arguments + /// ArrayList. + /// + /// The MethodBase argument might be null if the + /// emission of the arguments is known not to contain + /// a `params' field (for example in constructors or other routines + /// that keep their arguments in this structure) + /// </summary> + public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments) + { + ParameterData pd; + if (mb != null) + pd = GetParameterData (mb); + else + pd = null; + + // + // If we are calling a params method with no arguments, special case it + // + if (arguments == null){ + if (pd != null && pd.Count > 0 && + pd.ParameterModifier (0) == Parameter.Modifier.PARAMS){ + ILGenerator ig = ec.ig; + + IntConstant.EmitInt (ig, 0); + ig.Emit (OpCodes.Newarr, pd.ParameterType (0).GetElementType ()); + } + return; + } + + int top = arguments.Count; + + for (int i = 0; i < top; i++){ + Argument a = (Argument) arguments [i]; + + if (pd != null){ + if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){ + // + // Special case if we are passing the same data as the + // params argument, do not put it in an array. + // + if (pd.ParameterType (i) == a.Type) + a.Emit (ec); + else + EmitParams (ec, i, arguments); + return; + } + } + + a.Emit (ec); + } + + if (pd != null && pd.Count > top && + pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){ + ILGenerator ig = ec.ig; + + IntConstant.EmitInt (ig, 0); + ig.Emit (OpCodes.Newarr, pd.ParameterType (top).GetElementType ()); + } + } + + /// <remarks> + /// is_base tells whether we want to force the use of the `call' + /// opcode instead of using callvirt. Call is required to call + /// a specific method, while callvirt will always use the most + /// recent method in the vtable. + /// + /// is_static tells whether this is an invocation on a static method + /// + /// instance_expr is an expression that represents the instance + /// it must be non-null if is_static is false. + /// + /// method is the method to invoke. + /// + /// Arguments is the list of arguments to pass to the method or constructor. + /// </remarks> + public static void EmitCall (EmitContext ec, bool is_base, + bool is_static, Expression instance_expr, + MethodBase method, ArrayList Arguments, Location loc) + { + EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, null, loc); + } + + public static void EmitCall (EmitContext ec, bool is_base, + bool is_static, Expression instance_expr, + MethodBase method, ArrayList Arguments, ArrayList prop_args, Location loc) + { + ILGenerator ig = ec.ig; + bool struct_call = false; + + Type decl_type = method.DeclaringType; + + if (!RootContext.StdLib) + { + // Replace any calls to the system's System.Array type with calls to + // the newly created one. + if (method == TypeManager.system_int_array_get_length) + method = TypeManager.int_array_get_length; + else if (method == TypeManager.system_int_array_get_rank) + method = TypeManager.int_array_get_rank; + else if (method == TypeManager.system_object_array_clone) + method = TypeManager.object_array_clone; + else if (method == TypeManager.system_int_array_get_length_int) + method = TypeManager.int_array_get_length_int; + else if (method == TypeManager.system_int_array_get_lower_bound_int) + method = TypeManager.int_array_get_lower_bound_int; + else if (method == TypeManager.system_int_array_get_upper_bound_int) + method = TypeManager.int_array_get_upper_bound_int; + else if (method == TypeManager.system_void_array_copyto_array_int) + method = TypeManager.void_array_copyto_array_int; + } + + // + // This checks the `ConditionalAttribute' on the method, and the + // ObsoleteAttribute + // + TypeManager.MethodFlags flags = TypeManager.GetMethodFlags (method, loc); + if ((flags & TypeManager.MethodFlags.IsObsoleteError) != 0) + return; + if ((flags & TypeManager.MethodFlags.ShouldIgnore) != 0) + return; + + if (!is_static) + { + if (decl_type.IsValueType) + struct_call = true; + // + // If this is ourselves, push "this" + // + if (instance_expr == null) + { + ig.Emit (OpCodes.Ldarg_0); + } + else + { + // + // Push the instance expression + // + if (instance_expr.Type.IsValueType) + { + // + // Special case: calls to a function declared in a + // reference-type with a value-type argument need + // to have their value boxed. + + struct_call = true; + if (decl_type.IsValueType) + { + // + // If the expression implements IMemoryLocation, then + // we can optimize and use AddressOf on the + // return. + // + // If not we have to use some temporary storage for + // it. + if (instance_expr is IMemoryLocation) + { + ((IMemoryLocation)instance_expr). + AddressOf (ec, AddressOp.LoadStore); + } + else + { + Type t = instance_expr.Type; + + instance_expr.Emit (ec); + LocalBuilder temp = ig.DeclareLocal (t); + ig.Emit (OpCodes.Stloc, temp); + ig.Emit (OpCodes.Ldloca, temp); + } + } + else + { + instance_expr.Emit (ec); + ig.Emit (OpCodes.Box, instance_expr.Type); + } + } + else + instance_expr.Emit (ec); + } + } + + if (prop_args != null && prop_args.Count > 0) + { + if (Arguments == null) + Arguments = new ArrayList(); + + for (int i = prop_args.Count-1; i >=0 ; i--) + { + Arguments.Insert (0,prop_args[i]); + } + + } + + EmitArguments (ec, method, Arguments); + + if (is_static || struct_call || is_base) + { + if (method is MethodInfo) + { + ig.Emit (OpCodes.Call, (MethodInfo) method); + } + else + ig.Emit (OpCodes.Call, (ConstructorInfo) method); + } + else + { + if (method is MethodInfo) + ig.Emit (OpCodes.Callvirt, (MethodInfo) method); + else + ig.Emit (OpCodes.Callvirt, (ConstructorInfo) method); + } + } + + static void EmitPropertyArgs (EmitContext ec, ArrayList prop_args) + { + int top = prop_args.Count; + + for (int i = 0; i < top; i++) + { + Argument a = (Argument) prop_args [i]; + a.Emit (ec); + } + } + + public override void Emit (EmitContext ec) + { + MethodGroupExpr mg = (MethodGroupExpr) this.expr; + + EmitCall ( + ec, is_base, method.IsStatic, mg.InstanceExpression, method, Arguments, loc); + } + + public override void EmitStatement (EmitContext ec) + { + Emit (ec); + + // + // Pop the return value if there is one + // + if (method is MethodInfo){ + Type ret = ((MethodInfo)method).ReturnType; + if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type) + ec.ig.Emit (OpCodes.Pop); + } + } + } + + // + // This class is used to "disable" the code generation for the + // temporary variable when initializing value types. + // + class EmptyAddressOf : EmptyExpression, IMemoryLocation { + public void AddressOf (EmitContext ec, AddressOp Mode) + { + // nothing + } + } + + /// <summary> + /// Implements the new expression + /// </summary> + public class New : ExpressionStatement { + public readonly ArrayList Arguments; + public readonly Expression RequestedType; + + MethodBase method = null; + + // + // If set, the new expression is for a value_target, and + // we will not leave anything on the stack. + // + Expression value_target; + bool value_target_set = false; + + public New (Expression requested_type, ArrayList arguments, Location l) + { + RequestedType = requested_type; + Arguments = arguments; + loc = l; + } + + public Expression ValueTypeVariable { + get { + return value_target; + } + + set { + value_target = value; + value_target_set = true; + } + } + + // + // This function is used to disable the following code sequence for + // value type initialization: + // + // AddressOf (temporary) + // Construct/Init + // LoadTemporary + // + // Instead the provide will have provided us with the address on the + // stack to store the results. + // + static Expression MyEmptyExpression; + + public void DisableTemporaryValueType () + { + if (MyEmptyExpression == null) + MyEmptyExpression = new EmptyAddressOf (); + + // + // To enable this, look into: + // test-34 and test-89 and self bootstrapping. + // + // For instance, we can avoid a copy by using `newobj' + // instead of Call + Push-temp on value types. +// value_target = MyEmptyExpression; + } + + public override Expression DoResolve (EmitContext ec) + { + type = ec.DeclSpace.ResolveType (RequestedType, false, loc); + + if (type == null) + return null; + + bool IsDelegate = TypeManager.IsDelegateType (type); + + if (IsDelegate) + return (new NewDelegate (type, Arguments, loc)).Resolve (ec); + + if (type.IsInterface || type.IsAbstract){ + Error ( + 144, "It is not possible to create instances of interfaces " + + "or abstract classes"); + return null; + } + + bool is_struct = false; + is_struct = type.IsValueType; + eclass = ExprClass.Value; + + // + // SRE returns a match for .ctor () on structs (the object constructor), + // so we have to manually ignore it. + // + if (is_struct && Arguments == null) + return this; + + Expression ml; + ml = MemberLookupFinal (ec, type, ".ctor", + MemberTypes.Constructor, + AllBindingFlags | BindingFlags.Public, loc); + + if (ml == null) + return null; + + if (! (ml is MethodGroupExpr)){ + if (!is_struct){ + ml.Error118 ("method group"); + return null; + } + } + + if (ml != null) { + if (Arguments != null){ + foreach (Argument a in Arguments){ + if (!a.Resolve (ec, loc)) + return null; + } + } + + method = Invocation.OverloadResolve (ec, (MethodGroupExpr) ml, + Arguments, loc); + + } + + if (method == null) { + if (!is_struct || Arguments.Count > 0) { + Error (1501, + "New invocation: Can not find a constructor for " + + "this argument list"); + return null; + } + } + return this; + } + + // + // This DoEmit can be invoked in two contexts: + // * As a mechanism that will leave a value on the stack (new object) + // * As one that wont (init struct) + // + // You can control whether a value is required on the stack by passing + // need_value_on_stack. The code *might* leave a value on the stack + // so it must be popped manually + // + // If we are dealing with a ValueType, we have a few + // situations to deal with: + // + // * The target is a ValueType, and we have been provided + // the instance (this is easy, we are being assigned). + // + // * The target of New is being passed as an argument, + // to a boxing operation or a function that takes a + // ValueType. + // + // In this case, we need to create a temporary variable + // that is the argument of New. + // + // Returns whether a value is left on the stack + // + bool DoEmit (EmitContext ec, bool need_value_on_stack) + { + bool is_value_type = type.IsValueType; + ILGenerator ig = ec.ig; + + if (is_value_type){ + IMemoryLocation ml; + + // Allow DoEmit() to be called multiple times. + // We need to create a new LocalTemporary each time since + // you can't share LocalBuilders among ILGeneators. + if (!value_target_set) + value_target = new LocalTemporary (ec, type); + + ml = (IMemoryLocation) value_target; + ml.AddressOf (ec, AddressOp.Store); + } + + if (method != null) + Invocation.EmitArguments (ec, method, Arguments); + + if (is_value_type){ + if (method == null) + ig.Emit (OpCodes.Initobj, type); + else + ig.Emit (OpCodes.Call, (ConstructorInfo) method); + if (need_value_on_stack){ + value_target.Emit (ec); + return true; + } + return false; + } else { + ig.Emit (OpCodes.Newobj, (ConstructorInfo) method); + return true; + } + } + + public override void Emit (EmitContext ec) + { + DoEmit (ec, true); + } + + public override void EmitStatement (EmitContext ec) + { + if (DoEmit (ec, false)) + ec.ig.Emit (OpCodes.Pop); + } + } + + /// <summary> + /// 14.5.10.2: Represents an array creation expression. + /// </summary> + /// + /// <remarks> + /// There are two possible scenarios here: one is an array creation + /// expression that specifies the dimensions and optionally the + /// initialization data and the other which does not need dimensions + /// specified but where initialization data is mandatory. + /// </remarks> + public class ArrayCreation : ExpressionStatement { + Expression requested_base_type; + ArrayList initializers; + + // + // The list of Argument types. + // This is used to construct the `newarray' or constructor signature + // + ArrayList arguments; + + // + // Method used to create the array object. + // + MethodBase new_method = null; + + Type array_element_type; + Type underlying_type; + bool is_one_dimensional = false; + bool is_builtin_type = false; + bool expect_initializers = false; + int num_arguments = 0; + int dimensions = 0; + string rank; + + ArrayList array_data; + + Hashtable bounds; + + // + // The number of array initializers that we can handle + // via the InitializeArray method - through EmitStaticInitializers + // + int num_automatic_initializers; + + public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l) + { + this.requested_base_type = requested_base_type; + this.initializers = initializers; + this.rank = rank; + loc = l; + + arguments = new ArrayList (); + + foreach (Expression e in exprs) { + arguments.Add (new Argument (e, Argument.AType.Expression)); + num_arguments++; + } + } + + public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l) + { + this.requested_base_type = requested_base_type; + this.initializers = initializers; + this.rank = rank; + loc = l; + + //this.rank = rank.Substring (0, rank.LastIndexOf ("[")); + // + //string tmp = rank.Substring (rank.LastIndexOf ("[")); + // + //dimensions = tmp.Length - 1; + expect_initializers = true; + } + + public Expression FormArrayType (Expression base_type, int idx_count, string rank) + { + StringBuilder sb = new StringBuilder (rank); + + sb.Append ("["); + for (int i = 1; i < idx_count; i++) + sb.Append (","); + + sb.Append ("]"); + + return new ComposedCast (base_type, sb.ToString (), loc); + } + + void Error_IncorrectArrayInitializer () + { + Error (178, "Incorrectly structured array initializer"); + } + + public bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims) + { + if (specified_dims) { + Argument a = (Argument) arguments [idx]; + + if (!a.Resolve (ec, loc)) + return false; + + if (!(a.Expr is Constant)) { + Error (150, "A constant value is expected"); + return false; + } + + int value = (int) ((Constant) a.Expr).GetValue (); + + if (value != probe.Count) { + Error_IncorrectArrayInitializer (); + return false; + } + + bounds [idx] = value; + } + + int child_bounds = -1; + foreach (object o in probe) { + if (o is ArrayList) { + int current_bounds = ((ArrayList) o).Count; + + if (child_bounds == -1) + child_bounds = current_bounds; + + else if (child_bounds != current_bounds){ + Error_IncorrectArrayInitializer (); + return false; + } + bool ret = CheckIndices (ec, (ArrayList) o, idx + 1, specified_dims); + if (!ret) + return false; + } else { + if (child_bounds != -1){ + Error_IncorrectArrayInitializer (); + return false; + } + + Expression tmp = (Expression) o; + tmp = tmp.Resolve (ec); + if (tmp == null) + continue; + + // Console.WriteLine ("I got: " + tmp); + // Handle initialization from vars, fields etc. + + Expression conv = ConvertImplicitRequired ( + ec, tmp, underlying_type, loc); + + if (conv == null) + return false; + + if (conv is StringConstant) + array_data.Add (conv); + else if (conv is Constant) { + array_data.Add (conv); + num_automatic_initializers++; + } else + array_data.Add (conv); + } + } + + return true; + } + + public void UpdateIndices (EmitContext ec) + { + int i = 0; + for (ArrayList probe = initializers; probe != null;) { + if (probe.Count > 0 && probe [0] is ArrayList) { + Expression e = new IntConstant (probe.Count); + arguments.Add (new Argument (e, Argument.AType.Expression)); + + bounds [i++] = probe.Count; + + probe = (ArrayList) probe [0]; + + } else { + Expression e = new IntConstant (probe.Count); + arguments.Add (new Argument (e, Argument.AType.Expression)); + + bounds [i++] = probe.Count; + probe = null; + } + } + + } + + public bool ValidateInitializers (EmitContext ec, Type array_type) + { + if (initializers == null) { + if (expect_initializers) + return false; + else + return true; + } + + if (underlying_type == null) + return false; + + // + // We use this to store all the date values in the order in which we + // will need to store them in the byte blob later + // + array_data = new ArrayList (); + bounds = new Hashtable (); + + bool ret; + + if (arguments != null) { + ret = CheckIndices (ec, initializers, 0, true); + return ret; + } else { + arguments = new ArrayList (); + + ret = CheckIndices (ec, initializers, 0, false); + + if (!ret) + return false; + + UpdateIndices (ec); + + if (arguments.Count != dimensions) { + Error_IncorrectArrayInitializer (); + return false; + } + + return ret; + } + } + + void Error_NegativeArrayIndex () + { + Error (284, "Can not create array with a negative size"); + } + + // + // Converts `source' to an int, uint, long or ulong. + // + Expression ExpressionToArrayArgument (EmitContext ec, Expression source) + { + Expression target; + + bool old_checked = ec.CheckState; + ec.CheckState = true; + + target = ConvertImplicit (ec, source, TypeManager.int32_type, loc); + if (target == null){ + target = ConvertImplicit (ec, source, TypeManager.uint32_type, loc); + if (target == null){ + target = ConvertImplicit (ec, source, TypeManager.int64_type, loc); + if (target == null){ + target = ConvertImplicit (ec, source, TypeManager.uint64_type, loc); + if (target == null) + Expression.Error_CannotConvertImplicit (loc, source.Type, TypeManager.int32_type); + } + } + } + ec.CheckState = old_checked; + + // + // Only positive constants are allowed at compile time + // + if (target is Constant){ + if (target is IntConstant){ + if (((IntConstant) target).Value < 0){ + Error_NegativeArrayIndex (); + return null; + } + } + + if (target is LongConstant){ + if (((LongConstant) target).Value < 0){ + Error_NegativeArrayIndex (); + return null; + } + } + + } + + return target; + } + + // + // Creates the type of the array + // + bool LookupType (EmitContext ec) + { + StringBuilder array_qualifier = new StringBuilder (rank); + + // + // `In the first form allocates an array instace of the type that results + // from deleting each of the individual expression from the expression list' + // + if (num_arguments > 0) { + array_qualifier.Append ("["); + for (int i = num_arguments-1; i > 0; i--) + array_qualifier.Append (","); + array_qualifier.Append ("]"); + } + + // + // Lookup the type + // + Expression array_type_expr; + array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc); + type = ec.DeclSpace.ResolveType (array_type_expr, false, loc); + + if (type == null) + return false; + + underlying_type = type; + if (underlying_type.IsArray) + underlying_type = TypeManager.TypeToCoreType (underlying_type.GetElementType ()); + dimensions = type.GetArrayRank (); + + return true; + } + + public override Expression DoResolve (EmitContext ec) + { + int arg_count; + + if (!LookupType (ec)) + return null; + + // + // First step is to validate the initializers and fill + // in any missing bits + // + if (!ValidateInitializers (ec, type)) + return null; + + if (arguments == null) + arg_count = 0; + else { + arg_count = arguments.Count; + foreach (Argument a in arguments){ + if (!a.Resolve (ec, loc)) + return null; + + Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc); + if (real_arg == null) + return null; + + a.Expr = real_arg; + } + } + + array_element_type = TypeManager.TypeToCoreType (type.GetElementType ()); + + if (arg_count == 1) { + is_one_dimensional = true; + eclass = ExprClass.Value; + return this; + } + + is_builtin_type = TypeManager.IsBuiltinType (type); + + if (is_builtin_type) { + Expression ml; + + ml = MemberLookup (ec, type, ".ctor", MemberTypes.Constructor, + AllBindingFlags, loc); + + if (!(ml is MethodGroupExpr)) { + ml.Error118 ("method group"); + return null; + } + + if (ml == null) { + Error (-6, "New invocation: Can not find a constructor for " + + "this argument list"); + return null; + } + + new_method = Invocation.OverloadResolve (ec, (MethodGroupExpr) ml, arguments, loc); + + if (new_method == null) { + Error (-6, "New invocation: Can not find a constructor for " + + "this argument list"); + return null; + } + + eclass = ExprClass.Value; + return this; + } else { + ModuleBuilder mb = CodeGen.ModuleBuilder; + ArrayList args = new ArrayList (); + + if (arguments != null) { + for (int i = 0; i < arg_count; i++) + args.Add (TypeManager.int32_type); + } + + Type [] arg_types = null; + + if (args.Count > 0) + arg_types = new Type [args.Count]; + + args.CopyTo (arg_types, 0); + + new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null, + arg_types); + + if (new_method == null) { + Error (-6, "New invocation: Can not find a constructor for " + + "this argument list"); + return null; + } + + eclass = ExprClass.Value; + return this; + } + } + + public static byte [] MakeByteBlob (ArrayList array_data, Type underlying_type, Location loc) + { + int factor; + byte [] data; + byte [] element; + int count = array_data.Count; + + if (underlying_type.IsEnum) + underlying_type = TypeManager.EnumToUnderlying (underlying_type); + + factor = GetTypeSize (underlying_type); + if (factor == 0) + throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type); + + data = new byte [(count * factor + 4) & ~3]; + int idx = 0; + + for (int i = 0; i < count; ++i) { + object v = array_data [i]; + + if (v is EnumConstant) + v = ((EnumConstant) v).Child; + + if (v is Constant && !(v is StringConstant)) + v = ((Constant) v).GetValue (); + else { + idx += factor; + continue; + } + + if (underlying_type == TypeManager.int64_type){ + if (!(v is Expression)){ + long val = (long) v; + + for (int j = 0; j < factor; ++j) { + data [idx + j] = (byte) (val & 0xFF); + val = (val >> 8); + } + } + } else if (underlying_type == TypeManager.uint64_type){ + if (!(v is Expression)){ + ulong val = (ulong) v; + + for (int j = 0; j < factor; ++j) { + data [idx + j] = (byte) (val & 0xFF); + val = (val >> 8); + } + } + } else if (underlying_type == TypeManager.float_type) { + if (!(v is Expression)){ + element = BitConverter.GetBytes ((float) v); + + for (int j = 0; j < factor; ++j) + data [idx + j] = element [j]; + } + } else if (underlying_type == TypeManager.double_type) { + if (!(v is Expression)){ + element = BitConverter.GetBytes ((double) v); + + for (int j = 0; j < factor; ++j) + data [idx + j] = element [j]; + } + } else if (underlying_type == TypeManager.char_type){ + if (!(v is Expression)){ + int val = (int) ((char) v); + + data [idx] = (byte) (val & 0xff); + data [idx+1] = (byte) (val >> 8); + } + } else if (underlying_type == TypeManager.short_type){ + if (!(v is Expression)){ + int val = (int) ((short) v); + + data [idx] = (byte) (val & 0xff); + data [idx+1] = (byte) (val >> 8); + } + } else if (underlying_type == TypeManager.ushort_type){ + if (!(v is Expression)){ + int val = (int) ((ushort) v); + + data [idx] = (byte) (val & 0xff); + data [idx+1] = (byte) (val >> 8); + } + } else if (underlying_type == TypeManager.int32_type) { + if (!(v is Expression)){ + int val = (int) v; + + data [idx] = (byte) (val & 0xff); + data [idx+1] = (byte) ((val >> 8) & 0xff); + data [idx+2] = (byte) ((val >> 16) & 0xff); + data [idx+3] = (byte) (val >> 24); + } + } else if (underlying_type == TypeManager.uint32_type) { + if (!(v is Expression)){ + uint val = (uint) v; + + data [idx] = (byte) (val & 0xff); + data [idx+1] = (byte) ((val >> 8) & 0xff); + data [idx+2] = (byte) ((val >> 16) & 0xff); + data [idx+3] = (byte) (val >> 24); + } + } else if (underlying_type == TypeManager.sbyte_type) { + if (!(v is Expression)){ + sbyte val = (sbyte) v; + data [idx] = (byte) val; + } + } else if (underlying_type == TypeManager.byte_type) { + if (!(v is Expression)){ + byte val = (byte) v; + data [idx] = (byte) val; + } + } else if (underlying_type == TypeManager.bool_type) { + if (!(v is Expression)){ + bool val = (bool) v; + data [idx] = (byte) (val ? 1 : 0); + } + } else if (underlying_type == TypeManager.decimal_type){ + if (!(v is Expression)){ + int [] bits = Decimal.GetBits ((decimal) v); + int p = idx; + + for (int j = 0; j < 4; j++){ + data [p++] = (byte) (bits [j] & 0xff); + data [p++] = (byte) ((bits [j] >> 8) & 0xff); + data [p++] = (byte) ((bits [j] >> 16) & 0xff); + data [p++] = (byte) (bits [j] >> 24); + } + } + } else + throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type); + + idx += factor; + } + + return data; + } + + // + // Emits the initializers for the array + // + void EmitStaticInitializers (EmitContext ec, bool is_expression) + { + // + // First, the static data + // + FieldBuilder fb; + ILGenerator ig = ec.ig; + + byte [] data = MakeByteBlob (array_data, underlying_type, loc); + + fb = RootContext.MakeStaticData (data); + + if (is_expression) + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Ldtoken, fb); + ig.Emit (OpCodes.Call, + TypeManager.void_initializearray_array_fieldhandle); + } + + // + // Emits pieces of the array that can not be computed at compile + // time (variables and string locations). + // + // This always expect the top value on the stack to be the array + // + void EmitDynamicInitializers (EmitContext ec, bool is_expression) + { + ILGenerator ig = ec.ig; + int dims = bounds.Count; + int [] current_pos = new int [dims]; + int top = array_data.Count; + LocalBuilder temp = ig.DeclareLocal (type); + + ig.Emit (OpCodes.Stloc, temp); + + MethodInfo set = null; + + if (dims != 1){ + Type [] args; + ModuleBuilder mb = null; + mb = CodeGen.ModuleBuilder; + args = new Type [dims + 1]; + + int j; + for (j = 0; j < dims; j++) + args [j] = TypeManager.int32_type; + + args [j] = array_element_type; + + set = mb.GetArrayMethod ( + type, "Set", + CallingConventions.HasThis | CallingConventions.Standard, + TypeManager.void_type, args); + } + + for (int i = 0; i < top; i++){ + + Expression e = null; + + if (array_data [i] is Expression) + e = (Expression) array_data [i]; + + if (e != null) { + // + // Basically we do this for string literals and + // other non-literal expressions + // + if (e is StringConstant || !(e is Constant) || + num_automatic_initializers <= 2) { + Type etype = e.Type; + + ig.Emit (OpCodes.Ldloc, temp); + + for (int idx = 0; idx < dims; idx++) + IntConstant.EmitInt (ig, current_pos [idx]); + + // + // If we are dealing with a struct, get the + // address of it, so we can store it. + // + if ((dims == 1) && + etype.IsSubclassOf (TypeManager.value_type) && + (!TypeManager.IsBuiltinType (etype) || + etype == TypeManager.decimal_type)) { + if (e is New){ + New n = (New) e; + + // + // Let new know that we are providing + // the address where to store the results + // + n.DisableTemporaryValueType (); + } + + ig.Emit (OpCodes.Ldelema, etype); + } + + e.Emit (ec); + + if (dims == 1) + ArrayAccess.EmitStoreOpcode (ig, array_element_type); + else + ig.Emit (OpCodes.Call, set); + } + } + + // + // Advance counter + // + for (int j = dims - 1; j >= 0; j--){ + current_pos [j]++; + if (current_pos [j] < (int) bounds [j]) + break; + current_pos [j] = 0; + } + } + + if (is_expression) + ig.Emit (OpCodes.Ldloc, temp); + } + + void EmitArrayArguments (EmitContext ec) + { + ILGenerator ig = ec.ig; + + foreach (Argument a in arguments) { + Type atype = a.Type; + a.Emit (ec); + + if (atype == TypeManager.uint64_type) + ig.Emit (OpCodes.Conv_Ovf_U4); + else if (atype == TypeManager.int64_type) + ig.Emit (OpCodes.Conv_Ovf_I4); + } + } + + void DoEmit (EmitContext ec, bool is_statement) + { + ILGenerator ig = ec.ig; + + EmitArrayArguments (ec); + if (is_one_dimensional) + ig.Emit (OpCodes.Newarr, array_element_type); + else { + if (is_builtin_type) + ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method); + else + ig.Emit (OpCodes.Newobj, (MethodInfo) new_method); + } + + if (initializers != null){ + // + // FIXME: Set this variable correctly. + // + bool dynamic_initializers = true; + + if (underlying_type != TypeManager.string_type && + underlying_type != TypeManager.object_type) { + if (num_automatic_initializers > 2) + EmitStaticInitializers (ec, dynamic_initializers || !is_statement); + } + + if (dynamic_initializers) + EmitDynamicInitializers (ec, !is_statement); + } + } + + public override void Emit (EmitContext ec) + { + DoEmit (ec, false); + } + + public override void EmitStatement (EmitContext ec) + { + DoEmit (ec, true); + } + + } + + /// <summary> + /// Represents the `this' construct + /// </summary> + public class This : Expression, IAssignMethod, IMemoryLocation, IVariable { + + Block block; + VariableInfo vi; + + public This (Block block, Location loc) + { + this.loc = loc; + this.block = block; + } + + public This (Location loc) + { + this.loc = loc; + } + + public bool IsAssigned (EmitContext ec, Location loc) + { + if (vi == null) + return true; + + return vi.IsAssigned (ec, loc); + } + + public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc) + { + if (vi == null) + return true; + + return vi.IsFieldAssigned (ec, field_name, loc); + } + + public void SetAssigned (EmitContext ec) + { + if (vi != null) + vi.SetAssigned (ec); + } + + public void SetFieldAssigned (EmitContext ec, string field_name) + { + if (vi != null) + vi.SetFieldAssigned (ec, field_name); + } + + public override Expression DoResolve (EmitContext ec) + { + eclass = ExprClass.Variable; + type = ec.ContainerType; + + if (ec.IsStatic){ + Error (26, "Keyword this not valid in static code"); + return null; + } + + if (block != null) + vi = block.ThisVariable; + + return this; + } + + override public Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + DoResolve (ec); + + VariableInfo vi = ec.CurrentBlock.ThisVariable; + if (vi != null) + vi.SetAssigned (ec); + + if (ec.TypeContainer is Class){ + Error (1604, "Cannot assign to `this'"); + return null; + } + + return this; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + ig.Emit (OpCodes.Ldarg_0); + if (ec.TypeContainer is Struct) + ig.Emit (OpCodes.Ldobj, type); + } + + public void EmitAssign (EmitContext ec, Expression source) + { + ILGenerator ig = ec.ig; + + if (ec.TypeContainer is Struct){ + ig.Emit (OpCodes.Ldarg_0); + source.Emit (ec); + ig.Emit (OpCodes.Stobj, type); + } else { + source.Emit (ec); + ig.Emit (OpCodes.Starg, 0); + } + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + ec.ig.Emit (OpCodes.Ldarg_0); + + // FIMXE + // FIGURE OUT WHY LDARG_S does not work + // + // consider: struct X { int val; int P { set { val = value; }}} + // + // Yes, this looks very bad. Look at `NOTAS' for + // an explanation. + // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0); + } + } + + /// <summary> + /// Implements the typeof operator + /// </summary> + public class TypeOf : Expression { + public readonly Expression QueriedType; + Type typearg; + + public TypeOf (Expression queried_type, Location l) + { + QueriedType = queried_type; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + typearg = ec.DeclSpace.ResolveType (QueriedType, false, loc); + + if (typearg == null) + return null; + + type = TypeManager.type_type; + eclass = ExprClass.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + ec.ig.Emit (OpCodes.Ldtoken, typearg); + ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle); + } + + public Type TypeArg { + get { return typearg; } + } + } + + /// <summary> + /// Implements the sizeof expression + /// </summary> + public class SizeOf : Expression { + public readonly Expression QueriedType; + Type type_queried; + + public SizeOf (Expression queried_type, Location l) + { + this.QueriedType = queried_type; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + if (!ec.InUnsafe) { + Error (233, "Sizeof may only be used in an unsafe context " + + "(consider using System.Runtime.InteropServices.Marshal.Sizeof"); + return null; + } + + type_queried = ec.DeclSpace.ResolveType (QueriedType, false, loc); + if (type_queried == null) + return null; + + if (!TypeManager.IsUnmanagedType (type_queried)){ + Report.Error (208, "Cannot take the size of an unmanaged type (" + TypeManager.CSharpName (type_queried) + ")"); + return null; + } + + type = TypeManager.int32_type; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + int size = GetTypeSize (type_queried); + + if (size == 0) + ec.ig.Emit (OpCodes.Sizeof, type_queried); + else + IntConstant.EmitInt (ec.ig, size); + } + } + + /// <summary> + /// Implements the member access expression + /// </summary> + public class MemberAccess : Expression, ITypeExpression { + public readonly string Identifier; + Expression expr; + Expression member_lookup; + + public MemberAccess (Expression expr, string id, Location l) + { + this.expr = expr; + Identifier = id; + loc = l; + } + + public Expression Expr { + get { + return expr; + } + } + + static void error176 (Location loc, string name) + { + Report.Error (176, loc, "Static member `" + + name + "' cannot be accessed " + + "with an instance reference, qualify with a " + + "type name instead"); + } + + static bool IdenticalNameAndTypeName (EmitContext ec, Expression left_original, Location loc) + { + if (left_original == null) + return false; + + if (!(left_original is SimpleName)) + return false; + + SimpleName sn = (SimpleName) left_original; + + Type t = RootContext.LookupType (ec.DeclSpace, sn.Name, true, loc); + if (t != null) + return true; + + return false; + } + + public static Expression ResolveMemberAccess (EmitContext ec, Expression member_lookup, + Expression left, Location loc, + Expression left_original) + { + bool left_is_type, left_is_explicit; + + // If `left' is null, then we're called from SimpleNameResolve and this is + // a member in the currently defining class. + if (left == null) { + left_is_type = ec.IsStatic || ec.IsFieldInitializer; + left_is_explicit = false; + + // Implicitly default to `this' unless we're static. + if (!ec.IsStatic && !ec.IsFieldInitializer && !ec.InEnumContext) + left = ec.This; + } else { + left_is_type = left is TypeExpr; + left_is_explicit = true; + } + + if (member_lookup is FieldExpr){ + FieldExpr fe = (FieldExpr) member_lookup; + FieldInfo fi = fe.FieldInfo; + Type decl_type = fi.DeclaringType; + + if (fi is FieldBuilder) { + Const c = TypeManager.LookupConstant ((FieldBuilder) fi); + + if (c != null) { + object o = c.LookupConstantValue (ec); + object real_value = ((Constant) c.Expr).GetValue (); + + return Constantify (real_value, fi.FieldType); + } + } + + if (fi.IsLiteral) { + Type t = fi.FieldType; + + object o; + + if (fi is FieldBuilder) + o = TypeManager.GetValue ((FieldBuilder) fi); + else + o = fi.GetValue (fi); + + if (decl_type.IsSubclassOf (TypeManager.enum_type)) { + if (left_is_explicit && !left_is_type && + !IdenticalNameAndTypeName (ec, left_original, loc)) { + error176 (loc, fe.FieldInfo.Name); + return null; + } + + Expression enum_member = MemberLookup ( + ec, decl_type, "value__", MemberTypes.Field, + AllBindingFlags, loc); + + Enum en = TypeManager.LookupEnum (decl_type); + + Constant c; + if (en != null) + c = Constantify (o, en.UnderlyingType); + else + c = Constantify (o, enum_member.Type); + + return new EnumConstant (c, decl_type); + } + + Expression exp = Constantify (o, t); + + if (left_is_explicit && !left_is_type) { + error176 (loc, fe.FieldInfo.Name); + return null; + } + + return exp; + } + + if (fi.FieldType.IsPointer && !ec.InUnsafe){ + UnsafeError (loc); + return null; + } + } + + if (member_lookup is EventExpr) { + + EventExpr ee = (EventExpr) member_lookup; + + // + // If the event is local to this class, we transform ourselves into + // a FieldExpr + // + + if (ee.EventInfo.DeclaringType == ec.ContainerType) { + MemberInfo mi = GetFieldFromEvent (ee); + + if (mi == null) { + // + // If this happens, then we have an event with its own + // accessors and private field etc so there's no need + // to transform ourselves : we should instead flag an error + // + Assign.error70 (ee.EventInfo, loc); + return null; + } + + Expression ml = ExprClassFromMemberInfo (ec, mi, loc); + + if (ml == null) { + Report.Error (-200, loc, "Internal error!!"); + return null; + } + + return ResolveMemberAccess (ec, ml, left, loc, left_original); + } + } + + if (member_lookup is IMemberExpr) { + IMemberExpr me = (IMemberExpr) member_lookup; + + if (left_is_type){ + MethodGroupExpr mg = me as MethodGroupExpr; + if ((mg != null) && left_is_explicit && left.Type.IsInterface) + mg.IsExplicitImpl = left_is_explicit; + + if (!me.IsStatic){ + if (IdenticalNameAndTypeName (ec, left_original, loc)) + return member_lookup; + + SimpleName.Error_ObjectRefRequired (ec, loc, me.Name); + return null; + } + + } else { + if (!me.IsInstance){ + if (IdenticalNameAndTypeName (ec, left_original, loc)) + return member_lookup; + + if (left_is_explicit) { + error176 (loc, me.Name); + return null; + } + } + + // + // Since we can not check for instance objects in SimpleName, + // becaue of the rule that allows types and variables to share + // the name (as long as they can be de-ambiguated later, see + // IdenticalNameAndTypeName), we have to check whether left + // is an instance variable in a static context + // + // However, if the left-hand value is explicitly given, then + // it is already our instance expression, so we aren't in + // static context. + // + + if (ec.IsStatic && !left_is_explicit && left is IMemberExpr){ + IMemberExpr mexp = (IMemberExpr) left; + + if (!mexp.IsStatic){ + SimpleName.Error_ObjectRefRequired (ec, loc, mexp.Name); + return null; + } + } + + me.InstanceExpression = left; + } + + return member_lookup; + } + + if (member_lookup is TypeExpr){ + member_lookup.Resolve (ec, ResolveFlags.Type); + return member_lookup; + } + + Console.WriteLine ("Left is: " + left); + Report.Error (-100, loc, "Support for [" + member_lookup + "] is not present yet"); + Environment.Exit (0); + return null; + } + + public Expression DoResolve (EmitContext ec, Expression right_side, ResolveFlags flags) + { + if (type != null) + throw new Exception (); + // + // Resolve the expression with flow analysis turned off, we'll do the definite + // assignment checks later. This is because we don't know yet what the expression + // will resolve to - it may resolve to a FieldExpr and in this case we must do the + // definite assignment check on the actual field and not on the whole struct. + // + + Expression original = expr; + expr = expr.Resolve (ec, flags | ResolveFlags.DisableFlowAnalysis); + + if (expr == null) + return null; + + if (expr is SimpleName){ + SimpleName child_expr = (SimpleName) expr; + + Expression new_expr = new SimpleName (child_expr.Name + "." + Identifier, loc); + + return new_expr.Resolve (ec, flags); + } + + // + // TODO: I mailed Ravi about this, and apparently we can get rid + // of this and put it in the right place. + // + // Handle enums here when they are in transit. + // Note that we cannot afford to hit MemberLookup in this case because + // it will fail to find any members at all + // + + int errors = Report.Errors; + + Type expr_type = expr.Type; + if ((expr is TypeExpr) && (expr_type.IsSubclassOf (TypeManager.enum_type))){ + + Enum en = TypeManager.LookupEnum (expr_type); + + if (en != null) { + object value = en.LookupEnumValue (ec, Identifier, loc); + + if (value != null){ + Constant c = Constantify (value, en.UnderlyingType); + return new EnumConstant (c, expr_type); + } + } + } + + if (expr_type.IsPointer){ + Error (23, "The `.' operator can not be applied to pointer operands (" + + TypeManager.CSharpName (expr_type) + ")"); + return null; + } + + member_lookup = MemberLookup (ec, expr_type, Identifier, loc); + + if (member_lookup == null){ + // Error has already been reported. + if (errors < Report.Errors) + return null; + + // + // Try looking the member up from the same type, if we find + // it, we know that the error was due to limited visibility + // + object lookup = TypeManager.MemberLookup ( + expr_type, expr_type, AllMemberTypes, AllBindingFlags | + BindingFlags.NonPublic, Identifier); + if (lookup == null) + Error (117, "`" + expr_type + "' does not contain a " + + "definition for `" + Identifier + "'"); + else if ((expr_type != ec.ContainerType) && + ec.ContainerType.IsSubclassOf (expr_type)){ + + // Although a derived class can access protected members of + // its base class it cannot do so through an instance of the + // base class (CS1540). If the expr_type is a parent of the + // ec.ContainerType and the lookup succeeds with the latter one, + // then we are in this situation. + + lookup = TypeManager.MemberLookup ( + ec.ContainerType, ec.ContainerType, AllMemberTypes, + AllBindingFlags, Identifier); + + if (lookup != null) + Error (1540, "Cannot access protected member `" + + expr_type + "." + Identifier + "' " + + "via a qualifier of type `" + + TypeManager.CSharpName (expr_type) + "'; the " + + "qualifier must be of type `" + + TypeManager.CSharpName (ec.ContainerType) + "' " + + "(or derived from it)"); + else + Error (122, "`" + expr_type + "." + Identifier + "' " + + "is inaccessible because of its protection level"); + } else + Error (122, "`" + expr_type + "." + Identifier + "' " + + "is inaccessible because of its protection level"); + + return null; + } + + if (member_lookup is TypeExpr){ + member_lookup.Resolve (ec, ResolveFlags.Type); + return member_lookup; + } else if ((flags & ResolveFlags.MaskExprClass) == ResolveFlags.Type) + return null; + + member_lookup = ResolveMemberAccess (ec, member_lookup, expr, loc, original); + if (member_lookup == null) + return null; + + // The following DoResolve/DoResolveLValue will do the definite assignment + // check. + + if (right_side != null) + member_lookup = member_lookup.DoResolveLValue (ec, right_side); + else + member_lookup = member_lookup.DoResolve (ec); + + return member_lookup; + } + + public override Expression DoResolve (EmitContext ec) + { + return DoResolve (ec, null, ResolveFlags.VariableOrValue | + ResolveFlags.SimpleName | ResolveFlags.Type); + } + + public override Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + return DoResolve (ec, right_side, ResolveFlags.VariableOrValue | + ResolveFlags.SimpleName | ResolveFlags.Type); + } + + public Expression DoResolveType (EmitContext ec) + { + return DoResolve (ec, null, ResolveFlags.Type); + } + + public override void Emit (EmitContext ec) + { + throw new Exception ("Should not happen"); + } + + public override string ToString () + { + return expr + "." + Identifier; + } + } + + /// <summary> + /// Implements checked expressions + /// </summary> + public class CheckedExpr : Expression { + + public Expression Expr; + + public CheckedExpr (Expression e, Location l) + { + Expr = e; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + bool last_const_check = ec.ConstantCheckState; + + ec.ConstantCheckState = true; + Expr = Expr.Resolve (ec); + ec.ConstantCheckState = last_const_check; + + if (Expr == null) + return null; + + if (Expr is Constant) + return Expr; + + eclass = Expr.eclass; + type = Expr.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + bool last_check = ec.CheckState; + bool last_const_check = ec.ConstantCheckState; + + ec.CheckState = true; + ec.ConstantCheckState = true; + Expr.Emit (ec); + ec.CheckState = last_check; + ec.ConstantCheckState = last_const_check; + } + + } + + /// <summary> + /// Implements the unchecked expression + /// </summary> + public class UnCheckedExpr : Expression { + + public Expression Expr; + + public UnCheckedExpr (Expression e, Location l) + { + Expr = e; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + bool last_const_check = ec.ConstantCheckState; + + ec.ConstantCheckState = false; + Expr = Expr.Resolve (ec); + ec.ConstantCheckState = last_const_check; + + if (Expr == null) + return null; + + if (Expr is Constant) + return Expr; + + eclass = Expr.eclass; + type = Expr.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + bool last_check = ec.CheckState; + bool last_const_check = ec.ConstantCheckState; + + ec.CheckState = false; + ec.ConstantCheckState = false; + Expr.Emit (ec); + ec.CheckState = last_check; + ec.ConstantCheckState = last_const_check; + } + + } + + /// <summary> + /// An Element Access expression. + /// + /// During semantic analysis these are transformed into + /// IndexerAccess or ArrayAccess + /// </summary> + public class ElementAccess : Expression { + public ArrayList Arguments; + public Expression Expr; + + public ElementAccess (Expression e, ArrayList e_list, Location l) + { + Expr = e; + + loc = l; + + if (e_list == null) + return; + + Arguments = new ArrayList (); + foreach (Expression tmp in e_list) + Arguments.Add (new Argument (tmp, Argument.AType.Expression)); + + } + + bool CommonResolve (EmitContext ec) + { + Expr = Expr.Resolve (ec); + + if (Expr == null) + return false; + + if (Arguments == null) + return false; + + foreach (Argument a in Arguments){ + if (!a.Resolve (ec, loc)) + return false; + } + + return true; + } + + Expression MakePointerAccess () + { + Type t = Expr.Type; + + if (t == TypeManager.void_ptr_type){ + Error ( + 242, + "The array index operation is not valid for void pointers"); + return null; + } + if (Arguments.Count != 1){ + Error ( + 196, + "A pointer must be indexed by a single value"); + return null; + } + Expression p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, + t, loc); + return new Indirection (p, loc); + } + + public override Expression DoResolve (EmitContext ec) + { + if (!CommonResolve (ec)) + return null; + + // + // We perform some simple tests, and then to "split" the emit and store + // code we create an instance of a different class, and return that. + // + // I am experimenting with this pattern. + // + Type t = Expr.Type; + + if (t.IsArray) + return (new ArrayAccess (this, loc)).Resolve (ec); + else if (t.IsPointer) + return MakePointerAccess (); + else + return (new IndexerAccess (this, loc)).Resolve (ec); + } + + public override Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + if (!CommonResolve (ec)) + return null; + + Type t = Expr.Type; + if (t.IsArray) + return (new ArrayAccess (this, loc)).ResolveLValue (ec, right_side); + else if (t.IsPointer) + return MakePointerAccess (); + else + return (new IndexerAccess (this, loc)).ResolveLValue (ec, right_side); + } + + public override void Emit (EmitContext ec) + { + throw new Exception ("Should never be reached"); + } + } + + /// <summary> + /// Implements array access + /// </summary> + public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation { + // + // Points to our "data" repository + // + ElementAccess ea; + + LocalTemporary [] cached_locations; + + public ArrayAccess (ElementAccess ea_data, Location l) + { + ea = ea_data; + eclass = ExprClass.Variable; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + ExprClass eclass = ea.Expr.eclass; + +#if false + // As long as the type is valid + if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess || + eclass == ExprClass.Value)) { + ea.Expr.Error118 ("variable or value"); + return null; + } +#endif + + Type t = ea.Expr.Type; + if (t.GetArrayRank () != ea.Arguments.Count){ + ea.Error (22, + "Incorrect number of indexes for array " + + " expected: " + t.GetArrayRank () + " got: " + + ea.Arguments.Count); + return null; + } + type = TypeManager.TypeToCoreType (t.GetElementType ()); + if (type.IsPointer && !ec.InUnsafe){ + UnsafeError (ea.Location); + return null; + } + + foreach (Argument a in ea.Arguments){ + Type argtype = a.Type; + + if (argtype == TypeManager.int32_type || + argtype == TypeManager.uint32_type || + argtype == TypeManager.int64_type || + argtype == TypeManager.uint64_type) + continue; + + // + // Mhm. This is strage, because the Argument.Type is not the same as + // Argument.Expr.Type: the value changes depending on the ref/out setting. + // + // Wonder if I will run into trouble for this. + // + a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location); + if (a.Expr == null) + return null; + } + + eclass = ExprClass.Variable; + + return this; + } + + /// <summary> + /// Emits the right opcode to load an object of Type `t' + /// from an array of T + /// </summary> + static public void EmitLoadOpcode (ILGenerator ig, Type type) + { + if (type == TypeManager.byte_type || type == TypeManager.bool_type) + ig.Emit (OpCodes.Ldelem_U1); + else if (type == TypeManager.sbyte_type) + ig.Emit (OpCodes.Ldelem_I1); + else if (type == TypeManager.short_type) + ig.Emit (OpCodes.Ldelem_I2); + else if (type == TypeManager.ushort_type || type == TypeManager.char_type) + ig.Emit (OpCodes.Ldelem_U2); + else if (type == TypeManager.int32_type) + ig.Emit (OpCodes.Ldelem_I4); + else if (type == TypeManager.uint32_type) + ig.Emit (OpCodes.Ldelem_U4); + else if (type == TypeManager.uint64_type) + ig.Emit (OpCodes.Ldelem_I8); + else if (type == TypeManager.int64_type) + ig.Emit (OpCodes.Ldelem_I8); + else if (type == TypeManager.float_type) + ig.Emit (OpCodes.Ldelem_R4); + else if (type == TypeManager.double_type) + ig.Emit (OpCodes.Ldelem_R8); + else if (type == TypeManager.intptr_type) + ig.Emit (OpCodes.Ldelem_I); + else if (type.IsValueType){ + ig.Emit (OpCodes.Ldelema, type); + ig.Emit (OpCodes.Ldobj, type); + } else + ig.Emit (OpCodes.Ldelem_Ref); + } + + /// <summary> + /// Emits the right opcode to store an object of Type `t' + /// from an array of T. + /// </summary> + static public void EmitStoreOpcode (ILGenerator ig, Type t) + { + t = TypeManager.TypeToCoreType (t); + if (TypeManager.IsEnumType (t) && t != TypeManager.enum_type) + t = TypeManager.EnumToUnderlying (t); + if (t == TypeManager.byte_type || t == TypeManager.sbyte_type || + t == TypeManager.bool_type) + ig.Emit (OpCodes.Stelem_I1); + else if (t == TypeManager.short_type || t == TypeManager.ushort_type || t == TypeManager.char_type) + ig.Emit (OpCodes.Stelem_I2); + else if (t == TypeManager.int32_type || t == TypeManager.uint32_type) + ig.Emit (OpCodes.Stelem_I4); + else if (t == TypeManager.int64_type || t == TypeManager.uint64_type) + ig.Emit (OpCodes.Stelem_I8); + else if (t == TypeManager.float_type) + ig.Emit (OpCodes.Stelem_R4); + else if (t == TypeManager.double_type) + ig.Emit (OpCodes.Stelem_R8); + else if (t == TypeManager.intptr_type) + ig.Emit (OpCodes.Stelem_I); + else if (t.IsValueType){ + ig.Emit (OpCodes.Stobj, t); + } else + ig.Emit (OpCodes.Stelem_Ref); + } + + MethodInfo FetchGetMethod () + { + ModuleBuilder mb = CodeGen.ModuleBuilder; + int arg_count = ea.Arguments.Count; + Type [] args = new Type [arg_count]; + MethodInfo get; + + for (int i = 0; i < arg_count; i++){ + //args [i++] = a.Type; + args [i] = TypeManager.int32_type; + } + + get = mb.GetArrayMethod ( + ea.Expr.Type, "Get", + CallingConventions.HasThis | + CallingConventions.Standard, + type, args); + return get; + } + + + MethodInfo FetchAddressMethod () + { + ModuleBuilder mb = CodeGen.ModuleBuilder; + int arg_count = ea.Arguments.Count; + Type [] args = new Type [arg_count]; + MethodInfo address; + string ptr_type_name; + Type ret_type; + + ptr_type_name = type.FullName + "&"; + ret_type = Type.GetType (ptr_type_name); + + // + // It is a type defined by the source code we are compiling + // + if (ret_type == null){ + ret_type = mb.GetType (ptr_type_name); + } + + for (int i = 0; i < arg_count; i++){ + //args [i++] = a.Type; + args [i] = TypeManager.int32_type; + } + + address = mb.GetArrayMethod ( + ea.Expr.Type, "Address", + CallingConventions.HasThis | + CallingConventions.Standard, + ret_type, args); + + return address; + } + + // + // Load the array arguments into the stack. + // + // If we have been requested to cache the values (cached_locations array + // initialized), then load the arguments the first time and store them + // in locals. otherwise load from local variables. + // + void LoadArrayAndArguments (EmitContext ec) + { + ILGenerator ig = ec.ig; + + if (cached_locations == null){ + ea.Expr.Emit (ec); + foreach (Argument a in ea.Arguments){ + Type argtype = a.Expr.Type; + + a.Expr.Emit (ec); + + if (argtype == TypeManager.int64_type) + ig.Emit (OpCodes.Conv_Ovf_I); + else if (argtype == TypeManager.uint64_type) + ig.Emit (OpCodes.Conv_Ovf_I_Un); + } + return; + } + + if (cached_locations [0] == null){ + cached_locations [0] = new LocalTemporary (ec, ea.Expr.Type); + ea.Expr.Emit (ec); + ig.Emit (OpCodes.Dup); + cached_locations [0].Store (ec); + + int j = 1; + + foreach (Argument a in ea.Arguments){ + Type argtype = a.Expr.Type; + + cached_locations [j] = new LocalTemporary (ec, TypeManager.intptr_type /* a.Expr.Type */); + a.Expr.Emit (ec); + if (argtype == TypeManager.int64_type) + ig.Emit (OpCodes.Conv_Ovf_I); + else if (argtype == TypeManager.uint64_type) + ig.Emit (OpCodes.Conv_Ovf_I_Un); + + ig.Emit (OpCodes.Dup); + cached_locations [j].Store (ec); + j++; + } + return; + } + + foreach (LocalTemporary lt in cached_locations) + lt.Emit (ec); + } + + public new void CacheTemporaries (EmitContext ec) + { + cached_locations = new LocalTemporary [ea.Arguments.Count + 1]; + } + + public override void Emit (EmitContext ec) + { + int rank = ea.Expr.Type.GetArrayRank (); + ILGenerator ig = ec.ig; + + LoadArrayAndArguments (ec); + + if (rank == 1) + EmitLoadOpcode (ig, type); + else { + MethodInfo method; + + method = FetchGetMethod (); + ig.Emit (OpCodes.Call, method); + } + } + + public void EmitAssign (EmitContext ec, Expression source) + { + int rank = ea.Expr.Type.GetArrayRank (); + ILGenerator ig = ec.ig; + Type t = source.Type; + + LoadArrayAndArguments (ec); + + // + // The stobj opcode used by value types will need + // an address on the stack, not really an array/array + // pair + // + if (rank == 1){ + if (t == TypeManager.enum_type || t == TypeManager.decimal_type || + (t.IsSubclassOf (TypeManager.value_type) && !TypeManager.IsEnumType (t) && !TypeManager.IsBuiltinType (t))) + ig.Emit (OpCodes.Ldelema, t); + } + + source.Emit (ec); + + if (rank == 1) + EmitStoreOpcode (ig, t); + else { + ModuleBuilder mb = CodeGen.ModuleBuilder; + int arg_count = ea.Arguments.Count; + Type [] args = new Type [arg_count + 1]; + MethodInfo set; + + for (int i = 0; i < arg_count; i++){ + //args [i++] = a.Type; + args [i] = TypeManager.int32_type; + } + + args [arg_count] = type; + + set = mb.GetArrayMethod ( + ea.Expr.Type, "Set", + CallingConventions.HasThis | + CallingConventions.Standard, + TypeManager.void_type, args); + + ig.Emit (OpCodes.Call, set); + } + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + int rank = ea.Expr.Type.GetArrayRank (); + ILGenerator ig = ec.ig; + + LoadArrayAndArguments (ec); + + if (rank == 1){ + ig.Emit (OpCodes.Ldelema, type); + } else { + MethodInfo address = FetchAddressMethod (); + ig.Emit (OpCodes.Call, address); + } + } + } + + + class Indexers { + public ArrayList getters, setters; + static Hashtable map; + + static Indexers () + { + map = new Hashtable (); + } + + Indexers (MemberInfo [] mi) + { + foreach (PropertyInfo property in mi){ + MethodInfo get, set; + + get = property.GetGetMethod (true); + if (get != null){ + if (getters == null) + getters = new ArrayList (); + + getters.Add (get); + } + + set = property.GetSetMethod (true); + if (set != null){ + if (setters == null) + setters = new ArrayList (); + setters.Add (set); + } + } + } + + static private Indexers GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type) + { + Indexers ix = (Indexers) map [lookup_type]; + + if (ix != null) + return ix; + + string p_name = TypeManager.IndexerPropertyName (lookup_type); + + MemberInfo [] mi = TypeManager.MemberLookup ( + caller_type, lookup_type, MemberTypes.Property, + BindingFlags.Public | BindingFlags.Instance, p_name); + + if (mi == null || mi.Length == 0) + return null; + + ix = new Indexers (mi); + map [lookup_type] = ix; + + return ix; + } + + static public Indexers GetIndexersForType (Type caller_type, Type lookup_type, Location loc) + { + Indexers ix = (Indexers) map [lookup_type]; + + if (ix != null) + return ix; + + ix = GetIndexersForTypeOrInterface (caller_type, lookup_type); + if (ix != null) + return ix; + + Type [] ifaces = TypeManager.GetInterfaces (lookup_type); + if (ifaces != null) { + foreach (Type itype in ifaces) { + ix = GetIndexersForTypeOrInterface (caller_type, itype); + if (ix != null) + return ix; + } + } + + Report.Error (21, loc, + "Type `" + TypeManager.CSharpName (lookup_type) + + "' does not have any indexers defined"); + return null; + } + } + + /// <summary> + /// Expressions that represent an indexer call. + /// </summary> + public class IndexerAccess : Expression, IAssignMethod { + // + // Points to our "data" repository + // + MethodInfo get, set; + Indexers ilist; + ArrayList set_arguments; + bool is_base_indexer; + + protected Type indexer_type; + protected Type current_type; + protected Expression instance_expr; + protected ArrayList arguments; + + public IndexerAccess (ElementAccess ea, Location loc) + : this (ea.Expr, false, loc) + { + this.arguments = ea.Arguments; + } + + protected IndexerAccess (Expression instance_expr, bool is_base_indexer, + Location loc) + { + this.instance_expr = instance_expr; + this.is_base_indexer = is_base_indexer; + this.eclass = ExprClass.Value; + this.loc = loc; + } + + protected virtual bool CommonResolve (EmitContext ec) + { + indexer_type = instance_expr.Type; + current_type = ec.ContainerType; + + return true; + } + + public override Expression DoResolve (EmitContext ec) + { + if (!CommonResolve (ec)) + return null; + + // + // Step 1: Query for all `Item' *properties*. Notice + // that the actual methods are pointed from here. + // + // This is a group of properties, piles of them. + + if (ilist == null) + ilist = Indexers.GetIndexersForType ( + current_type, indexer_type, loc); + + // + // Step 2: find the proper match + // + if (ilist != null && ilist.getters != null && ilist.getters.Count > 0) + get = (MethodInfo) Invocation.OverloadResolve ( + ec, new MethodGroupExpr (ilist.getters, loc), arguments, loc); + + if (get == null){ + Error (154, "indexer can not be used in this context, because " + + "it lacks a `get' accessor"); + return null; + } + + type = get.ReturnType; + if (type.IsPointer && !ec.InUnsafe){ + UnsafeError (loc); + return null; + } + + eclass = ExprClass.IndexerAccess; + return this; + } + + public override Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + if (!CommonResolve (ec)) + return null; + + Type right_type = right_side.Type; + + if (ilist == null) + ilist = Indexers.GetIndexersForType ( + current_type, indexer_type, loc); + + if (ilist != null && ilist.setters != null && ilist.setters.Count > 0){ + set_arguments = (ArrayList) arguments.Clone (); + set_arguments.Add (new Argument (right_side, Argument.AType.Expression)); + + set = (MethodInfo) Invocation.OverloadResolve ( + ec, new MethodGroupExpr (ilist.setters, loc), set_arguments, loc); + } + + if (set == null){ + Error (200, "indexer X.this [" + TypeManager.CSharpName (right_type) + + "] lacks a `set' accessor"); + return null; + } + + type = TypeManager.void_type; + eclass = ExprClass.IndexerAccess; + return this; + } + + public override void Emit (EmitContext ec) + { + Invocation.EmitCall (ec, false, false, instance_expr, get, arguments, loc); + } + + // + // source is ignored, because we already have a copy of it from the + // LValue resolution and we have already constructed a pre-cached + // version of the arguments (ea.set_arguments); + // + public void EmitAssign (EmitContext ec, Expression source) + { + Invocation.EmitCall (ec, false, false, instance_expr, set, set_arguments, loc); + } + } + + /// <summary> + /// The base operator for method names + /// </summary> + public class BaseAccess : Expression { + string member; + + public BaseAccess (string member, Location l) + { + this.member = member; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + Expression member_lookup; + Type current_type = ec.ContainerType; + Type base_type = current_type.BaseType; + Expression e; + + if (ec.IsStatic){ + Error (1511, + "Keyword base is not allowed in static method"); + return null; + } + + member_lookup = MemberLookup (ec, base_type, base_type, member, + AllMemberTypes, AllBindingFlags, loc); + if (member_lookup == null) { + Error (117, + TypeManager.CSharpName (base_type) + " does not " + + "contain a definition for `" + member + "'"); + return null; + } + + Expression left; + + if (ec.IsStatic) + left = new TypeExpr (base_type, loc); + else + left = ec.This; + + e = MemberAccess.ResolveMemberAccess (ec, member_lookup, left, loc, null); + + if (e is PropertyExpr){ + PropertyExpr pe = (PropertyExpr) e; + + pe.IsBase = true; + } + + return e; + } + + public override void Emit (EmitContext ec) + { + throw new Exception ("Should never be called"); + } + } + + /// <summary> + /// The base indexer operator + /// </summary> + public class BaseIndexerAccess : IndexerAccess { + public BaseIndexerAccess (ArrayList args, Location loc) + : base (null, true, loc) + { + arguments = new ArrayList (); + foreach (Expression tmp in args) + arguments.Add (new Argument (tmp, Argument.AType.Expression)); + } + + protected override bool CommonResolve (EmitContext ec) + { + instance_expr = ec.This; + + current_type = ec.ContainerType.BaseType; + indexer_type = current_type; + + foreach (Argument a in arguments){ + if (!a.Resolve (ec, loc)) + return false; + } + + return true; + } + } + + /// <summary> + /// This class exists solely to pass the Type around and to be a dummy + /// that can be passed to the conversion functions (this is used by + /// foreach implementation to typecast the object return value from + /// get_Current into the proper type. All code has been generated and + /// we only care about the side effect conversions to be performed + /// + /// This is also now used as a placeholder where a no-action expression + /// is needed (the `New' class). + /// </summary> + public class EmptyExpression : Expression { + public EmptyExpression () + { + type = TypeManager.object_type; + eclass = ExprClass.Value; + loc = Location.Null; + } + + public EmptyExpression (Type t) + { + type = t; + eclass = ExprClass.Value; + loc = Location.Null; + } + + public override Expression DoResolve (EmitContext ec) + { + return this; + } + + public override void Emit (EmitContext ec) + { + // nothing, as we only exist to not do anything. + } + + // + // This is just because we might want to reuse this bad boy + // instead of creating gazillions of EmptyExpressions. + // (CanConvertImplicit uses it) + // + public void SetType (Type t) + { + type = t; + } + } + + public class UserCast : Expression { + MethodBase method; + Expression source; + + public UserCast (MethodInfo method, Expression source, Location l) + { + this.method = method; + this.source = source; + type = method.ReturnType; + eclass = ExprClass.Value; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + // + // We are born fully resolved + // + return this; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + source.Emit (ec); + + if (method is MethodInfo) + ig.Emit (OpCodes.Call, (MethodInfo) method); + else + ig.Emit (OpCodes.Call, (ConstructorInfo) method); + + } + } + + // <summary> + // This class is used to "construct" the type during a typecast + // operation. Since the Type.GetType class in .NET can parse + // the type specification, we just use this to construct the type + // one bit at a time. + // </summary> + public class ComposedCast : Expression, ITypeExpression { + Expression left; + string dim; + + public ComposedCast (Expression left, string dim, Location l) + { + this.left = left; + this.dim = dim; + loc = l; + } + + public Expression DoResolveType (EmitContext ec) + { + Type ltype = ec.DeclSpace.ResolveType (left, false, loc); + if (ltype == null) + return null; + + // + // ltype.Fullname is already fully qualified, so we can skip + // a lot of probes, and go directly to TypeManager.LookupType + // + string cname = ltype.FullName + dim; + type = TypeManager.LookupTypeDirect (cname); + if (type == null){ + // + // For arrays of enumerations we are having a problem + // with the direct lookup. Need to investigate. + // + // For now, fall back to the full lookup in that case. + // + type = RootContext.LookupType ( + ec.DeclSpace, cname, false, loc); + + if (type == null) + return null; + } + + if (!ec.ResolvingTypeTree){ + // + // If the above flag is set, this is being invoked from the ResolveType function. + // Upper layers take care of the type validity in this context. + // + if (!ec.InUnsafe && type.IsPointer){ + UnsafeError (loc); + return null; + } + } + + eclass = ExprClass.Type; + return this; + } + + public override Expression DoResolve (EmitContext ec) + { + return DoResolveType (ec); + } + + public override void Emit (EmitContext ec) + { + throw new Exception ("This should never be called"); + } + + public override string ToString () + { + return left + dim; + } + } + + // + // This class is used to represent the address of an array, used + // only by the Fixed statement, this is like the C "&a [0]" construct. + // + public class ArrayPtr : Expression { + Expression array; + + public ArrayPtr (Expression array, Location l) + { + Type array_type = array.Type.GetElementType (); + + this.array = array; + + string array_ptr_type_name = array_type.FullName + "*"; + + type = Type.GetType (array_ptr_type_name); + if (type == null){ + ModuleBuilder mb = CodeGen.ModuleBuilder; + + type = mb.GetType (array_ptr_type_name); + } + + eclass = ExprClass.Value; + loc = l; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + array.Emit (ec); + IntLiteral.EmitInt (ig, 0); + ig.Emit (OpCodes.Ldelema, array.Type.GetElementType ()); + } + + public override Expression DoResolve (EmitContext ec) + { + // + // We are born fully resolved + // + return this; + } + } + + // + // Used by the fixed statement + // + public class StringPtr : Expression { + LocalBuilder b; + + public StringPtr (LocalBuilder b, Location l) + { + this.b = b; + eclass = ExprClass.Value; + type = TypeManager.char_ptr_type; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + ig.Emit (OpCodes.Ldloc, b); + ig.Emit (OpCodes.Conv_I); + ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data); + ig.Emit (OpCodes.Add); + } + } + + // + // Implements the `stackalloc' keyword + // + public class StackAlloc : Expression { + Type otype; + Expression t; + Expression count; + + public StackAlloc (Expression type, Expression count, Location l) + { + t = type; + this.count = count; + loc = l; + } + + public override Expression DoResolve (EmitContext ec) + { + count = count.Resolve (ec); + if (count == null) + return null; + + if (count.Type != TypeManager.int32_type){ + count = ConvertImplicitRequired (ec, count, TypeManager.int32_type, loc); + if (count == null) + return null; + } + + if (ec.InCatch || ec.InFinally){ + Error (255, + "stackalloc can not be used in a catch or finally block"); + return null; + } + + otype = ec.DeclSpace.ResolveType (t, false, loc); + + if (otype == null) + return null; + + if (!TypeManager.VerifyUnManaged (otype, loc)) + return null; + + string ptr_name = otype.FullName + "*"; + type = Type.GetType (ptr_name); + if (type == null){ + ModuleBuilder mb = CodeGen.ModuleBuilder; + + type = mb.GetType (ptr_name); + } + eclass = ExprClass.Value; + + return this; + } + + public override void Emit (EmitContext ec) + { + int size = GetTypeSize (otype); + ILGenerator ig = ec.ig; + + if (size == 0) + ig.Emit (OpCodes.Sizeof, otype); + else + IntConstant.EmitInt (ig, size); + count.Emit (ec); + ig.Emit (OpCodes.Mul); + ig.Emit (OpCodes.Localloc); + } + } +} diff --git a/mcs/mbas/genericparser.cs b/mcs/mbas/genericparser.cs new file mode 100644 index 00000000000..2b7a27c381c --- /dev/null +++ b/mcs/mbas/genericparser.cs @@ -0,0 +1,336 @@ +// +// GenericParser.cs: The Base Parser for the Mono compilers +// +// Author: A Rafael D Teixeira (rafaelteixeirabr@hotmail.com) +// +// Licensed under the terms of the GNU GPL +// +// Copyright (C) 2001 Ximian, Inc. +// + +namespace Mono.Languages +{ + using System; + using System.Reflection; + using System.Collections; + using System.IO; + using Mono.CSharp; + + /// <summary> + /// Base class to support multiple Jay generated parsers + /// </summary> + public abstract class GenericParser + { + // --------------------------------------------------- + // Class state + + // Count of errors found while parsing + static protected int global_errors; + + // Maps extensions to specific parsers + static private Hashtable mapOfParsers; + + // Indicates if parsing should be verbose + static public bool yacc_verbose_flag = false; + + // Context to use + static public ArrayList defines; + + // --------------------------------------------------- + // Instance state + + // Name of the file we are parsing + protected string name; + + // Input stream to parse from. + protected System.IO.TextReader input; + + // Current namespace definition + protected Namespace current_namespace; + + // Current typecontainer definition + protected TypeContainer current_container; + + // --------------------------------------------------- + // What the descendants MUST reimplement + + /// <summary> + /// Parses the current "input" + /// </summary> + public abstract int parse(); + + /// <summary> + /// Lists the extensions this parser can handle + /// </summary> + public abstract string[] extensions(); + /* { + string [] list = { ".cs" }; + return list; + } */ + + // --------------------------------------------------- + // What the descendants DONT HAVE to reimplement + + /// <summary> + /// Initializes this parser from a file and parses it + /// </summary> + /// <param name="fileName">Name of the file to be parsed</param> + /// <param name="context">Context to output the parsed tree</param> + public int ParseFile(string fileName) + { + // file exceptions must be caught by caller + + global_errors = 0; + name = fileName; + // TODO: Encoding switching as needed + // We are here forcing StreamReader to assume current system codepage, + // because normally it defaults to UTF-8 + input = new StreamReader(fileName, System.Text.Encoding.Default); + //rc = context; + return parse(); + } + + /// <summary> + /// Initializes this parser from a string and parses it + /// </summary> + /// <param name="source">String to be parsed</param> + /// <param name="sourceName">Name of the source to be parsed (just for error reporting)</param> + /// <param name="context">Context to output the parsed tree</param> + public int ParseString(string source, string sourceName) + { + global_errors = 0; + name = sourceName; + input = new StringReader(source); + //rc = context; + return parse(); + } + + // --------------------------------------------------- + // Class methods + + static private void MapParsers() + { + + mapOfParsers = new Hashtable(); + + Assembly thisAssembly = Assembly.GetExecutingAssembly(); + foreach(Type type in thisAssembly.GetTypes()) + { + if (type.BaseType != null) + if (type.BaseType.FullName == "Mono.Languages.GenericParser") + { + GenericParser parser = (GenericParser)Activator.CreateInstance(type); + foreach(string fileExtension in parser.extensions()) + { + string theFileExtension = fileExtension.ToLower(); + if (mapOfParsers.Contains(theFileExtension)) + Console.WriteLine("[TRACE] " + type.FullName + " can't try to parse '" + theFileExtension + "' files too"); + else + mapOfParsers.Add(theFileExtension, parser); + } + } + } + } + + /// <summary> + /// Find the descendant parser that knows how to parse the specified file + /// based on the files extension + /// </summary> + /// <param name="fileName">Name of the file to be parsed</param> + public static GenericParser GetSpecificParserFor(string fileName) + { + int i; + string fileExtension; + + if (mapOfParsers == null) + MapParsers(); + + if ((i = fileName.LastIndexOf(".")) < 0) + return null; + else + fileExtension = fileName.Substring(i).ToLower(); + + return (GenericParser)mapOfParsers[fileExtension]; + } + + + public static int Tokenize(string fileName) + { + GenericParser parser = GetSpecificParserFor(fileName); + + if (parser == null) + { + Console.WriteLine("Do not know how to compile " + fileName); + return 1; + } + +/* Stream input; + + try { + input = File.OpenRead (input_file); + + } catch { + Report.Error (2001, "Source file '" + input_file + "' could not be opened"); + return 1; + } + + using (input){ + Tokenizer lexer = new Tokenizer (input, input_file, defines); + int token, tokens = 0, errors = 0; + + while ((token = lexer.token ()) != Token.EOF){ + Location l = lexer.Location; + tokens++; + if (token == Token.ERROR) + errors++; + } + Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors"); + } +*/ + return 0; + } + + + /// <summary> + /// Find the descendant parser that knows how to parse the specified file + /// based on the files extension, and parses it using the chosen parser + /// </summary> + /// <param name="fileName">Name of the file to be parsed</param> + /// <param name="context">Context to output the parsed tree</param> + public static int Parse(string fileName) + { + int errors; + GenericParser parser = GetSpecificParserFor(fileName); + + if (parser == null) + { + Console.WriteLine("Do not know how to compile " + fileName); + return 1; + } + + try + { + errors = parser.ParseFile(fileName); + } + catch (FileNotFoundException ex) + { + error(2001, "Source file \'" + fileName + "\' could not be found!!!"); + Console.WriteLine (ex); + return 1; + } + catch (Exception ex) + { + Console.WriteLine (ex); + Console.WriteLine ("Compilation aborted"); + return 1; + } + + return errors; + } + + // <summary> + // Given the @class_name name, it creates a fully qualified name + // based on the containing declaration space + // </summary> + protected string MakeName(string class_name) + { + string ns = current_namespace.Name; + string container_name = current_container.Name; + + if (container_name == "") + { + if (ns != "") + return ns + "." + class_name; + else + return class_name; + } + else + return container_name + "." + class_name; + } + + // <summary> + // Used to report back to the user the result of a declaration + // in the current declaration space + // </summary> + protected void CheckDef (AdditionResult result, string name, Location l) + { + if (result == AdditionResult.Success) + return; + + switch (result) + { + case AdditionResult.NameExists: + Report.Error (102, l, "The container '" + current_container.Name + + "' already contains a definition for '"+ + name + "'"); + break; + + + // + // This is handled only for static Constructors, because + // in reality we handle these by the semantic analysis later + // + case AdditionResult.MethodExists: + Report.Error ( + 111, l, "Class `"+current_container.Name+ + "' already defines a member called '" + + name + "' with the same parameter types (more than one default constructor)"); + break; + + case AdditionResult.EnclosingClash: + Report.Error (542, l, "Member names cannot be the same as their enclosing type"); + break; + + case AdditionResult.NotAConstructor: + Report.Error (1520, l, "Class, struct, or interface method must have a return type"); + break; + } + } + + // <summary> + // Used to report back to the user the result of a declaration + // in the current declaration space + // </summary> + protected void CheckDef (bool result, string name, Location l) + { + if (result) + return; + CheckDef (AdditionResult.NameExists, name, l); + } + + + /// <summary> + /// Emits error messages and increments a global count of them + /// </summary> + /// <param name="code"></param> + /// <param name="desc"></param> + static public void error (int code, string desc) + { + Console.WriteLine ("error MC"+code+": "+ desc); + global_errors++; + } + + // Emits error messages with location info. + // FIXME : Ideally, all error reporting should happen + // with Report.Error but how do you get at that non-static + // method everywhere you need it ? + static public void error (int code, Mono.CSharp.Location l, string text) + { + Console.WriteLine (l.Name + "(" + l.Row + ",?" + /*l.Col +*/ + "): error MC" + code + ": " + text); + global_errors++; + } + + // --------------------------------------------------- + // Constructors + + public GenericParser() + { + // DO NOTHING + } + + } +} + + + diff --git a/mcs/mbas/interface.cs b/mcs/mbas/interface.cs new file mode 100644 index 00000000000..1d2dc2f5cca --- /dev/null +++ b/mcs/mbas/interface.cs @@ -0,0 +1,1061 @@ +// +// interface.cs: Interface handler +// +// Author: Miguel de Icaza (miguel@gnu.org) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// +#define CACHE +using System.Collections; +using System; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; + +namespace Mono.CSharp { + + /// <summary> + /// Interfaces + /// </summary> + public class Interface : DeclSpace, IMemberContainer { + const MethodAttributes interface_method_attributes = + MethodAttributes.Public | + MethodAttributes.Abstract | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.Virtual; + + const MethodAttributes property_attributes = + MethodAttributes.Public | + MethodAttributes.Abstract | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.SpecialName | + MethodAttributes.Virtual; + + ArrayList bases; + + ArrayList defined_method; + ArrayList defined_indexer; + ArrayList defined_events; + ArrayList defined_properties; + + ArrayList method_builders; + ArrayList property_builders; + ArrayList event_builders; + + Attributes OptAttributes; + + public string IndexerName; + + IMemberContainer parent_container; + MemberCache member_cache; + + bool members_defined; + + // These will happen after the semantic analysis + + // Hashtable defined_indexers; + // Hashtable defined_methods; + + /// <summary> + /// Modifiers allowed in a class declaration + /// </summary> + public const int AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.UNSAFE | + Modifiers.PRIVATE; + + public Interface (TypeContainer parent, string name, int mod, Attributes attrs, Location l) + : base (parent, name, l) + { + ModFlags = Modifiers.Check (AllowedModifiers, mod, Modifiers.PRIVATE, l); + OptAttributes = attrs; + + method_builders = new ArrayList (); + property_builders = new ArrayList (); + event_builders = new ArrayList (); + } + + public AdditionResult AddMethod (InterfaceMethod imethod) + { + string name = imethod.Name; + Object value = defined_names [name]; + + if (value != null){ + if (!(value is InterfaceMethod)) + return AdditionResult.NameExists; + } + + if (defined_method == null) + defined_method = new ArrayList (); + + defined_method.Add (imethod); + if (value == null) + DefineName (name, imethod); + + return AdditionResult.Success; + } + + public AdditionResult AddProperty (InterfaceProperty iprop) + { + AdditionResult res; + string name = iprop.Name; + + if ((res = IsValid (name)) != AdditionResult.Success) + return res; + + DefineName (name, iprop); + + if (defined_properties == null) + defined_properties = new ArrayList (); + + defined_properties.Add (iprop); + return AdditionResult.Success; + } + + public AdditionResult AddEvent (InterfaceEvent ievent) + { + string name = ievent.Name; + AdditionResult res; + + if ((res = IsValid (name)) != AdditionResult.Success) + return res; + + DefineName (name, ievent); + + if (defined_events == null) + defined_events = new ArrayList (); + + defined_events.Add (ievent); + return AdditionResult.Success; + } + + public bool AddIndexer (InterfaceIndexer iindexer) + { + if (defined_indexer == null) + defined_indexer = new ArrayList (); + + defined_indexer.Add (iindexer); + return true; + } + + public ArrayList InterfaceMethods { + get { + return defined_method; + } + } + + public ArrayList InterfaceProperties { + get { + return defined_properties; + } + } + + public ArrayList InterfaceEvents { + get { + return defined_events; + } + } + + public ArrayList InterfaceIndexers { + get { + return defined_indexer; + } + } + + public ArrayList Bases { + get { + return bases; + } + + set { + bases = value; + } + } + + public virtual TypeAttributes InterfaceAttr { + get { + TypeAttributes x = TypeAttributes.Interface | TypeAttributes.Abstract; + + if (IsTopLevel == false) { + + if ((ModFlags & Modifiers.PROTECTED) != 0 + && (ModFlags & Modifiers.INTERNAL) != 0) + x |= TypeAttributes.NestedFamORAssem; + else if ((ModFlags & Modifiers.PROTECTED) != 0) + x |= TypeAttributes.NestedFamily; + else if ((ModFlags & Modifiers.INTERNAL) != 0) + x |= TypeAttributes.NestedAssembly; + else if ((ModFlags & Modifiers.PUBLIC) != 0) + x |= TypeAttributes.NestedPublic; + else + x |= TypeAttributes.NestedPrivate; + } else { + if ((ModFlags & Modifiers.PUBLIC) != 0) + x |= TypeAttributes.Public; + else if ((ModFlags & Modifiers.PRIVATE) != 0) + x |= TypeAttributes.NotPublic; + } + + if ((ModFlags & Modifiers.ABSTRACT) != 0) + x |= TypeAttributes.Abstract; + + if ((ModFlags & Modifiers.SEALED) != 0) + x |= TypeAttributes.Sealed; + + return x; + } + } + + void Error111 (InterfaceMemberBase ib) + { + Report.Error ( + 111, + "Interface `" + Name + "' already contains a definition with the " + + "same return value and parameter types for member `" + ib.Name + "'"); + } + + bool RegisterMethod (MethodBase mb, InternalParameters ip, Type [] types) + { + if (!TypeManager.RegisterMethod (mb, ip, types)) + return false; + + method_builders.Add (mb); + return true; + } + + // + // This might trigger a definition of the methods. This happens only + // with Attributes, as Attribute classes are processed before interfaces. + // Ideally, we should make everything just define recursively in terms + // of its dependencies. + // + public MethodInfo [] GetMethods (TypeContainer container) + { + int n = 0; + + if (!members_defined){ + if (DefineMembers (container)) + n = method_builders.Count; + } else + n = method_builders.Count; + + MethodInfo [] mi = new MethodInfo [n]; + + method_builders.CopyTo (mi, 0); + + return mi; + } + + // Hack around System.Reflection as found everywhere else + public override MemberList FindMembers (MemberTypes mt, BindingFlags bf, + MemberFilter filter, object criteria) + { + ArrayList members = new ArrayList (); + + if ((mt & MemberTypes.Method) != 0) { + foreach (MethodBuilder mb in method_builders) + if (filter (mb, criteria)) + members.Add (mb); + } + + if ((mt & MemberTypes.Property) != 0) { + foreach (PropertyBuilder pb in property_builders) + if (filter (pb, criteria)) + members.Add (pb); + } + + if ((mt & MemberTypes.Event) != 0) { + foreach (MyEventBuilder eb in event_builders) + if (filter (eb, criteria)) + members.Add (eb); + } + + if (((bf & BindingFlags.DeclaredOnly) == 0) && (TypeBuilder.BaseType != null)) { + MemberList parent_mi; + + parent_mi = TypeContainer.FindMembers ( + TypeBuilder.BaseType, mt, bf, filter, criteria); + + members.AddRange (parent_mi); + } + + return new MemberList (members); + } + + public override MemberCache MemberCache { + get { + return member_cache; + } + } + + // + // Populates the methods in the interface + // + void PopulateMethod (TypeContainer parent, DeclSpace decl_space, InterfaceMethod im) + { + Type return_type = this.ResolveType (im.ReturnType, false, im.Location); + Type [] arg_types = im.ParameterTypes (this); + MethodBuilder mb; + Parameter [] p; + int i; + + if (return_type == null) + return; + + if (return_type.IsPointer && !UnsafeOK (this)) + return; + + if (arg_types == null) + return; + + foreach (Type t in arg_types){ + + if (t == null) + return; + + if (t.IsPointer && !UnsafeOK (this)) + return; + } + + // + // Create the method + // + mb = TypeBuilder.DefineMethod ( + im.Name, interface_method_attributes, + return_type, arg_types); + + InternalParameters ip = new InternalParameters (arg_types, im.Parameters); + + if (!RegisterMethod (mb, ip, arg_types)) { + Error111 (im); + return; + } + + // + // Define each type attribute (in/out/ref) and + // the argument names. + // + p = im.Parameters.FixedParameters; + if (p != null){ + for (i = 0; i < p.Length; i++) + mb.DefineParameter (i + 1, p [i].Attributes, p [i].Name); + + if (i != arg_types.Length) + Console.WriteLine ("Implement the type definition for params"); + } + + EmitContext ec = new EmitContext (parent, decl_space, Location, null, + return_type, ModFlags, false); + + if (im.OptAttributes != null) + Attribute.ApplyAttributes (ec, mb, im, im.OptAttributes, Location); + } + + // + // Populates the properties in the interface + // + void PopulateProperty (TypeContainer parent, DeclSpace decl_space, InterfaceProperty ip) + { + PropertyBuilder pb; + MethodBuilder get = null, set = null; + ip.Type = this.ResolveTypeExpr (ip.Type, false, ip.Location); + Type prop_type = ip.Type.Type; + Type [] setter_args = new Type [1]; + + if (prop_type == null) + return; + + if (prop_type.IsPointer && !UnsafeOK (this)) + return; + + setter_args [0] = prop_type; + + // + // FIXME: properties are missing the following + // flags: hidebysig newslot specialname + // + pb = TypeBuilder.DefineProperty ( + ip.Name, PropertyAttributes.None, + prop_type, null); + + if (ip.HasGet){ + get = TypeBuilder.DefineMethod ( + "get_" + ip.Name, property_attributes , + prop_type, null); + + // + // HACK because System.Reflection.Emit is lame + // + Type [] null_types = null; + InternalParameters inp = new InternalParameters + (null_types, Parameters.EmptyReadOnlyParameters); + + if (!RegisterMethod (get, inp, null)) { + Error111 (ip); + return; + } + + pb.SetGetMethod (get); + } + + if (ip.HasSet){ + setter_args [0] = prop_type; + + set = TypeBuilder.DefineMethod ( + "set_" + ip.Name, property_attributes, + TypeManager.void_type, setter_args); + + set.DefineParameter (1, ParameterAttributes.None, "value"); + pb.SetSetMethod (set); + + // + // HACK because System.Reflection.Emit is lame + // + Parameter [] parms = new Parameter [1]; + parms [0] = new Parameter (ip.Type, "value", Parameter.Modifier.NONE, null); + InternalParameters ipp = new InternalParameters ( + this, new Parameters (parms, null, Location.Null)); + + if (!RegisterMethod (set, ipp, setter_args)) { + Error111 (ip); + return; + } + } + + EmitContext ec = new EmitContext (parent, decl_space, Location, null, + null, ModFlags, false); + + if (ip.OptAttributes != null) + Attribute.ApplyAttributes (ec, pb, ip, ip.OptAttributes, Location); + + TypeManager.RegisterProperty (pb, get, set); + property_builders.Add (pb); + } + + // + // Populates the events in the interface + // + void PopulateEvent (TypeContainer parent, DeclSpace decl_space, InterfaceEvent ie) + { + // + // FIXME: We need to do this after delegates have been + // declared or we declare them recursively. + // + MyEventBuilder eb; + MethodBuilder add = null, remove = null; + ie.Type = this.ResolveTypeExpr (ie.Type, false, ie.Location); + Type event_type = ie.Type.Type; + + if (event_type == null) + return; + + if (event_type.IsPointer && !UnsafeOK (this)) + return; + + Type [] parameters = new Type [1]; + parameters [0] = event_type; + + eb = new MyEventBuilder (TypeBuilder, ie.Name, + EventAttributes.None, event_type); + + // + // Now define the accessors + // + string add_name = "add_" + ie.Name; + + add = TypeBuilder.DefineMethod ( + add_name, property_attributes, null, parameters); + add.DefineParameter (1, ParameterAttributes.None, "value"); + eb.SetAddOnMethod (add); + + string remove_name = "remove_" + ie.Name; + remove = TypeBuilder.DefineMethod ( + remove_name, property_attributes, null, parameters); + remove.DefineParameter (1, ParameterAttributes.None, "value"); + eb.SetRemoveOnMethod (remove); + + Parameter [] parms = new Parameter [1]; + parms [0] = new Parameter (ie.Type, "value", Parameter.Modifier.NONE, null); + InternalParameters ip = new InternalParameters ( + this, new Parameters (parms, null, Location.Null)); + + if (!RegisterMethod (add, ip, parameters)) { + Error111 (ie); + return; + } + + if (!RegisterMethod (remove, ip, parameters)) { + Error111 (ie); + return; + } + + EmitContext ec = new EmitContext (parent, decl_space, Location, null, + null, ModFlags, false); + + + if (ie.OptAttributes != null) + Attribute.ApplyAttributes (ec, eb, ie, ie.OptAttributes, Location); + + TypeManager.RegisterEvent (eb, add, remove); + event_builders.Add (eb); + } + + // + // Populates the indexers in the interface + // + void PopulateIndexer (TypeContainer parent, DeclSpace decl_space, InterfaceIndexer ii) + { + PropertyBuilder pb; + ii.Type = this.ResolveTypeExpr (ii.Type, false, ii.Location); + Type prop_type = ii.Type.Type; + Type [] arg_types = ii.ParameterTypes (this); + Type [] value_arg_types; + + if (prop_type == null) + return; + + if (prop_type.IsPointer && !UnsafeOK (this)) + return; + + // + // Sets up the extra invisible `value' argument for setters. + // + if (arg_types != null){ + int count = arg_types.Length; + value_arg_types = new Type [count + 1]; + + arg_types.CopyTo (value_arg_types, 0); + value_arg_types [count] = prop_type; + + foreach (Type t in arg_types){ + if (t.IsPointer && !UnsafeOK (this)) + return; + } + } else { + value_arg_types = new Type [1]; + + value_arg_types [1] = prop_type; + } + + EmitContext ec = new EmitContext (parent, decl_space, Location, null, + null, ModFlags, false); + + IndexerName = Attribute.ScanForIndexerName (ec, ii.OptAttributes); + if (IndexerName == null) + IndexerName = "Item"; + + pb = TypeBuilder.DefineProperty ( + IndexerName, PropertyAttributes.None, + prop_type, arg_types); + + MethodBuilder set_item = null, get_item = null; + if (ii.HasGet){ + Parameter [] p = ii.Parameters.FixedParameters; + + get_item = TypeBuilder.DefineMethod ( + "get_" + IndexerName, property_attributes, + prop_type, arg_types); + pb.SetGetMethod (get_item); + // + // HACK because System.Reflection.Emit is lame + // + InternalParameters ip = new InternalParameters ( + arg_types, ii.Parameters); + + if (!RegisterMethod (get_item, ip, arg_types)) { + Error111 (ii); + return; + } + + if (p != null){ + for (int i = 0; i < p.Length; i++) + get_item.DefineParameter ( + i + 1, + p [i].Attributes, p [i].Name); + } + } + + if (ii.HasSet){ + Parameter [] p = ii.Parameters.FixedParameters; + Parameter [] pv; + int i = 0; + + pv = new Parameter [p.Length + 1]; + p.CopyTo (pv, 0); + pv [p.Length] = new Parameter (ii.Type, "value", Parameter.Modifier.NONE, null); + Parameters value_params = new Parameters (pv, null, Location.Null); + value_params.GetParameterInfo (decl_space); + + set_item = TypeBuilder.DefineMethod ( + "set_" + IndexerName, property_attributes, + TypeManager.void_type, value_arg_types); + pb.SetSetMethod (set_item); + // + // HACK because System.Reflection.Emit is lame + // + InternalParameters ip = new InternalParameters ( + value_arg_types, value_params); + if (!RegisterMethod (set_item, ip, value_arg_types)) { + Error111 (ii); + return; + } + + if (p != null){ + for (; i < p.Length; i++) + set_item.DefineParameter ( + i + 1, + p [i].Attributes, p [i].Name); + } + + set_item.DefineParameter (i + 1, ParameterAttributes.None, "value"); + } + + if (ii.OptAttributes != null) + Attribute.ApplyAttributes (ec, pb, ii, ii.OptAttributes, Location); + + property_builders.Add (pb); + } + + /// <summary> + /// Performs the semantic analysis for all the interface members + /// that were declared + /// </summary> + bool SemanticAnalysis () + { + Hashtable methods = new Hashtable (); + + + if (defined_method != null){ + foreach (InterfaceMethod im in defined_method){ + string sig = im.GetSignature (this); + + // + // If there was an undefined Type on the signatures + // + if (sig == null) + continue; + + if (methods [sig] != null){ + Error111 (im); + return false; + } + } + } + + // + // FIXME: Here I should check i + // + return true; + } + + Type GetInterfaceTypeByName (string name) + { + Type t = FindType (Location, name); + + if (t == null) { + Report.Error (246, Location, "The type or namespace `" + name + + "' could not be found"); + return null; + } + + if (t.IsInterface) + return t; + + string cause; + + if (t.IsValueType) + cause = "is a struct"; + else if (t.IsClass) + cause = "is a class"; + else + cause = "Should not happen."; + + Report.Error (527, Location, "`"+name+"' " + cause + + ", need an interface instead"); + + return null; + } + + // + // Returns the list of interfaces that this interface implements + // Or null if it does not implement any interface. + // + // Sets the error boolean accoringly. + // + Type [] GetInterfaceBases (out bool error) + { + Type [] tbases; + int i; + + error = false; + if (Bases == null) + return null; + + tbases = new Type [Bases.Count]; + i = 0; + + foreach (string name in Bases){ + Type t; + + t = GetInterfaceTypeByName (name); + if (t == null){ + error = true; + return null; + } + + if (!Parent.AsAccessible (t, ModFlags)) + Report.Error (61, Location, + "Inconsistent accessibility: base interface `" + + TypeManager.CSharpName (t) + "' is less " + + "accessible than interface `" + + Name + "'"); + + tbases [i++] = t; + } + + return TypeManager.ExpandInterfaces (tbases); + } + + // + // <summary> + // Defines the Interface in the appropriate ModuleBuilder or TypeBuilder + // </summary> + // + // TODO: + // Rework the way we recurse, because for recursive + // definitions of interfaces (A:B and B:A) we report the + // error twice, rather than once. + + public override TypeBuilder DefineType () + { + Type [] ifaces; + bool error; + + if (TypeBuilder != null) + return TypeBuilder; + + if (InTransit) + return null; + + InTransit = true; + + ifaces = GetInterfaceBases (out error); + + if (error) + return null; + + if (IsTopLevel) { + ModuleBuilder builder = CodeGen.ModuleBuilder; + + TypeBuilder = builder.DefineType ( + Name, + InterfaceAttr, + (Type)null, // Parent Type + ifaces); + RootContext.RegisterOrder (this); + } else { + TypeBuilder builder = Parent.TypeBuilder; + + TypeBuilder = builder.DefineNestedType ( + Basename, + InterfaceAttr, + (Type) null, //parent type + ifaces); + + TypeContainer tc = TypeManager.LookupTypeContainer (builder); + tc.RegisterOrder (this); + } + + TypeManager.AddUserInterface (Name, TypeBuilder, this, ifaces); + InTransit = false; + + return TypeBuilder; + } + + // + // Defines the indexers, and also verifies that the IndexerNameAttribute in the + // interface is consistent. Either it is `Item' or it is the name defined by all the + // indexers with the `IndexerName' attribute. + // + // Turns out that the IndexerNameAttribute is applied to each indexer, + // but it is never emitted, instead a DefaultName attribute is attached + // to the interface + // + void DefineIndexers (TypeContainer parent) + { + string interface_indexer_name = null; + + foreach (InterfaceIndexer ii in defined_indexer){ + + PopulateIndexer (parent, this, ii); + + if (interface_indexer_name == null){ + interface_indexer_name = IndexerName; + continue; + } + + if (IndexerName == interface_indexer_name) + continue; + + Report.Error ( + 668, "Two indexers have different names, " + + " you should use the same name for all your indexers"); + } + if (interface_indexer_name == null) + interface_indexer_name = "Item"; + IndexerName = interface_indexer_name; + } + + /// <summary> + /// Performs semantic analysis, and then generates the IL interfaces + /// </summary> + public override bool DefineMembers (TypeContainer parent) + { + if (!SemanticAnalysis ()) + return false; + + if (defined_method != null){ + foreach (InterfaceMethod im in defined_method) + PopulateMethod (parent, this, im); + } + + if (defined_properties != null){ + foreach (InterfaceProperty ip in defined_properties) + PopulateProperty (parent, this, ip); + } + + if (defined_events != null) + foreach (InterfaceEvent ie in defined_events) + PopulateEvent (parent, this, ie); + + if (defined_indexer != null) { + DefineIndexers (parent); + + CustomAttributeBuilder cb = EmitDefaultMemberAttr ( + parent, IndexerName, ModFlags, Location); + if (cb != null) + TypeBuilder.SetCustomAttribute (cb); + } + +#if CACHE + if (TypeBuilder.BaseType != null) + parent_container = TypeManager.LookupMemberContainer (TypeBuilder.BaseType); + + member_cache = new MemberCache (this); +#endif + members_defined = true; + return true; + } + + /// <summary> + /// Applies all the attributes. + /// </summary> + public override bool Define (TypeContainer parent) + { + if (OptAttributes != null) { + EmitContext ec = new EmitContext (parent, this, Location, null, null, + ModFlags, false); + Attribute.ApplyAttributes (ec, TypeBuilder, this, OptAttributes, Location); + } + + return true; + } + + public static CustomAttributeBuilder EmitDefaultMemberAttr (TypeContainer parent, + string name, + int flags, + Location loc) + { + EmitContext ec = new EmitContext (parent, loc, null, null, flags); + + Expression ml = Expression.MemberLookup (ec, TypeManager.default_member_type, + ".ctor", MemberTypes.Constructor, + BindingFlags.Public | BindingFlags.Instance, + Location.Null); + + if (!(ml is MethodGroupExpr)) { + Console.WriteLine ("Internal error !!!!"); + return null; + } + + MethodGroupExpr mg = (MethodGroupExpr) ml; + + MethodBase constructor = mg.Methods [0]; + + string [] vals = { name }; + + CustomAttributeBuilder cb = null; + try { + cb = new CustomAttributeBuilder ((ConstructorInfo) constructor, vals); + } catch { + Report.Warning (-100, "Can not set the indexer default member attribute"); + } + + return cb; + } + + // + // IMemberContainer + // + + string IMemberContainer.Name { + get { + return Name; + } + } + + Type IMemberContainer.Type { + get { + return TypeBuilder; + } + } + + IMemberContainer IMemberContainer.Parent { + get { + return parent_container; + } + } + + MemberCache IMemberContainer.MemberCache { + get { + return member_cache; + } + } + + bool IMemberContainer.IsInterface { + get { + return true; + } + } + + MemberList IMemberContainer.GetMembers (MemberTypes mt, BindingFlags bf) + { + // Interfaces only contain instance members. + if ((bf & BindingFlags.Instance) == 0) + return MemberList.Empty; + if ((bf & BindingFlags.Public) == 0) + return MemberList.Empty; + + ArrayList members = new ArrayList (); + + if ((mt & MemberTypes.Method) != 0) + members.AddRange (method_builders); + + if ((mt & MemberTypes.Property) != 0) + members.AddRange (property_builders); + + if ((mt & MemberTypes.Event) != 0) + members.AddRange (event_builders); + + return new MemberList (members); + } + } + + public class InterfaceMemberBase { + public readonly string Name; + public readonly bool IsNew; + public Attributes OptAttributes; + + public InterfaceMemberBase (string name, bool is_new, Attributes attrs) + { + Name = name; + IsNew = is_new; + OptAttributes = attrs; + } + } + + public class InterfaceProperty : InterfaceMemberBase { + public readonly bool HasSet; + public readonly bool HasGet; + public readonly Location Location; + public Expression Type; + + public InterfaceProperty (Expression type, string name, + bool is_new, bool has_get, bool has_set, + Attributes attrs, Location loc) + : base (name, is_new, attrs) + { + Type = type; + HasGet = has_get; + HasSet = has_set; + Location = loc; + } + } + + public class InterfaceEvent : InterfaceMemberBase { + public readonly Location Location; + public Expression Type; + + public InterfaceEvent (Expression type, string name, bool is_new, Attributes attrs, + Location loc) + : base (name, is_new, attrs) + { + Type = type; + Location = loc; + } + } + + public class InterfaceMethod : InterfaceMemberBase { + public readonly Expression ReturnType; + public readonly Parameters Parameters; + public readonly Location Location; + + public InterfaceMethod (Expression return_type, string name, bool is_new, Parameters args, + Attributes attrs, Location l) + : base (name, is_new, attrs) + { + this.ReturnType = return_type; + this.Parameters = args; + Location = l; + } + + /// <summary> + /// Returns the signature for this interface method + /// </summary> + public string GetSignature (DeclSpace ds) + { + Type ret = ds.ResolveType (ReturnType, false, Location); + string args = Parameters.GetSignature (ds); + + if ((ret == null) || (args == null)) + return null; + + return (IsNew ? "new-" : "") + ret.FullName + "(" + args + ")"; + } + + public Type [] ParameterTypes (DeclSpace ds) + { + return Parameters.GetParameterInfo (ds); + } + } + + public class InterfaceIndexer : InterfaceMemberBase { + public readonly bool HasGet, HasSet; + public readonly Parameters Parameters; + public readonly Location Location; + public Expression Type; + + public InterfaceIndexer (Expression type, Parameters args, bool do_get, bool do_set, + bool is_new, Attributes attrs, Location loc) + : base ("", is_new, attrs) + { + Type = type; + Parameters = args; + HasGet = do_get; + HasSet = do_set; + Location = loc; + } + + public Type [] ParameterTypes (DeclSpace ds) + { + return Parameters.GetParameterInfo (ds); + } + } +} diff --git a/mcs/mbas/literal.cs b/mcs/mbas/literal.cs new file mode 100644 index 00000000000..a307030db6a --- /dev/null +++ b/mcs/mbas/literal.cs @@ -0,0 +1,187 @@ +// +// literal.cs: Literal representation for the IL tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. +// +// +// Notice that during parsing we create objects of type Literal, but the +// types are not loaded (thats why the Resolve method has to assign the +// type at that point). +// +// Literals differ from the constants in that we know we encountered them +// as a literal in the source code (and some extra rules apply there) and +// they have to be resolved (since during parsing we have not loaded the +// types yet) while constants are created only after types have been loaded +// and are fully resolved when born. +// + +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace Mono.CSharp { + + public class NullLiteral : Constant { + public static readonly NullLiteral Null; + + static NullLiteral () + { + Null = new NullLiteral (); + } + + public NullLiteral () + { + if (Null != null) + throw new Exception ("More than one null has been created!"); + eclass = ExprClass.Value; + } + + override public string AsString () + { + return "null"; + } + + public override object GetValue () + { + return null; + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.object_type; + return this; + } + + public override void Emit (EmitContext ec) + { + ec.ig.Emit (OpCodes.Ldnull); + } + } + + public class BoolLiteral : BoolConstant { + public BoolLiteral (bool val) : base (val) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.bool_type; + return this; + } + } + + public class CharLiteral : CharConstant { + public CharLiteral (char c) : base (c) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.char_type; + return this; + } + } + + public class IntLiteral : IntConstant { + public IntLiteral (int l) : base (l) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.int32_type; + return this; + } + } + + public class UIntLiteral : UIntConstant { + public UIntLiteral (uint l) : base (l) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.uint32_type; + return this; + } + } + + public class LongLiteral : LongConstant { + public LongLiteral (long l) : base (l) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.int64_type; + + return this; + } + } + + public class ULongLiteral : ULongConstant { + public ULongLiteral (ulong l) : base (l) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.uint64_type; + return this; + } + } + + public class FloatLiteral : FloatConstant { + + public FloatLiteral (float f) : base (f) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.float_type; + return this; + } + } + + public class DoubleLiteral : DoubleConstant { + public DoubleLiteral (double d) : base (d) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.double_type; + + return this; + } + } + + public class DecimalLiteral : DecimalConstant { + public DecimalLiteral (decimal d) : base (d) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.decimal_type; + return this; + } + } + + public class StringLiteral : StringConstant { + public StringLiteral (string s) : base (s) + { + } + + public override Expression DoResolve (EmitContext ec) + { + type = TypeManager.string_type; + + return this; + } + } +} diff --git a/mcs/mbas/location.cs b/mcs/mbas/location.cs new file mode 100644 index 00000000000..72c4485aef9 --- /dev/null +++ b/mcs/mbas/location.cs @@ -0,0 +1,154 @@ +// +// location.cs: Keeps track of the location of source code entity +// +// Author: +// Miguel de Icaza +// +// (C) 2001 Ximian, Inc. +// + +using System; +using System.IO; +using System.Collections; +using System.Diagnostics.SymbolStore; + +namespace Mono.CSharp { + /// <summary> + /// Keeps track of the location in the program + /// </summary> + /// + /// <remarks> + /// This uses a compact representation and a couple of auxiliary + /// structures to keep track of tokens to (file,line) mappings. + /// + /// We could probably also keep track of columns by storing those + /// in 8 bits (and say, map anything after char 255 to be `255+'). + /// </remarks> + public struct Location { + public int token; + + static Hashtable map; + static Hashtable sym_docs; + static ArrayList list; + static int global_count; + static int module_base; + + public readonly static Location Null; + + static Location () + { + map = new Hashtable (); + list = new ArrayList (); + sym_docs = new Hashtable (); + global_count = 0; + module_base = 0; + Null.token = -1; + } + + static public void Push (string name) + { + map.Remove (global_count); + map.Add (global_count, name); + list.Add (global_count); + module_base = global_count; + } + + public Location (int row) + { + if (row < 0) + token = -1; + else { + token = module_base + row; + if (global_count < token) + global_count = token; + } + } + + public override string ToString () + { + return Name + ": (" + Row + ")"; + } + + /// <summary> + /// Whether the Location is Null + /// </summary> + static public bool IsNull (Location l) + { + return l.token == -1; + } + + public string Name { + get { + int best = 0; + + if (token < 0) + return "Internal"; + + foreach (int b in list){ + if (token > b) + best = b; + } + return (string) map [best]; + } + } + + public int Row { + get { + int best = 0; + + if (token < 0) + return 1; + + foreach (int b in list){ + if (token > b) + best = b; + } + return token - best; + } + } + + // The ISymbolDocumentWriter interface is used by the symbol writer to + // describe a single source file - for each source file there's exactly + // one corresponding ISymbolDocumentWriter instance. + // + // This class has an internal hash table mapping source document names + // to such ISymbolDocumentWriter instances - so there's exactly one + // instance per document. + // + // This property returns the ISymbolDocumentWriter instance which belongs + // to the location's source file. + // + // If we don't have a symbol writer, this property is always null. + public ISymbolDocumentWriter SymbolDocument { + get { + ISymbolWriter sw = CodeGen.SymbolWriter; + ISymbolDocumentWriter doc; + + if (token < 0) + return null; + + // If we don't have a symbol writer, return null. + if (sw == null) + return null; + + string path = Path.GetFullPath (Name); + + if (sym_docs.Contains (path)) + // If we already created an ISymbolDocumentWriter + // instance for this document, return it. + doc = (ISymbolDocumentWriter) sym_docs [path]; + else { + // Create a new ISymbolDocumentWriter instance and + // store it in the hash table. + doc = sw.DefineDocument (path, SymLanguageType.CSharp, + SymLanguageVendor.Microsoft, + SymDocumentType.Text); + + sym_docs.Add (path, doc); + } + + return doc; + } + } + } +} diff --git a/mcs/mbas/makefile b/mcs/mbas/makefile new file mode 100644 index 00000000000..c1336de45ad --- /dev/null +++ b/mcs/mbas/makefile @@ -0,0 +1,50 @@ +CSC=mcs +CSCFLAGS=/nologo /optimize /target:exe /r:System.dll /r:Mono.GetOptions.dll /out:mbas.exe + +COMPILER_SOURCES = \ + AssemblyInfo.cs \ + assign.cs \ + attribute.cs \ + cfold.cs \ + class.cs \ + codegen.cs \ + const.cs \ + constant.cs \ + decl.cs \ + delegate.cs \ + driver.cs \ + enum.cs \ + ecore.cs \ + expression.cs \ + genericparser.cs \ + interface.cs \ + literal.cs \ + location.cs \ + mb-parser.cs \ + mb-tokenizer.cs \ + modifiers.cs \ + module.cs \ + namespace.cs \ + parameter.cs \ + pending.cs \ + report.cs \ + rootcontext.cs \ + statement.cs \ + statementCollection.cs \ + support.cs \ + tree.cs \ + typemanager.cs + +all: mbas.exe + +test: mbas.exe + mono mbas.exe --main WriteOK testmbas/WriteOK.vb + +mbas.exe: $(COMPILER_SOURCES) + $(CSC) $(CSCFLAGS) $(COMPILER_SOURCES) + +clean: + rm -f mbas.exe y.output mbas.pdb *~ .*~ mb-parser.cs mbas.log response + +mb-parser.cs: mb-parser.jay + ../jay/jay -ctv < ../jay/skeleton.cs mb-parser.jay > mb-parser.cs diff --git a/mcs/mbas/makefile.gnu b/mcs/mbas/makefile.gnu new file mode 100644 index 00000000000..bacfdcc3eac --- /dev/null +++ b/mcs/mbas/makefile.gnu @@ -0,0 +1,60 @@ +MCS = mcs +MCS_FLAGS = /target:exe $(MCS_DEFINES) +INSTALL = /usr/bin/install +prefix = /usr + +COMPILER_SOURCES = \ + AssemblyInfo.cs \ + assign.cs \ + attribute.cs \ + cfold.cs \ + class.cs \ + codegen.cs \ + const.cs \ + constant.cs \ + decl.cs \ + delegate.cs \ + driver.cs \ + enum.cs \ + ecore.cs \ + expression.cs \ + genericparser.cs \ + interface.cs \ + literal.cs \ + location.cs \ + mb-parser.cs \ + mb-tokenizer.cs \ + modifiers.cs \ + module.cs \ + namespace.cs \ + parameter.cs \ + pending.cs \ + report.cs \ + rootcontext.cs \ + statement.cs \ + statementCollection.cs \ + support.cs \ + tree.cs \ + typemanager.cs + +all: mbas.exe + +mbas.exe: $(COMPILER_SOURCES) + $(MCS) $(MCSFLAGS) /r:Mono.GetOptions.dll /out:mbas.exe $(COMPILER_SOURCES) + +clean: + rm -f mbas.exe y.output mbas.pdb *~ .*~ mb-parser.cs mbas.log response + +mb-parser.cs: mb-parser.jay + ../jay/jay -ctv < ../jay/skeleton.cs mb-parser.jay > mb-parser.cs + +install: all + mkdir -p $(prefix)/bin/ + $(INSTALL) -m 755 mbas.exe $(prefix)/bin/ + +test: + mono mbas.exe --main WriteOK testmbas/WriteOK.vb + +test-gtk: + mono mbas.exe testmbas/gtk.vb -r gtk-sharp + diff --git a/mcs/mbas/mb-parser.jay b/mcs/mbas/mb-parser.jay new file mode 100644 index 00000000000..b37bb9cee4a --- /dev/null +++ b/mcs/mbas/mb-parser.jay @@ -0,0 +1,2954 @@ +%{ +// +// Mono.MonoBASIC.Parser.cs (from .jay): The Parser for the MonoBASIC compiler +// +// Author: A Rafael D Teixeira (rafaelteixeirabr@hotmail.com) +// +// Licensed under the terms of the GNU GPL +// +// Copyright (C) 2001 A Rafael D Teixeira +// +// TODO: +// Nearly everything +// + +namespace Mono.MonoBASIC +{ + using System.Text; + using System; + using System.Reflection; + using System.Collections; + using Mono.Languages; + using Mono.CSharp; + + /// <summary> + /// The MonoBASIC Parser + /// </summary> + public class Parser : GenericParser + { + + + /// <summary> + /// Current block is used to add statements as we find + /// them. + /// </summary> + Block current_block; + + /// <summary> + /// Tmp block is used to store block endings in if/select's + /// </summary> + Block tmp_block; + + /// <summary> + /// Tmp block is used to store tmp copies of expressions + /// </summary> + Expression tmp_expr; + + /// <summary> + /// Tmp catch is used to store catch clauses in try..catch..finally + /// </summary> + ArrayList tmp_catch_clauses; + + /// <summary> + /// Current interface is used by the various declaration + /// productions in the interface declaration to "add" + /// the interfaces as we find them. + /// </summary> + Interface current_interface; + + /// <summary> + /// This is used by the unary_expression code to resolve + /// a name against a parameter. + /// </summary> + Parameters current_local_parameters; + + /// <summary> + /// This are used when parsing parameters in property + /// declarations. + /// </summary> + Parameters set_parameters; + Parameters get_parameters; + + /// <summary> + /// This is used by the sub_header parser to store modifiers + /// to be passed to sub/constructor + /// </summary> + int current_modifiers; + + /// <summary> + /// This is used by the sub_header parser to store attributes + /// to be passed to sub/constructor + /// </summary> + Attributes current_attributes; + + /// <summary> + /// Using during property parsing to describe the implicit + /// value parameter that is passed to the "set" accessor + /// method + /// </summary> + string get_implicit_value_parameter_name; + + // <summary> + // Using during property parsing to describe the implicit + // value parameter that is passed to the "set" and "get"accesor + // methods (properties and indexers). + // </summary> + Expression get_implicit_value_parameter_type; + + /// <summary> + /// Using during property parsing to describe the implicit + /// value parameter that is passed to the "set" accessor + /// method + /// </summary> + string set_implicit_value_parameter_name; + + // <summary> + // Using during property parsing to describe the implicit + // value parameter that is passed to the "set" and "get"accesor + // methods (properties and indexers). + // </summary> + Expression set_implicit_value_parameter_type; + + // An out-of-band stack. + // + Stack oob_stack; + + DoOptions do_type; + // + // Switch stack. + // + Stack switch_stack; + + bool UseExtendedSyntax; // for ".mbs" files + + public override string[] extensions() + { + string [] list = { ".vb", ".mbs" }; + return list; + } + +%} + +%token EOF +%token NONE /* This token is never returned by our lexer */ +%token ERROR // This is used not by the parser, but by the tokenizer. + // do not remove. + +/* + *These are the MonoBASIC keywords + */ +%token ADDHANDLER +%token ADDRESSOF +%token ALIAS +%token AND +%token ANDALSO +%token ANSI +%token AS +%token ASSEMBLY +%token AUTO +%token BOOLEAN +%token BYREF +%token BYTE +%token BYVAL +%token CALL +%token CASE +%token CATCH +%token CBOOL +%token CBYTE +%token CCHAR +%token CDATE +%token CDEC +%token CDBL +%token CHAR +%token CINT +%token CLASS +%token CLNG +%token COBJ +//%token COMPARE +%token CONST +%token CSHORT +%token CSNG +%token CSTR +%token CTYPE +%token DATE +%token DECIMAL +%token DECLARE +%token DEFAULT +%token DELEGATE +%token DESCRIPTION // MonoBASIC extension +%token DIM +%token DO +%token DOUBLE +%token EACH +%token ELSE +%token ELSEIF +%token END +%token ENUM +%token EOL +%token ERASE +%token ERROR +%token EVENT +%token EXIT +//%token EXPLICIT +%token FALSE +%token FINALLY +%token FOR +%token FRIEND +%token FUNCTION +%token GET +%token GETTYPE +%token GOTO +%token HANDLES +%token IF +%token IMPLEMENTS +%token IMPORTS +%token IN +%token INHERITS +%token INTEGER +%token INTERFACE +%token IS +%token LET +%token LIB +%token LIKE +%token LONG +%token LOOP +%token ME +%token MOD +%token MODULE +%token MUSTINHERIT +%token MUSTOVERRIDE +%token MYBASE +%token MYCLASS +%token NAMESPACE +%token NEW +%token NEXT +%token NOT +%token NOTHING +%token NOTINHERITABLE +%token NOTOVERRIDABLE +%token OBJECT +%token ON +%token OPTION +%token OPTIONAL +%token OR +%token ORELSE +%token OVERLOADS +%token OVERRIDABLE +%token OVERRIDES +%token PARAMETER // MonoBASIC extension +%token PARAM_ARRAY +%token PRESERVE +%token PRIVATE +%token PROPERTY +%token PROTECTED +%token PUBLIC +%token RAISEEVENT +%token READONLY +%token REDIM +%token REM +%token REMOVEHANDLER +%token RESUME +%token RETURN +%token SELECT +%token SET +%token SHADOWS +%token SHARED +%token SHORT +%token SINGLE +%token SIZEOF +%token STATIC +%token STEP +%token STOP +%token STRING +%token STRUCTURE +%token SUB +%token SUMMARY // MonoBASIC extension +%token SYNCLOCK +%token THEN +%token THROW +%token TO +%token TRUE +%token TRY +%token TYPEOF +%token UNICODE +%token UNTIL +%token VARIANT +%token WHEN +%token WHILE +%token WITH +%token WITHEVENTS +%token WRITEONLY +%token XOR + +/* MonoBASIC single character operators/punctuation. */ +%token OPEN_BRACKET "[" +%token CLOSE_BRACKET "]" +%token OPEN_PARENS "(" +%token CLOSE_PARENS ")" +%token DOT "." +%token COMMA "," +%token COLON ":" + +%token PLUS "+" +%token MINUS "-" +%token ASSIGN "=" +%token OP_LT "<" +%token OP_GT ">" +%token STAR "*" +%token PERCENT "%" +%token DIV "/" +%token OP_EXP "^" +%token INTERR "?" +%token OP_IDIV "\\" +%token OP_CONCAT "&" + +/* MonoBASIC multi-character operators. */ +%token OP_LE "<=" +%token OP_GE ">=" +%token OP_EQ "==" +%token OP_NE "<>" +%token OP_AND //"and" +%token OP_OR //"or" +%token OP_XOR //"xor" +%token OP_MODULUS //"mod" +%token OP_MULT_ASSIGN "*=" +%token OP_DIV_ASSIGN "/=" +%token OP_IDIV_ASSIGN "\\=" +%token OP_ADD_ASSIGN "+=" +%token OP_SUB_ASSIGN "-=" +%token OP_CONCAT_ASSIGN "&=" +%token OP_EXP_ASSIGN "^=" + +/* Numbers */ +%token LITERAL_INTEGER "int literal" +%token LITERAL_SINGLE "float literal" +%token LITERAL_DOUBLE "double literal" +%token LITERAL_DECIMAL "decimal literal" +%token LITERAL_CHARACTER "character literal" +%token LITERAL_STRING "string literal" + +%token IDENTIFIER + +/* Add precedence rules to solve dangling else s/r conflict */ +%nonassoc LOWPREC +%nonassoc IF +%nonassoc ELSE +%right ASSIGN +%left OP_OR +%left OP_AND +%left BITWISE_OR +%left BITWISE_AND +%left OP_SHIFT_LEFT OP_SHIFT_RIGHT +%left PLUS MINUS +%left STAR DIV PERCENT +%right BITWISE_NOT CARRET UMINUS +%nonassoc OP_INC OP_DEC +%left OPEN_PARENS +%left OPEN_BRACKET OPEN_BRACE +%left DOT +%nonassoc HIGHPREC + +%start compilation_unit +%% + +compilation_unit + : opt_imports_directives + opt_attributes + opt_declarations + EOF + { + $$ = $3; + } + ; + +opt_declarations + : /* empty */ + | declarations + ; + +declarations + : declaration + | declarations declaration + ; + +declaration + : namespace_declaration + | type_declaration + { + string name = ""; + int mod_flags; + + if ($1 is Class){ + Class c = (Class) $1; + mod_flags = c.ModFlags; + name = c.Name; + } else if ($1 is Struct){ + Struct s = (Struct) $1; + mod_flags = s.ModFlags; + name = s.Name; + } else if ($1 is Module){ + Module m = (Module) $1; + mod_flags = m.ModFlags; + name = m.Name; + } else + break; + + if ((mod_flags & (Modifiers.PRIVATE|Modifiers.PROTECTED)) != 0){ + Report.Error ( + 1527, lexer.Location, + "Namespace elements cannot be explicitly " + + "declared private or protected in '" + name + "'"); + } + } + ; + +qualified_identifier + : IDENTIFIER + | qualified_identifier DOT IDENTIFIER + { + $$ = (($1).ToString ()) + "." + ($3.ToString ()); + } + ; +opt_imports_directives + : /* empty */ + | imports_directives + ; + +imports_directives + : imports_directive + | imports_directives imports_directive + ; + +imports_directive + : /* imports_alias_directive + | */ imports_namespace_directive + ; + +imports_namespace_directive + : IMPORTS qualified_identifier EOL + { + current_namespace.Using ((string) $2, lexer.Location); + } + ; + +opt_attributes + : /* empty */ + | OP_LT attribute_list OP_GT + ; + +attribute_list + : attribute + | attribute_list COMMA attribute + ; + +attribute + : IDENTIFIER + ; + +namespace_declaration + : NAMESPACE qualified_identifier EOL + { + current_namespace = RootContext.Tree.RecordNamespace(current_namespace, name, (string)$2); + } + opt_imports_directives + opt_declarations + END NAMESPACE EOL + { + current_namespace = current_namespace.Parent; + } + ; + +type_declaration + : opt_attributes + opt_modifiers + { + current_attributes = (Attributes) $1; + current_modifiers = (int) $2; + } + type_spec_declaration + ; + +type_spec_declaration + : class_declaration + | module_declaration + | interface_declaration + | delegate_declaration +// | struct_declaration +// | enum_declaration + ; + +class_declaration + : CLASS IDENTIFIER EOL opt_class_base + { + Class new_class; + string name; + + name = MakeName ((string) $2); + + new_class = new Class (current_container, name, current_modifiers, + (Attributes) current_attributes, lexer.Location); + + current_container = new_class; + current_container.Namespace = current_namespace; + RootContext.Tree.RecordDecl (name, new_class); + } + opt_class_member_declarations + END CLASS EOL + { + Class new_class = (Class) current_container; + new_class.Bases = (ArrayList) $4; + + current_container = current_container.Parent; + CheckDef (current_container.AddClass (new_class), new_class.Name, new_class.Location); + + $$ = new_class; + } + ; + +opt_class_base + : /* empty */ { $$ = null; } + | class_base { $$ = $1; } + ; + +class_base + : inherits_or_implements type_list EOL { $$ = $2; } + ; + +inherits_or_implements + : INHERITS + | IMPLEMENTS + ; + +opt_modifiers + : /* empty */ { $$ = (int) 0; current_modifiers = 0; } + | modifiers { $$ = $1; current_modifiers = (int) $1; } + ; + +modifiers + : modifier + | modifiers modifier + { + int m1 = (int) $1; + int m2 = (int) $2; + + if ((m1 & m2) != 0) { + Location l = lexer.Location; + Report.Error (1004, l, "Duplicate modifier: `" + Modifiers.Name (m2) + "'"); + } + $$ = (int) (m1 | m2); + } + ; + +modifier + : PUBLIC { $$ = Modifiers.PUBLIC; } + | PROTECTED { $$ = Modifiers.PROTECTED; } + | PRIVATE { $$ = Modifiers.PRIVATE; } + | STATIC { $$ = Modifiers.STATIC; } + /* FIXME: FRIEND and PROTECTED FRIEND are missing */ + ; + +module_declaration + : MODULE IDENTIFIER EOL + { + Module new_module; + string name; + // FIXME : Check for valid module modifiers + name = MakeName((string) $2); + new_module = new Module(current_container, + name, + current_modifiers, + (Attributes) current_attributes, + lexer.Location); + current_container = new_module; + current_container.Namespace = current_namespace; + RootContext.Tree.RecordDecl(name, new_module); + } + opt_class_member_declarations + END MODULE EOL + { + Module new_module = (Module)current_container; + + current_container = current_container.Parent; + CheckDef (current_container.AddClass(new_module), new_module.Name, new_module.Location); + + $$ = new_module; + } + ; + +opt_class_member_declarations + : /* empty */ + | class_member_declarations + ; + +class_member_declarations + : class_member_declaration + | class_member_declarations class_member_declaration + ; + +class_member_declaration + : opt_attributes + opt_modifiers + { + current_attributes = (Attributes) $1; + current_modifiers = (int) $2; + } + class_member_declarator + { + $$ = $3; + } + ; + +class_member_declarator + : constructor_declaration + | method_declaration + { + Method method = (Method) $1; + CheckDef (current_container.AddMethod (method), method.Name, method.Location); + } + | field_declaration + | property_declaration +// | event_declaration + | withevents_declaration /* This is a field but must be treated specially, see below */ + | type_declaration + ; + + +method_declaration + : sub_declaration + | func_declaration + ; + +sub_declaration + : SUB IDENTIFIER OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS opt_evt_handler EOL + { + + current_local_parameters = (Parameters) $4; + start_block(); + } + opt_local_declarations + { + /* This is WEIRD: declaring a method (sub) in a module as static will + trigger a syntax error, but the same methods MUST be static in order + to be properly called + */ + if (current_container is Module) { + if (current_modifiers == Modifiers.STATIC) { + Report.Error (30810, lexer.Location, "Methods cannot be declared 'Static'"); + } + else + { + current_modifiers = Modifiers.STATIC; + } + } + } + opt_statement_list + END SUB EOL + { + Method method = new Method (TypeManager.system_void_expr, (int) current_modifiers, (string) $2, + (Parameters) current_local_parameters, null, lexer.Location); + + method.Block = (Block) end_block(); + $$ = method; + + if ($6 != null) { /* we have an event handler to take care of */ + // This wouldn't work: AddHandler ((Expression)$6, (string) $2); + string evt_def = ((MemberAccess)$6).ToString(); + int pos = evt_def.LastIndexOf ("."); + string evt_target = ((string) $2).Substring (0, pos); + + foreach (Property p in current_container.Properties) { + if (p.Name == evt_target) { + // FIXME: See below + // RemoveHandler (p.Set.Block, (Expression)$6, (string) $2); + AddHandler (p.Set.Block, (Expression)$6, (string) $2); + break; + } + } + + + } + } + ; + +func_declaration + : FUNCTION IDENTIFIER + OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS AS type EOL + { + + current_local_parameters = (Parameters) $4; + start_block(); + } + opt_local_declarations + { + /* This is WEIRD: declaring a method (sub) in a module as static will + trigger a syntax error, but the same methods MUST be static in order + to be properly called + */ + if (current_container is Module) { + if (current_modifiers == Modifiers.STATIC) { + Report.Error (30810, lexer.Location, "Methods cannot be declared 'Static'"); + } + else + { + current_modifiers = Modifiers.STATIC; + } + } + // Add local var declaration + // for return value + ArrayList retval = new ArrayList (); + retval.Add (new VariableDeclaration ((string) $2, null, lexer.Location)); + declare_local_variables ((Expression) $7, retval, lexer.Location); + } + opt_statement_list + END FUNCTION EOL + { + Method method = new Method ((Expression) $7, (int) current_modifiers, (string) $2, + (Parameters) current_local_parameters, null, lexer.Location); + + method.Block = end_block(); + $$ = method; + + } + ; + +interface_declaration + : INTERFACE IDENTIFIER EOL + { + Interface new_interface; + string full_interface_name = MakeName ((string) $2); + + new_interface = new Interface (current_container, full_interface_name, (int) current_modifiers, + (Attributes) current_attributes, lexer.Location); + if (current_interface != null) { + Location l = lexer.Location; + Report.Error (-2, l, "Internal compiler error: interface inside interface"); + } + current_interface = new_interface; + new_interface.Namespace = current_namespace; + RootContext.Tree.RecordDecl (full_interface_name, new_interface); + } + opt_interface_base + interface_body + { + Interface new_interface = (Interface) current_interface; + + if ($5 != null) + new_interface.Bases = (ArrayList) $5; + + current_interface = null; + CheckDef (current_container.AddInterface (new_interface), + new_interface.Name, new_interface.Location); + + } + END INTERFACE EOL + ; + +opt_interface_base + : /* empty */ { $$ = null; } + | interface_base + ; + +interface_base + : INHERITS interface_type_list { $$ = $2; } + ; + +interface_type_list + : interface_type + { + ArrayList interfaces = new ArrayList (); + + interfaces.Add ($1); + $$ = interfaces; + } + | interface_type_list COMMA interface_type + { + ArrayList interfaces = (ArrayList) $1; + interfaces.Add ($3); + $$ = interfaces; + } + ; + +interface_body + : opt_interface_member_declarations + ; + +opt_interface_member_declarations + : /* empty */ + | interface_member_declarations + ; + +interface_member_declarations + : interface_member_declaration + | interface_member_declarations interface_member_declaration + ; + +interface_member_declaration + : interface_method_declaration + { + InterfaceMethod m = (InterfaceMethod) $1; + + CheckDef (current_interface.AddMethod (m), m.Name, m.Location); + } + | interface_property_declaration + { + InterfaceProperty p = (InterfaceProperty) $1; + + CheckDef (current_interface.AddProperty (p), p.Name, p.Location); + } + | interface_event_declaration + { + InterfaceEvent e = (InterfaceEvent) $1; + + CheckDef (current_interface.AddEvent (e), e.Name, lexer.Location); + } + ; + +opt_new + : /* empty */ { $$ = false; } + | NEW { $$ = true; } + ; + +interface_method_declaration + : SUB IDENTIFIER + OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS EOL + { + $$ = new InterfaceMethod (TypeManager.system_void_expr, (string) $2, false, + (Parameters) $4, current_attributes, lexer.Location); + } + | FUNCTION IDENTIFIER + OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS AS type + { + $$ = new InterfaceMethod ( + (Expression) $7, (string) $2, false, (Parameters) $4, + current_attributes, lexer.Location); + } + ; + +interface_property_declaration + : PROPERTY IDENTIFIER + OPEN_PARENS + opt_formal_parameter_list + CLOSE_PARENS opt_type_spec EOL + { + // FIXME we MUST pass property parameters + $$ = new InterfaceProperty ((Expression) $6, (string) $2, false, + false, false, current_attributes, + lexer.Location); + } + ; + +interface_event_declaration + : opt_attributes opt_new EVENT type IDENTIFIER EOL + { + $$ = new InterfaceEvent ((Expression) $4, (string) $5, (bool) $2, (Attributes) $1, + lexer.Location); + } + ; + +property_declaration + : PROPERTY IDENTIFIER opt_property_parameters AS type EOL + { + get_implicit_value_parameter_type = (Expression) $5; + get_implicit_value_parameter_name = (string) $2; + + current_local_parameters = (Parameters) $3; + if (current_local_parameters != Parameters.EmptyReadOnlyParameters) { + get_parameters = current_local_parameters.Copy (lexer.Location); + set_parameters = current_local_parameters.Copy (lexer.Location); + } + else + { + get_parameters = Parameters.EmptyReadOnlyParameters; + set_parameters = new Parameters (null, null ,lexer.Location); + } + lexer.PropertyParsing = true; + + $$ = lexer.Location; + } + accessor_declarations + END PROPERTY EOL + { + lexer.PropertyParsing = false; + + Property prop; + Pair pair = (Pair) $8; + Accessor get_block = (Accessor) pair.First; + Accessor set_block = (Accessor) pair.Second; + Location loc = lexer.Location; + + prop = new Property ((Expression) $5, (string) $2, current_modifiers, get_block, set_block, + current_attributes, loc, set_implicit_value_parameter_name, + get_parameters, set_parameters); + + CheckDef (current_container.AddProperty (prop), prop.Name, loc); + get_implicit_value_parameter_type = null; + set_implicit_value_parameter_type = null; + get_parameters = null; + set_parameters = null; + current_local_parameters = null; + } + ; + +opt_property_parameters + : /* empty */ + { + $$ = Parameters.EmptyReadOnlyParameters; ; + } + | OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS + { + $$ = $2; + } + ; + +accessor_declarations + : get_accessor_declaration opt_set_accessor_declaration + { + $$ = new Pair ($1, $2); + } + | set_accessor_declaration opt_get_accessor_declaration + { + $$ = new Pair ($2, $1); + } + ; + +opt_get_accessor_declaration + : /* empty */ { $$ = null; } + | get_accessor_declaration + ; + +opt_set_accessor_declaration + : /* empty */ { $$ = null; } + | set_accessor_declaration + ; + +get_accessor_declaration + : opt_attributes GET EOL + { + current_local_parameters = get_parameters; + + lexer.PropertyParsing = false; + + start_block(); + // Add local var declaration + // for return value + ArrayList retval = new ArrayList (); + retval.Add (new VariableDeclaration (get_implicit_value_parameter_name, null, lexer.Location)); + declare_local_variables (get_implicit_value_parameter_type, retval, lexer.Location); + + } + opt_statement_list + END GET EOL + { + $$ = new Accessor ((Block) end_block(), (Attributes) $1); + current_local_parameters = null; + lexer.PropertyParsing = true; + } + ; + +set_accessor_declaration + : opt_attributes SET opt_set_parameter EOL + { + + Parameter implicit_value_parameter = new Parameter ( + set_implicit_value_parameter_type, + set_implicit_value_parameter_name, + Parameter.Modifier.NONE, null); + + current_local_parameters = set_parameters; + current_local_parameters.AppendParameter (implicit_value_parameter); + + start_block(); + lexer.PropertyParsing = false; + } + opt_statement_list + END SET EOL + { + $$ = new Accessor ((Block) end_block(), (Attributes) $1); + current_local_parameters = null; + lexer.PropertyParsing = true; + } + ; + +opt_set_parameter + : /* empty */ + { + set_implicit_value_parameter_type = (Expression) TypeManager.system_object_expr; + set_implicit_value_parameter_name = "Value"; + } + | OPEN_PARENS opt_identifier opt_type_spec CLOSE_PARENS + { + /* FIXME: possible syntax error which must be caught + Set ( As <type>) is currently (and wrongly so) legal + */ + set_implicit_value_parameter_type = (Expression) $3; + if ($2 != null) + set_implicit_value_parameter_name = (string) $2; + else + set_implicit_value_parameter_name = "Value"; + } + ; + +field_declaration + : opt_dim_stmt + variable_declarators EOL + { + int mod = (int) current_modifiers; + + + VariableDeclaration.FixupTypes ((ArrayList) $2); + VariableDeclaration.FixupArrayTypes ((ArrayList) $2); + + if (current_container is Module) + mod = mod | Modifiers.STATIC; + + foreach (VariableDeclaration var in (ArrayList) $2){ + Location l = var.Location; + + Field field = new Field (var.type, mod, var.identifier, + var.expression_or_array_initializer, + (Attributes) null, l); + + CheckDef (current_container.AddField (field), field.Name, l); + } + } + ; + +withevents_declaration + : WITHEVENTS variable_declarators EOL + { + /* WithEvents Fields must be resolved into properties + with a bit of magic behind the scenes */ + + VariableDeclaration.FixupTypes ((ArrayList) $2); + + foreach (VariableDeclaration var in (ArrayList) $2) { + // 1 - We create a private field + Location l = var.Location; + Property prop; + if ((current_modifiers & Modifiers.STATIC) > 0) + Report.Error (30234, l, "'Static' is not valid on a WithEvents declaration."); + + Field field = new Field (var.type, Modifiers.PRIVATE, "_" + var.identifier, + var.expression_or_array_initializer, + (Attributes) null, l); + + CheckDef (current_container.AddField (field), field.Name, l); + + // 2 - Public property + + prop = BuildSimpleProperty (var.type, (string) var.identifier, + field, (int) current_modifiers, + (Attributes) current_attributes, l); + + CheckDef (current_container.AddProperty (prop), prop.Name, l); + } + } + ; + +opt_dim_stmt + : /* empty */ + | DIM + ; + +delegate_declaration + : DELEGATE SUB + IDENTIFIER OPEN_PARENS + opt_formal_parameter_list + CLOSE_PARENS + EOL + { + Location l = lexer.Location; + Mono.CSharp.Delegate del = new Mono.CSharp.Delegate (current_container, TypeManager.system_void_expr, + (int) current_modifiers, + MakeName ((string) $3), (Parameters) $5, + (Attributes) current_attributes, l); + + del.Namespace = current_namespace; + CheckDef (current_container.AddDelegate (del), del.Name, l); + } + | DELEGATE FUNCTION + IDENTIFIER OPEN_PARENS + opt_formal_parameter_list + CLOSE_PARENS AS type + { + Location l = lexer.Location; + Mono.CSharp.Delegate del = new Mono.CSharp.Delegate ( + current_container, + (Expression) $8, (int) current_modifiers, MakeName ((string) $3), + (Parameters) $5, (Attributes) current_attributes, l); + + del.Namespace = current_namespace; + CheckDef (current_container.AddDelegate (del), del.Name, l); + } + ; + +opt_evt_handler + : /* empty */ + { $$ = null; } + | HANDLES qualified_identifier + { + $$ = (Expression) DecomposeQI ((string)$2, lexer.Location); + } + ; + +constructor_declaration + : constructor_declarator + opt_local_declarations + opt_statement_list + { + Constructor c = (Constructor) $1; + c.Block = (Block) end_block(); + c.ModFlags = (int) current_modifiers; + c.OptAttributes = (Attributes) null; + + CheckDef (current_container.AddConstructor (c), c.Name, c.Location); + current_local_parameters = null; + } + END SUB EOL + ; + +constructor_declarator + : SUB NEW OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS EOL + { + current_local_parameters = (Parameters) $4; + start_block(); + oob_stack.Push (lexer.Location); + + Location l = (Location) oob_stack.Pop (); + $$ = new Constructor ((string) "New", (Parameters) $4, (ConstructorInitializer) null, l); + } + ; + + +opt_formal_parameter_list + : /* empty */ + { + $$ = Parameters.EmptyReadOnlyParameters; + } + | formal_parameter_list + { + $$ = $1; + //Parameter p = ((Parameters) $1).FixedParameters[0]; + } + ; + +formal_parameter_list + : fixed_parameters + { + ArrayList pars_list = (ArrayList) $1; + + Parameter [] pars = new Parameter [pars_list.Count]; + pars_list.CopyTo (pars); + $$ = new Parameters (pars, null, lexer.Location); + } + | fixed_parameters COMMA parameter_array + { + ArrayList pars_list = (ArrayList) $1; + + Parameter [] pars = new Parameter [pars_list.Count]; + pars_list.CopyTo (pars); + + $$ = new Parameters (pars, (Parameter) $3, lexer.Location); + } + | parameter_array + { + $$ = new Parameters (null, (Parameter) $1, lexer.Location); + } + ; + +fixed_parameters + : fixed_parameter + { + ArrayList pars = new ArrayList (); + + pars.Add ($1); + $$ = pars; + } + | fixed_parameters COMMA fixed_parameter + { + ArrayList pars = (ArrayList) $1; + + pars.Add ($3); + $$ = $1; + } + ; + +fixed_parameter + : opt_attributes + opt_parameter_modifier + IDENTIFIER opt_type_spec opt_variable_initializer + { + Parameter.Modifier pm = (Parameter.Modifier)$2; + bool opt_parm = ((pm & Parameter.Modifier.OPTIONAL) != 0); + + if (opt_parm && ($5 == null)) + Report.Error (999, "Optional parameters must have a default value"); + + if (opt_parm) + pm = Parameter.Modifier.NONE; //FIXME: should take into account BYREF + + $$ = new Parameter ((Expression) $4, (string) $3, + pm, (Attributes) $1, (Expression) $5, opt_parm); + } + ; + +parameter_array + : PARAM_ARRAY IDENTIFIER opt_parens AS type + { + $$ = new Parameter ((Expression) $5, (string) $2, Parameter.Modifier.PARAMS, null); + // note ("type must be a single-dimension array type"); + } + ; + +opt_parens + : /* empty */ + | OPEN_PARENS CLOSE_PARENS + ; + +opt_type_spec + : /* empty */ { $$ = (Expression) TypeManager.system_object_expr; } + | AS type { $$ = (Expression) $2; }; + ; + +opt_parameter_modifier + : /* empty */ { $$ = Parameter.Modifier.VAL; } + | parameter_modifiers { $$ = $1; } + ; + +parameter_modifiers + : parameter_modifiers parameter_modifier { $$ = (Parameter.Modifier)$1 | (Parameter.Modifier)$2; } + | parameter_modifier { $$ = $1; } + ; + +parameter_modifier + : BYREF { $$ = Parameter.Modifier.REF | Parameter.Modifier.ISBYREF; } + | BYVAL { $$ = Parameter.Modifier.VAL; } + | OPTIONAL { $$ = Parameter.Modifier.OPTIONAL; } + ; + +opt_actual_parameters + : /* empty */ + | qualified_identifier + | LITERAL_STRING + ; + +opt_statement_list + : /* empty */ + | statement_list EOL + ; + +statement_list + : statement + | statement_list EOL statement + ; + +statement : embedded_statement + { + Statement s = (Statement) $1; + + current_block.AddStatement ((Statement) $1); + } + | labeled_statement EOL + | ADDHANDLER prefixed_unary_expression COMMA ADDRESSOF qualified_identifier + { + AddHandler ((Expression) $2, (string) $5); + } + ; + + +labeled_statement + : IDENTIFIER COLON + { + LabeledStatement labeled = new LabeledStatement ((string) $1, lexer.Location); + + if (!current_block.AddLabel ((string) $1, labeled)){ + Location l = lexer.Location; + Report.Error (140, l, "The label '" + ((string) $1) + "' is a duplicate"); + } + current_block.AddStatement (labeled); + } + statement + ; + +embedded_statement + : expression_statement + | selection_statement + | iteration_statement + | try_statement + | jump_statement + ; + +jump_statement + : /*break_statement + | continue_statement + | goto_statement + | throw_statement + | */return_statement + | exit_statement + ; + +exit_statement + : EXIT exit_type + { + $$ = new Exit ((ExitType)$2, lexer.Location); + } + ; + +exit_type + : DO { $$ = ExitType.DO; } + | FOR { $$ = ExitType.FOR; } + | WHILE { $$ = ExitType.WHILE; } + | SELECT { $$ = ExitType.SELECT; } + | SUB { $$ = ExitType.SUB; } + | FUNCTION { $$ = ExitType.FUNCTION; } + | PROPERTY { $$ = ExitType.PROPERTY; } + | TRY { $$ = ExitType.TRY; } + ; +return_statement + : RETURN opt_expression + { + $$ = new Return ((Expression) $2, lexer.Location); + } + ; + +iteration_statement + : while_statement + | do_statement + | for_statement + /*| foreach_statement*/ + ; + +try_statement + : try_catch + | try_catch_finally + ; + +try_catch + : try_header + END TRY + { + Catch g = null; + ArrayList s = new ArrayList (); + + foreach (Catch cc in (ArrayList) tmp_catch_clauses) { + if (cc.IsGeneral) + g = cc; + else + s.Add (cc); + } + + // Now s contains the list of specific catch clauses + // and g contains the general one. + Block b = end_block(); + + $$ = new Try ((Block) b, s, g, null, lexer.Location); + } + ; + +try_catch_finally + : try_header + { + tmp_block = end_block(); + } + FINALLY EOL + { + start_block(); + } + opt_statement_list + END TRY + { + Catch g = null; + ArrayList s = new ArrayList (); + ArrayList catch_list = (ArrayList) tmp_catch_clauses; + + if (catch_list != null){ + foreach (Catch cc in catch_list) { + if (cc.IsGeneral) + g = cc; + else + s.Add (cc); + } + } + + $$ = new Try ((Block) tmp_block, s, g, (Block) end_block(), lexer.Location); + + } + ; + +try_header + : TRY EOL + { Console.WriteLine ("try_header"); + start_block(); + } + opt_statement_list + opt_catch_clauses + { + tmp_catch_clauses = (ArrayList) $5; + } + ; + +opt_catch_clauses + : /* empty */ { $$ = null; } + | catch_clauses + ; + +catch_clauses + : catch_clause + { + ArrayList l = new ArrayList (); + + l.Add ($1); + $$ = l; + } + | catch_clauses catch_clause + { + ArrayList l = (ArrayList) $1; + + l.Add ($2); + $$ = l; + } + ; + +opt_identifier + : /* empty */ { $$ = null; } + | IDENTIFIER + ; + +catch_clause + : CATCH opt_catch_args EOL + { + Expression type = null; + string id = null; + + if ($2 != null) { + DictionaryEntry cc = (DictionaryEntry) $2; + type = (Expression) cc.Key; + id = (string) cc.Value; + + if (id != null){ + ArrayList one = new ArrayList (); + Location loc = lexer.Location; + + one.Add (new VariableDeclaration (id, null, loc)); + + + $1 = current_block; + current_block = new Block (current_block); + Block b = declare_local_variables (type, one, loc); + current_block = b; + } + } + + } + opt_statement_list { + Expression type = null; + string id = null; + Block b_catch = current_block; + + if ($2 != null){ + DictionaryEntry cc = (DictionaryEntry) $2; + type = (Expression) cc.Key; + id = (string) cc.Value; + + if ($1 != null) { + // + // FIXME: I can change this for an assignment. + // + while (current_block != (Block) $1) + current_block = current_block.Parent; + } + } + + $$ = new Catch (type, id , (Block)b_catch, lexer.Location); + } + ; + +opt_catch_args + : /* empty */ { $$ = null; } + | catch_args + ; + +catch_args + : IDENTIFIER AS type + { + $$ = new DictionaryEntry ($3, $1); + } + ; + + +do_statement + : DO opt_do_construct EOL + { + start_block(); + oob_stack.Push (lexer.Location); + } + opt_statement_list + LOOP opt_do_construct + { + Expression t_before = (Expression) $2; + Expression t_after = (Expression) $7; + Expression t; + + if ((t_before != null) && (t_after != null)) + Report.Error (30238, "'Loop' cannot have a condition if matching 'Do' has one."); + + if ((t_before == null) && (t_after == null)) + t = new BoolLiteral (true); + else + t = (t_before != null) ? t_before : t_after; + + DoOptions test_type = (t_before != null) ? DoOptions.TEST_BEFORE : DoOptions.TEST_AFTER; + + if (((do_type == DoOptions.WHILE) && (test_type == DoOptions.TEST_BEFORE)) || + ((do_type == DoOptions.UNTIL) && (test_type == DoOptions.TEST_AFTER))) + t = new Unary (Unary.Operator.LogicalNot, (Expression) t, lexer.Location); + + $$ = new Do ((Statement) end_block(), (Expression) t, test_type, lexer.Location); + } + ; + +opt_do_construct + : /* empty */ { $$ = null; } + | while_or_until boolean_expression + { + do_type = (DoOptions)$1; + $$ = (Expression) $2; + } + ; + +while_or_until + : WHILE { $$ = DoOptions.WHILE; } + | UNTIL { $$ = DoOptions.UNTIL; } + ; + +while_statement + : WHILE + { + start_block(); + oob_stack.Push (lexer.Location); + } + boolean_expression EOL + opt_statement_list + END WHILE + { + Location l = (Location) oob_stack.Pop (); + Block b = end_block(); + Expression e = (Expression) $3; + $$ = new While ((Expression) e, (Statement) b, l); + } + ; + + +for_statement + : FOR qualified_identifier ASSIGN expression TO expression opt_step EOL + { + start_block(); + } + opt_statement_list + NEXT opt_next_identifier + { + Block statement = end_block(); + Expression for_var = (Expression) DecomposeQI ((string)$2, lexer.Location);; + + Expression assign_expr = new Assign (for_var, (Expression) $4, lexer.Location); + Expression test_expr = new Binary (Binary.Operator.LessThanOrEqual, + for_var, (Expression) $6, lexer.Location); + Expression step_expr = new Assign (for_var, (Expression) new Binary (Binary.Operator.Addition, + for_var, (Expression) $7, lexer.Location), lexer.Location); + + Statement assign_stmt = new StatementExpression ((ExpressionStatement) assign_expr, lexer.Location); + Statement step_stmt = new StatementExpression ((ExpressionStatement) step_expr, lexer.Location); + + $$ = new For (assign_stmt, test_expr, step_stmt, statement, lexer.Location); + } + ; + +opt_step + : /* empty */ { $$ = new IntLiteral ((Int32) 1); } + | STEP expression { $$ = $2; } + ; + +opt_next_identifier + : /* empty */ + | qualified_identifier + ; + +selection_statement + : if_statement + | select_statement + ; + +if_statement + : if_statement_open if_statement_rest + { + $$ = $2; + } + ; + +if_statement_open + : IF boolean_expression THEN EOL + { + oob_stack.Push (lexer.Location); + start_block(); + tmp_expr = (Expression) $2; + } + ; + +if_statement_rest + : + opt_statement_list + END IF + { + Location l = (Location) oob_stack.Pop (); + + $$ = new If ((Expression) tmp_expr, (Statement) end_block(), l); + + } + | + opt_statement_list + ELSE EOL + { + tmp_block = end_block(); + start_block(); + } + opt_statement_list + END IF + { + Location l = (Location) oob_stack.Pop (); + + $$ = new If ((Expression) tmp_expr, (Statement) tmp_block, (Statement) end_block(), l); + } + ; + +select_statement + : SELECT opt_case expression EOL + { + oob_stack.Push (lexer.Location); + switch_stack.Push (current_block); + } + opt_case_sections + END SELECT + { + $$ = new Switch ((Expression) $3, (ArrayList) $6, (Location) oob_stack.Pop ()); + current_block = (Block) switch_stack.Pop (); + } + ; + +opt_case_sections + : /* empty */ { $$ = null; } + | case_sections { $$ = $1; } + ; + +case_sections + : case_sections case_section + { + ArrayList sections = (ArrayList) $1; + + sections.Add ($2); + $$ = sections; + } + | case_section + { + ArrayList sections = new ArrayList (); + + sections.Add ($1); + $$ = sections; + } + ; + +case_section + : CASE case_clauses EOL + { + start_block(); + } + opt_statement_list + { + Block topmost = current_block; + + while (topmost.Implicit) + topmost = topmost.Parent; + + // FIXME: This is a horrible hack which MUST go + topmost.statements.Add (new Break (lexer.Location)); + $$ = new SwitchSection ((ArrayList) $2, topmost); + } + | CASE ELSE EOL + /* FIXME: we should somehow flag an error + (BC30321 'Case' cannot follow a 'Case Else' + in the same 'Select' statement.) + if Case Else is not the last of the Case clauses + */ + { + start_block(); + } + opt_statement_list + { + Block topmost = current_block; + + while (topmost.Implicit) + topmost = topmost.Parent; + + // FIXME: This is a horrible hack which MUST go + topmost.statements.Add (new Break (lexer.Location)); + + ArrayList a = new ArrayList(); + a.Add (new SwitchLabel (null, lexer.Location)); + $$ = new SwitchSection ((ArrayList) a, topmost); + } + ; + +case_clauses + : case_clause + { + ArrayList labels = new ArrayList (); + + labels.Add ($1); + $$ = labels; + } + | case_clauses COMMA case_clause + { + ArrayList labels = (ArrayList) ($1); + labels.Add ($2); + + $$ = labels; + } + ; + +case_clause + : opt_is comparison_operator expression + | expression + { + $$ = new SwitchLabel ((Expression) $1, lexer.Location); + } + ; + +opt_is + : /* empty */ + | IS + ; + +comparison_operator + : OP_LT + | OP_GT + | OP_LT + | OP_LE + | OP_NE + | OP_EQ + ; + +opt_case + : /* empty */ + | CASE + ; + +expression_statement + : statement_expression + { + $$ = $1; + } + ; + +statement_expression + : invocation_expression { $$ = new StatementExpression ((ExpressionStatement) $1, lexer.Location); } + | object_creation_expression { $$ = new StatementExpression ((ExpressionStatement) $1, lexer.Location); } + | assignment_expression { $$ = new StatementExpression ((ExpressionStatement) $1, lexer.Location); } + ; + +object_creation_expression + : NEW type OPEN_PARENS opt_argument_list CLOSE_PARENS + { + $$ = new New ((Expression) $2, (ArrayList) $4, lexer.Location); + } + ; + +new_expression + : object_creation_expression + /* | array_creation_expression */ + ; + +assignment_expression + : prefixed_unary_expression ASSIGN expression + { + $$ = new Assign ((Expression) $1, (Expression) $3, lexer.Location); + } + ; + +opt_local_declarations + : /* empty */ + | local_declarations + { if ($1 != null && (Block) $1 != current_block){ + current_block.AddStatement ((Statement) $1); + current_block = (Block) $1; + } + } + ; + +local_declarations + : local_declaration EOL + | local_declaration EOL local_declarations + ; + +local_declaration + : local_variable_declaration + { + if ($1 != null){ + DictionaryEntry de = (DictionaryEntry) $1; + + $$ = declare_local_variables ((Expression) de.Key, (ArrayList) de.Value, lexer.Location); + } + } + + | local_constant_declaration + { + if ($1 != null){ + DictionaryEntry de = (DictionaryEntry) $1; + + $$ = declare_local_constant ((Expression) de.Key, (VariableDeclaration) de.Value); + } + } + ; + +local_variable_declaration + : DIM variable_declarators + { + $$ = new DictionaryEntry (DecomposeQI("_local_vars_", lexer.Location), $2); + } + | DIM variable_declarators + { + $$ = new DictionaryEntry (TypeManager.system_object_expr, $2); + } + /*| DIM variable_declarators AS object_creation_expression + { + if ($4 != null) + $$ = new DictionaryEntry ($4, $2); + else + $$ = null; + + } */ + ; + + +local_constant_declaration + : CONST constant_declarator + { + if ($2 != null) + $$ = new DictionaryEntry ($1, $2); + else + $$ = null; + } + ; + +constant_declarator + : IDENTIFIER ASSIGN constant_expression + { + $$ = new VariableDeclaration ((string) $1, $3, lexer.Location); + } + ; + +variable_declarators + : variable_declarator + { + ArrayList decl = new ArrayList (); + decl.Add ($1); + $$ = decl; + } + | variable_declarators COMMA variable_declarator + { + ArrayList decls = (ArrayList) $1; + decls.Add ($3); + $$ = $1; + } + ; + +variable_declarator + : variable_identifier opt_type_decl opt_variable_initializer + { + string varname = (string)$1; + string dims = ""; + Expression vartype = (Expression) $2; + Expression varinit = (Expression) $3; + /* + Check for a declaration like Dim a(2) or Dim a(2,3) + If this is the case, we must generate an ArrayCreationExpression + and, in case, add the initializer after the array has been created. + */ + if (VariableDeclaration.IsArrayDecl (varname)) { + if (VariableDeclaration.HasDimBounds(varname)) { + varname = VariableDeclaration.StripDims (varname, ref dims); + ArrayList a_dims = VariableDeclaration.ParseDims(dims); + varinit = new ArrayCreation (vartype, a_dims,"", null, lexer.Location); + } + vartype = DecomposeQI (vartype.ToString() + VariableDeclaration.GetRank (dims), lexer.Location); + } + + $$ = new VariableDeclaration (varname, vartype, varinit, lexer.Location, null); + } + ; + +variable_identifier + : IDENTIFIER opt_array_name_modifier + { + $$ = $1; + if ($2 != null) + $$ = (string)$$ + (string)$2; + } + ; + +opt_type_decl + : /* empty */ + { + $$ = null; + } + | AS type opt_argument_list /* FIXME must handle argument list*/ + { + $$ = $2; + } + | AS NEW type opt_argument_list /* FIXME must handle NEW clause + argument list */ + { + $$ = $3; + } + ; + +opt_array_name_modifier + : /* empty */ { $$ = null; } + | array_type_modifier { $$ = $1; } + | array_initialization_modifier + ; + +array_type_modifier + : rank_specifiers { $$ = $1; } + ; + +opt_dim_separators + : /* empty */ + { + $$ = ""; + } + | dim_separators + { + $$ = $1; + } + ; + +dim_separators + : COMMA + { + $$ = ","; + } + | dim_separators COMMA + { + $$ = (string) $1 + ","; + } + ; + +opt_variable_initializer + : /* empty */ { $$ = null; } + | ASSIGN variable_initializer { $$ = $2; } + ; + +array_initialization_modifier + : /* empty */ { $$ = null; } + ; + +variable_initializer + : expression + { + $$ = $1; + } + /*| array_initializer + { + $$ = $1; + } + */ + ; + +/* + * The following is from Rhys' grammar: + * > Types in local variable declarations must be recognized as + * > expressions to prevent reduce/reduce errors in the grammar. + * > The expressions are converted into types during semantic analysis. + */ +local_variable_type + : primary_expression opt_rank_specifier + { + // FIXME: Do something smart here regarding the composition of the type. + + // Ok, the above "primary_expression" is there to get rid of + // both reduce/reduce and shift/reduces in the grammar, it should + // really just be "type_name". If you use type_name, a reduce/reduce + // creeps up. If you use qualified_identifier (which is all we need + // really) two shift/reduces appear. + // + + // So the super-trick is that primary_expression + // can only be either a SimpleName or a MemberAccess. + // The MemberAccess case arises when you have a fully qualified type-name like : + // Foo.Bar.Blah i; + // SimpleName is when you have + // Blah i; + + Expression expr = (Expression) $1; + if (!(expr is SimpleName || expr is MemberAccess)) { + Error_ExpectingTypeName (lexer.Location, expr); + $$ = null; + } else { + // + // So we extract the string corresponding to the SimpleName + // or MemberAccess + // + if ((string) $2 == "") + $$ = $1; + else + $$ = new ComposedCast ((Expression) $1, (string) $2, lexer.Location); + } + } + | builtin_types opt_rank_specifier + { + if ((string) $2 == "") + $$ = $1; + else + $$ = new ComposedCast ((Expression) $1, (string) $2, lexer.Location); + } + ; + +rank_specifiers + : rank_specifier + { + $$ = $1; + } + | rank_specifiers rank_specifier + { + $$ = (string) $2 + (string) $1; + } + ; + +rank_specifier + : OPEN_PARENS opt_dim_separators CLOSE_PARENS + { + $$ = "[" + (string) $2 + "]"; + } + ; + +opt_rank_specifier + : /* empty */ + { + $$ = ""; + } + | rank_specifiers + { + $$ = $1; + } + ; + +opt_dim_separators + : /* empty */ + { + $$ = ""; + } + | dim_separators + { + $$ = $1; + } + | dim_specifiers + { + $$ = $1; + } + ; + +dim_separators + : COMMA + { + $$ = ","; + } + | dim_separators COMMA + { + $$ = (string) $1 + ","; + } + ; + +dim_specifiers + : integer_literal { $$ = ((IntLiteral)$1).AsString(); } + | dim_specifiers COMMA integer_literal { $$ = $1 + "," + ((IntLiteral)$3).AsString(); } + ; + +/* Expressions */ +primary_expression + : literal + { + // 7.5.1: Literals + } + + | qualified_identifier + { + string name = (string) $1; + + $$ = null; + $$ = DecomposeQI (name, lexer.Location); + } + | parenthesized_expression + | member_access + | invocation_expression + | element_access + | this_access + | base_access + | new_expression + ; + +literal + : boolean_literal + | integer_literal + | real_literal + | LITERAL_CHARACTER { $$ = new CharLiteral ((char) lexer.Value); } + | LITERAL_STRING { $$ = new StringLiteral ((string) lexer.Value); } + | NOTHING { $$ = NullLiteral.Null; } + ; + +real_literal + : LITERAL_SINGLE { $$ = new FloatLiteral ((float) lexer.Value); } + | LITERAL_DOUBLE { $$ = new DoubleLiteral ((double) lexer.Value); } + | LITERAL_DECIMAL { $$ = new DecimalLiteral ((decimal) lexer.Value); } + ; + +integer_literal + : LITERAL_INTEGER { + object v = lexer.Value; + + if (v is int) + $$ = new IntLiteral ((Int32) v); + else if (v is uint) + $$ = new UIntLiteral ((UInt32) v); + else if (v is long) + $$ = new LongLiteral ((Int64) v); + else if (v is ulong) + $$ = new ULongLiteral ((UInt64) v); + else + Console.WriteLine ("OOPS. Unexpected result from scanner"); + + } + ; + +boolean_literal + : TRUE { $$ = new BoolLiteral (true); } + | FALSE { $$ = new BoolLiteral (false); } + ; + +parenthesized_expression + : OPEN_PARENS expression CLOSE_PARENS + { $$ = $2; } + ; + +member_access + : primary_expression DOT IDENTIFIER + { + $$ = new MemberAccess ((Expression) $1, (string) $3, lexer.Location); + } + | predefined_type DOT IDENTIFIER + { + $$ = new MemberAccess ((Expression) $1, (string) $3, lexer.Location); + } + ; + +predefined_type + : builtin_types + ; + +invocation_expression + : primary_expression OPEN_PARENS opt_argument_list CLOSE_PARENS + { + if ($1 == null) { + Location l = lexer.Location; + Report.Error (1, l, "THIS IS CRAZY"); + } + $$ = new Invocation ((Expression) $1, (ArrayList) $3, lexer.Location); + } + ; + +opt_argument_list + : /* empty */ { $$ = null; } + | argument_list + ; + +argument_list + : argument + { + ArrayList list = new ArrayList (); + list.Add ($1); + $$ = list; + } + | argument_list COMMA argument + { + ArrayList list = (ArrayList) $1; + list.Add ($3); + $$ = list; + } + ; + +argument + : expression + { + $$ = new Argument ((Expression) $1, Argument.AType.Expression); + } + | BYREF variable_reference + { + $$ = new Argument ((Expression) $2, Argument.AType.Ref); + } + | /* empty */ + { + $$ = new Argument (new EmptyExpression (), Argument.AType.NoArg); + } + ; + +variable_reference + : expression {/* note ("section 5.4"); */ $$ = $1; } + ; + +element_access + : primary_expression OPEN_PARENS expression_list CLOSE_PARENS + { + $$ = new ElementAccess ((Expression) $1, (ArrayList) $3, lexer.Location); + } + /*| primary_expression rank_specifiers + { + // So the super-trick is that primary_expression + // can only be either a SimpleName or a MemberAccess. + // The MemberAccess case arises when you have a fully qualified type-name like : + // Foo.Bar.Blah i; + // SimpleName is when you have + // Blah i; + Expression expr = (Expression) $1; + + if (!(expr is SimpleName || expr is MemberAccess)) { + Error_ExpectingTypeName (lexer.Location, expr); + $$ = TypeManager.system_object_expr; + } else { + // + // So we extract the string corresponding to the SimpleName + // or MemberAccess + // + $$ = new SimpleName (GetQualifiedIdentifier (expr) + (string) $2, lexer.Location); + } + }*/ + ; + +opt_expression + : /* empty */ + | expression + ; + +expression_list + : expression + { + ArrayList list = new ArrayList (); + list.Add ($1); + $$ = list; + } + | expression_list COMMA expression + { + ArrayList list = (ArrayList) $1; + list.Add ($3); + $$ = list; + } + ; + +this_access + : ME + { + $$ = new This (current_block, lexer.Location); + } + ; + +base_access + : MYBASE DOT IDENTIFIER + { + $$ = new BaseAccess ((string) $3, lexer.Location); + } + | MYBASE OPEN_BRACKET expression_list CLOSE_BRACKET + { + $$ = new BaseIndexerAccess ((ArrayList) $3, lexer.Location); + } + ; + +post_increment_expression + : primary_expression OP_INC + { + $$ = new UnaryMutator (UnaryMutator.Mode.PostIncrement, + (Expression) $1, lexer.Location); + } + ; + +unary_expression + : primary_expression + | NOT prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.LogicalNot, (Expression) $2, lexer.Location); + } + ; + + // + // The idea to split this out is from Rhys' grammar + // to solve the problem with casts. + // +prefixed_unary_expression + : unary_expression + | PLUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryPlus, (Expression) $2, lexer.Location); + } + | MINUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryNegation, (Expression) $2, lexer.Location); + } + | ADDRESSOF prefixed_unary_expression + { + // FIXME: We should generate an error if AddressOf is NOT used + // during delegate creation + $$ = $2; + } + ; + +multiplicative_expression + : prefixed_unary_expression + | multiplicative_expression STAR prefixed_unary_expression + { + $$ = new Binary (Binary.Operator.Multiply, + (Expression) $1, (Expression) $3, lexer.Location); + } + | multiplicative_expression DIV prefixed_unary_expression + { + $$ = new Binary (Binary.Operator.Division, + (Expression) $1, (Expression) $3, lexer.Location); + } + | multiplicative_expression OP_MODULUS prefixed_unary_expression + { + $$ = new Binary (Binary.Operator.Modulus, + (Expression) $1, (Expression) $3, lexer.Location); + } + ; + +additive_expression + : multiplicative_expression + | additive_expression PLUS multiplicative_expression + { + $$ = new Binary (Binary.Operator.Addition, + (Expression) $1, (Expression) $3, lexer.Location); + } + | additive_expression MINUS multiplicative_expression + { + $$ = new Binary (Binary.Operator.Subtraction, + (Expression) $1, (Expression) $3, lexer.Location); + } + ; + +relational_expression + : additive_expression + | relational_expression OP_LT additive_expression + { + $$ = new Binary (Binary.Operator.LessThan, + (Expression) $1, (Expression) $3, lexer.Location); + } + | relational_expression OP_GT additive_expression + { + $$ = new Binary (Binary.Operator.GreaterThan, + (Expression) $1, (Expression) $3, lexer.Location); + } + | relational_expression OP_LE additive_expression + { + $$ = new Binary (Binary.Operator.LessThanOrEqual, + (Expression) $1, (Expression) $3, lexer.Location); + } + | relational_expression OP_GE additive_expression + { + $$ = new Binary (Binary.Operator.GreaterThanOrEqual, + (Expression) $1, (Expression) $3, lexer.Location); + } + | relational_expression IS type + { + $$ = new Is ((Expression) $1, (Expression) $3, lexer.Location); + } + | relational_expression AS type + { + $$ = new As ((Expression) $1, (Expression) $3, lexer.Location); + } + ; + +equality_expression + : relational_expression + | equality_expression OP_EQ relational_expression + { + $$ = new Binary (Binary.Operator.Equality, + (Expression) $1, (Expression) $3, lexer.Location); + } + | equality_expression OP_NE relational_expression + { + $$ = new Binary (Binary.Operator.Inequality, + (Expression) $1, (Expression) $3, lexer.Location); + } + ; + +and_expression + : equality_expression + | and_expression OP_AND equality_expression + { + $$ = new Binary (Binary.Operator.BitwiseAnd, + (Expression) $1, (Expression) $3, lexer.Location); + } + ; + +exclusive_or_expression + : and_expression + | exclusive_or_expression OP_XOR and_expression + { + $$ = new Binary (Binary.Operator.ExclusiveOr, + (Expression) $1, (Expression) $3, lexer.Location); + } + ; + +conditional_and_expression + : exclusive_or_expression + | conditional_and_expression OP_AND exclusive_or_expression + { + $$ = new Binary (Binary.Operator.LogicalAnd, + (Expression) $1, (Expression) $3, lexer.Location); + } + ; + +conditional_or_expression + : conditional_and_expression + | conditional_or_expression OP_OR conditional_and_expression + { + $$ = new Binary (Binary.Operator.LogicalOr, + (Expression) $1, (Expression) $3, lexer.Location); + } + ; + +conditional_expression + : conditional_or_expression + ; + +assignment_expression + : prefixed_unary_expression ASSIGN expression + { + $$ = new Assign ((Expression) $1, (Expression) $3, lexer.Location); + } + ; + +expression + : conditional_expression + | assignment_expression + ; + +constant_expression + : expression + ; + +boolean_expression + : expression + ; + +type + : type_name { /* class_type */ + /* + This does interfaces, delegates, struct_types, class_types, + parent classes, and more! 4.2 + */ + $$ = DecomposeQI ((string) $1, lexer.Location); + } + | builtin_types + /*| array_type + | pointer_type */ + ; + +type_list + : type + { + ArrayList types = new ArrayList (); + + types.Add ($1); + $$ = types; + } + | type_list COMMA type + { + ArrayList types = (ArrayList) $1; + + types.Add ($3); + $$ = types; + } + ; + +type_name + : namespace_or_type_name + ; + +namespace_or_type_name + : qualified_identifier + ; + +array_type + : type rank_specifiers + { + $$ = new ComposedCast ((Expression) $1, (string) $2, lexer.Location); + } + ; + +rank_specifiers + : rank_specifier opt_rank_specifier + { + $$ = (string) $2 + (string) $1; + } + ; + +rank_specifier + : OPEN_BRACKET opt_dim_separators CLOSE_BRACKET + { + $$ = "[" + (string) $2 + "]"; + } + ; + +opt_dim_separators + : /* empty */ + { + $$ = ""; + } + | dim_separators + { + $$ = $1; + } + ; + +dim_separators + : COMMA + { + $$ = ","; + } + | dim_separators COMMA + { + $$ = (string) $1 + ","; + } + ; +/* Built-in / Integral types */ +builtin_types + : OBJECT { $$ = TypeManager.system_object_expr; } + | STRING { $$ = TypeManager.system_string_expr; } + | BOOLEAN { $$ = TypeManager.system_boolean_expr; } + | DECIMAL { $$ = TypeManager.system_decimal_expr; } + | SINGLE { $$ = TypeManager.system_single_expr; } + | DOUBLE { $$ = TypeManager.system_double_expr; } + | integral_type + ; + +integral_type + : /*SBYTE { $$ = TypeManager.system_sbyte_expr; } + | BYTE { $$ = TypeManager.system_byte_expr; } + | SHORT { $$ = TypeManager.system_int16_expr; } + | USHORT { $$ = TypeManager.system_uint16_expr; } + | */ INTEGER { $$ = TypeManager.system_int32_expr; }/* + | UINT { $$ = TypeManager.system_uint32_expr; } + | LONG { $$ = TypeManager.system_int64_expr; } + | ULONG { $$ = TypeManager.system_uint64_expr; } + | CHAR { $$ = TypeManager.system_char_expr; } + | VOID { $$ = TypeManager.system_void_expr; }*/ + ; + +interface_type + : type_name + ; +%% + + +Tokenizer lexer; + +public Tokenizer Lexer { + get { + return lexer; + } +} + +Expression DecomposeQI (string name, Location loc) +{ + Expression o; + + if (name.IndexOf ('.') == -1){ + return new SimpleName (name, loc); + } else { + int pos = name.LastIndexOf ("."); + string left = name.Substring (0, pos); + string right = name.Substring (pos + 1); + + o = DecomposeQI (left, loc); + + return new MemberAccess (o, right, loc); + } +} + +Block declare_local_variables (Expression dummy_type, ArrayList variable_declarators, Location loc) +{ + Block implicit_block; + ArrayList inits = null; + + // + // We use the `Used' property to check whether statements + // have been added to the current block. If so, we need + // to create another block to contain the new declaration + // otherwise, as an optimization, we use the same block to + // add the declaration. + // + // FIXME: A further optimization is to check if the statements + // that were added were added as part of the initialization + // below. In which case, no other statements have been executed + // and we might be able to reduce the number of blocks for + // situations like this: + // + // int j = 1; int k = j + 1; + // + + VariableDeclaration.FixupTypes (variable_declarators); + + if (current_block.Used) { + implicit_block = new Block (current_block, true, loc, Location.Null); + implicit_block.AddChildVariableNames (current_block); + } else + implicit_block = current_block; + + foreach (VariableDeclaration decl in variable_declarators){ + Expression type = decl.type; + if (implicit_block.AddVariable (type, decl.identifier, current_local_parameters, decl.Location) != null) { + if (decl.expression_or_array_initializer != null){ + if (inits == null) + inits = new ArrayList (); + inits.Add (decl); + } + } + } + + if (inits == null) + return implicit_block; + + foreach (VariableDeclaration decl in inits){ + Assign assign; + Expression expr; + Expression type = decl.type; + + if (decl.expression_or_array_initializer is Expression){ + expr = (Expression) decl.expression_or_array_initializer; + + } else { + ArrayList init = (ArrayList) decl.expression_or_array_initializer; + + expr = new ArrayCreation (type, "", init, decl.Location); + } + + LocalVariableReference var; + var = new LocalVariableReference (implicit_block, decl.identifier, loc); + + assign = new Assign (var, expr, decl.Location); + + implicit_block.AddStatement (new StatementExpression (assign, lexer.Location)); + } + + return implicit_block; +} + + +Block declare_local_constant (Expression type, VariableDeclaration decl) +{ + Block implicit_block; + + if (current_block.Used) + implicit_block = new Block (current_block, true); + else + implicit_block = current_block; + + if (!(implicit_block.AddConstant (type, decl.identifier, (Expression) decl.expression_or_array_initializer, + current_local_parameters, decl.Location))){ + } + + return implicit_block; +} + +// <summary> +// A class used to pass around variable declarations and constants +// </summary> +public class VariableDeclaration { + public string identifier; + public object expression_or_array_initializer; + public Location Location; + public Attributes OptAttributes; + public Expression type; + public ArrayList dims; + + public VariableDeclaration (string id, object eoai, Location l, Attributes opt_attrs) : this + (id, TypeManager.system_object_expr, eoai, l, opt_attrs) + { + } + + public VariableDeclaration (string id, Expression t, object eoai, Location l, Attributes opt_attrs) + { + this.identifier = id; + this.expression_or_array_initializer = eoai; + this.Location = l; + this.OptAttributes = opt_attrs; + this.type = t; + this.dims = null; + } + + public VariableDeclaration (string id, object eoai, Location l) : this (id, eoai, l, null) + { + } + + public static void FixupTypes (ArrayList vars) + { + int varcount = vars.Count; + VariableDeclaration lf = (VariableDeclaration) vars[varcount - 1]; + + if (lf.type == null) + lf.type = TypeManager.system_object_expr; + + Expression cur_type = lf.type; + int n = varcount - 1; + + while (n >= 0) { + VariableDeclaration var = (VariableDeclaration) vars[n--]; + if (var.type == null) + var.type = cur_type; + else + cur_type = var.type; + } + } + + public static bool HasDimBounds (string varname) + { + bool res = false; + + if (varname.IndexOf("[") >= 0) { + char[] ds = {'1','2','3','4','5','6','7','8','9'}; + + string dimpart = varname.Substring(varname.IndexOf("["), (varname.LastIndexOf("]") - varname.IndexOf("["))+1); + if (dimpart.IndexOfAny (ds) >= 0) + res = true; + } + return (res); + } + + public static string StripDims (string varname, ref string d) + { + string res = varname; + string dres = ""; + + if (varname.IndexOf("[") >= 0) { + dres = varname.Substring(varname.IndexOf("["), (varname.LastIndexOf("]") - varname.IndexOf("["))+1); + res = varname.Substring(0, varname.IndexOf("[")); + } + d = dres; + return (res); + } + + public static string GetRank (string dims) + { + string res = ""; + int x; + + for (x = 0; x < dims.Length; x++) { + if (dims[x] == '[' || dims[x] == ']' || dims[x] == ',') + res = res + dims[x]; + } + return (res); + } + + public static ArrayList ParseDims (string dims) + { + ArrayList res = new ArrayList(); + string d = dims.Substring (1, dims.Length -2); + Array a = d.Split (','); + + if (a.GetLength(0) > 32) { + Report.Error (999, "Arrays cannot have more than 32 dimensions"); + } + + foreach (string s in a) + res.Add (new IntLiteral ((Int32) Convert.ToInt32(s))); + + return (res); + } + + public static bool IsArrayDecl (string varname) + { + return (varname.IndexOf("[") >= 0); + } + + public static void FixupArrayTypes (ArrayList vars) + { + int varcount = vars.Count; + string dims; + + foreach (VariableDeclaration var in vars) { + if (var.identifier.EndsWith(",")) { + dims = "[" + var.identifier.Substring(var.identifier.IndexOf (","), + var.identifier.LastIndexOf(",")) + "]"; + var.identifier = var.identifier.Substring (0, var.identifier.IndexOf (",")); + var.type = new ComposedCast (var.type, (string) dims, var.Location); + } + } + } +} + +public Property BuildSimpleProperty (Expression p_type, string name, + Field p_fld, int mod_flags, + Attributes attrs, Location loc) +{ + Property p; + Block get_block, set_block; + Accessor acc_set, acc_get; + StatementExpression a_set; + Statement a_get; + Parameter [] args; + + // Build SET Block + Parameter implicit_value_parameter = new Parameter (p_type, "value", Parameter.Modifier.NONE, null); + args = new Parameter [1]; + args [0] = implicit_value_parameter; + + Parameters set_params = new Parameters (args, null, loc); + a_set = new StatementExpression ((ExpressionStatement) new Assign ((Expression) DecomposeQI(p_fld.Name, loc), + (Expression) new SimpleName("value", loc), loc), loc); + + set_block = new Block (current_block, set_params, loc, Location.Null); + set_block.AddStatement ((Statement) a_set); + acc_set = new Accessor (set_block, attrs); + + // Build GET Block + a_get = (Statement) new Return ((Expression) DecomposeQI(p_fld.Name, loc), loc); + get_block = new Block (current_block, null, loc, Location.Null); + get_block.AddStatement ((Statement) a_get); + acc_get = new Accessor (get_block, attrs); + + p = new Property (p_type, name, mod_flags, (Accessor) acc_get, (Accessor) acc_set, attrs, loc); + + return (p); +} + +void start_block () +{ + current_block = new Block (current_block, current_local_parameters, + lexer.Location, Location.Null); +} + +Block end_block () +{ + Block res; + + while (current_block.Implicit) + current_block = current_block.Parent; + + res = current_block; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + return (res); +} + +private void AddHandler (Expression evt_definition, string handler_name) +{ + AddHandler (current_block, evt_definition, handler_name); +} + +private void AddHandler (Block b, Expression evt_definition, string handler_name) +{ + Location loc = lexer.Location; + ArrayList neh_args = new ArrayList(); + neh_args.Add (new Argument (DecomposeQI(handler_name, loc), Argument.AType.Expression)); + + ExpressionStatement se = (ExpressionStatement)new New (DecomposeQI("System.EventHandler", loc), neh_args, loc); + + CompoundAssign ca = new CompoundAssign ( + Binary.Operator.Addition, evt_definition, (Expression) se, loc); + + Statement s = (Statement)(new StatementExpression ((ExpressionStatement) ca, loc)); + b.AddStatement (s); +} + +// FIXME: THIS DOES NOT WORK!!! +private void RemoveHandler (Block b, Expression evt_definition, string handler_name) +{ + Location loc = lexer.Location; + ArrayList neh_args = new ArrayList(); + neh_args.Add (new Argument (DecomposeQI(handler_name, loc), Argument.AType.Expression)); + + ExpressionStatement se = (ExpressionStatement)new New (DecomposeQI("System.EventHandler", loc), neh_args, loc); + + CompoundAssign ca = new CompoundAssign ( + Binary.Operator.Subtraction, evt_definition, (Expression) se, loc); + + Statement s = (Statement)(new StatementExpression ((ExpressionStatement) ca, loc)); + b.AddStatement (s); +} + +// <summary> +// This method is used to get at the complete string representation of +// a fully-qualified type name, hiding inside a MemberAccess ;-) +// This is necessary because local_variable_type admits primary_expression +// as the type of the variable. So we do some extra checking +// </summary> +string GetQualifiedIdentifier (Expression expr) +{ + if (expr is SimpleName) + return ((SimpleName)expr).Name; + else if (expr is MemberAccess) + return GetQualifiedIdentifier (((MemberAccess)expr).Expr) + "." + ((MemberAccess) expr).Identifier; + else + throw new Exception ("Expr has to be either SimpleName or MemberAccess! (" + expr + ")"); + +} + +private void RemoveHandler (Expression evt_definition, string handler_name) +{ + RemoveHandler (current_block, evt_definition, handler_name); +} + + + +void Error_ExpectingTypeName (Location l, Expression expr) +{ + if (expr is Invocation){ + Report.Error (1002, l, "; expected"); + } else { + Report.Error (-1, l, "Invalid Type definition"); + } +} + +static bool AlwaysAccept (MemberInfo m, object filterCriteria) { + return true; +} + +public override int parse () +{ + current_namespace = new Namespace (null, ""); + current_container = RootContext.Tree.Types; + current_container.Namespace = current_namespace; + oob_stack = new Stack (); + switch_stack = new Stack (); + + UseExtendedSyntax = name.EndsWith(".mbs"); + + lexer = new Tokenizer (input, name, defines); + StringBuilder value = new StringBuilder (); + //yacc_verbose_flag=true; + try + { + if (yacc_verbose_flag) + yyparse (lexer, new yydebug.yyDebugSimple ()); + else + yyparse (lexer); + } + catch (Exception e) + { + Console.WriteLine (lexer.location + " : Parsing error in " + lexer.ref_name); + Report.Error (9999, lexer.Location, ""); + Console.WriteLine (e); + } + + return Report.Errors; +} + +/* end end end */ +} diff --git a/mcs/mbas/mb-tokenizer.cs b/mcs/mbas/mb-tokenizer.cs new file mode 100644 index 00000000000..028c16cceb3 --- /dev/null +++ b/mcs/mbas/mb-tokenizer.cs @@ -0,0 +1,888 @@ +// +// Mono.MonoBASIC.Tokenizer.cs: The Tokenizer for the MonoBASIC compiler +// +// Author: A Rafael D Teixeira (rafaelteixeirabr@hotmail.com) +// +// Based on cs-tokenizer.cs by Miguel de Icaza (miguel@gnu.org) +// +// Licensed under the terms of the GNU GPL +// +// Copyright (C) 2001 A Rafael D Teixeira +// + +namespace Mono.MonoBASIC +{ + using System; + using System.Text; + using System.Collections; + using System.IO; + using System.Globalization; + using Mono.Languages; + using Mono.CSharp; + + /// <summary> + /// Tokenizer for MonoBASIC source code. + /// </summary> + + public class Tokenizer : yyParser.yyInput + { + TextReader reader; + public string ref_name; + public int ref_line = 1; + public int line = 1; + public int col = 1; + public int current_token; + bool handle_get_set = false; + + public int ExpandedTabsSize = 4; + + public string location { + get { + string det; + + if (current_token == Token.ERROR) + det = "detail: " + error_details; + else + det = ""; + + return "Line: "+line+" Col: "+col + "\n" + + "VirtLine: "+ref_line + + " Token: "+current_token + " " + det; + } + } + + public bool properties { + get { + return handle_get_set; + } + + set { + handle_get_set = value; + } + } + + // + // Class variables + // + static Hashtable keywords; + static NumberStyles styles; + static NumberFormatInfo csharp_format_info; + + // + // Values for the associated token returned + // + System.Text.StringBuilder number; + int putback_char; + Object val; + + // + // Details about the error encoutered by the tokenizer + // + string error_details; + + public string error { + get { + return error_details; + } + } + + public int Line { + get { + return line; + } + } + + public int Col { + get { + return col; + } + } + + static void initTokens () + { + keywords = new Hashtable (); + + keywords.Add ("addhandler", Token.ADDHANDLER); + keywords.Add ("addressof", Token.ADDRESSOF); + keywords.Add ("alias", Token.ALIAS); + keywords.Add ("and", Token.AND); + keywords.Add ("andalso", Token.ANDALSO); + keywords.Add ("ansi", Token.ANSI); + keywords.Add ("as", Token.AS); + keywords.Add ("assembly", Token.ASSEMBLY); + keywords.Add ("auto", Token.AUTO); + keywords.Add ("boolean", Token.BOOLEAN); + keywords.Add ("byref", Token.BYREF); + keywords.Add ("byte", Token.BYTE); + keywords.Add ("byval", Token.BYVAL); + keywords.Add ("call", Token.CALL); + keywords.Add ("case", Token.CASE); + keywords.Add ("catch", Token.CATCH); + keywords.Add ("cbool", Token.CBOOL); + keywords.Add ("cbyte", Token.CBYTE); + keywords.Add ("cchar", Token.CCHAR); + keywords.Add ("cdate", Token.CDATE); + keywords.Add ("cdec", Token.CDEC); + keywords.Add ("cdbl", Token.CDBL); + keywords.Add ("char", Token.CHAR); + keywords.Add ("cint", Token.CINT); + keywords.Add ("class", Token.CLASS); + keywords.Add ("clng", Token.CLNG); + keywords.Add ("cobj", Token.COBJ); + //keywords.Add ("compare", Token.COMPARE); + keywords.Add ("const", Token.CONST); + keywords.Add ("cshort", Token.CSHORT); + keywords.Add ("csng", Token.CSNG); + keywords.Add ("cstr", Token.CSTR); + keywords.Add ("ctype", Token.CTYPE); + keywords.Add ("date", Token.DATE); + keywords.Add ("decimal", Token.DECIMAL); + keywords.Add ("declare", Token.DECLARE); + keywords.Add ("default", Token.DEFAULT); + keywords.Add ("delegate", Token.DELEGATE); + keywords.Add ("dim", Token.DIM); + keywords.Add ("do", Token.DO); + keywords.Add ("double", Token.DOUBLE); + keywords.Add ("each", Token.EACH); + keywords.Add ("else", Token.ELSE); + keywords.Add ("elseif", Token.ELSEIF); + keywords.Add ("end", Token.END); + keywords.Add ("enum", Token.ENUM); + keywords.Add ("erase", Token.ERASE); + keywords.Add ("error", Token.ERROR); + keywords.Add ("event", Token.EVENT); + keywords.Add ("exit", Token.EXIT); + //keywords.Add ("explicit", Token.EXPLICIT); + keywords.Add ("false", Token.FALSE); + keywords.Add ("finally", Token.FINALLY); + keywords.Add ("for", Token.FOR); + keywords.Add ("friend", Token.FRIEND); + keywords.Add ("function", Token.FUNCTION); + keywords.Add ("get", Token.GET); + keywords.Add ("gettype", Token.GETTYPE); + keywords.Add ("goto", Token.GOTO); + keywords.Add ("handles", Token.HANDLES); + keywords.Add ("if", Token.IF); + keywords.Add ("implements", Token.IMPLEMENTS); + keywords.Add ("imports", Token.IMPORTS); + keywords.Add ("in", Token.IN); + keywords.Add ("inherits", Token.INHERITS); + keywords.Add ("integer", Token.INTEGER); + keywords.Add ("interface", Token.INTERFACE); + keywords.Add ("is", Token.IS); + keywords.Add ("let ", Token.LET ); + keywords.Add ("lib ", Token.LIB ); + keywords.Add ("like ", Token.LIKE ); + keywords.Add ("long", Token.LONG); + keywords.Add ("loop", Token.LOOP); + keywords.Add ("me", Token.ME); + keywords.Add ("mod", Token.MOD); + keywords.Add ("module", Token.MODULE); + keywords.Add ("mustinherit", Token.MUSTINHERIT); + keywords.Add ("mustoverride", Token.MUSTOVERRIDE); + keywords.Add ("mybase", Token.MYBASE); + keywords.Add ("myclass", Token.MYCLASS); + keywords.Add ("namespace", Token.NAMESPACE); + keywords.Add ("new", Token.NEW); + keywords.Add ("next", Token.NEXT); + keywords.Add ("not", Token.NOT); + keywords.Add ("nothing", Token.NOTHING); + keywords.Add ("notinheritable", Token.NOTINHERITABLE); + keywords.Add ("notoverridable", Token.NOTOVERRIDABLE); + keywords.Add ("object", Token.OBJECT); + keywords.Add ("on", Token.ON); + keywords.Add ("option", Token.OPTION); + keywords.Add ("optional", Token.OPTIONAL); + keywords.Add ("or", Token.OR); + keywords.Add ("orelse", Token.ORELSE); + keywords.Add ("overloads", Token.OVERLOADS); + keywords.Add ("overridable", Token.OVERRIDABLE); + keywords.Add ("overrides", Token.OVERRIDES); + keywords.Add ("paramarray", Token.PARAM_ARRAY); + keywords.Add ("preserve", Token.PRESERVE); + keywords.Add ("private", Token.PRIVATE); + keywords.Add ("property", Token.PROPERTY); + keywords.Add ("protected", Token.PROTECTED); + keywords.Add ("public", Token.PUBLIC); + keywords.Add ("raiseevent", Token.RAISEEVENT); + keywords.Add ("readonly", Token.READONLY); + keywords.Add ("redim", Token.REDIM); + keywords.Add ("rem", Token.REM); + keywords.Add ("removehandler", Token.REMOVEHANDLER); + keywords.Add ("resume", Token.RESUME); + keywords.Add ("return", Token.RETURN); + keywords.Add ("select", Token.SELECT); + keywords.Add ("set", Token.SET); + keywords.Add ("shadows", Token.SHADOWS); + keywords.Add ("shared", Token.SHARED); + keywords.Add ("short", Token.SHORT); + keywords.Add ("single", Token.SINGLE); + keywords.Add ("sizeof", Token.SIZEOF); + keywords.Add ("static", Token.STATIC); + keywords.Add ("step", Token.STEP); + keywords.Add ("stop", Token.STOP); + keywords.Add ("string", Token.STRING); + keywords.Add ("structure", Token.STRUCTURE); + keywords.Add ("sub", Token.SUB); + keywords.Add ("synclock", Token.SYNCLOCK); + keywords.Add ("then", Token.THEN); + keywords.Add ("throw", Token.THROW); + keywords.Add ("to", Token.TO); + keywords.Add ("true", Token.TRUE); + keywords.Add ("try", Token.TRY); + keywords.Add ("typeof", Token.TYPEOF); + keywords.Add ("unicode", Token.UNICODE); + keywords.Add ("until", Token.UNTIL); + keywords.Add ("variant", Token.VARIANT); + keywords.Add ("when", Token.WHEN); + keywords.Add ("while", Token.WHILE); + keywords.Add ("with", Token.WITH); + keywords.Add ("withevents", Token.WITHEVENTS); + keywords.Add ("writeonly", Token.WRITEONLY); + keywords.Add ("xor", Token.XOR); + } + + // + // Class initializer + // + static Tokenizer () + { + initTokens (); + csharp_format_info = new NumberFormatInfo (); + csharp_format_info.CurrencyDecimalSeparator = "."; + styles = NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint; + } + + bool is_keyword (string name) + { + bool res; + + res = keywords.Contains(name.ToLower()); + if ((name == "get" || name == "set") && handle_get_set == false) + return false; + return res; + } + + int getKeyword (string name) + { + return (int) (keywords [name.ToLower()]); + } + + public Location Location { + get { + return new Location (ref_line); + } + } + + public bool PropertyParsing { + get { + return handle_get_set; + } + + set { + handle_get_set = value; + } + } + + bool is_identifier_start_character (char c) + { + return Char.IsLetter (c) || c == '_' ; + } + + bool is_identifier_part_character (char c) + { + return (Char.IsLetter (c) || Char.IsDigit (c) || c == '_'); + } + + int is_punct (char c, ref bool doread) + { + int idx = "{}[](),:;~+-*/%&|^!=<>?".IndexOf (c); + int d; + int t; + + doread = false; + + switch (c){ +// case '[': +// return Token.OPEN_BRACKET; +// case ']': +// return Token.CLOSE_BRACKET; + case '(': + return Token.OPEN_PARENS; + case ')': + return Token.CLOSE_PARENS; + case ',': + return Token.COMMA; + case ':': + return Token.COLON; + case '?': + return Token.INTERR; + } + + d = peekChar (); + if (c == '+'){ + + if (d == '+') + t = Token.OP_INC; + else if (d == '=') + t = Token.OP_ADD_ASSIGN; + else + return Token.PLUS; + doread = true; + return t; + } + if (c == '-'){ + if (d == '=') + t = Token.OP_SUB_ASSIGN; + else + return Token.MINUS; + doread = true; + return t; + } + + if (c == '='){ + /*if (d == '='){ + doread = true; + return Token.OP_EQ; + }*/ + return Token.ASSIGN; + } + + if (c == '*'){ + if (d == '='){ + doread = true; + return Token.OP_MULT_ASSIGN; + } + return Token.STAR; + } + + if (c == '/'){ + if (d == '='){ + doread = true; + return Token.OP_DIV_ASSIGN; + } + return Token.DIV; + } + + if (c == '\\'){ + if (d == '='){ + doread = true; + return Token.OP_IDIV_ASSIGN; + } + return Token.OP_IDIV; + } + + if (c == '^'){ + if (d == '='){ + doread = true; + return Token.OP_EXP_ASSIGN; + } + return Token.OP_EXP; + } + + if (c == '<'){ + if (d == '>') + { + doread = true; + return Token.OP_NE; + } + if (d == '='){ + doread = true; + return Token.OP_LE; + } + return Token.OP_LT; + } + + if (c == '>'){ + if (d == '='){ + doread = true; + return Token.OP_GE; + } + return Token.OP_GT; + } + return Token.ERROR; + } + + bool decimal_digits (int c) + { + int d; + bool seen_digits = false; + + if (c != -1) + number.Append ((char) c); + + while ((d = peekChar ()) != -1){ + if (Char.IsDigit ((char)d)){ + number.Append ((char) d); + getChar (); + seen_digits = true; + } else + break; + } + return seen_digits; + } + + void hex_digits (int c) + { + int d; + + if (c != -1) + number.Append ((char) c); + while ((d = peekChar ()) != -1){ + char e = Char.ToUpper ((char) d); + + if (Char.IsDigit (e) || + (e >= 'A' && e <= 'F')){ + number.Append ((char) e); + getChar (); + } else + break; + } + } + + int real_type_suffix (int c) + { + int t; + + switch (c){ + case 'F': case 'f': + t = Token.LITERAL_SINGLE; + break; + case 'D': case 'd': + t = Token.LITERAL_DOUBLE; + break; + case 'M': case 'm': + t= Token.LITERAL_DECIMAL; + break; + default: + return Token.NONE; + } + getChar (); + return t; + } + + int integer_type_suffix (int c) + { + // FIXME: Handle U and L suffixes. + // We also need to see in which kind of + // Int the thing fits better according to the spec. + return Token.LITERAL_INTEGER; + } + + void adjust_int (int t) + { + val = new System.Int32(); + val = System.Int32.Parse (number.ToString (), 0); + } + + int adjust_real (int t) + { + string s = number.ToString (); + + Console.WriteLine (s); + switch (t){ + case Token.LITERAL_DECIMAL: + val = new System.Decimal (); + val = System.Decimal.Parse ( + s, styles, csharp_format_info); + break; + case Token.LITERAL_DOUBLE: + val = new System.Double (); + val = System.Double.Parse ( + s, styles, csharp_format_info); + break; + case Token.LITERAL_SINGLE: + val = new System.Double (); + val = (float) System.Double.Parse ( + s, styles, csharp_format_info); + break; + + case Token.NONE: + val = new System.Double (); + val = System.Double.Parse ( + s, styles, csharp_format_info); + t = Token.LITERAL_DOUBLE; + break; + } + return t; + } + + // + // Invoked if we know we have .digits or digits + // + int is_number (int c) + { + bool is_real = false; + number = new System.Text.StringBuilder (); + int type; + + number.Length = 0; + + if (Char.IsDigit ((char)c)){ + if (c == '0' && peekChar () == 'x' || peekChar () == 'X'){ + getChar (); + hex_digits (-1); + val = new System.Int32 (); + val = System.Int32.Parse (number.ToString (), NumberStyles.HexNumber); + return integer_type_suffix (peekChar ()); + } + decimal_digits (c); + c = getChar (); + } + + // + // We need to handle the case of + // "1.1" vs "1.string" (LITERAL_SINGLE vs NUMBER DOT IDENTIFIER) + // + if (c == '.'){ + if (decimal_digits ('.')){ + is_real = true; + c = peekChar (); + } else { + putback ('.'); + number.Length -= 1; + adjust_int (Token.LITERAL_INTEGER); + return Token.LITERAL_INTEGER; + } + } + + if (c == 'e' || c == 'E'){ + is_real = true; + number.Append ("e"); + getChar (); + + c = peekChar (); + if (c == '+'){ + number.Append ((char) c); + getChar (); + c = peekChar (); + } else if (c == '-'){ + number.Append ((char) c); + getChar (); + c = peekChar (); + } + decimal_digits (-1); + c = peekChar (); + } + + type = real_type_suffix (c); + if (type == Token.NONE && !is_real){ + type = integer_type_suffix (c); + adjust_int (type); + putback (c); + return type; + } else + is_real = true; + + if (is_real) + return adjust_real (type); + + Console.WriteLine ("This should not be reached"); + throw new Exception ("Is Number should never reach this point"); + } + + int escape (int c) + { + int d; + int v; + + d = peekChar (); + if (c != '\\') + return c; + + switch (d){ + case 'a': + v = '\a'; break; + case 'b': + v = '\b'; break; + case 'n': + v = '\n'; break; + case 't': + v = '\t'; break; + case 'v': + v = '\v'; break; + case 'r': + v = 'c'; break; + case '\\': + v = '\\'; break; + case 'f': + v = '\f'; break; + case '0': + v = 0; break; + case '"': + v = '"'; break; + case '\'': + v = '\''; break; + default: + error_details = "cs1009: Unrecognized escape sequence " + (char)d; + return -1; + } + getChar (); + return v; + } + + int getChar () + { + if (putback_char != -1){ + int x = putback_char; + putback_char = -1; + + return x; + } + return reader.Read (); + } + + int peekChar () + { + if (putback_char != -1) + return putback_char; + return reader.Peek (); + } + + void putback (int c) + { + if (putback_char != -1) + throw new Exception ("This should not happen putback on putback"); + putback_char = c; + } + + public bool advance () + { + return current_token != Token.EOF ; + } + + public Object Value { + get { + return val; + } + } + + public Object value () + { + return val; + } + + private bool IsEOL(int currentChar) + { + if (currentChar == 0x0D) + { + if (peekChar() == 0x0A) // if it is a CR-LF pair consume LF also + getChar(); + + return true; + } + return (currentChar == -1 || currentChar == 0x0A || currentChar == 0x2028 || currentChar == 0x2029); + } + + private int DropComments() + { + int d; + while (!IsEOL(d = getChar ())) + col++; + line++; + ref_line++; + col = 0; + + return Token.EOL; + } + + public int token () + { + int lastToken = current_token; + do + { + current_token = xtoken (); + if (current_token == 0) + return Token.EOF; + if (current_token == Token.REM) + current_token = DropComments(); + } while (lastToken == Token.EOL && current_token == Token.EOL); + + return current_token; + } + + private string GetIdentifier() + { + int c = getChar(); + if (is_identifier_start_character ((char) c)) + return GetIdentifier(c); + else + return null; + } + + private string GetIdentifier(int c) + { + System.Text.StringBuilder id = new System.Text.StringBuilder (); + + id.Append ((char) c); + + while ((c = peekChar ()) != -1) + { + if (is_identifier_part_character ((char) c)) + { + id.Append ((char)getChar ()); + col++; + } + else + break; + } + + return id.ToString (); + } + + public int xtoken () + { + int t; + bool doread = false; + int c; + + val = null; + for (;(c = getChar ()) != -1; col++) { + + // Handle line comments. + if (c == '\'') + return Token.REM; + + // Handle EOL. + if (IsEOL(c)) + { + line++; + ref_line++; + col = 0; + if (current_token == Token.EOL) // if last token was also EOL keep skipping + continue; + return Token.EOL; + } + + // Handle escaped identifiers + if (c == '[') + { + if ((val = GetIdentifier()) == null) + break; + if ((c = getChar()) != ']') + break; + return Token.IDENTIFIER; + } + + // Handle unescaped identifiers + if (is_identifier_start_character ((char) c)) + { + string id; + if ((id = GetIdentifier(c)) == null) + break; + if (is_keyword(id)) + return getKeyword(id); + val = id; + return Token.IDENTIFIER; + } + + // handle numeric literals + if (c == '.'){ + if (Char.IsDigit ((char) peekChar ())) + return is_number (c); + return Token.DOT; + } + + if (Char.IsDigit ((char) c)) + return is_number (c); + + /* For now, limited support for pre-processor commands */ + if (col == 1 && c == '#'){ + System.Text.StringBuilder s = new System.Text.StringBuilder (); + + while ((c = getChar ()) != -1 && (c != '\n')){ + s.Append ((char) c); + } + if (String.Compare (s.ToString (), 0, "line", 0, 4) == 0){ + string arg = s.ToString ().Substring (5); + int pos; + + if ((pos = arg.IndexOf (' ')) != -1 && pos != 0){ + ref_line = System.Int32.Parse (arg.Substring (0, pos)); + pos++; + + char [] quotes = { '\"' }; + + ref_name = arg.Substring (pos); + ref_name.TrimStart (quotes); + ref_name.TrimEnd (quotes); + } else + ref_line = System.Int32.Parse (arg); + } + line++; + ref_line++; + continue; + } + + if ((t = is_punct ((char)c, ref doread)) != Token.ERROR){ + if (doread){ + getChar (); + col++; + } + return t; + } + + // Treat string literals + if (c == '"'){ + System.Text.StringBuilder s = new System.Text.StringBuilder (); + + while ((c = getChar ()) != -1){ + if (c == '"'){ // TODO: treat double-doublequotes + val = s.ToString (); + return Token.LITERAL_STRING; + } + + c = escape (c); + if (c == -1) + return Token.ERROR; + s.Append ((char) c); + } + } + + // expand tabs for location and ignore it as whitespace + if (c == '\t') + { + col = (((col + ExpandedTabsSize) / ExpandedTabsSize) * ExpandedTabsSize) - 1; + continue; + } + + // white space + if (c == ' ' || c == '\f' || c == '\v') + continue; + + error_details = ((char)c).ToString (); + + return Token.ERROR; + } + + if (current_token != Token.EOL) // if last token wasn´t EOL send it before EOF + return Token.EOL; + + return Token.EOF; + } + + public void cleanup ()
+ { +/* borrowed from mcs - have to work it to have preprocessing in mbas +
+ if (ifstack != null && ifstack.Count >= 1) {
+ int state = (int) ifstack.Pop ();
+ if ((state & REGION) != 0)
+ Report.Error (1038, "#endregion directive expected");
+ else
+ Report.Error (1027, "#endif directive expected");
+ }
+*/
+ }
+
+ public Tokenizer (System.IO.TextReader input, string fname, ArrayList defines) + { + this.ref_name = fname; + reader = input; + putback_char = -1; + + Location.Push (fname); + } + + } +} diff --git a/mcs/mbas/mbas.csproj b/mcs/mbas/mbas.csproj new file mode 100644 index 00000000000..38360bcd2f1 --- /dev/null +++ b/mcs/mbas/mbas.csproj @@ -0,0 +1,264 @@ +<VisualStudioProject> + <CSHARP + ProjectType = "Local" + ProductVersion = "7.0.9466" + SchemaVersion = "1.0" + ProjectGuid = "{D9868C8B-B8C9-43E0-8702-F33AD49F1CC3}" + > + <Build> + <Settings + ApplicationIcon = "mbas.ico" + AssemblyKeyContainerName = "" + AssemblyName = "mbas" + AssemblyOriginatorKeyFile = "" + DefaultClientScript = "JScript" + DefaultHTMLPageLayout = "Grid" + DefaultTargetSchema = "IE50" + DelaySign = "false" + OutputType = "Exe" + RootNamespace = "Mono.Languages" + StartupObject = "" + > + <Config + Name = "Debug" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "DEBUG;TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "true" + Optimize = "false" + OutputPath = ".\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + <Config + Name = "Release" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "TRACE" + DocumentationFile = "" + DebugSymbols = "false" + FileAlignment = "4096" + IncrementalBuild = "false" + Optimize = "true" + OutputPath = "bin\Release\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + </Settings> + <References> + <Reference + Name = "System" + AssemblyName = "System" + HintPath = "..\..\..\..\WINNT\Microsoft.NET\Framework\v1.0.3705\System.dll" + /> + <Reference + Name = "System.Data" + AssemblyName = "System.Data" + HintPath = "..\..\..\..\WINNT\Microsoft.NET\Framework\v1.0.3705\System.Data.dll" + /> + <Reference + Name = "System.XML" + AssemblyName = "System.Xml" + HintPath = "..\..\..\..\WINNT\Microsoft.NET\Framework\v1.0.3705\System.XML.dll" + /> + <Reference + Name = "Mono.GetOptions" + Project = "{8D3008AB-7C0F-4DBE-A305-752926C366A7}" + Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + /> + </References> + </Build> + <Files> + <Include> + <File + RelPath = "AssemblyInfo.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "assign.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "attribute.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "cfold.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ChangeLog" + BuildAction = "None" + /> + <File + RelPath = "class.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "codegen.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "const.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "constant.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "decl.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "delegate.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "driver.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ecore.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "enum.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "expression.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "genericparser.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "interface.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "literal.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "location.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "mbas.ico" + BuildAction = "Content" + /> + <File + RelPath = "mb-parser.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "mb-parser.jay" + BuildAction = "None" + /> + <File + RelPath = "mb-tokenizer.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "modifiers.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "module.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "namespace.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "parameter.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "pending.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "report.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "rootcontext.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "statement.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "statementCollection.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "support.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "tree.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "typemanager.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "testmbas\filelist" + BuildAction = "Content" + /> + </Include> + </Files> + </CSHARP> +</VisualStudioProject> + diff --git a/mcs/mbas/mbas.ico b/mcs/mbas/mbas.ico Binary files differnew file mode 100644 index 00000000000..e134617dd82 --- /dev/null +++ b/mcs/mbas/mbas.ico diff --git a/mcs/mbas/mbas.sln b/mcs/mbas/mbas.sln new file mode 100644 index 00000000000..95f0b4a015e --- /dev/null +++ b/mcs/mbas/mbas.sln @@ -0,0 +1,33 @@ +Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mbas", "mbas.csproj", "{D9868C8B-B8C9-43E0-8702-F33AD49F1CC3}"
+EndProject
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "testmbas", "testmbas\testmbas.vbproj", "{64A40514-2574-4F75-B967-855531F2F01D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.GetOptions", "..\class\Mono.GetOptions\Mono.GetOptions.csproj", "{8D3008AB-7C0F-4DBE-A305-752926C366A7}"
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ ConfigName.0 = Debug
+ ConfigName.1 = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {D9868C8B-B8C9-43E0-8702-F33AD49F1CC3}.Debug.ActiveCfg = Debug|.NET
+ {D9868C8B-B8C9-43E0-8702-F33AD49F1CC3}.Debug.Build.0 = Debug|.NET
+ {D9868C8B-B8C9-43E0-8702-F33AD49F1CC3}.Release.ActiveCfg = Release|.NET
+ {D9868C8B-B8C9-43E0-8702-F33AD49F1CC3}.Release.Build.0 = Release|.NET
+ {64A40514-2574-4F75-B967-855531F2F01D}.Debug.ActiveCfg = Debug|.NET
+ {64A40514-2574-4F75-B967-855531F2F01D}.Debug.Build.0 = Debug|.NET
+ {64A40514-2574-4F75-B967-855531F2F01D}.Release.ActiveCfg = Release|.NET
+ {64A40514-2574-4F75-B967-855531F2F01D}.Release.Build.0 = Release|.NET
+ {8D3008AB-7C0F-4DBE-A305-752926C366A7}.Debug.ActiveCfg = Debug|.NET
+ {8D3008AB-7C0F-4DBE-A305-752926C366A7}.Debug.Build.0 = Debug|.NET
+ {8D3008AB-7C0F-4DBE-A305-752926C366A7}.Release.ActiveCfg = Release|.NET
+ {8D3008AB-7C0F-4DBE-A305-752926C366A7}.Release.Build.0 = Release|.NET
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/mcs/mbas/modifiers.cs b/mcs/mbas/modifiers.cs new file mode 100644 index 00000000000..a5cf6f62c03 --- /dev/null +++ b/mcs/mbas/modifiers.cs @@ -0,0 +1,241 @@ +// +// modifiers.cs: Modifier handling. +// +using System; +using System.Reflection; + +namespace Mono.CSharp { + public class Modifiers { + + // + // The ordering of the following 4 constants + // has been carefully done. + // + public const int PROTECTED = 0x0001; + public const int PUBLIC = 0x0002; + public const int PRIVATE = 0x0004; + public const int INTERNAL = 0x0008; + public const int NEW = 0x0010; + public const int ABSTRACT = 0x0020; + public const int SEALED = 0x0040; + public const int STATIC = 0x0080; + public const int READONLY = 0x0100; + public const int VIRTUAL = 0x0200; + public const int OVERRIDE = 0x0400; + public const int EXTERN = 0x0800; + public const int VOLATILE = 0x1000; + public const int UNSAFE = 0x2000; + public const int TOP = 0x2000; + + public const int Accessibility = + PUBLIC | PROTECTED | INTERNAL | PRIVATE; + + static public string Name (int i) + { + string s = ""; + + switch (i) { + case Modifiers.NEW: + s = "new"; break; + case Modifiers.PUBLIC: + s = "public"; break; + case Modifiers.PROTECTED: + s = "protected"; break; + case Modifiers.INTERNAL: + s = "internal"; break; + case Modifiers.PRIVATE: + s = "private"; break; + case Modifiers.ABSTRACT: + s = "abstract"; break; + case Modifiers.SEALED: + s = "sealed"; break; + case Modifiers.STATIC: + s = "static"; break; + case Modifiers.READONLY: + s = "readonly"; break; + case Modifiers.VIRTUAL: + s = "virtual"; break; + case Modifiers.OVERRIDE: + s = "override"; break; + case Modifiers.EXTERN: + s = "extern"; break; + case Modifiers.VOLATILE: + s = "volatile"; break; + } + + return s; + } + + public static TypeAttributes TypeAttr (int mod_flags, bool is_toplevel) + { + TypeAttributes t = 0; + + if (is_toplevel){ + if ((mod_flags & PUBLIC) != 0) + t |= TypeAttributes.Public; + if ((mod_flags & PRIVATE) != 0) + t |= TypeAttributes.NotPublic; + } else { + if ((mod_flags & PUBLIC) != 0) + t |= TypeAttributes.NestedPublic; + if ((mod_flags & PRIVATE) != 0) + t |= TypeAttributes.NestedPrivate; + if ((mod_flags & PROTECTED) != 0 && (mod_flags & INTERNAL) != 0) + t |= TypeAttributes.NestedFamORAssem; + if ((mod_flags & PROTECTED) != 0) + t |= TypeAttributes.NestedFamily; + if ((mod_flags & INTERNAL) != 0) + t |= TypeAttributes.NestedAssembly; + } + + if ((mod_flags & SEALED) != 0) + t |= TypeAttributes.Sealed; + if ((mod_flags & ABSTRACT) != 0) + t |= TypeAttributes.Abstract; + + return t; + } + + public static TypeAttributes TypeAttr (int mod_flags, TypeContainer caller) + { + TypeAttributes t = TypeAttr (mod_flags, caller.IsTopLevel); + + // If we do not have static constructors, static methods + // can be invoked without initializing the type. + if (!caller.HaveStaticConstructor) + t |= TypeAttributes.BeforeFieldInit; + + return t; + } + + public static FieldAttributes FieldAttr (int mod_flags) + { + FieldAttributes fa = 0; + + if ((mod_flags & PUBLIC) != 0) + fa |= FieldAttributes.Public; + if ((mod_flags & PRIVATE) != 0) + fa |= FieldAttributes.Private; + if ((mod_flags & PROTECTED) != 0){ + if ((mod_flags & INTERNAL) != 0) + fa |= FieldAttributes.FamORAssem; + else + fa |= FieldAttributes.Family; + } else { + if ((mod_flags & INTERNAL) != 0) + fa |= FieldAttributes.Assembly; + } + + if ((mod_flags & STATIC) != 0) + fa |= FieldAttributes.Static; + if ((mod_flags & READONLY) != 0) + fa |= FieldAttributes.InitOnly; + + return fa; + } + + public static MethodAttributes MethodAttr (int mod_flags) + { + MethodAttributes ma = 0; + + if ((mod_flags & PUBLIC) != 0) + ma |= MethodAttributes.Public; + if ((mod_flags & PRIVATE) != 0) + ma |= MethodAttributes.Private; + if ((mod_flags & PROTECTED) != 0){ + if ((mod_flags & INTERNAL) != 0) + ma |= MethodAttributes.FamORAssem; + else + ma |= MethodAttributes.Family; + } else { + if ((mod_flags & INTERNAL) != 0) + ma |= MethodAttributes.Assembly; + } + + if ((mod_flags & STATIC) != 0) + ma |= MethodAttributes.Static; + if ((mod_flags & ABSTRACT) != 0){ + ma |= MethodAttributes.Abstract | MethodAttributes.Virtual | + MethodAttributes.HideBySig; + } + if ((mod_flags & SEALED) != 0) + ma |= MethodAttributes.Final; + + if ((mod_flags & VIRTUAL) != 0) + ma |= MethodAttributes.Virtual; + + if ((mod_flags & OVERRIDE) != 0) + ma |= MethodAttributes.Virtual | MethodAttributes.HideBySig; + else { + if ((ma & MethodAttributes.Virtual) != 0) + ma |= MethodAttributes.NewSlot; + } + + if ((mod_flags & NEW) != 0) + ma |= MethodAttributes.HideBySig; + + return ma; + } + + // <summary> + // Checks the object @mod modifiers to be in @allowed. + // Returns the new mask. Side effect: reports any + // incorrect attributes. + // </summary> + public static int Check (int allowed, int mod, int def_access, Location l) + { + int invalid_flags = (~allowed) & mod; + int i; + + if (invalid_flags == 0){ + int a = mod; + + if ((mod & Modifiers.UNSAFE) != 0){ + if (!RootContext.Unsafe){ + Report.Error (227, l, + "Unsafe code requires the --unsafe command " + + "line option to be specified"); + } + } + + // + // If no accessibility bits provided + // then provide the defaults. + // + if ((mod & Accessibility) == 0){ + mod |= def_access; + return mod; + } + + // + // Make sure that no conflicting accessibility + // bits have been set. Protected+Internal is + // allowed, that is why they are placed on bits + // 1 and 4 (so the shift 3 basically merges them) + // + a &= 15; + a |= (a >> 3); + a = ((a & 2) >> 1) + (a & 5); + a = ((a & 4) >> 2) + (a & 3); + if (a > 1) + Report.Error (107, l, "More than one protection modifier specified"); + + return mod; + } + + for (i = 1; i < TOP; i <<= 1){ + if ((i & invalid_flags) == 0) + continue; + + Error_InvalidModifier (l, Name (i)); + } + + return allowed & mod; + } + + public static void Error_InvalidModifier (Location l, string name) + { + Report.Error (106, l, "the modifier " + name + " is not valid for this item"); + } + } +} diff --git a/mcs/mbas/module.cs b/mcs/mbas/module.cs new file mode 100644 index 00000000000..ec9982d7e02 --- /dev/null +++ b/mcs/mbas/module.cs @@ -0,0 +1,74 @@ +// +// module.cs: Module handler +// +// Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2002 Rafael Teixeira +// +using System; +using System.Collections; +using System.Diagnostics.SymbolStore; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using Mono.CSharp ; + +namespace Mono.MonoBASIC +{ + public class Utils + { + public static void AddSpecializedAttribute(ref Attributes attrs, string attributeName, ArrayList args, Location loc) + { + Mono.CSharp.Attribute specialAttr = new Mono.CSharp.Attribute(attributeName, args, loc); + ArrayList al = new ArrayList(); + al.Add(specialAttr); + AttributeSection asec = new AttributeSection(null, al); + if (attrs == null) + attrs = new Attributes(asec, loc); + else + attrs.AddAttribute(asec); + } + } + + /// <summary> + /// Summary description for module. + /// </summary> + public class Module : Mono.CSharp.Class + { + // <summary> + // Modifiers allowed in a class declaration + // </summary> + public new const int AllowedModifiers = + Modifiers.PUBLIC | + Modifiers.INTERNAL; + + public Module(TypeContainer parent, string name, int mod, Attributes attrs, Location l) + : base (parent, name, 0, null, l) + { + if (parent.Parent != null) + Report.Error (30617, l, + "'Module' statements can occur only at file or namespace level"); + + // overwrite ModFlags + this.ModFlags = Modifiers.Check (AllowedModifiers, mod, Modifiers.INTERNAL, l); + + // add specialized attribute + Utils.AddSpecializedAttribute(ref attrs, "Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute", null, l); + this.attributes = attrs; + } + + // + // FIXME: How do we deal with the user specifying a different + // layout? + // + public override TypeAttributes TypeAttr + { + get + { + return base.TypeAttr | TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.Sealed; + } + } + } +} diff --git a/mcs/mbas/namespace.cs b/mcs/mbas/namespace.cs new file mode 100644 index 00000000000..a173b903620 --- /dev/null +++ b/mcs/mbas/namespace.cs @@ -0,0 +1,179 @@ +// +// namespace.cs: Tracks namespaces +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. +// +using System; +using System.Collections; +using Mono.Languages; + +namespace Mono.CSharp { + + /// <summary> + /// Keeps track of the namespaces defined in the C# code. + /// </summary> + public class Namespace { + static ArrayList all_namespaces = new ArrayList (); + + Namespace parent; + string name; + ArrayList using_clauses; + Hashtable aliases; + public bool DeclarationFound = false; + + // + // This class holds the location where a using definition is + // done, and whether it has been used by the program or not. + // + // We use this to flag using clauses for namespaces that do not + // exist. + // + public class UsingEntry { + public string Name; + public bool Used; + public Location Location; + + public UsingEntry (string name, Location loc) + { + Name = name; + Location = loc; + Used = false; + } + } + + /// <summary> + /// Constructor Takes the current namespace and the + /// name. This is bootstrapped with parent == null + /// and name = "" + /// </summary> + public Namespace (Namespace parent, string name) + { + this.name = name; + this.parent = parent; + + all_namespaces.Add (this); + } + + /// <summary> + /// The qualified name of the current namespace + /// </summary> + public string Name { + get { + string pname = parent != null ? parent.Name : ""; + + if (pname == "") + return name; + else + return parent.Name + "." + name; + } + } + + /// <summary> + /// The parent of this namespace, used by the parser to "Pop" + /// the current namespace declaration + /// </summary> + public Namespace Parent { + get { + return parent; + } + } + + /// <summary> + /// Records a new namespace for resolving name references + /// </summary> + public void Using (string ns, Location loc) + { + if (DeclarationFound){ + Report.Error (1529, loc, "A using clause must precede all other namespace elements"); + return; + } + + if (using_clauses == null) + using_clauses = new ArrayList (); + + UsingEntry ue = new UsingEntry (ns, loc); + using_clauses.Add (ue); + } + + public ArrayList UsingTable { + get { + return using_clauses; + } + } + + public void UsingAlias (string alias, string namespace_or_type, Location loc) + { + if (aliases == null) + aliases = new Hashtable (); + + if (aliases.Contains (alias)){ + Report.Error (1537, loc, "The using alias `" + alias + + "' appeared previously in this namespace"); + return; + } + + aliases [alias] = namespace_or_type; + } + + public string LookupAlias (string alias) + { + string value = null; + + // System.Console.WriteLine ("Lookup " + alias + " in " + name); + + if (aliases != null) + value = (string) (aliases [alias]); + if (value == null && Parent != null) + value = Parent.LookupAlias (alias); + + return value; + } + + /// <summary> + /// Used to validate that all the using clauses are correct + /// after we are finished parsing all the files. + /// </summary> + public static bool VerifyUsing () + { + ArrayList unused = new ArrayList (); + int errors = 0; + + foreach (Namespace ns in all_namespaces){ + ArrayList uses = ns.UsingTable; + if (uses == null) + continue; + + foreach (UsingEntry ue in uses){ + if (ue.Used) + continue; + unused.Add (ue); + } + } + + // + // If we have unused using aliases, load all namespaces and check + // whether it is unused, or it was missing + // + if (unused.Count > 0){ + Hashtable namespaces = TypeManager.GetNamespaces (); + + foreach (UsingEntry ue in unused){ + if (namespaces.Contains (ue.Name)){ + Report.Warning (6024, ue.Location, "Unused namespace in `using' declaration"); + continue; + } + + errors++; + Report.Error (246, ue.Location, "The namespace `" + ue.Name + + "' can not be found (missing assembly reference?)"); + } + } + + return errors == 0; + } + + } +} diff --git a/mcs/mbas/parameter.cs b/mcs/mbas/parameter.cs new file mode 100644 index 00000000000..d0eac31da32 --- /dev/null +++ b/mcs/mbas/parameter.cs @@ -0,0 +1,537 @@ +// +// parameter.cs: Parameter definition. +// +// Author: Miguel de Icaza (miguel@gnu.org) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// +// +// +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections; + +namespace Mono.CSharp { + + + /// <summary> + /// Represents a single method parameter + /// </summary> + public class Parameter { + [Flags] + public enum Modifier : byte { + NONE = 0, + VAL = 0, + REF = 1, + OUT = 2, + PARAMS = 4, + // This is a flag which says that it's either REF or OUT. + ISBYREF = 8, + OPTIONAL = 16 + } + + public readonly Expression TypeName; + public readonly Modifier ModFlags; + public Attributes OptAttributes; + public readonly string Name; + public Type parameter_type; + public readonly Expression ParameterInitializer; + public readonly bool IsOptional; + + public Parameter (Expression type, string name, Modifier mod, Attributes attrs) + { + Name = name; + ModFlags = mod; + TypeName = type; + OptAttributes = attrs; + ParameterInitializer = null; + IsOptional = false; + } + + public Parameter (Expression type, string name, Modifier mod, Attributes attrs, Expression pi) + { + Name = name; + ModFlags = mod; + TypeName = type; + OptAttributes = attrs; + ParameterInitializer = pi; + IsOptional = false; + } + + public Parameter (Expression type, string name, Modifier mod, Attributes attrs, Expression pi, bool opt) + { + Name = name; + ModFlags = mod; + TypeName = type; + OptAttributes = attrs; + ParameterInitializer = pi; + IsOptional = opt; + } + + + // <summary> + // Resolve is used in method definitions + // </summary> + public bool Resolve (DeclSpace ds, Location l) + { + parameter_type = ds.ResolveType (TypeName, false, l); + + if (parameter_type == TypeManager.void_type){ + Report.Error (1536, l, "`void' parameter is not permitted"); + return false; + } + + return parameter_type != null; + } + + public Type ExternalType (DeclSpace ds, Location l) + { + if ((ModFlags & Parameter.Modifier.ISBYREF) != 0){ + string n = parameter_type.FullName + "&"; + + Type t = RootContext.LookupType (ds, n, false, l); + + return t; + } + + return parameter_type; + } + + public Type ParameterType { + get { + return parameter_type; + } + } + + public ParameterAttributes Attributes { + get { + int flags = ((int) ModFlags) & ~((int) Parameter.Modifier.ISBYREF); + switch ((Modifier) flags) { + case Modifier.NONE: + return ParameterAttributes.None; + case Modifier.REF: + return ParameterAttributes.None; + case Modifier.OUT: + return ParameterAttributes.Out; + case Modifier.PARAMS: + return 0; + } + + return ParameterAttributes.None; + } + } + + /// <summary> + /// Returns the signature for this parameter evaluating it on the + /// @tc context + /// </summary> + public string GetSignature (DeclSpace ds, Location loc) + { + if (parameter_type == null){ + if (!Resolve (ds, loc)) + return null; + } + + return ExternalType (ds, loc).FullName; + } + } + + /// <summary> + /// Represents the methods parameters + /// </summary> + public class Parameters { + public Parameter [] FixedParameters; + public readonly Parameter ArrayParameter; + string signature; + Type [] types; + Location loc; + + static Parameters empty_parameters; + + public Parameters (Parameter [] fixed_parameters, Parameter array_parameter, Location l) + { + FixedParameters = fixed_parameters; + ArrayParameter = array_parameter; + loc = l; + } + + /// <summary> + /// This is used to reuse a set of empty parameters, because they + /// are common + /// </summary> + public static Parameters EmptyReadOnlyParameters { + get { + if (empty_parameters == null) + empty_parameters = new Parameters (null, null, Location.Null); + + return empty_parameters; + } + } + + public bool HasOptional() + { + bool res = false; + + foreach (Parameter p in FixedParameters) + { + if (p.IsOptional) + { + res = true; + break; + } + } + return (res); + } + + /// <summary> + /// Returns the number of standard (i.e. non-optional) parameters + /// </summary> + public int CountStandardParams() + { + int res = 0; + if (FixedParameters == null) + return 0; + + foreach (Parameter p in FixedParameters) { + if (!p.IsOptional) + res++; + } + return (res); + } + + /// <summary> + /// Returns the number of optional parameters + /// </summary> + public int CountOptionalParams() + { + int res = 0; + if (FixedParameters == null) + return 0; + + foreach (Parameter p in FixedParameters) { + if (p.IsOptional) + res++; + } + return (res); + } + + public Expression GetDefaultValue (int i) + { + Parameter p = FixedParameters[i]; + if (p.IsOptional) + return p.ParameterInitializer; + else + return null; + } + + public void AppendParameter (Parameter p) + { + if (FixedParameters != null) + { + Parameter [] pa = new Parameter [FixedParameters.Length+1]; + FixedParameters.CopyTo (pa, 0); + pa[FixedParameters.Length] = p; + FixedParameters = pa; + } + else + { + FixedParameters = new Parameter [1]; + FixedParameters[0] = p; + } + } + + public void PrependParameter (Parameter p) + { + Parameter [] pa = new Parameter [FixedParameters.Length+1]; + FixedParameters.CopyTo (pa, 1); + pa[0] = p; + FixedParameters = pa; + } + + public Parameters Copy (Location l) + { + Parameters p = new Parameters (null, null, l); + p.FixedParameters = new Parameter[this.FixedParameters.Length]; + this.FixedParameters.CopyTo (p.FixedParameters, 0); + + return (p); + + } + + public bool Empty { + get { + return (FixedParameters == null) && (ArrayParameter == null); + } + } + + public void ComputeSignature (DeclSpace ds) + { + signature = ""; + if (FixedParameters != null){ + for (int i = 0; i < FixedParameters.Length; i++){ + Parameter par = FixedParameters [i]; + + signature += par.GetSignature (ds, loc); + } + } + // + // Note: as per the spec, the `params' arguments (ArrayParameter) + // are not used in the signature computation for a method + // + } + + static void Error_DuplicateParameterName (string name) + { + Report.Error ( + 100, "The parameter name `" + name + "' is a duplicate"); + } + + public bool VerifyArgs () + { + int count; + int i, j; + + if (FixedParameters == null) + return true; + + count = FixedParameters.Length; + string array_par_name = ArrayParameter != null ? ArrayParameter.Name : null; + for (i = 0; i < count; i++){ + string base_name = FixedParameters [i].Name; + + for (j = i + 1; j < count; j++){ + if (base_name != FixedParameters [j].Name) + continue; + Error_DuplicateParameterName (base_name); + return false; + } + + if (base_name == array_par_name){ + Error_DuplicateParameterName (base_name); + return false; + } + } + return true; + } + + /// <summary> + /// Returns the signature of the Parameters evaluated in + /// the @tc environment + /// </summary> + public string GetSignature (DeclSpace ds) + { + if (signature == null){ + VerifyArgs (); + ComputeSignature (ds); + } + + return signature; + } + + /// <summary> + /// Returns the paramenter information based on the name + /// </summary> + public Parameter GetParameterByName (string name, out int idx) + { + idx = 0; + int i = 0; + + if (FixedParameters != null){ + foreach (Parameter par in FixedParameters){ + if (par.Name == name){ + idx = i; + return par; + } + i++; + } + } + + if (ArrayParameter != null){ + if (name == ArrayParameter.Name){ + idx = i; + return ArrayParameter; + } + } + + return null; + } + + bool ComputeParameterTypes (DeclSpace ds) + { + int extra = (ArrayParameter != null) ? 1 : 0; + int i = 0; + int pc; + + if (FixedParameters == null) + pc = extra; + else + pc = extra + FixedParameters.Length; + + types = new Type [pc]; + + if (!VerifyArgs ()){ + FixedParameters = null; + return false; + } + + bool failed = false; + if (FixedParameters != null){ + foreach (Parameter p in FixedParameters){ + Type t = null; + + if (p.Resolve (ds, loc)) + t = p.ExternalType (ds, loc); + else + failed = true; + + types [i] = t; + i++; + } + } + + if (extra > 0){ + if (ArrayParameter.Resolve (ds, loc)) + types [i] = ArrayParameter.ExternalType (ds, loc); + else + failed = true; + } + + if (failed){ + types = null; + return false; + } + + return true; + } + + // + // This variant is used by Delegates, because they need to + // resolve/define names, instead of the plain LookupType + // + public bool ComputeAndDefineParameterTypes (DeclSpace ds) + { + int extra = (ArrayParameter != null) ? 1 : 0; + int i = 0; + int pc; + + if (FixedParameters == null) + pc = extra; + else + pc = extra + FixedParameters.Length; + + types = new Type [pc]; + + if (!VerifyArgs ()){ + FixedParameters = null; + return false; + } + + bool ok_flag = true; + + if (FixedParameters != null){ + foreach (Parameter p in FixedParameters){ + Type t = null; + + if (p.Resolve (ds, loc)) + t = p.ExternalType (ds, loc); + else + ok_flag = false; + + types [i] = t; + i++; + } + } + + if (extra > 0){ + if (ArrayParameter.Resolve (ds, loc)) + types [i] = ArrayParameter.ExternalType (ds, loc); + else + ok_flag = false; + } + + // + // invalidate the cached types + // + if (!ok_flag){ + types = null; + } + + return ok_flag; + } + + /// <summary> + /// Returns the argument types as an array + /// </summary> + static Type [] no_types = new Type [0]; + + public Type [] GetParameterInfo (DeclSpace ds) + { + if (types != null) + return types; + + if (FixedParameters == null && ArrayParameter == null) + return no_types; + + if (ComputeParameterTypes (ds) == false){ + types = null; + return null; + } + + return types; + } + + /// <summary> + /// Returns the type of a given parameter, and stores in the `is_out' + /// boolean whether this is an out or ref parameter. + /// + /// Note that the returned type will not contain any dereference in this + /// case (ie, you get "int" for a ref int instead of "int&" + /// </summary> + public Type GetParameterInfo (DeclSpace ds, int idx, out Parameter.Modifier mod) + { + mod = Parameter.Modifier.NONE; + + if (!VerifyArgs ()){ + FixedParameters = null; + return null; + } + + if (FixedParameters == null && ArrayParameter == null) + return null; + + if (types == null) + if (ComputeParameterTypes (ds) == false) + return null; + + // + // If this is a request for the variable lenght arg. + // + int array_idx = (FixedParameters != null ? FixedParameters.Length : 0); + if (idx == array_idx) + return types [idx]; + + // + // Otherwise, it is a fixed parameter + // + Parameter p = FixedParameters [idx]; + mod = p.ModFlags; + + if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0) + mod |= Parameter.Modifier.ISBYREF; + + return p.ParameterType; + } + + public CallingConventions GetCallingConvention () + { + // For now this is the only correc thing to do + return CallingConventions.Standard; + } + } +} + + + diff --git a/mcs/mbas/pending.cs b/mcs/mbas/pending.cs new file mode 100644 index 00000000000..5f01b009548 --- /dev/null +++ b/mcs/mbas/pending.cs @@ -0,0 +1,514 @@ +// +// pending.cs: Pending method implementation +// +// Author: +// Miguel de Icaza (miguel@gnu.org) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001, 2002 Ximian, Inc (http://www.ximian.com) +// +// + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +namespace Mono.CSharp { + + struct TypeAndMethods { + public Type type; + public MethodInfo [] methods; + + // Far from ideal, but we want to avoid creating a copy + // of methods above. + public Type [][] args; + + // + // This flag on the method says `We found a match, but + // because it was private, we could not use the match + // + public bool [] found; + + // If a method is defined here, then we always need to + // create a proxy for it. This is used when implementing + // an interface's indexer with a different IndexerName. + public MethodInfo [] need_proxy; + } + + public class PendingImplementation { + /// <summary> + /// The container for this PendingImplementation + /// </summary> + TypeContainer container; + + /// <summary> + /// This filter is used by FindMembers, and it is used to + /// extract only virtual/abstract fields + /// </summary> + static MemberFilter virtual_method_filter; + + /// <summary> + /// This is the array of TypeAndMethods that describes the pending implementations + /// (both interfaces and abstract methods in parent class) + /// </summary> + TypeAndMethods [] pending_implementations; + + static bool IsVirtualFilter (MemberInfo m, object filterCriteria) + { + if (!(m is MethodInfo)) + return false; + + return ((MethodInfo) m).IsVirtual; + } + + /// <summary> + /// Inits the virtual_method_filter + /// </summary> + static PendingImplementation () + { + virtual_method_filter = new MemberFilter (IsVirtualFilter); + } + + // <remarks> + // Returns a list of the abstract methods that are exposed by all of our + // parents that we must implement. Notice that this `flattens' the + // method search space, and takes into account overrides. + // </remarks> + static ArrayList GetAbstractMethods (Type t) + { + ArrayList list = null; + bool searching = true; + Type current_type = t; + + do { + MemberList mi; + + mi = TypeContainer.FindMembers ( + current_type, MemberTypes.Method, + BindingFlags.Public | BindingFlags.Instance | + BindingFlags.DeclaredOnly, + virtual_method_filter, null); + + if (current_type == TypeManager.object_type) + searching = false; + else { + current_type = current_type.BaseType; + if (!current_type.IsAbstract) + searching = false; + } + + if (mi.Count == 0) + continue; + + if (mi.Count == 1 && !(mi [0] is MethodBase)) + searching = false; + else + list = TypeManager.CopyNewMethods (list, mi); + } while (searching); + + if (list == null) + return null; + + for (int i = 0; i < list.Count; i++){ + while (list.Count > i && !((MethodInfo) list [i]).IsAbstract) + list.RemoveAt (i); + } + + if (list.Count == 0) + return null; + + return list; + } + + PendingImplementation (TypeContainer container, Type [] ifaces, ArrayList abstract_methods, int total) + { + TypeBuilder type_builder = container.TypeBuilder; + + this.container = container; + pending_implementations = new TypeAndMethods [total]; + + int i = 0; + if (ifaces != null){ + foreach (Type t in ifaces){ + MethodInfo [] mi; + + if (t is TypeBuilder){ + Interface iface; + + iface = TypeManager.LookupInterface (t); + + mi = iface.GetMethods (container); + } else + mi = t.GetMethods (); + + int count = mi.Length; + pending_implementations [i].type = t; + pending_implementations [i].methods = mi; + pending_implementations [i].args = new Type [count][]; + pending_implementations [i].found = new bool [count]; + pending_implementations [i].need_proxy = new MethodInfo [count]; + + int j = 0; + foreach (MethodInfo m in mi){ + Type [] types = TypeManager.GetArgumentTypes (m); + + pending_implementations [i].args [j] = types; + j++; + } + i++; + } + } + + if (abstract_methods != null){ + int count = abstract_methods.Count; + pending_implementations [i].methods = new MethodInfo [count]; + pending_implementations [i].need_proxy = new MethodInfo [count]; + + abstract_methods.CopyTo (pending_implementations [i].methods, 0); + pending_implementations [i].found = new bool [count]; + pending_implementations [i].args = new Type [count][]; + pending_implementations [i].type = type_builder; + + int j = 0; + foreach (MemberInfo m in abstract_methods){ + MethodInfo mi = (MethodInfo) m; + + Type [] types = TypeManager.GetArgumentTypes (mi); + + pending_implementations [i].args [j] = types; + j++; + } + } + } + + // + // Factory method: if there are pending implementation methods, we return a PendingImplementation + // object, otherwise we return null. + // + // Register method implementations are either abstract methods + // flagged as such on the base class or interface methods + // + static public PendingImplementation GetPendingImplementations (TypeContainer container) + { + TypeBuilder type_builder = container.TypeBuilder; + Type [] ifaces; + Type b = type_builder.BaseType; + int icount = 0; + + // + // Notice that TypeBuilders will only return the interfaces that the Type + // is supposed to implement, not all the interfaces that the type implements. + // + // Completely broken. Anyways, we take advantage of this, so we only register + // the implementations that we need, as they are those that are listed by the + // TypeBuilder. + // + ifaces = type_builder.GetInterfaces (); + +#if DEBUG + { + Type x = type_builder; + + while (x != null){ + Type [] iff = x.GetInterfaces (); + Console.WriteLine ("Type: " + x.Name); + + foreach (Type tt in iff){ + Console.WriteLine (" Iface: " + tt.Name); + } + x = x.BaseType; + } + } +#endif + + icount = ifaces.Length; + + // + // If we are implementing an abstract class, and we are not + // ourselves abstract, and there are abstract methods (C# allows + // abstract classes that have no abstract methods), then allocate + // one slot. + // + // We also pre-compute the methods. + // + bool implementing_abstract = ((b != null) && b.IsAbstract && !type_builder.IsAbstract); + ArrayList abstract_methods = null; + + if (implementing_abstract){ + abstract_methods = GetAbstractMethods (b); + + if (abstract_methods == null) + implementing_abstract = false; + } + + int total = icount + (implementing_abstract ? 1 : 0); + if (total == 0) + return null; + + return new PendingImplementation (container, ifaces, abstract_methods, total); + } + + public enum Operation { + // + // If you change this, review the whole InterfaceMethod routine as there + // are a couple of assumptions on these three states + // + Lookup, ClearOne, ClearAll + } + + /// <summary> + /// Whether the specified method is an interface method implementation + /// </summary> + public MethodInfo IsInterfaceMethod (Type t, string name, Type ret_type, Type [] args) + { + return InterfaceMethod (t, name, ret_type, args, Operation.Lookup, null); + } + + public MethodInfo IsInterfaceIndexer (Type t, Type ret_type, Type [] args) + { + return InterfaceMethod (t, null, ret_type, args, Operation.Lookup, null); + } + + public void ImplementMethod (Type t, string name, Type ret_type, Type [] args, bool clear_one) + { + InterfaceMethod (t, name, ret_type, args, + clear_one ? Operation.ClearOne : Operation.ClearAll, null); + } + + public void ImplementIndexer (Type t, MethodInfo mi, Type ret_type, Type [] args, bool clear_one) + { + InterfaceMethod (t, mi.Name, ret_type, args, + clear_one ? Operation.ClearOne : Operation.ClearAll, mi); + } + + /// <remarks> + /// If a method in Type `t' (or null to look in all interfaces + /// and the base abstract class) with name `Name', return type `ret_type' and + /// arguments `args' implements an interface, this method will + /// return the MethodInfo that this method implements. + /// + /// If `name' is null, we operate solely on the method's signature. This is for + /// instance used when implementing indexers. + /// + /// The `Operation op' controls whether to lookup, clear the pending bit, or clear + /// all the methods with the given signature. + /// + /// The `MethodInfo need_proxy' is used when we're implementing an interface's + /// indexer in a class. If the new indexer's IndexerName does not match the one + /// that was used in the interface, then we always need to create a proxy for it. + /// + /// </remarks> + public MethodInfo InterfaceMethod (Type t, string name, Type ret_type, Type [] args, + Operation op, MethodInfo need_proxy) + { + int arg_len = args.Length; + + if (pending_implementations == null) + return null; + + foreach (TypeAndMethods tm in pending_implementations){ + if (!(t == null || tm.type == t)) + continue; + + int i = 0; + foreach (MethodInfo m in tm.methods){ + if (m == null){ + i++; + continue; + } + + // `need_proxy' is not null when we're implementing an + // interface indexer and this is Clear(One/All) operation. + // If `name' is null, then we do a match solely based on the + // signature and not on the name (this is done in the Lookup + // for an interface indexer). + if ((name != null) && (need_proxy == null) && (name != m.Name)){ + i++; + continue; + } + + if (ret_type != m.ReturnType){ + if (!((ret_type == null && m.ReturnType == TypeManager.void_type) || + (m.ReturnType == null && ret_type == TypeManager.void_type))) + { + i++; + continue; + } + } + + // + // Check if we have the same parameters + // + if (tm.args [i].Length != arg_len){ + i++; + continue; + } + + int j, top = args.Length; + bool fail = false; + + for (j = 0; j < top; j++){ + if (tm.args [i][j] != args[j]){ + fail = true; + break; + } + } + if (fail){ + i++; + continue; + } + + if (op != Operation.Lookup){ + // If `t != null', then this is an explicitly interface + // implementation and we can always clear the method. + // `need_proxy' is not null if we're implementing an + // interface indexer. In this case, we need to create + // a proxy if the implementation's IndexerName doesn't + // match the IndexerName in the interface. + if ((t == null) && (need_proxy != null) && (name != m.Name)) + tm.need_proxy [i] = need_proxy; + else + tm.methods [i] = null; + } + tm.found [i] = true; + + // + // Lookups and ClearOne return + // + if (op != Operation.ClearAll) + return m; + } + + // If a specific type was requested, we can stop now. + if (tm.type == t) + return null; + } + return null; + } + + /// <summary> + /// C# allows this kind of scenarios: + /// interface I { void M (); } + /// class X { public void M (); } + /// class Y : X, I { } + /// + /// For that case, we create an explicit implementation function + /// I.M in Y. + /// </summary> + void DefineProxy (Type iface, MethodInfo parent_method, MethodInfo iface_method, + Type [] args) + { + MethodBuilder proxy; + + string proxy_name = iface.Name + "." + iface_method.Name; + + proxy = container.TypeBuilder.DefineMethod ( + proxy_name, + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.Virtual, + CallingConventions.Standard | CallingConventions.HasThis, + parent_method.ReturnType, args); + + int top = args.Length; + ILGenerator ig = proxy.GetILGenerator (); + + ig.Emit (OpCodes.Ldarg_0); + for (int i = 0; i < top; i++){ + switch (i){ + case 0: + ig.Emit (OpCodes.Ldarg_1); break; + case 1: + ig.Emit (OpCodes.Ldarg_2); break; + case 2: + ig.Emit (OpCodes.Ldarg_3); break; + default: + ig.Emit (OpCodes.Ldarg, i - 1); break; + } + } + ig.Emit (OpCodes.Call, parent_method); + ig.Emit (OpCodes.Ret); + + container.TypeBuilder.DefineMethodOverride (proxy, iface_method); + } + + /// <summary> + /// This function tells whether one of our parent classes implements + /// the given method (which turns out, it is valid to have an interface + /// implementation in a parent + /// </summary> + bool ParentImplements (Type iface_type, MethodInfo mi) + { + MethodSignature ms; + + Type [] args = TypeManager.GetArgumentTypes (mi); + ms = new MethodSignature (mi.Name, mi.ReturnType, args); + MemberList list = TypeContainer.FindMembers ( + container.TypeBuilder.BaseType, MemberTypes.Method | MemberTypes.Property, + BindingFlags.Public | BindingFlags.Instance, + MethodSignature.method_signature_filter, ms); + + if (list.Count == 0) + return false; + + DefineProxy (iface_type, (MethodInfo) list [0], mi, args); + return true; + } + + /// <summary> + /// Verifies that any pending abstract methods or interface methods + /// were implemented. + /// </summary> + public bool VerifyPendingMethods () + { + int top = pending_implementations.Length; + bool errors = false; + int i; + + for (i = 0; i < top; i++){ + Type type = pending_implementations [i].type; + int j = 0; + + foreach (MethodInfo mi in pending_implementations [i].methods){ + if (mi == null) + continue; + + if (type.IsInterface){ + MethodInfo need_proxy = + pending_implementations [i].need_proxy [j]; + + if (need_proxy != null) { + Type [] args = TypeManager.GetArgumentTypes (mi); + DefineProxy (type, need_proxy, mi, args); + continue; + } + + if (ParentImplements (type, mi)) + continue; + + string extra = ""; + + if (pending_implementations [i].found [j]) + extra = ". (method might be private or static)"; + Report.Error ( + 536, container.Location, + "`" + container.Name + "' does not implement " + + "interface member `" + + type.FullName + "." + mi.Name + "'" + extra); + } else { + Report.Error ( + 534, container.Location, + "`" + container.Name + "' does not implement " + + "inherited abstract member `" + + type.FullName + "." + mi.Name + "'"); + } + errors = true; + j++; + } + } + return errors; + } + } /* end of class */ +} diff --git a/mcs/mbas/report.cs b/mcs/mbas/report.cs new file mode 100644 index 00000000000..946355815ec --- /dev/null +++ b/mcs/mbas/report.cs @@ -0,0 +1,359 @@ +// +// report.cs: report errors and warnings. +// +// Author: Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. (http://www.ximian.com) +// + +// +// FIXME: currently our class library does not support custom number format strings +// +using System; +using System.Text; +using System.Collections; +using System.Diagnostics; + +namespace Mono.CSharp { + + /// <summary> + /// This class is used to report errors and warnings t te user. + /// </summary> + public class Report { + /// <summary> + /// Errors encountered so far + /// </summary> + static public int Errors; + + /// <summary> + /// Warnings encountered so far + /// </summary> + static public int Warnings; + + /// <summary> + /// Whether errors should be throw an exception + /// </summary> + static public bool Fatal; + + /// <summary> + /// Whether warnings should be considered errors + /// </summary> + static public bool WarningsAreErrors; + + /// <summary> + /// Whether to dump a stack trace on errors. + /// </summary> + static public bool Stacktrace; + + // + // If the 'expected' error code is reported then the + // compilation succeeds. + // + // Used for the test suite to excercise the error codes + // + static int expected_error = 0; + + // + // Keeps track of the warnings that we are ignoring + // + static Hashtable warning_ignore_table; + + static void Check (int code) + { + if (code == expected_error){ + if (Fatal) + throw new Exception (); + + Environment.Exit (0); + } + } + + static public void RealError (string msg) + { + Errors++; + Console.WriteLine (msg); + + if (Stacktrace) + Console.WriteLine (new StackTrace ().ToString ()); + if (Fatal) + throw new Exception (msg); + } + + static public void Error (int code, Location l, string text) + { + string msg = String.Format ( + "{0}({1}) error BC{2:0000}: {3}", l.Name, l.Row, code, text); +// "{0}({1}) error BC{2}: {3}", l.Name, l.Row, code, text); + + RealError (msg); + Check (code); + } + + static public void Warning (int code, Location l, string text) + { + if (warning_ignore_table != null){ + if (warning_ignore_table.Contains (code)) + return; + } + + if (WarningsAreErrors) + Error (code, l, text); + else { + string row; + + if (Location.IsNull (l)) + row = ""; + else + row = l.Row.ToString (); + + Console.WriteLine (String.Format ( + "{0}({1}) warning BC{2:0000}: {3}", +// "{0}({1}) warning BC{2}: {3}", + l.Name, row, code, text)); + Warnings++; + Check (code); + + if (Stacktrace) + Console.WriteLine (new StackTrace ().ToString ()); + } + } + + static public void Warning (int code, string text) + { + Warning (code, Location.Null, text); + } + + static public void Warning (int code, int level, string text) + { + if (RootContext.WarningLevel >= level) + Warning (code, Location.Null, text); + } + + static public void Warning (int code, int level, Location l, string text) + { + if (RootContext.WarningLevel >= level) + Warning (code, l, text); + } + + static public void Error (int code, string text) + { + string msg = String.Format ("error BC{0:0000}: {1}", code, text); +// string msg = String.Format ("error BC{0}: {1}", code, text); + + RealError (msg); + Check (code); + } + + static public void Message (Message m) + { + if (m is ErrorMessage) + Error (m.code, m.text); + else + Warning (m.code, m.text); + } + + static public void SetIgnoreWarning (int code) + { + if (warning_ignore_table == null) + warning_ignore_table = new Hashtable (); + + warning_ignore_table [code] = true; + } + + static public int ExpectedError { + set { + expected_error = value; + } + get { + return expected_error; + } + } + + public static int DebugFlags = 0; + + [Conditional ("MCS_DEBUG")] + static public void Debug (string message, params object[] args) + { + Debug (4, message, args); + } + + [Conditional ("MCS_DEBUG")] + static public void Debug (int category, string message, params object[] args) + { + if ((category & DebugFlags) == 0) + return; + + StringBuilder sb = new StringBuilder (message); + + if ((args != null) && (args.Length > 0)) { + sb.Append (": "); + + bool first = true; + foreach (object arg in args) { + if (first) + first = false; + else + sb.Append (","); + if (arg == null) + sb.Append ("null"); + else if (arg is ICollection) + sb.Append (PrintCollection ((ICollection) arg)); + else + sb.Append (arg); + } + } + + Console.WriteLine (sb.ToString ()); + } + + static public string PrintCollection (ICollection collection) + { + StringBuilder sb = new StringBuilder (); + + sb.Append (collection.GetType ()); + sb.Append ("("); + + bool first = true; + foreach (object o in collection) { + if (first) + first = false; + else + sb.Append (","); + sb.Append (o); + } + + sb.Append (")"); + return sb.ToString (); + } + } + + public class Message { + public int code; + public string text; + + public Message (int code, string text) + { + this.code = code; + this.text = text; + } + } + + public class WarningMessage : Message { + public WarningMessage (int code, string text) : base (code, text) + { + } + } + + public class ErrorMessage : Message { + public ErrorMessage (int code, string text) : base (code, text) + { + } + + // + // For compatibility reasons with old code. + // + public static void report_error (string error) + { + Console.Write ("ERROR: "); + Console.WriteLine (error); + } + } + + public enum TimerType { + FindMembers = 0, + TcFindMembers = 1, + MemberLookup = 2, + CachedLookup = 3, + CacheInit = 4, + MiscTimer = 5, + CountTimers = 6 + } + + public enum CounterType { + FindMembers = 0, + MemberCache = 1, + MiscCounter = 2, + CountCounters = 3 + } + + public class Timer + { + static DateTime[] timer_start; + static TimeSpan[] timers; + static long[] timer_counters; + static long[] counters; + + static Timer () + { + timer_start = new DateTime [(int) TimerType.CountTimers]; + timers = new TimeSpan [(int) TimerType.CountTimers]; + timer_counters = new long [(int) TimerType.CountTimers]; + counters = new long [(int) CounterType.CountCounters]; + + for (int i = 0; i < (int) TimerType.CountTimers; i++) { + timer_start [i] = DateTime.Now; + timers [i] = TimeSpan.Zero; + } + } + + [Conditional("TIMER")] + static public void IncrementCounter (CounterType which) + { + ++counters [(int) which]; + } + + [Conditional("TIMER")] + static public void StartTimer (TimerType which) + { + timer_start [(int) which] = DateTime.Now; + } + + [Conditional("TIMER")] + static public void StopTimer (TimerType which) + { + timers [(int) which] += DateTime.Now - timer_start [(int) which]; + ++timer_counters [(int) which]; + } + + [Conditional("TIMER")] + static public void ShowTimers () + { + ShowTimer (TimerType.FindMembers, "- FindMembers timer"); + ShowTimer (TimerType.TcFindMembers, "- TypeContainer.FindMembers timer"); + ShowTimer (TimerType.MemberLookup, "- MemberLookup timer"); + ShowTimer (TimerType.CachedLookup, "- CachedLookup timer"); + ShowTimer (TimerType.CacheInit, "- Cache init"); + ShowTimer (TimerType.MiscTimer, "- Misc timer"); + + ShowCounter (CounterType.FindMembers, "- Find members"); + ShowCounter (CounterType.MemberCache, "- Member cache"); + ShowCounter (CounterType.MiscCounter, "- Misc counter"); + } + + static public void ShowCounter (CounterType which, string msg) + { + Console.WriteLine ("{0} {1}", counters [(int) which], msg); + } + + static public void ShowTimer (TimerType which, string msg) + { + Console.WriteLine ( + "[{0:00}:{1:000}] {2} (used {3} times)", + (int) timers [(int) which].TotalSeconds, + timers [(int) which].Milliseconds, msg, + timer_counters [(int) which]); + } + } + + public class InternalErrorException : Exception { + public InternalErrorException () + : base ("Internal error") + { + } + + public InternalErrorException (string message) + : base (message) + { + } + } +} diff --git a/mcs/mbas/rootcontext.cs b/mcs/mbas/rootcontext.cs new file mode 100644 index 00000000000..5505d95d8cc --- /dev/null +++ b/mcs/mbas/rootcontext.cs @@ -0,0 +1,840 @@ +// +// rootcontext.cs: keeps track of our tree representation, and assemblies loaded. +// +// Author: Miguel de Icaza (miguel@ximian.com) +// Ravi Pratap (ravi@ximian.com) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; + +namespace Mono.CSharp { + + public class RootContext { + + // + // Contains the parsed tree + // + static Tree tree; + + // + // This hashtable contains all of the #definitions across the source code + // it is used by the ConditionalAttribute handler. + // + public static Hashtable AllDefines = new Hashtable (); + + // + // The list of global attributes (those that target the assembly) + // + static Hashtable global_attributes = new Hashtable (); + + // + // Whether we are being linked against the standard libraries. + // This is only used to tell whether `System.Object' should + // have a parent or not. + // + public static bool StdLib = true; + + // + // This keeps track of the order in which classes were defined + // so that we can poulate them in that order. + // + // Order is important, because we need to be able to tell by + // examining the parent's list of methods which ones are virtual + // or abstract as well as the parent names (to implement new, + // override). + // + static ArrayList type_container_resolve_order; + static ArrayList interface_resolve_order; + static ArrayList attribute_types; + + // + // Holds a reference to the Private Implementation Details + // class. + // + static TypeBuilder impl_details_class; + + public static int WarningLevel = 2; + + // + // Constructor + // + static RootContext () + { + tree = new Tree (); + interface_resolve_order = new ArrayList (); + type_container_resolve_order = new ArrayList (); + } + + static public Tree Tree { + get { + return tree; + } + } + + static public string MainClass; + + public static void RegisterOrder (Interface iface) + { + interface_resolve_order.Add (iface); + } + + public static void RegisterOrder (TypeContainer tc) + { + type_container_resolve_order.Add (tc); + } + + public static void RegisterAttribute (TypeContainer tc) + { + if (attribute_types == null) + attribute_types = new ArrayList (); + + attribute_types.Add (tc); + } + + // + // The default compiler checked state + // + static public bool Checked = false; + + // + // Whether to allow Unsafe code + // + static public bool Unsafe = false; + + static string MakeFQN (string nsn, string name) + { + string prefix = (nsn == "" ? "" : nsn + "."); + + return prefix + name; + } + + // <remarks> + // This function is used to resolve the hierarchy tree. + // It processes interfaces, structs and classes in that order. + // + // It creates the TypeBuilder's as it processes the user defined + // types. + // </remarks> + static public void ResolveTree () + { + // + // Process the attribute types separately and before anything else + // + if (attribute_types != null) + foreach (TypeContainer tc in attribute_types) + tc.DefineType (); + + // + // Interfaces are processed next, as classes and + // structs might inherit from an object or implement + // a set of interfaces, we need to be able to tell + // them appart by just using the TypeManager. + // + TypeContainer root = Tree.Types; + + ArrayList ifaces = root.Interfaces; + if (ifaces != null){ + foreach (Interface i in ifaces) + i.DefineType (); + } + + + foreach (TypeContainer tc in root.Types) + tc.DefineType (); + + if (root.Delegates != null) + foreach (Delegate d in root.Delegates) + d.DefineType (); + + if (root.Enums != null) + foreach (Enum e in root.Enums) + e.DefineType (); + + } + + static void Error_TypeConflict (string name, Location loc) + { + Report.Error ( + 520, loc, "`" + name + "' conflicts with a predefined type"); + } + + static void Error_TypeConflict (string name) + { + Report.Error ( + 520, "`" + name + "' conflicts with a predefined type"); + } + + // + // Resolves a single class during the corlib bootstrap process + // + static TypeBuilder BootstrapCorlib_ResolveClass (TypeContainer root, string name) + { + object o = root.GetDefinition (name); + if (o == null){ + Report.Error (518, "The predefined type `" + name + "' is not defined"); + return null; + } + + if (!(o is Class)){ + if (o is DeclSpace){ + DeclSpace d = (DeclSpace) o; + + Error_TypeConflict (name, d.Location); + } else + Error_TypeConflict (name); + + return null; + } + + return ((DeclSpace) o).DefineType (); + } + + // + // Resolves a struct during the corlib bootstrap process + // + static void BootstrapCorlib_ResolveStruct (TypeContainer root, string name) + { + object o = root.GetDefinition (name); + if (o == null){ + Report.Error (518, "The predefined type `" + name + "' is not defined"); + return; + } + + if (!(o is Struct)){ + if (o is DeclSpace){ + DeclSpace d = (DeclSpace) o; + + Error_TypeConflict (name, d.Location); + } else + Error_TypeConflict (name); + + return; + } + + ((DeclSpace) o).DefineType (); + } + + // + // Resolves a struct during the corlib bootstrap process + // + static void BootstrapCorlib_ResolveInterface (TypeContainer root, string name) + { + object o = root.GetDefinition (name); + if (o == null){ + Report.Error (518, "The predefined type `" + name + "' is not defined"); + return; + } + + if (!(o is Interface)){ + if (o is DeclSpace){ + DeclSpace d = (DeclSpace) o; + + Error_TypeConflict (name, d.Location); + } else + Error_TypeConflict (name); + + return; + } + + ((DeclSpace) o).DefineType (); + } + + // + // Resolves a delegate during the corlib bootstrap process + // + static void BootstrapCorlib_ResolveDelegate (TypeContainer root, string name) + { + object o = root.GetDefinition (name); + if (o == null){ + Report.Error (518, "The predefined type `" + name + "' is not defined"); + Environment.Exit (0); + } + + if (!(o is Delegate)){ + Error_TypeConflict (name); + return; + } + + ((DeclSpace) o).DefineType (); + } + + + /// <summary> + /// Resolves the core types in the compiler when compiling with --nostdlib + /// </summary> + static public void ResolveCore () + { + TypeContainer root = Tree.Types; + + TypeManager.object_type = BootstrapCorlib_ResolveClass (root, "System.Object"); + TypeManager.value_type = BootstrapCorlib_ResolveClass (root, "System.ValueType"); + TypeManager.attribute_type = BootstrapCorlib_ResolveClass (root, "System.Attribute"); + + string [] interfaces_first_stage = { + "System.IComparable", "System.ICloneable", + "System.IConvertible", + + "System.Collections.IEnumerable", + "System.Collections.ICollection", + "System.Collections.IEnumerator", + "System.Collections.IList", + "System.IAsyncResult", + "System.IDisposable", + + "System.Runtime.Serialization.ISerializable", + + "System.Reflection.IReflect", + "System.Reflection.ICustomAttributeProvider" + }; + + foreach (string iname in interfaces_first_stage) + BootstrapCorlib_ResolveInterface (root, iname); + + // + // These are the base value types + // + string [] structs_first_stage = { + "System.Byte", "System.SByte", + "System.Int16", "System.UInt16", + "System.Int32", "System.UInt32", + "System.Int64", "System.UInt64", + }; + + foreach (string cname in structs_first_stage) + BootstrapCorlib_ResolveStruct (root, cname); + + // + // Now, we can load the enumerations, after this point, + // we can use enums. + // + TypeManager.InitEnumUnderlyingTypes (); + + string [] structs_second_stage = { + "System.Single", "System.Double", + "System.Char", "System.Boolean", + "System.Decimal", "System.Void", + "System.RuntimeFieldHandle", + "System.RuntimeTypeHandle", + "System.IntPtr" + }; + + foreach (string cname in structs_second_stage) + BootstrapCorlib_ResolveStruct (root, cname); + + // + // These are classes that depends on the core interfaces + // + string [] classes_second_stage = { + "System.Reflection.MemberInfo", + "System.Type", + "System.Exception", + + // + // These are not really important in the order, but they + // are used by the compiler later on (typemanager/CoreLookupType-d) + // + "System.Runtime.CompilerServices.RuntimeHelpers", + "System.Reflection.DefaultMemberAttribute", + "System.Threading.Monitor", + + "System.AttributeUsageAttribute", + "System.Runtime.InteropServices.DllImportAttribute", + "System.Runtime.CompilerServices.MethodImplAttribute", + "System.Runtime.InteropServices.MarshalAsAttribute", + "System.Diagnostics.ConditionalAttribute", + "System.ObsoleteAttribute", + "System.ParamArrayAttribute", + "System.Security.UnverifiableCodeAttribute", + "System.Runtime.CompilerServices.IndexerNameAttribute", + }; + + // We must store them here before calling BootstrapCorlib_ResolveDelegate. + TypeManager.string_type = BootstrapCorlib_ResolveClass (root, "System.String"); + TypeManager.enum_type = BootstrapCorlib_ResolveClass (root, "System.Enum"); + TypeManager.array_type = BootstrapCorlib_ResolveClass (root, "System.Array"); + TypeManager.multicast_delegate_type = BootstrapCorlib_ResolveClass (root, "System.MulticastDelegate"); + TypeManager.delegate_type = BootstrapCorlib_ResolveClass (root, "System.Delegate"); + + foreach (string cname in classes_second_stage) + BootstrapCorlib_ResolveClass (root, cname); + + BootstrapCorlib_ResolveDelegate (root, "System.AsyncCallback"); + } + + // <summary> + // Closes all open types + // </summary> + // + // <remarks> + // We usually use TypeBuilder types. When we are done + // creating the type (which will happen after we have added + // methods, fields, etc) we need to "Define" them before we + // can save the Assembly + // </remarks> + static public void CloseTypes () + { + TypeContainer root = Tree.Types; + + ArrayList ifaces = root.Interfaces; + + if (root.Enums != null) + foreach (Enum en in root.Enums) + en.CloseType (); + + if (attribute_types != null) + foreach (TypeContainer tc in attribute_types) + tc.CloseType (); + + foreach (Interface iface in interface_resolve_order) + iface.CloseType (); + + // + // We do this in two passes, first we close the structs, + // then the classes, because it seems the code needs it this + // way. If this is really what is going on, we should probably + // make sure that we define the structs in order as well. + // + foreach (TypeContainer tc in type_container_resolve_order){ + if (tc is Struct && tc.Parent == tree.Types){ + tc.CloseType (); + } + } + + foreach (TypeContainer tc in type_container_resolve_order){ + if (!(tc is Struct && tc.Parent == tree.Types)) + tc.CloseType (); + } + + if (root.Delegates != null) + foreach (Delegate d in root.Delegates) + d.CloseType (); + + + // + // If we have a <PrivateImplementationDetails> class, close it + // + if (impl_details_class != null){ + impl_details_class.CreateType (); + } + } + + // + // This idea is from Felix Arrese-Igor + // + // Returns : the implicit parent of a composite namespace string + // eg. Implicit parent of A.B is A + // + static public string ImplicitParent (string ns) + { + int i = ns.LastIndexOf ("."); + if (i < 0) + return null; + + return ns.Substring (0, i); + } + + static Type NamespaceLookup (Namespace curr_ns, string name, Location loc) + { + Type t; + + // + // Try in the current namespace and all its implicit parents + // + for (string ns = curr_ns.Name; ns != null; ns = ImplicitParent (ns)) { + t = TypeManager.LookupType (MakeFQN (ns, name)); + if (t != null) + return t; + } + + // + // It's possible that name already is fully qualified. So we do + // a simple direct lookup without adding any namespace names + // + t = TypeManager.LookupType (name); + if (t != null) + return t; + + // + // Try the aliases in the current namespace + // + string alias = curr_ns.LookupAlias (name); + + if (alias != null) { + t = TypeManager.LookupType (alias); + if (t != null) + return t; + + t = TypeManager.LookupType (MakeFQN (alias, name)); + if (t != null) + return t; + } + + for (Namespace ns = curr_ns; ns != null; ns = ns.Parent) { + // + // Look in the namespace ns + // + t = TypeManager.LookupType (MakeFQN (ns.Name, name)); + if (t != null) + return t; + + // + // Then try with the using clauses + // + ArrayList using_list = ns.UsingTable; + + if (using_list == null) + continue; + + Type match = null; + foreach (Namespace.UsingEntry ue in using_list) { + match = TypeManager.LookupType (MakeFQN (ue.Name, name)); + if (match != null){ + if (t != null){ + DeclSpace.Error_AmbiguousTypeReference (loc, name, t, match); + return null; + } + + t = match; + ue.Used = true; + } + } + if (t != null) + return t; + + // + // Try with aliases + // + string a = ns.LookupAlias (name); + if (a != null) { + t = TypeManager.LookupType (a); + if (t != null) + return t; + + t = TypeManager.LookupType (MakeFQN (a, name)); + if (t != null) + return t; + } + } + + return null; + } + + // + // Public function used to locate types, this can only + // be used after the ResolveTree function has been invoked. + // + // Returns: Type or null if they type can not be found. + // + // Come to think of it, this should be a DeclSpace + // + static public Type LookupType (DeclSpace ds, string name, bool silent, Location loc) + { + Type t; + + if (ds.Cache.Contains (name)){ + t = (Type) ds.Cache [name]; + if (t != null) + return t; + } else { + // + // For the case the type we are looking for is nested within this one + // or is in any base class + // + DeclSpace containing_ds = ds; + while (containing_ds != null){ + Type current_type = containing_ds.TypeBuilder; + + while (current_type != null) { + // + // nested class + // + t = TypeManager.LookupType (current_type.FullName + "." + name); + if (t != null){ + ds.Cache [name] = t; + return t; + } + + current_type = current_type.BaseType; + } + + containing_ds = containing_ds.Parent; + } + + t = NamespaceLookup (ds.Namespace, name, loc); + if (t != null){ + ds.Cache [name] = t; + return t; + } + } + + if (!silent) + Report.Error (246, loc, "Cannot find type `"+name+"'"); + + return null; + } + + // <summary> + // This is the silent version of LookupType, you can use this + // to `probe' for a type + // </summary> + static public Type LookupType (TypeContainer tc, string name, Location loc) + { + return LookupType (tc, name, true, loc); + } + + static public bool IsNamespace (string name) + { + Namespace ns; + + if (tree.Namespaces != null){ + ns = (Namespace) tree.Namespaces [name]; + + if (ns != null) + return true; + } + + return false; + } + + static void Report1530 (Location loc) + { + Report.Error (1530, loc, "Keyword new not allowed for namespace elements"); + } + + static public void PopulateCoreType (TypeContainer root, string name) + { + DeclSpace ds = (DeclSpace) root.GetDefinition (name); + + ds.DefineMembers (root); + ds.Define (root); + } + + static public void BootCorlib_PopulateCoreTypes () + { + TypeContainer root = tree.Types; + + PopulateCoreType (root, "System.Object"); + PopulateCoreType (root, "System.ValueType"); + PopulateCoreType (root, "System.Attribute"); + } + + // <summary> + // Populates the structs and classes with fields and methods + // </summary> + // + // This is invoked after all interfaces, structs and classes + // have been defined through `ResolveTree' + static public void PopulateTypes () + { + TypeContainer root = Tree.Types; + + if (attribute_types != null) + foreach (TypeContainer tc in attribute_types) + tc.DefineMembers (root); + + if (interface_resolve_order != null){ + foreach (Interface iface in interface_resolve_order) + if ((iface.ModFlags & Modifiers.NEW) == 0) + iface.DefineMembers (root); + else + Report1530 (iface.Location); + } + + + if (type_container_resolve_order != null){ + foreach (TypeContainer tc in type_container_resolve_order) { + // When compiling corlib, these types have already been + // populated from BootCorlib_PopulateCoreTypes (). + if (!RootContext.StdLib && + ((tc.Name == "System.Object") || + (tc.Name == "System.Attribute") || + (tc.Name == "System.ValueType"))) + continue; + + if ((tc.ModFlags & Modifiers.NEW) == 0) + tc.DefineMembers (root); + else + Report1530 (tc.Location); + } + } + + ArrayList delegates = root.Delegates; + if (delegates != null){ + foreach (Delegate d in delegates) + if ((d.ModFlags & Modifiers.NEW) == 0) + d.DefineMembers (root); + else + Report1530 (d.Location); + } + + ArrayList enums = root.Enums; + if (enums != null){ + foreach (Enum en in enums) + if ((en.ModFlags & Modifiers.NEW) == 0) + en.DefineMembers (root); + else + Report1530 (en.Location); + } + } + + static public void DefineTypes () + { + TypeContainer root = Tree.Types; + + if (attribute_types != null) + foreach (TypeContainer tc in attribute_types) + tc.Define (root); + + if (interface_resolve_order != null){ + foreach (Interface iface in interface_resolve_order) + if ((iface.ModFlags & Modifiers.NEW) == 0) + iface.Define (root); + } + + + if (type_container_resolve_order != null){ + foreach (TypeContainer tc in type_container_resolve_order) { + // When compiling corlib, these types have already been + // populated from BootCorlib_PopulateCoreTypes (). + if (!RootContext.StdLib && + ((tc.Name == "System.Object") || + (tc.Name == "System.Attribute") || + (tc.Name == "System.ValueType"))) + continue; + + if ((tc.ModFlags & Modifiers.NEW) == 0) + tc.Define (root); + } + } + + ArrayList delegates = root.Delegates; + if (delegates != null){ + foreach (Delegate d in delegates) + if ((d.ModFlags & Modifiers.NEW) == 0) + d.Define (root); + } + + ArrayList enums = root.Enums; + if (enums != null){ + foreach (Enum en in enums) + if ((en.ModFlags & Modifiers.NEW) == 0) + en.Define (root); + } + } + + static public void EmitCode () + { + // + // Because of the strange way in which we do things, global + // attributes must be processed first. + // + if (global_attributes.Count > 0){ + AssemblyBuilder ab = CodeGen.AssemblyBuilder; + TypeContainer dummy = new TypeContainer (null, "", new Location (-1)); + EmitContext temp_ec = new EmitContext ( + dummy, Mono.CSharp.Location.Null, null, null, 0, false); + + foreach (DictionaryEntry de in global_attributes){ + Namespace ns = (Namespace) de.Key; + Attributes attrs = (Attributes) de.Value; + + dummy.Namespace = ns; + Attribute.ApplyAttributes (temp_ec, ab, ab, attrs, attrs.Location); + } + } + + if (attribute_types != null) + foreach (TypeContainer tc in attribute_types) + tc.Emit (); + + if (type_container_resolve_order != null) { + foreach (TypeContainer tc in type_container_resolve_order) + tc.EmitConstants (); + + foreach (TypeContainer tc in type_container_resolve_order) + tc.Emit (); + } + + if (Unsafe) { + if (TypeManager.unverifiable_code_ctor == null) { + Console.WriteLine ("Internal error ! Cannot set unverifiable code attribute."); + return; + } + + CustomAttributeBuilder cb = new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]); + CodeGen.ModuleBuilder.SetCustomAttribute (cb); + } + } + + // + // Public Field, used to track which method is the public entry + // point. + // + static public MethodInfo EntryPoint; + + // + // Track the location of the entry point. + // + static public Location EntryPointLocation; + + // + // These are used to generate unique names on the structs and fields. + // + static int field_count; + + // + // Makes an initialized struct, returns the field builder that + // references the data. Thanks go to Sergey Chaban for researching + // how to do this. And coming up with a shorter mechanism than I + // was able to figure out. + // + // This works but makes an implicit public struct $ArrayType$SIZE and + // makes the fields point to it. We could get more control if we did + // use instead: + // + // 1. DefineNestedType on the impl_details_class with our struct. + // + // 2. Define the field on the impl_details_class + // + static public FieldBuilder MakeStaticData (byte [] data) + { + FieldBuilder fb; + int size = data.Length; + + if (impl_details_class == null) + impl_details_class = CodeGen.ModuleBuilder.DefineType ( + "<PrivateImplementationDetails>", TypeAttributes.NotPublic, TypeManager.object_type); + + fb = impl_details_class.DefineInitializedData ( + "$$field-" + (field_count++), data, + FieldAttributes.Static | FieldAttributes.Assembly); + + return fb; + } + + // + // Adds a global attribute that was declared in `container', + // the attribute is in `attr', and it was defined at `loc' + // + static public void AddGlobalAttribute (TypeContainer container, + AttributeSection attr, Location loc) + { + Namespace ns = container.Namespace; + Attributes a = (Attributes) global_attributes [ns]; + + if (a == null) + global_attributes [ns] = new Attributes (attr, loc); + else + a.AddAttribute (attr); + } + } +} + + diff --git a/mcs/mbas/statement.cs b/mcs/mbas/statement.cs new file mode 100644 index 00000000000..25bd4c45cd7 --- /dev/null +++ b/mcs/mbas/statement.cs @@ -0,0 +1,5416 @@ +// +// statement.cs: Statement representation for the IL tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Martin Baulig (martin@gnome.org) +// +// (C) 2001, 2002 Ximian, Inc. +// + +using System; +using System.Text; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; + +namespace Mono.CSharp { + + using System.Collections; + + public abstract class Statement { + public Location loc; + + /// + /// Resolves the statement, true means that all sub-statements + /// did resolve ok. + // + public virtual bool Resolve (EmitContext ec) + { + return true; + } + + /// <summary> + /// Return value indicates whether all code paths emitted return. + /// </summary> + protected abstract bool DoEmit (EmitContext ec); + + /// <summary> + /// Return value indicates whether all code paths emitted return. + /// </summary> + public virtual bool Emit (EmitContext ec) + { + ec.Mark (loc); + Report.Debug (8, "MARK", this, loc); + return DoEmit (ec); + } + + public static Expression ResolveBoolean (EmitContext ec, Expression e, Location loc) + { + e = e.Resolve (ec); + if (e == null) + return null; + + if (e.Type != TypeManager.bool_type){ + e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type, + new Location (-1)); + } + + if (e == null){ + Report.Error ( + 31, loc, "Can not convert the expression to a boolean"); + } + + ec.Mark (loc); + + return e; + } + + /// <remarks> + /// Encapsulates the emission of a boolean test and jumping to a + /// destination. + /// + /// This will emit the bool expression in `bool_expr' and if + /// `target_is_for_true' is true, then the code will generate a + /// brtrue to the target. Otherwise a brfalse. + /// </remarks> + public static void EmitBoolExpression (EmitContext ec, Expression bool_expr, + Label target, bool target_is_for_true) + { + ILGenerator ig = ec.ig; + + bool invert = false; + if (bool_expr is Unary){ + Unary u = (Unary) bool_expr; + + if (u.Oper == Unary.Operator.LogicalNot){ + invert = true; + + u.EmitLogicalNot (ec); + } + } else if (bool_expr is Binary){ + Binary b = (Binary) bool_expr; + + if (b.EmitBranchable (ec, target, target_is_for_true)) + return; + } + + if (!invert) + bool_expr.Emit (ec); + + if (target_is_for_true){ + if (invert) + ig.Emit (OpCodes.Brfalse, target); + else + ig.Emit (OpCodes.Brtrue, target); + } else { + if (invert) + ig.Emit (OpCodes.Brtrue, target); + else + ig.Emit (OpCodes.Brfalse, target); + } + } + + public static void Warning_DeadCodeFound (Location loc) + { + Report.Warning (162, loc, "Unreachable code detected"); + } + } + + public class EmptyStatement : Statement { + public override bool Resolve (EmitContext ec) + { + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + return false; + } + } + + public class If : Statement { + Expression expr; + public Statement TrueStatement; + public Statement FalseStatement; + + public If (Expression expr, Statement trueStatement, Location l) + { + this.expr = expr; + TrueStatement = trueStatement; + loc = l; + } + + public If (Expression expr, + Statement trueStatement, + Statement falseStatement, + Location l) + { + this.expr = expr; + TrueStatement = trueStatement; + FalseStatement = falseStatement; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + Report.Debug (1, "START IF BLOCK", loc); + + expr = ResolveBoolean (ec, expr, loc); + if (expr == null){ + return false; + } + + ec.StartFlowBranching (FlowBranchingType.BLOCK, loc); + + if (!TrueStatement.Resolve (ec)) { + ec.KillFlowBranching (); + return false; + } + + ec.CurrentBranching.CreateSibling (); + + if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) { + ec.KillFlowBranching (); + return false; + } + + ec.EndFlowBranching (); + + Report.Debug (1, "END IF BLOCK", loc); + + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + Label false_target = ig.DefineLabel (); + Label end; + bool is_true_ret, is_false_ret; + + // + // Dead code elimination + // + if (expr is BoolConstant){ + bool take = ((BoolConstant) expr).Value; + + if (take){ + if (FalseStatement != null){ + Warning_DeadCodeFound (FalseStatement.loc); + } + return TrueStatement.Emit (ec); + } else { + Warning_DeadCodeFound (TrueStatement.loc); + if (FalseStatement != null) + return FalseStatement.Emit (ec); + } + } + + EmitBoolExpression (ec, expr, false_target, false); + + is_true_ret = TrueStatement.Emit (ec); + is_false_ret = is_true_ret; + + if (FalseStatement != null){ + bool branch_emitted = false; + + end = ig.DefineLabel (); + if (!is_true_ret){ + ig.Emit (OpCodes.Br, end); + branch_emitted = true; + } + + ig.MarkLabel (false_target); + is_false_ret = FalseStatement.Emit (ec); + + if (branch_emitted) + ig.MarkLabel (end); + } else { + ig.MarkLabel (false_target); + is_false_ret = false; + } + + return is_true_ret && is_false_ret; + } + } + + public enum DoOptions { + WHILE, + UNTIL, + TEST_BEFORE, + TEST_AFTER + }; + + public class Do : Statement { + public Expression expr; + public readonly Statement EmbeddedStatement; + //public DoOptions type; + public DoOptions test; + bool infinite, may_return; + + + public Do (Statement statement, Expression boolExpr, DoOptions do_test, Location l) + { + expr = boolExpr; + EmbeddedStatement = statement; +// type = do_type; + test = do_test; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + bool ok = true; + + ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc); + + if (!EmbeddedStatement.Resolve (ec)) + ok = false; + + expr = ResolveBoolean (ec, expr, loc); + if (expr == null) + ok = false; + else if (expr is BoolConstant){ + bool res = ((BoolConstant) expr).Value; + + if (res) + infinite = true; + } + + ec.CurrentBranching.Infinite = infinite; + FlowReturns returns = ec.EndFlowBranching (); + may_return = returns != FlowReturns.NEVER; + + return ok; + } + + protected override bool DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + Label loop = ig.DefineLabel (); + Label old_begin = ec.LoopBegin; + Label old_end = ec.LoopEnd; + bool old_inloop = ec.InLoop; + int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel; + + ec.LoopBegin = ig.DefineLabel (); + ec.LoopEnd = ig.DefineLabel (); + ec.InLoop = true; + ec.LoopBeginTryCatchLevel = ec.TryCatchLevel; + + if (test == DoOptions.TEST_AFTER) { + ig.MarkLabel (loop); + EmbeddedStatement.Emit (ec); + ig.MarkLabel (ec.LoopBegin); + + // + // Dead code elimination + // + if (expr is BoolConstant){ + bool res = ((BoolConstant) expr).Value; + + if (res) + ec.ig.Emit (OpCodes.Br, loop); + } else + EmitBoolExpression (ec, expr, loop, true); + + ig.MarkLabel (ec.LoopEnd); + } + else + { + ig.MarkLabel (loop); + ig.MarkLabel (ec.LoopBegin); + + // + // Dead code elimination + // + if (expr is BoolConstant){ + bool res = ((BoolConstant) expr).Value; + + if (res) + ec.ig.Emit (OpCodes.Br, ec.LoopEnd); + } else + EmitBoolExpression (ec, expr, ec.LoopEnd, true); + + EmbeddedStatement.Emit (ec); + ec.ig.Emit (OpCodes.Br, loop); + ig.MarkLabel (ec.LoopEnd); + } + ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level; + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + ec.InLoop = old_inloop; + + if (infinite) + return may_return == false; + else + return false; + } + } + + public class While : Statement { + public Expression expr; + public readonly Statement Statement; + bool may_return, empty, infinite; + + public While (Expression boolExpr, Statement statement, Location l) + { + this.expr = boolExpr; + Statement = statement; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + bool ok = true; + + expr = ResolveBoolean (ec, expr, loc); + if (expr == null) + return false; + + ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc); + + // + // Inform whether we are infinite or not + // + if (expr is BoolConstant){ + BoolConstant bc = (BoolConstant) expr; + + if (bc.Value == false){ + Warning_DeadCodeFound (Statement.loc); + empty = true; + } else + infinite = true; + } else { + // + // We are not infinite, so the loop may or may not be executed. + // + ec.CurrentBranching.CreateSibling (); + } + + if (!Statement.Resolve (ec)) + ok = false; + + if (empty) + ec.KillFlowBranching (); + else { + ec.CurrentBranching.Infinite = infinite; + FlowReturns returns = ec.EndFlowBranching (); + may_return = returns != FlowReturns.NEVER; + } + + return ok; + } + + protected override bool DoEmit (EmitContext ec) + { + if (empty) + return false; + + ILGenerator ig = ec.ig; + Label old_begin = ec.LoopBegin; + Label old_end = ec.LoopEnd; + bool old_inloop = ec.InLoop; + int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel; + bool ret; + + ec.LoopBegin = ig.DefineLabel (); + ec.LoopEnd = ig.DefineLabel (); + ec.InLoop = true; + ec.LoopBeginTryCatchLevel = ec.TryCatchLevel; + + // + // Inform whether we are infinite or not + // + if (expr is BoolConstant){ + BoolConstant bc = (BoolConstant) expr; + + ig.MarkLabel (ec.LoopBegin); + Statement.Emit (ec); + ig.Emit (OpCodes.Br, ec.LoopBegin); + + // + // Inform that we are infinite (ie, `we return'), only + // if we do not `break' inside the code. + // + ret = may_return == false; + ig.MarkLabel (ec.LoopEnd); + } else { + Label while_loop = ig.DefineLabel (); + + ig.Emit (OpCodes.Br, ec.LoopBegin); + ig.MarkLabel (while_loop); + + Statement.Emit (ec); + + ig.MarkLabel (ec.LoopBegin); + + EmitBoolExpression (ec, expr, while_loop, true); + ig.MarkLabel (ec.LoopEnd); + + ret = false; + } + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + ec.InLoop = old_inloop; + ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level; + + return ret; + } + } + + public class For : Statement { + Expression Test; + readonly Statement InitStatement; + readonly Statement Increment; + readonly Statement Statement; + bool may_return, infinite, empty; + + public For (Statement initStatement, + Expression test, + Statement increment, + Statement statement, + Location l) + { + InitStatement = initStatement; + Test = test; + Increment = increment; + Statement = statement; + loc = l; + } + + + public override bool Resolve (EmitContext ec) + { + bool ok = true; + + if (InitStatement != null){ + if (!InitStatement.Resolve (ec)) + ok = false; + } + + if (Test != null){ + Test = ResolveBoolean (ec, Test, loc); + if (Test == null) + ok = false; + else if (Test is BoolConstant){ + BoolConstant bc = (BoolConstant) Test; + + if (bc.Value == false){ + Warning_DeadCodeFound (Statement.loc); + empty = true; + } else + infinite = true; + } + } else + infinite = true; + + if (Increment != null){ + if (!Increment.Resolve (ec)) + ok = false; + } + + ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc); + if (!infinite) + ec.CurrentBranching.CreateSibling (); + + if (!Statement.Resolve (ec)) + ok = false; + + if (empty) + ec.KillFlowBranching (); + else { + ec.CurrentBranching.Infinite = infinite; + FlowReturns returns = ec.EndFlowBranching (); + may_return = returns != FlowReturns.NEVER; + } + + return ok; + } + + protected override bool DoEmit (EmitContext ec) + { + if (empty) + return false; + + ILGenerator ig = ec.ig; + Label old_begin = ec.LoopBegin; + Label old_end = ec.LoopEnd; + bool old_inloop = ec.InLoop; + int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel; + Label loop = ig.DefineLabel (); + Label test = ig.DefineLabel (); + + if (InitStatement != null) + if (! (InitStatement is EmptyStatement)) + InitStatement.Emit (ec); + + ec.LoopBegin = ig.DefineLabel (); + ec.LoopEnd = ig.DefineLabel (); + ec.InLoop = true; + ec.LoopBeginTryCatchLevel = ec.TryCatchLevel; + + ig.Emit (OpCodes.Br, test); + ig.MarkLabel (loop); + Statement.Emit (ec); + + ig.MarkLabel (ec.LoopBegin); + if (!(Increment is EmptyStatement)) + Increment.Emit (ec); + + ig.MarkLabel (test); + // + // If test is null, there is no test, and we are just + // an infinite loop + // + if (Test != null) + EmitBoolExpression (ec, Test, loop, true); + else + ig.Emit (OpCodes.Br, loop); + ig.MarkLabel (ec.LoopEnd); + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + ec.InLoop = old_inloop; + ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level; + + // + // Inform whether we are infinite or not + // + if (Test != null){ + if (Test is BoolConstant){ + BoolConstant bc = (BoolConstant) Test; + + if (bc.Value) + return may_return == false; + } + return false; + } else + return may_return == false; + } + } + + public class StatementExpression : Statement { + Expression expr; + + public StatementExpression (ExpressionStatement expr, Location l) + { + this.expr = expr; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + expr = (Expression) expr.Resolve (ec); + return expr != null; + } + + protected override bool DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + if (expr is ExpressionStatement) + ((ExpressionStatement) expr).EmitStatement (ec); + else { + expr.Emit (ec); + ig.Emit (OpCodes.Pop); + } + + return false; + } + + public override string ToString () + { + return "StatementExpression (" + expr + ")"; + } + } + + /// <summary> + /// Implements the return statement + /// </summary> + public class Return : Statement { + public Expression Expr; + + public Return (Expression expr, Location l) + { + Expr = expr; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + if (Expr != null){ + Expr = Expr.Resolve (ec); + if (Expr == null) + return false; + } + + FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector; + + if (ec.CurrentBranching.InTryBlock ()) + ec.CurrentBranching.AddFinallyVector (vector); + else + vector.CheckOutParameters (ec.CurrentBranching); + + vector.Returns = FlowReturns.ALWAYS; + vector.Breaks = FlowReturns.ALWAYS; + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + if (ec.InFinally){ + Report.Error (157,loc,"Control can not leave the body of the finally block"); + return false; + } + + if (ec.ReturnType == null){ + if (Expr != null){ + Report.Error (127, loc, "Return with a value not allowed here"); + return true; + } + } else { + if (Expr == null){ + Report.Error (126, loc, "An object of type `" + + TypeManager.CSharpName (ec.ReturnType) + "' is " + + "expected for the return statement"); + return true; + } + + if (Expr.Type != ec.ReturnType) + Expr = Expression.ConvertImplicitRequired ( + ec, Expr, ec.ReturnType, loc); + + if (Expr == null) + return true; + + Expr.Emit (ec); + + if (ec.InTry || ec.InCatch) + ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ()); + } + + if (ec.InTry || ec.InCatch) { + if (!ec.HasReturnLabel) { + ec.ReturnLabel = ec.ig.DefineLabel (); + ec.HasReturnLabel = true; + } + ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel); + } else + ec.ig.Emit (OpCodes.Ret); + + return true; + } + } + + public class Goto : Statement { + string target; + Block block; + LabeledStatement label; + + public override bool Resolve (EmitContext ec) + { + label = block.LookupLabel (target); + if (label == null){ + Report.Error ( + 159, loc, + "No such label `" + target + "' in this scope"); + return false; + } + + // If this is a forward goto. + if (!label.IsDefined) + label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector); + + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS; + + return true; + } + + public Goto (Block parent_block, string label, Location l) + { + block = parent_block; + loc = l; + target = label; + } + + public string Target { + get { + return target; + } + } + + protected override bool DoEmit (EmitContext ec) + { + Label l = label.LabelTarget (ec); + ec.ig.Emit (OpCodes.Br, l); + + return false; + } + } + + public class LabeledStatement : Statement { + public readonly Location Location; + string label_name; + bool defined; + bool referenced; + Label label; + + ArrayList vectors; + + public LabeledStatement (string label_name, Location l) + { + this.label_name = label_name; + this.Location = l; + } + + public Label LabelTarget (EmitContext ec) + { + if (defined) + return label; + label = ec.ig.DefineLabel (); + defined = true; + + return label; + } + + public bool IsDefined { + get { + return defined; + } + } + + public bool HasBeenReferenced { + get { + return referenced; + } + } + + public void AddUsageVector (FlowBranching.UsageVector vector) + { + if (vectors == null) + vectors = new ArrayList (); + + vectors.Add (vector.Clone ()); + } + + public override bool Resolve (EmitContext ec) + { + if (vectors != null) + ec.CurrentBranching.CurrentUsageVector.MergeJumpOrigins (vectors); + else { + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.NEVER; + ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.NEVER; + } + + referenced = true; + + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + LabelTarget (ec); + ec.ig.MarkLabel (label); + + return false; + } + } + + + /// <summary> + /// `goto default' statement + /// </summary> + public class GotoDefault : Statement { + + public GotoDefault (Location l) + { + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.UNREACHABLE; + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + if (ec.Switch == null){ + Report.Error (153, loc, "goto default is only valid in a switch statement"); + return false; + } + + if (!ec.Switch.GotDefault){ + Report.Error (159, loc, "No default target on switch statement"); + return false; + } + ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget); + return false; + } + } + + /// <summary> + /// `goto case' statement + /// </summary> + public class GotoCase : Statement { + Expression expr; + Label label; + + public GotoCase (Expression e, Location l) + { + expr = e; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + if (ec.Switch == null){ + Report.Error (153, loc, "goto case is only valid in a switch statement"); + return false; + } + + expr = expr.Resolve (ec); + if (expr == null) + return false; + + if (!(expr is Constant)){ + Report.Error (159, loc, "Target expression for goto case is not constant"); + return false; + } + + object val = Expression.ConvertIntLiteral ( + (Constant) expr, ec.Switch.SwitchType, loc); + + if (val == null) + return false; + + SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val]; + + if (sl == null){ + Report.Error ( + 159, loc, + "No such label 'case " + val + "': for the goto case"); + } + + label = sl.ILLabelCode; + + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.UNREACHABLE; + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + ec.ig.Emit (OpCodes.Br, label); + return true; + } + } + + public class Throw : Statement { + Expression expr; + + public Throw (Expression expr, Location l) + { + this.expr = expr; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + if (expr != null){ + expr = expr.Resolve (ec); + if (expr == null) + return false; + + ExprClass eclass = expr.eclass; + + if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess || + eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) { + expr.Error118 ("value, variable, property or indexer access "); + return false; + } + + Type t = expr.Type; + + if ((t != TypeManager.exception_type) && + !t.IsSubclassOf (TypeManager.exception_type) && + !(expr is NullLiteral)) { + Report.Error (155, loc, + "The type caught or thrown must be derived " + + "from System.Exception"); + return false; + } + } + + ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.EXCEPTION; + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.EXCEPTION; + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + if (expr == null){ + if (ec.InCatch) + ec.ig.Emit (OpCodes.Rethrow); + else { + Report.Error ( + 156, loc, + "A throw statement with no argument is only " + + "allowed in a catch clause"); + } + return false; + } + + expr.Emit (ec); + + ec.ig.Emit (OpCodes.Throw); + + return true; + } + } + + public class Break : Statement { + + public Break (Location l) + { + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + ec.CurrentBranching.MayLeaveLoop = true; + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS; + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + if (ec.InLoop == false && ec.Switch == null){ + Report.Error (139, loc, "No enclosing loop or switch to continue to"); + return false; + } + + if (ec.InTry || ec.InCatch) + ig.Emit (OpCodes.Leave, ec.LoopEnd); + else + ig.Emit (OpCodes.Br, ec.LoopEnd); + + return false; + } + } + + public enum ExitType { + DO, + FOR, + WHILE, + SELECT, + SUB, + FUNCTION, + PROPERTY, + TRY + }; + + public class Exit : Statement { + public readonly ExitType type; + public Exit (ExitType t, Location l) + { + loc = l; + type = t; + } + + public override bool Resolve (EmitContext ec) + { + ec.CurrentBranching.MayLeaveLoop = true; + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS; + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + if (ec.InLoop == false && ec.Switch == null){ + Report.Error (139, loc, "No enclosing loop or switch to continue to"); + return false; + } + + if (ec.InTry || ec.InCatch) + ig.Emit (OpCodes.Leave, ec.LoopEnd); + else + ig.Emit (OpCodes.Br, ec.LoopEnd); + + return false; + } + } + + public class Continue : Statement { + + public Continue (Location l) + { + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS; + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + Label begin = ec.LoopBegin; + + if (!ec.InLoop){ + Report.Error (139, loc, "No enclosing loop to continue to"); + return false; + } + + // + // UGH: Non trivial. This Br might cross a try/catch boundary + // How can we tell? + // + // while () { + // try { ... } catch { continue; } + // } + // + // From: + // try {} catch { while () { continue; }} + // + if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel) + ec.ig.Emit (OpCodes.Leave, begin); + else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel) + throw new Exception ("Should never happen"); + else + ec.ig.Emit (OpCodes.Br, begin); + return false; + } + } + + // <summary> + // This is used in the control flow analysis code to specify whether the + // current code block may return to its enclosing block before reaching + // its end. + // </summary> + public enum FlowReturns { + // It can never return. + NEVER, + + // This means that the block contains a conditional return statement + // somewhere. + SOMETIMES, + + // The code always returns, ie. there's an unconditional return / break + // statement in it. + ALWAYS, + + // The code always throws an exception. + EXCEPTION, + + // The current code block is unreachable. This happens if it's immediately + // following a FlowReturns.ALWAYS block. + UNREACHABLE + } + + // <summary> + // This is a special bit vector which can inherit from another bit vector doing a + // copy-on-write strategy. The inherited vector may have a smaller size than the + // current one. + // </summary> + public class MyBitVector { + public readonly int Count; + public readonly MyBitVector InheritsFrom; + + bool is_dirty; + BitArray vector; + + public MyBitVector (int Count) + : this (null, Count) + { } + + public MyBitVector (MyBitVector InheritsFrom, int Count) + { + this.InheritsFrom = InheritsFrom; + this.Count = Count; + } + + // <summary> + // Checks whether this bit vector has been modified. After setting this to true, + // we won't use the inherited vector anymore, but our own copy of it. + // </summary> + public bool IsDirty { + get { + return is_dirty; + } + + set { + if (!is_dirty) + initialize_vector (); + } + } + + // <summary> + // Get/set bit `index' in the bit vector. + // </summary> + public bool this [int index] + { + get { + if (index > Count) + throw new ArgumentOutOfRangeException (); + + // We're doing a "copy-on-write" strategy here; as long + // as nobody writes to the array, we can use our parent's + // copy instead of duplicating the vector. + + if (vector != null) + return vector [index]; + else if (InheritsFrom != null) { + BitArray inherited = InheritsFrom.Vector; + + if (index < inherited.Count) + return inherited [index]; + else + return false; + } else + return false; + } + + set { + if (index > Count) + throw new ArgumentOutOfRangeException (); + + // Only copy the vector if we're actually modifying it. + + if (this [index] != value) { + initialize_vector (); + + vector [index] = value; + } + } + } + + // <summary> + // If you explicitly convert the MyBitVector to a BitArray, you will get a deep + // copy of the bit vector. + // </summary> + public static explicit operator BitArray (MyBitVector vector) + { + vector.initialize_vector (); + return vector.Vector; + } + + // <summary> + // Performs an `or' operation on the bit vector. The `new_vector' may have a + // different size than the current one. + // </summary> + public void Or (MyBitVector new_vector) + { + BitArray new_array = new_vector.Vector; + + initialize_vector (); + + int upper; + if (vector.Count < new_array.Count) + upper = vector.Count; + else + upper = new_array.Count; + + for (int i = 0; i < upper; i++) + vector [i] = vector [i] | new_array [i]; + } + + // <summary> + // Perfonrms an `and' operation on the bit vector. The `new_vector' may have + // a different size than the current one. + // </summary> + public void And (MyBitVector new_vector) + { + BitArray new_array = new_vector.Vector; + + initialize_vector (); + + int lower, upper; + if (vector.Count < new_array.Count) + lower = upper = vector.Count; + else { + lower = new_array.Count; + upper = vector.Count; + } + + for (int i = 0; i < lower; i++) + vector [i] = vector [i] & new_array [i]; + + for (int i = lower; i < upper; i++) + vector [i] = false; + } + + // <summary> + // This does a deep copy of the bit vector. + // </summary> + public MyBitVector Clone () + { + MyBitVector retval = new MyBitVector (Count); + + retval.Vector = Vector; + + return retval; + } + + BitArray Vector { + get { + if (vector != null) + return vector; + else if (!is_dirty && (InheritsFrom != null)) + return InheritsFrom.Vector; + + initialize_vector (); + + return vector; + } + + set { + initialize_vector (); + + for (int i = 0; i < Math.Min (vector.Count, value.Count); i++) + vector [i] = value [i]; + } + } + + void initialize_vector () + { + if (vector != null) + return; + + vector = new BitArray (Count, false); + if (InheritsFrom != null) + Vector = InheritsFrom.Vector; + + is_dirty = true; + } + + public override string ToString () + { + StringBuilder sb = new StringBuilder ("MyBitVector ("); + + BitArray vector = Vector; + sb.Append (Count); + sb.Append (","); + if (!IsDirty) + sb.Append ("INHERITED - "); + for (int i = 0; i < vector.Count; i++) { + if (i > 0) + sb.Append (","); + sb.Append (vector [i]); + } + + sb.Append (")"); + return sb.ToString (); + } + } + + // <summary> + // The type of a FlowBranching. + // </summary> + public enum FlowBranchingType { + // Normal (conditional or toplevel) block. + BLOCK, + + // A loop block. + LOOP_BLOCK, + + // Try/Catch block. + EXCEPTION, + + // Switch block. + SWITCH, + + // Switch section. + SWITCH_SECTION + } + + // <summary> + // A new instance of this class is created every time a new block is resolved + // and if there's branching in the block's control flow. + // </summary> + public class FlowBranching { + // <summary> + // The type of this flow branching. + // </summary> + public readonly FlowBranchingType Type; + + // <summary> + // The block this branching is contained in. This may be null if it's not + // a top-level block and it doesn't declare any local variables. + // </summary> + public readonly Block Block; + + // <summary> + // The parent of this branching or null if this is the top-block. + // </summary> + public readonly FlowBranching Parent; + + // <summary> + // Start-Location of this flow branching. + // </summary> + public readonly Location Location; + + // <summary> + // A list of UsageVectors. A new vector is added each time control flow may + // take a different path. + // </summary> + public ArrayList Siblings; + + // <summary> + // If this is an infinite loop. + // </summary> + public bool Infinite; + + // <summary> + // If we may leave the current loop. + // </summary> + public bool MayLeaveLoop; + + // + // Private + // + InternalParameters param_info; + int[] param_map; + MyStructInfo[] struct_params; + int num_params; + ArrayList finally_vectors; + + static int next_id = 0; + int id; + + // <summary> + // Performs an `And' operation on the FlowReturns status + // (for instance, a block only returns ALWAYS if all its siblings + // always return). + // </summary> + public static FlowReturns AndFlowReturns (FlowReturns a, FlowReturns b) + { + if (b == FlowReturns.UNREACHABLE) + return a; + + switch (a) { + case FlowReturns.NEVER: + if (b == FlowReturns.NEVER) + return FlowReturns.NEVER; + else + return FlowReturns.SOMETIMES; + + case FlowReturns.SOMETIMES: + return FlowReturns.SOMETIMES; + + case FlowReturns.ALWAYS: + if ((b == FlowReturns.ALWAYS) || (b == FlowReturns.EXCEPTION)) + return FlowReturns.ALWAYS; + else + return FlowReturns.SOMETIMES; + + case FlowReturns.EXCEPTION: + if (b == FlowReturns.EXCEPTION) + return FlowReturns.EXCEPTION; + else if (b == FlowReturns.ALWAYS) + return FlowReturns.ALWAYS; + else + return FlowReturns.SOMETIMES; + } + + return b; + } + + // <summary> + // The vector contains a BitArray with information about which local variables + // and parameters are already initialized at the current code position. + // </summary> + public class UsageVector { + // <summary> + // If this is true, then the usage vector has been modified and must be + // merged when we're done with this branching. + // </summary> + public bool IsDirty; + + // <summary> + // The number of parameters in this block. + // </summary> + public readonly int CountParameters; + + // <summary> + // The number of locals in this block. + // </summary> + public readonly int CountLocals; + + // <summary> + // If not null, then we inherit our state from this vector and do a + // copy-on-write. If null, then we're the first sibling in a top-level + // block and inherit from the empty vector. + // </summary> + public readonly UsageVector InheritsFrom; + + // + // Private. + // + MyBitVector locals, parameters; + FlowReturns real_returns, real_breaks; + bool is_finally; + + static int next_id = 0; + int id; + + // + // Normally, you should not use any of these constructors. + // + public UsageVector (UsageVector parent, int num_params, int num_locals) + { + this.InheritsFrom = parent; + this.CountParameters = num_params; + this.CountLocals = num_locals; + this.real_returns = FlowReturns.NEVER; + this.real_breaks = FlowReturns.NEVER; + + if (parent != null) { + locals = new MyBitVector (parent.locals, CountLocals); + if (num_params > 0) + parameters = new MyBitVector (parent.parameters, num_params); + real_returns = parent.Returns; + real_breaks = parent.Breaks; + } else { + locals = new MyBitVector (null, CountLocals); + if (num_params > 0) + parameters = new MyBitVector (null, num_params); + } + + id = ++next_id; + } + + public UsageVector (UsageVector parent) + : this (parent, parent.CountParameters, parent.CountLocals) + { } + + // <summary> + // This does a deep copy of the usage vector. + // </summary> + public UsageVector Clone () + { + UsageVector retval = new UsageVector (null, CountParameters, CountLocals); + + retval.locals = locals.Clone (); + if (parameters != null) + retval.parameters = parameters.Clone (); + retval.real_returns = real_returns; + retval.real_breaks = real_breaks; + + return retval; + } + + // + // State of parameter `number'. + // + public bool this [int number] + { + get { + if (number == -1) + return true; + else if (number == 0) + throw new ArgumentException (); + + return parameters [number - 1]; + } + + set { + if (number == -1) + return; + else if (number == 0) + throw new ArgumentException (); + + parameters [number - 1] = value; + } + } + + // + // State of the local variable `vi'. + // If the local variable is a struct, use a non-zero `field_idx' + // to check an individual field in it. + // + public bool this [VariableInfo vi, int field_idx] + { + get { + if (vi.Number == -1) + return true; + else if (vi.Number == 0) + throw new ArgumentException (); + + return locals [vi.Number + field_idx - 1]; + } + + set { + if (vi.Number == -1) + return; + else if (vi.Number == 0) + throw new ArgumentException (); + + locals [vi.Number + field_idx - 1] = value; + } + } + + // <summary> + // Specifies when the current block returns. + // If this is FlowReturns.UNREACHABLE, then control can never reach the + // end of the method (so that we don't need to emit a return statement). + // The same applies for FlowReturns.EXCEPTION, but in this case the return + // value will never be used. + // </summary> + public FlowReturns Returns { + get { + return real_returns; + } + + set { + real_returns = value; + } + } + + // <summary> + // Specifies whether control may return to our containing block + // before reaching the end of this block. This happens if there + // is a break/continue/goto/return in it. + // This can also be used to find out whether the statement immediately + // following the current block may be reached or not. + // </summary> + public FlowReturns Breaks { + get { + return real_breaks; + } + + set { + real_breaks = value; + } + } + + public bool AlwaysBreaks { + get { + return (Breaks == FlowReturns.ALWAYS) || + (Breaks == FlowReturns.EXCEPTION) || + (Breaks == FlowReturns.UNREACHABLE); + } + } + + public bool MayBreak { + get { + return Breaks != FlowReturns.NEVER; + } + } + + public bool AlwaysReturns { + get { + return (Returns == FlowReturns.ALWAYS) || + (Returns == FlowReturns.EXCEPTION); + } + } + + public bool MayReturn { + get { + return (Returns == FlowReturns.SOMETIMES) || + (Returns == FlowReturns.ALWAYS); + } + } + + // <summary> + // Merge a child branching. + // </summary> + public FlowReturns MergeChildren (FlowBranching branching, ICollection children) + { + MyBitVector new_locals = null; + MyBitVector new_params = null; + + FlowReturns new_returns = FlowReturns.NEVER; + FlowReturns new_breaks = FlowReturns.NEVER; + bool new_returns_set = false, new_breaks_set = false; + + Report.Debug (2, "MERGING CHILDREN", branching, branching.Type, + this, children.Count); + + foreach (UsageVector child in children) { + Report.Debug (2, " MERGING CHILD", child, child.is_finally); + + if (!child.is_finally) { + if (child.Breaks != FlowReturns.UNREACHABLE) { + // If Returns is already set, perform an + // `And' operation on it, otherwise just set just. + if (!new_returns_set) { + new_returns = child.Returns; + new_returns_set = true; + } else + new_returns = AndFlowReturns ( + new_returns, child.Returns); + } + + // If Breaks is already set, perform an + // `And' operation on it, otherwise just set just. + if (!new_breaks_set) { + new_breaks = child.Breaks; + new_breaks_set = true; + } else + new_breaks = AndFlowReturns ( + new_breaks, child.Breaks); + } + + // Ignore unreachable children. + if (child.Returns == FlowReturns.UNREACHABLE) + continue; + + // A local variable is initialized after a flow branching if it + // has been initialized in all its branches which do neither + // always return or always throw an exception. + // + // If a branch may return, but does not always return, then we + // can treat it like a never-returning branch here: control will + // only reach the code position after the branching if we did not + // return here. + // + // It's important to distinguish between always and sometimes + // returning branches here: + // + // 1 int a; + // 2 if (something) { + // 3 return; + // 4 a = 5; + // 5 } + // 6 Console.WriteLine (a); + // + // The if block in lines 3-4 always returns, so we must not look + // at the initialization of `a' in line 4 - thus it'll still be + // uninitialized in line 6. + // + // On the other hand, the following is allowed: + // + // 1 int a; + // 2 if (something) + // 3 a = 5; + // 4 else + // 5 return; + // 6 Console.WriteLine (a); + // + // Here, `a' is initialized in line 3 and we must not look at + // line 5 since it always returns. + // + if (child.is_finally) { + if (new_locals == null) + new_locals = locals.Clone (); + new_locals.Or (child.locals); + + if (parameters != null) { + if (new_params == null) + new_params = parameters.Clone (); + new_params.Or (child.parameters); + } + + } else { + if (!child.AlwaysReturns && !child.AlwaysBreaks) { + if (new_locals != null) + new_locals.And (child.locals); + else { + new_locals = locals.Clone (); + new_locals.Or (child.locals); + } + } else if (children.Count == 1) { + new_locals = locals.Clone (); + new_locals.Or (child.locals); + } + + // An `out' parameter must be assigned in all branches which do + // not always throw an exception. + if (parameters != null) { + if (child.Breaks != FlowReturns.EXCEPTION) { + if (new_params != null) + new_params.And (child.parameters); + else { + new_params = parameters.Clone (); + new_params.Or (child.parameters); + } + } else if (children.Count == 1) { + new_params = parameters.Clone (); + new_params.Or (child.parameters); + } + } + } + } + + Returns = new_returns; + if ((branching.Type == FlowBranchingType.BLOCK) || + (branching.Type == FlowBranchingType.EXCEPTION) || + (new_breaks == FlowReturns.UNREACHABLE) || + (new_breaks == FlowReturns.EXCEPTION)) + Breaks = new_breaks; + else if (branching.Type == FlowBranchingType.SWITCH_SECTION) + Breaks = new_returns; + else if (branching.Type == FlowBranchingType.SWITCH){ + if (new_breaks == FlowReturns.ALWAYS) + Breaks = FlowReturns.ALWAYS; + } + + // + // We've now either reached the point after the branching or we will + // never get there since we always return or always throw an exception. + // + // If we can reach the point after the branching, mark all locals and + // parameters as initialized which have been initialized in all branches + // we need to look at (see above). + // + + if (((new_breaks != FlowReturns.ALWAYS) && + (new_breaks != FlowReturns.EXCEPTION) && + (new_breaks != FlowReturns.UNREACHABLE)) || + (children.Count == 1)) { + if (new_locals != null) + locals.Or (new_locals); + + if (new_params != null) + parameters.Or (new_params); + } + + Report.Debug (2, "MERGING CHILDREN DONE", branching.Type, + new_params, new_locals, new_returns, new_breaks, + branching.Infinite, branching.MayLeaveLoop, this); + + if (branching.Type == FlowBranchingType.SWITCH_SECTION) { + if ((new_breaks != FlowReturns.ALWAYS) && + (new_breaks != FlowReturns.EXCEPTION) && + (new_breaks != FlowReturns.UNREACHABLE)) + Report.Error (163, branching.Location, + "Control cannot fall through from one " + + "case label to another"); + } + + if (branching.Infinite && !branching.MayLeaveLoop) { + Report.Debug (1, "INFINITE", new_returns, new_breaks, + Returns, Breaks, this); + + // We're actually infinite. + if (new_returns == FlowReturns.NEVER) { + Breaks = FlowReturns.UNREACHABLE; + return FlowReturns.UNREACHABLE; + } + + // If we're an infinite loop and do not break, the code after + // the loop can never be reached. However, if we may return + // from the loop, then we do always return (or stay in the loop + // forever). + if ((new_returns == FlowReturns.SOMETIMES) || + (new_returns == FlowReturns.ALWAYS)) { + Returns = FlowReturns.ALWAYS; + return FlowReturns.ALWAYS; + } + } + + return new_returns; + } + + // <summary> + // Tells control flow analysis that the current code position may be reached with + // a forward jump from any of the origins listed in `origin_vectors' which is a + // list of UsageVectors. + // + // This is used when resolving forward gotos - in the following example, the + // variable `a' is uninitialized in line 8 becase this line may be reached via + // the goto in line 4: + // + // 1 int a; + // + // 3 if (something) + // 4 goto World; + // + // 6 a = 5; + // + // 7 World: + // 8 Console.WriteLine (a); + // + // </summary> + public void MergeJumpOrigins (ICollection origin_vectors) + { + Report.Debug (1, "MERGING JUMP ORIGIN", this); + + real_breaks = FlowReturns.NEVER; + real_returns = FlowReturns.NEVER; + + foreach (UsageVector vector in origin_vectors) { + Report.Debug (1, " MERGING JUMP ORIGIN", vector); + + locals.And (vector.locals); + if (parameters != null) + parameters.And (vector.parameters); + Breaks = AndFlowReturns (Breaks, vector.Breaks); + Returns = AndFlowReturns (Returns, vector.Returns); + } + + Report.Debug (1, "MERGING JUMP ORIGIN DONE", this); + } + + // <summary> + // This is used at the beginning of a finally block if there were + // any return statements in the try block or one of the catch blocks. + // </summary> + public void MergeFinallyOrigins (ICollection finally_vectors) + { + Report.Debug (1, "MERGING FINALLY ORIGIN", this); + + real_breaks = FlowReturns.NEVER; + + foreach (UsageVector vector in finally_vectors) { + Report.Debug (1, " MERGING FINALLY ORIGIN", vector); + + if (parameters != null) + parameters.And (vector.parameters); + Breaks = AndFlowReturns (Breaks, vector.Breaks); + } + + is_finally = true; + + Report.Debug (1, "MERGING FINALLY ORIGIN DONE", this); + } + + public void CheckOutParameters (FlowBranching branching) + { + if (parameters != null) + branching.CheckOutParameters (parameters, branching.Location); + } + + // <summary> + // Performs an `or' operation on the locals and the parameters. + // </summary> + public void Or (UsageVector new_vector) + { + locals.Or (new_vector.locals); + if (parameters != null) + parameters.Or (new_vector.parameters); + } + + // <summary> + // Performs an `and' operation on the locals. + // </summary> + public void AndLocals (UsageVector new_vector) + { + locals.And (new_vector.locals); + } + + // <summary> + // Returns a deep copy of the parameters. + // </summary> + public MyBitVector Parameters { + get { + if (parameters != null) + return parameters.Clone (); + else + return null; + } + } + + // <summary> + // Returns a deep copy of the locals. + // </summary> + public MyBitVector Locals { + get { + return locals.Clone (); + } + } + + // + // Debugging stuff. + // + + public override string ToString () + { + StringBuilder sb = new StringBuilder (); + + sb.Append ("Vector ("); + sb.Append (id); + sb.Append (","); + sb.Append (Returns); + sb.Append (","); + sb.Append (Breaks); + if (parameters != null) { + sb.Append (" - "); + sb.Append (parameters); + } + sb.Append (" - "); + sb.Append (locals); + sb.Append (")"); + + return sb.ToString (); + } + } + + FlowBranching (FlowBranchingType type, Location loc) + { + this.Siblings = new ArrayList (); + this.Block = null; + this.Location = loc; + this.Type = type; + id = ++next_id; + } + + // <summary> + // Creates a new flow branching for `block'. + // This is used from Block.Resolve to create the top-level branching of + // the block. + // </summary> + public FlowBranching (Block block, InternalParameters ip, Location loc) + : this (FlowBranchingType.BLOCK, loc) + { + Block = block; + Parent = null; + + int count = (ip != null) ? ip.Count : 0; + + param_info = ip; + param_map = new int [count]; + struct_params = new MyStructInfo [count]; + num_params = 0; + + for (int i = 0; i < count; i++) { + Parameter.Modifier mod = param_info.ParameterModifier (i); + + if ((mod & Parameter.Modifier.OUT) == 0) + continue; + + param_map [i] = ++num_params; + + Type param_type = param_info.ParameterType (i); + + struct_params [i] = MyStructInfo.GetStructInfo (param_type); + if (struct_params [i] != null) + num_params += struct_params [i].Count; + } + + Siblings = new ArrayList (); + Siblings.Add (new UsageVector (null, num_params, block.CountVariables)); + } + + // <summary> + // Creates a new flow branching which is contained in `parent'. + // You should only pass non-null for the `block' argument if this block + // introduces any new variables - in this case, we need to create a new + // usage vector with a different size than our parent's one. + // </summary> + public FlowBranching (FlowBranching parent, FlowBranchingType type, + Block block, Location loc) + : this (type, loc) + { + Parent = parent; + Block = block; + + if (parent != null) { + param_info = parent.param_info; + param_map = parent.param_map; + struct_params = parent.struct_params; + num_params = parent.num_params; + } + + UsageVector vector; + if (Block != null) + vector = new UsageVector (parent.CurrentUsageVector, num_params, + Block.CountVariables); + else + vector = new UsageVector (Parent.CurrentUsageVector); + + Siblings.Add (vector); + + switch (Type) { + case FlowBranchingType.EXCEPTION: + finally_vectors = new ArrayList (); + break; + + default: + break; + } + } + + // <summary> + // Returns the branching's current usage vector. + // </summary> + public UsageVector CurrentUsageVector + { + get { + return (UsageVector) Siblings [Siblings.Count - 1]; + } + } + + // <summary> + // Creates a sibling of the current usage vector. + // </summary> + public void CreateSibling () + { + Siblings.Add (new UsageVector (Parent.CurrentUsageVector)); + + Report.Debug (1, "CREATED SIBLING", CurrentUsageVector); + } + + // <summary> + // Creates a sibling for a `finally' block. + // </summary> + public void CreateSiblingForFinally () + { + if (Type != FlowBranchingType.EXCEPTION) + throw new NotSupportedException (); + + CreateSibling (); + + CurrentUsageVector.MergeFinallyOrigins (finally_vectors); + } + + // <summary> + // Check whether all `out' parameters have been assigned. + // </summary> + public void CheckOutParameters (MyBitVector parameters, Location loc) + { + if (InTryBlock ()) + return; + + for (int i = 0; i < param_map.Length; i++) { + int index = param_map [i]; + + if (index == 0) + continue; + + if (parameters [index - 1]) + continue; + + // If it's a struct, we must ensure that all its fields have + // been assigned. If the struct has any non-public fields, this + // can only be done by assigning the whole struct. + + MyStructInfo struct_info = struct_params [index - 1]; + if ((struct_info == null) || struct_info.HasNonPublicFields) { + Report.Error ( + 177, loc, "The out parameter `" + + param_info.ParameterName (i) + "' must be " + + "assigned before control leave the current method."); + param_map [i] = 0; + continue; + } + + + for (int j = 0; j < struct_info.Count; j++) { + if (!parameters [index + j]) { + Report.Error ( + 177, loc, "The out parameter `" + + param_info.ParameterName (i) + "' must be " + + "assigned before control leave the current method."); + param_map [i] = 0; + break; + } + } + } + } + + // <summary> + // Merge a child branching. + // </summary> + public FlowReturns MergeChild (FlowBranching child) + { + FlowReturns returns = CurrentUsageVector.MergeChildren (child, child.Siblings); + + if (child.Type != FlowBranchingType.LOOP_BLOCK) + MayLeaveLoop |= child.MayLeaveLoop; + else + MayLeaveLoop = false; + + return returns; + } + + // <summary> + // Does the toplevel merging. + // </summary> + public FlowReturns MergeTopBlock () + { + if ((Type != FlowBranchingType.BLOCK) || (Block == null)) + throw new NotSupportedException (); + + UsageVector vector = new UsageVector (null, num_params, Block.CountVariables); + + Report.Debug (1, "MERGING TOP BLOCK", Location, vector); + + vector.MergeChildren (this, Siblings); + + Siblings.Clear (); + Siblings.Add (vector); + + Report.Debug (1, "MERGING TOP BLOCK DONE", Location, vector); + + if (vector.Breaks != FlowReturns.EXCEPTION) { + if (!vector.AlwaysBreaks) + CheckOutParameters (CurrentUsageVector.Parameters, Location); + return vector.AlwaysBreaks ? FlowReturns.ALWAYS : vector.Returns; + } else + return FlowReturns.EXCEPTION; + } + + public bool InTryBlock () + { + if (finally_vectors != null) + return true; + else if (Parent != null) + return Parent.InTryBlock (); + else + return false; + } + + public void AddFinallyVector (UsageVector vector) + { + if (finally_vectors != null) { + finally_vectors.Add (vector.Clone ()); + return; + } + + if (Parent != null) + Parent.AddFinallyVector (vector); + else + throw new NotSupportedException (); + } + + public bool IsVariableAssigned (VariableInfo vi) + { + if (CurrentUsageVector.AlwaysBreaks) + return true; + else + return CurrentUsageVector [vi, 0]; + } + + public bool IsVariableAssigned (VariableInfo vi, int field_idx) + { + if (CurrentUsageVector.AlwaysBreaks) + return true; + else + return CurrentUsageVector [vi, field_idx]; + } + + public void SetVariableAssigned (VariableInfo vi) + { + if (CurrentUsageVector.AlwaysBreaks) + return; + + CurrentUsageVector [vi, 0] = true; + } + + public void SetVariableAssigned (VariableInfo vi, int field_idx) + { + if (CurrentUsageVector.AlwaysBreaks) + return; + + CurrentUsageVector [vi, field_idx] = true; + } + + public bool IsParameterAssigned (int number) + { + int index = param_map [number]; + + if (index == 0) + return true; + + if (CurrentUsageVector [index]) + return true; + + // Parameter is not assigned, so check whether it's a struct. + // If it's either not a struct or a struct which non-public + // fields, return false. + MyStructInfo struct_info = struct_params [number]; + if ((struct_info == null) || struct_info.HasNonPublicFields) + return false; + + // Ok, so each field must be assigned. + for (int i = 0; i < struct_info.Count; i++) + if (!CurrentUsageVector [index + i]) + return false; + + return true; + } + + public bool IsParameterAssigned (int number, string field_name) + { + int index = param_map [number]; + + if (index == 0) + return true; + + MyStructInfo info = (MyStructInfo) struct_params [number]; + if (info == null) + return true; + + int field_idx = info [field_name]; + + return CurrentUsageVector [index + field_idx]; + } + + public void SetParameterAssigned (int number) + { + if (param_map [number] == 0) + return; + + if (!CurrentUsageVector.AlwaysBreaks) + CurrentUsageVector [param_map [number]] = true; + } + + public void SetParameterAssigned (int number, string field_name) + { + int index = param_map [number]; + + if (index == 0) + return; + + MyStructInfo info = (MyStructInfo) struct_params [number]; + if (info == null) + return; + + int field_idx = info [field_name]; + + if (!CurrentUsageVector.AlwaysBreaks) + CurrentUsageVector [index + field_idx] = true; + } + + public bool IsReachable () + { + bool reachable; + + switch (Type) { + case FlowBranchingType.SWITCH_SECTION: + // The code following a switch block is reachable unless the switch + // block always returns. + reachable = !CurrentUsageVector.AlwaysReturns; + break; + + case FlowBranchingType.LOOP_BLOCK: + // The code following a loop is reachable unless the loop always + // returns or it's an infinite loop without any `break's in it. + reachable = !CurrentUsageVector.AlwaysReturns && + (CurrentUsageVector.Breaks != FlowReturns.UNREACHABLE); + break; + + default: + // The code following a block or exception is reachable unless the + // block either always returns or always breaks. + reachable = !CurrentUsageVector.AlwaysBreaks && + !CurrentUsageVector.AlwaysReturns; + break; + } + + Report.Debug (1, "REACHABLE", Type, CurrentUsageVector.Returns, + CurrentUsageVector.Breaks, CurrentUsageVector, reachable); + + return reachable; + } + + public override string ToString () + { + StringBuilder sb = new StringBuilder ("FlowBranching ("); + + sb.Append (id); + sb.Append (","); + sb.Append (Type); + if (Block != null) { + sb.Append (" - "); + sb.Append (Block.ID); + sb.Append (" - "); + sb.Append (Block.StartLocation); + } + sb.Append (" - "); + sb.Append (Siblings.Count); + sb.Append (" - "); + sb.Append (CurrentUsageVector); + sb.Append (")"); + return sb.ToString (); + } + } + + public class MyStructInfo { + public readonly Type Type; + public readonly FieldInfo[] Fields; + public readonly FieldInfo[] NonPublicFields; + public readonly int Count; + public readonly int CountNonPublic; + public readonly bool HasNonPublicFields; + + private static Hashtable field_type_hash = new Hashtable (); + private Hashtable field_hash; + + // Private constructor. To save memory usage, we only need to create one instance + // of this class per struct type. + private MyStructInfo (Type type) + { + this.Type = type; + + if (type is TypeBuilder) { + TypeContainer tc = TypeManager.LookupTypeContainer (type); + + ArrayList fields = tc.Fields; + if (fields != null) { + foreach (Field field in fields) { + if ((field.ModFlags & Modifiers.STATIC) != 0) + continue; + if ((field.ModFlags & Modifiers.PUBLIC) != 0) + ++Count; + else + ++CountNonPublic; + } + } + + Fields = new FieldInfo [Count]; + NonPublicFields = new FieldInfo [CountNonPublic]; + + Count = CountNonPublic = 0; + if (fields != null) { + foreach (Field field in fields) { + if ((field.ModFlags & Modifiers.STATIC) != 0) + continue; + if ((field.ModFlags & Modifiers.PUBLIC) != 0) + Fields [Count++] = field.FieldBuilder; + else + NonPublicFields [CountNonPublic++] = + field.FieldBuilder; + } + } + + } else { + Fields = type.GetFields (BindingFlags.Instance|BindingFlags.Public); + Count = Fields.Length; + + NonPublicFields = type.GetFields (BindingFlags.Instance|BindingFlags.NonPublic); + CountNonPublic = NonPublicFields.Length; + } + + Count += NonPublicFields.Length; + + int number = 0; + field_hash = new Hashtable (); + foreach (FieldInfo field in Fields) + field_hash.Add (field.Name, ++number); + + if (NonPublicFields.Length != 0) + HasNonPublicFields = true; + + foreach (FieldInfo field in NonPublicFields) + field_hash.Add (field.Name, ++number); + } + + public int this [string name] { + get { + if (field_hash.Contains (name)) + return (int) field_hash [name]; + else + return 0; + } + } + + public FieldInfo this [int index] { + get { + if (index >= Fields.Length) + return NonPublicFields [index - Fields.Length]; + else + return Fields [index]; + } + } + + public static MyStructInfo GetStructInfo (Type type) + { + if (!TypeManager.IsValueType (type) || TypeManager.IsEnumType (type)) + return null; + + if (!(type is TypeBuilder) && TypeManager.IsBuiltinType (type)) + return null; + + MyStructInfo info = (MyStructInfo) field_type_hash [type]; + if (info != null) + return info; + + info = new MyStructInfo (type); + field_type_hash.Add (type, info); + return info; + } + + public static MyStructInfo GetStructInfo (TypeContainer tc) + { + MyStructInfo info = (MyStructInfo) field_type_hash [tc.TypeBuilder]; + if (info != null) + return info; + + info = new MyStructInfo (tc.TypeBuilder); + field_type_hash.Add (tc.TypeBuilder, info); + return info; + } + } + + public class VariableInfo : IVariable { + public Expression Type; + public LocalBuilder LocalBuilder; + public Type VariableType; + public readonly string Name; + public readonly Location Location; + public readonly int Block; + + public int Number; + + public bool Used; + public bool Assigned; + public bool ReadOnly; + + public VariableInfo (Expression type, string name, int block, Location l) + { + Type = type; + Name = name; + Block = block; + LocalBuilder = null; + Location = l; + } + + public VariableInfo (TypeContainer tc, int block, Location l) + { + VariableType = tc.TypeBuilder; + struct_info = MyStructInfo.GetStructInfo (tc); + Block = block; + LocalBuilder = null; + Location = l; + } + + MyStructInfo struct_info; + public MyStructInfo StructInfo { + get { + return struct_info; + } + } + + public bool IsAssigned (EmitContext ec, Location loc) + { + if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this)) + return true; + + MyStructInfo struct_info = StructInfo; + if ((struct_info == null) || (struct_info.HasNonPublicFields && (Name != null))) { + Report.Error (165, loc, "Use of unassigned local variable `" + Name + "'"); + ec.CurrentBranching.SetVariableAssigned (this); + return false; + } + + int count = struct_info.Count; + + for (int i = 0; i < count; i++) { + if (!ec.CurrentBranching.IsVariableAssigned (this, i+1)) { + if (Name != null) { + Report.Error (165, loc, + "Use of unassigned local variable `" + + Name + "'"); + ec.CurrentBranching.SetVariableAssigned (this); + return false; + } + + FieldInfo field = struct_info [i]; + Report.Error (171, loc, + "Field `" + TypeManager.CSharpName (VariableType) + + "." + field.Name + "' must be fully initialized " + + "before control leaves the constructor"); + return false; + } + } + + return true; + } + + public bool IsFieldAssigned (EmitContext ec, string name, Location loc) + { + if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this) || + (struct_info == null)) + return true; + + int field_idx = StructInfo [name]; + if (field_idx == 0) + return true; + + if (!ec.CurrentBranching.IsVariableAssigned (this, field_idx)) { + Report.Error (170, loc, + "Use of possibly unassigned field `" + name + "'"); + ec.CurrentBranching.SetVariableAssigned (this, field_idx); + return false; + } + + return true; + } + + public void SetAssigned (EmitContext ec) + { + if (ec.DoFlowAnalysis) + ec.CurrentBranching.SetVariableAssigned (this); + } + + public void SetFieldAssigned (EmitContext ec, string name) + { + if (ec.DoFlowAnalysis && (struct_info != null)) + ec.CurrentBranching.SetVariableAssigned (this, StructInfo [name]); + } + + public bool Resolve (DeclSpace decl) + { + if (struct_info != null) + return true; + + if (VariableType == null) + VariableType = decl.ResolveType (Type, false, Location); + + if (VariableType == null) + return false; + + struct_info = MyStructInfo.GetStructInfo (VariableType); + + return true; + } + + public void MakePinned () + { + TypeManager.MakePinned (LocalBuilder); + } + + public override string ToString () + { + return "VariableInfo (" + Number + "," + Type + "," + Location + ")"; + } + } + + /// <summary> + /// Block represents a C# block. + /// </summary> + /// + /// <remarks> + /// This class is used in a number of places: either to represent + /// explicit blocks that the programmer places or implicit blocks. + /// + /// Implicit blocks are used as labels or to introduce variable + /// declarations. + /// </remarks> + public class Block : Statement { + public readonly Block Parent; + public readonly bool Implicit; + public readonly Location StartLocation; + public Location EndLocation; + + // + // The statements in this block + // + public ArrayList statements; + + // + // An array of Blocks. We keep track of children just + // to generate the local variable declarations. + // + // Statements and child statements are handled through the + // statements. + // + ArrayList children; + + // + // Labels. (label, block) pairs. + // + Hashtable labels; + + // + // Keeps track of (name, type) pairs + // + Hashtable variables; + + // + // Keeps track of constants + Hashtable constants; + + // + // Maps variable names to ILGenerator.LocalBuilders + // + Hashtable local_builders; + + bool used = false; + + static int id; + + int this_id; + + public Block (Block parent) + : this (parent, false, Location.Null, Location.Null) + { } + + public Block (Block parent, bool implicit_block) + : this (parent, implicit_block, Location.Null, Location.Null) + { } + + public Block (Block parent, bool implicit_block, Parameters parameters) + : this (parent, implicit_block, parameters, Location.Null, Location.Null) + { } + + public Block (Block parent, Location start, Location end) + : this (parent, false, start, end) + { } + + public Block (Block parent, Parameters parameters, Location start, Location end) + : this (parent, false, parameters, start, end) + { } + + public Block (Block parent, bool implicit_block, Location start, Location end) + : this (parent, implicit_block, Parameters.EmptyReadOnlyParameters, + start, end) + { } + + public Block (Block parent, bool implicit_block, Parameters parameters, + Location start, Location end) + { + if (parent != null) + parent.AddChild (this); + + this.Parent = parent; + this.Implicit = implicit_block; + this.parameters = parameters; + this.StartLocation = start; + this.EndLocation = end; + this.loc = start; + this_id = id++; + statements = new ArrayList (); + } + + public int ID { + get { + return this_id; + } + } + + void AddChild (Block b) + { + if (children == null) + children = new ArrayList (); + + children.Add (b); + } + + public void SetEndLocation (Location loc) + { + EndLocation = loc; + } + + /// <summary> + /// Adds a label to the current block. + /// </summary> + /// + /// <returns> + /// false if the name already exists in this block. true + /// otherwise. + /// </returns> + /// + public bool AddLabel (string name, LabeledStatement target) + { + if (labels == null) + labels = new Hashtable (); + if (labels.Contains (name)) + return false; + + labels.Add (name, target); + return true; + } + + public LabeledStatement LookupLabel (string name) + { + if (labels != null){ + if (labels.Contains (name)) + return ((LabeledStatement) labels [name]); + } + + if (Parent != null) + return Parent.LookupLabel (name); + + return null; + } + + VariableInfo this_variable = null; + + // <summary> + // Returns the "this" instance variable of this block. + // See AddThisVariable() for more information. + // </summary> + public VariableInfo ThisVariable { + get { + if (this_variable != null) + return this_variable; + else if (Parent != null) + return Parent.ThisVariable; + else + return null; + } + } + + Hashtable child_variable_names; + + // <summary> + // Marks a variable with name @name as being used in a child block. + // If a variable name has been used in a child block, it's illegal to + // declare a variable with the same name in the current block. + // </summary> + public void AddChildVariableName (string name) + { + if (child_variable_names == null) + child_variable_names = new Hashtable (); + + if (!child_variable_names.Contains (name)) + child_variable_names.Add (name, true); + } + + // <summary> + // Marks all variables from block @block and all its children as being + // used in a child block. + // </summary> + public void AddChildVariableNames (Block block) + { + if (block.Variables != null) { + foreach (string name in block.Variables.Keys) + AddChildVariableName (name); + } + + foreach (Block child in block.children) { + if (child.Variables != null) { + foreach (string name in child.Variables.Keys) + AddChildVariableName (name); + } + } + } + + // <summary> + // Checks whether a variable name has already been used in a child block. + // </summary> + public bool IsVariableNameUsedInChildBlock (string name) + { + if (child_variable_names == null) + return false; + + return child_variable_names.Contains (name); + } + + // <summary> + // This is used by non-static `struct' constructors which do not have an + // initializer - in this case, the constructor must initialize all of the + // struct's fields. To do this, we add a "this" variable and use the flow + // analysis code to ensure that it's been fully initialized before control + // leaves the constructor. + // </summary> + public VariableInfo AddThisVariable (TypeContainer tc, Location l) + { + if (this_variable != null) + return this_variable; + + this_variable = new VariableInfo (tc, ID, l); + + if (variables == null) + variables = new Hashtable (); + variables.Add ("this", this_variable); + + return this_variable; + } + + public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l) + { + if (variables == null) + variables = new Hashtable (); + + VariableInfo vi = GetVariableInfo (name); + if (vi != null) { + if (vi.Block != ID) + Report.Error (136, l, "A local variable named `" + name + "' " + + "cannot be declared in this scope since it would " + + "give a different meaning to `" + name + "', which " + + "is already used in a `parent or current' scope to " + + "denote something else"); + else + Report.Error (128, l, "A local variable `" + name + "' is already " + + "defined in this scope"); + return null; + } + + if (IsVariableNameUsedInChildBlock (name)) { + Report.Error (136, l, "A local variable named `" + name + "' " + + "cannot be declared in this scope since it would " + + "give a different meaning to `" + name + "', which " + + "is already used in a `child' scope to denote something " + + "else"); + return null; + } + + if (pars != null) { + int idx = 0; + Parameter p = pars.GetParameterByName (name, out idx); + if (p != null) { + Report.Error (136, l, "A local variable named `" + name + "' " + + "cannot be declared in this scope since it would " + + "give a different meaning to `" + name + "', which " + + "is already used in a `parent or current' scope to " + + "denote something else"); + return null; + } + } + + vi = new VariableInfo (type, name, ID, l); + + variables.Add (name, vi); + + if (variables_initialized) + throw new Exception (); + + // Console.WriteLine ("Adding {0} to {1}", name, ID); + return vi; + } + + public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l) + { + if (AddVariable (type, name, pars, l) == null) + return false; + + if (constants == null) + constants = new Hashtable (); + + constants.Add (name, value); + return true; + } + + public Hashtable Variables { + get { + return variables; + } + } + + public VariableInfo GetVariableInfo (string name) + { + if (variables != null) { + object temp; + temp = variables [name]; + + if (temp != null){ + return (VariableInfo) temp; + } + } + + if (Parent != null) + return Parent.GetVariableInfo (name); + + return null; + } + + public Expression GetVariableType (string name) + { + VariableInfo vi = GetVariableInfo (name); + + if (vi != null) + return vi.Type; + + return null; + } + + public Expression GetConstantExpression (string name) + { + if (constants != null) { + object temp; + temp = constants [name]; + + if (temp != null) + return (Expression) temp; + } + + if (Parent != null) + return Parent.GetConstantExpression (name); + + return null; + } + + /// <summary> + /// True if the variable named @name has been defined + /// in this block + /// </summary> + public bool IsVariableDefined (string name) + { + // Console.WriteLine ("Looking up {0} in {1}", name, ID); + if (variables != null) { + if (variables.Contains (name)) + return true; + } + + if (Parent != null) + return Parent.IsVariableDefined (name); + + return false; + } + + /// <summary> + /// True if the variable named @name is a constant + /// </summary> + public bool IsConstant (string name) + { + Expression e = null; + + e = GetConstantExpression (name); + + return e != null; + } + + /// <summary> + /// Use to fetch the statement associated with this label + /// </summary> + public Statement this [string name] { + get { + return (Statement) labels [name]; + } + } + + Parameters parameters = null; + public Parameters Parameters { + get { + if (Parent != null) + return Parent.Parameters; + + return parameters; + } + } + + /// <returns> + /// A list of labels that were not used within this block + /// </returns> + public string [] GetUnreferenced () + { + // FIXME: Implement me + return null; + } + + public void AddStatement (Statement s) + { + statements.Add (s); + used = true; + } + + public bool Used { + get { + return used; + } + } + + public void Use () + { + used = true; + } + + bool variables_initialized = false; + int count_variables = 0, first_variable = 0; + + void UpdateVariableInfo (EmitContext ec) + { + DeclSpace ds = ec.DeclSpace; + + first_variable = 0; + + if (Parent != null) + first_variable += Parent.CountVariables; + + count_variables = first_variable; + if (variables != null) { + foreach (VariableInfo vi in variables.Values) { + if (!vi.Resolve (ds)) { + vi.Number = -1; + continue; + } + + vi.Number = ++count_variables; + + if (vi.StructInfo != null) + count_variables += vi.StructInfo.Count; + } + } + + variables_initialized = true; + } + + // + // <returns> + // The number of local variables in this block + // </returns> + public int CountVariables + { + get { + if (!variables_initialized) + throw new Exception (); + + return count_variables; + } + } + + /// <summary> + /// Emits the variable declarations and labels. + /// </summary> + /// <remarks> + /// tc: is our typecontainer (to resolve type references) + /// ig: is the code generator: + /// toplevel: the toplevel block. This is used for checking + /// that no two labels with the same name are used. + /// </remarks> + public void EmitMeta (EmitContext ec, Block toplevel) + { + DeclSpace ds = ec.DeclSpace; + ILGenerator ig = ec.ig; + + if (!variables_initialized) + UpdateVariableInfo (ec); + + // + // Process this block variables + // + if (variables != null){ + local_builders = new Hashtable (); + + foreach (DictionaryEntry de in variables){ + string name = (string) de.Key; + VariableInfo vi = (VariableInfo) de.Value; + + if (vi.VariableType == null) + continue; + + vi.LocalBuilder = ig.DeclareLocal (vi.VariableType); + + if (CodeGen.SymbolWriter != null) + vi.LocalBuilder.SetLocalSymInfo (name); + + if (constants == null) + continue; + + Expression cv = (Expression) constants [name]; + if (cv == null) + continue; + + Expression e = cv.Resolve (ec); + if (e == null) + continue; + + if (!(e is Constant)){ + Report.Error (133, vi.Location, + "The expression being assigned to `" + + name + "' must be constant (" + e + ")"); + continue; + } + + constants.Remove (name); + constants.Add (name, e); + } + } + + // + // Now, handle the children + // + if (children != null){ + foreach (Block b in children) + b.EmitMeta (ec, toplevel); + } + } + + public void UsageWarning () + { + string name; + + if (variables != null){ + foreach (DictionaryEntry de in variables){ + VariableInfo vi = (VariableInfo) de.Value; + + if (vi.Used) + continue; + + name = (string) de.Key; + + if (vi.Assigned){ + Report.Warning ( + 219, vi.Location, "The variable `" + name + + "' is assigned but its value is never used"); + } else { + Report.Warning ( + 168, vi.Location, "The variable `" + + name + + "' is declared but never used"); + } + } + } + + if (children != null) + foreach (Block b in children) + b.UsageWarning (); + } + + bool has_ret = false; + + public override bool Resolve (EmitContext ec) + { + Block prev_block = ec.CurrentBlock; + bool ok = true; + + ec.CurrentBlock = this; + ec.StartFlowBranching (this); + + Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching); + + if (!variables_initialized) + UpdateVariableInfo (ec); + + ArrayList new_statements = new ArrayList (); + bool unreachable = false, warning_shown = false; + + foreach (Statement s in statements){ + if (unreachable && !(s is LabeledStatement)) { + if (!warning_shown && !(s is EmptyStatement)) { + warning_shown = true; + Warning_DeadCodeFound (s.loc); + } + + continue; + } + + if (s.Resolve (ec) == false) { + ok = false; + continue; + } + + if (s is LabeledStatement) + unreachable = false; + else + unreachable = ! ec.CurrentBranching.IsReachable (); + + new_statements.Add (s); + } + + statements = new_statements; + + Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching); + + FlowReturns returns = ec.EndFlowBranching (); + ec.CurrentBlock = prev_block; + + // If we're a non-static `struct' constructor which doesn't have an + // initializer, then we must initialize all of the struct's fields. + if ((this_variable != null) && (returns != FlowReturns.EXCEPTION) && + !this_variable.IsAssigned (ec, loc)) + ok = false; + + if ((labels != null) && (RootContext.WarningLevel >= 2)) { + foreach (LabeledStatement label in labels.Values) + if (!label.HasBeenReferenced) + Report.Warning (164, label.Location, + "This label has not been referenced"); + } + + if ((returns == FlowReturns.ALWAYS) || + (returns == FlowReturns.EXCEPTION) || + (returns == FlowReturns.UNREACHABLE)) + has_ret = true; + + return ok; + } + + protected override bool DoEmit (EmitContext ec) + { + Block prev_block = ec.CurrentBlock; + + ec.CurrentBlock = this; + + ec.Mark (StartLocation); + foreach (Statement s in statements) + s.Emit (ec); + ec.Mark (EndLocation); + + ec.CurrentBlock = prev_block; + return has_ret; + } + } + + public class SwitchLabel { + Expression label; + object converted; + public Location loc; + public Label ILLabel; + public Label ILLabelCode; + + // + // if expr == null, then it is the default case. + // + public SwitchLabel (Expression expr, Location l) + { + label = expr; + loc = l; + } + + public Expression Label { + get { + return label; + } + } + + public object Converted { + get { + return converted; + } + } + + // + // Resolves the expression, reduces it to a literal if possible + // and then converts it to the requested type. + // + public bool ResolveAndReduce (EmitContext ec, Type required_type) + { + ILLabel = ec.ig.DefineLabel (); + ILLabelCode = ec.ig.DefineLabel (); + + if (label == null) + return true; + + Expression e = label.Resolve (ec); + + if (e == null) + return false; + + if (!(e is Constant)){ + Console.WriteLine ("Value is: " + label); + Report.Error (150, loc, "A constant value is expected"); + return false; + } + + if (e is StringConstant || e is NullLiteral){ + if (required_type == TypeManager.string_type){ + converted = e; + ILLabel = ec.ig.DefineLabel (); + return true; + } + } + + converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc); + if (converted == null) + return false; + + return true; + } + } + + public class SwitchSection { + // An array of SwitchLabels. + public readonly ArrayList Labels; + public readonly Block Block; + + public SwitchSection (ArrayList labels, Block block) + { + Labels = labels; + Block = block; + } + } + + public class Switch : Statement { + public readonly ArrayList Sections; + public Expression Expr; + + /// <summary> + /// Maps constants whose type type SwitchType to their SwitchLabels. + /// </summary> + public Hashtable Elements; + + /// <summary> + /// The governing switch type + /// </summary> + public Type SwitchType; + + // + // Computed + // + bool got_default; + Label default_target; + Expression new_expr; + + // + // The types allowed to be implicitly cast from + // on the governing type + // + static Type [] allowed_types; + + public Switch (Expression e, ArrayList sects, Location l) + { + Expr = e; + Sections = sects; + loc = l; + } + + public bool GotDefault { + get { + return got_default; + } + } + + public Label DefaultTarget { + get { + return default_target; + } + } + + // + // Determines the governing type for a switch. The returned + // expression might be the expression from the switch, or an + // expression that includes any potential conversions to the + // integral types or to string. + // + Expression SwitchGoverningType (EmitContext ec, Type t) + { + if (t == TypeManager.int32_type || + t == TypeManager.uint32_type || + t == TypeManager.char_type || + t == TypeManager.byte_type || + t == TypeManager.sbyte_type || + t == TypeManager.ushort_type || + t == TypeManager.short_type || + t == TypeManager.uint64_type || + t == TypeManager.int64_type || + t == TypeManager.string_type || + t == TypeManager.bool_type || + t.IsSubclassOf (TypeManager.enum_type)) + return Expr; + + if (allowed_types == null){ + allowed_types = new Type [] { + TypeManager.sbyte_type, + TypeManager.byte_type, + TypeManager.short_type, + TypeManager.ushort_type, + TypeManager.int32_type, + TypeManager.uint32_type, + TypeManager.int64_type, + TypeManager.uint64_type, + TypeManager.char_type, + TypeManager.bool_type, + TypeManager.string_type + }; + } + + // + // Try to find a *user* defined implicit conversion. + // + // If there is no implicit conversion, or if there are multiple + // conversions, we have to report an error + // + Expression converted = null; + foreach (Type tt in allowed_types){ + Expression e; + + e = Expression.ImplicitUserConversion (ec, Expr, tt, loc); + if (e == null) + continue; + + if (converted != null){ + Report.Error (-12, loc, "More than one conversion to an integral " + + " type exists for type `" + + TypeManager.CSharpName (Expr.Type)+"'"); + return null; + } else + converted = e; + } + return converted; + } + + void error152 (string n) + { + Report.Error ( + 152, "The label `" + n + ":' " + + "is already present on this switch statement"); + } + + // + // Performs the basic sanity checks on the switch statement + // (looks for duplicate keys and non-constant expressions). + // + // It also returns a hashtable with the keys that we will later + // use to compute the switch tables + // + bool CheckSwitch (EmitContext ec) + { + Type compare_type; + bool error = false; + Elements = new Hashtable (); + + got_default = false; + + if (TypeManager.IsEnumType (SwitchType)){ + compare_type = TypeManager.EnumToUnderlying (SwitchType); + } else + compare_type = SwitchType; + + foreach (SwitchSection ss in Sections){ + foreach (SwitchLabel sl in ss.Labels){ + if (!sl.ResolveAndReduce (ec, SwitchType)){ + error = true; + continue; + } + + if (sl.Label == null){ + if (got_default){ + error152 ("default"); + error = true; + } + got_default = true; + continue; + } + + object key = sl.Converted; + + if (key is Constant) + key = ((Constant) key).GetValue (); + + if (key == null) + key = NullLiteral.Null; + + string lname = null; + if (compare_type == TypeManager.uint64_type){ + ulong v = (ulong) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } else if (compare_type == TypeManager.int64_type){ + long v = (long) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } else if (compare_type == TypeManager.uint32_type){ + uint v = (uint) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } else if (compare_type == TypeManager.char_type){ + char v = (char) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } else if (compare_type == TypeManager.byte_type){ + byte v = (byte) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } else if (compare_type == TypeManager.sbyte_type){ + sbyte v = (sbyte) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } else if (compare_type == TypeManager.short_type){ + short v = (short) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } else if (compare_type == TypeManager.ushort_type){ + ushort v = (ushort) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } else if (compare_type == TypeManager.string_type){ + if (key is NullLiteral){ + if (Elements.Contains (NullLiteral.Null)) + lname = "null"; + else + Elements.Add (NullLiteral.Null, null); + } else { + string s = (string) key; + + if (Elements.Contains (s)) + lname = s; + else + Elements.Add (s, sl); + } + } else if (compare_type == TypeManager.int32_type) { + int v = (int) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } else if (compare_type == TypeManager.bool_type) { + bool v = (bool) key; + + if (Elements.Contains (v)) + lname = v.ToString (); + else + Elements.Add (v, sl); + } + else + { + throw new Exception ("Unknown switch type!" + + SwitchType + " " + compare_type); + } + + if (lname != null){ + error152 ("case + " + lname); + error = true; + } + } + } + if (error) + return false; + + return true; + } + + void EmitObjectInteger (ILGenerator ig, object k) + { + if (k is int) + IntConstant.EmitInt (ig, (int) k); + else if (k is Constant) { + EmitObjectInteger (ig, ((Constant) k).GetValue ()); + } + else if (k is uint) + IntConstant.EmitInt (ig, unchecked ((int) (uint) k)); + else if (k is long) + { + if ((long) k >= int.MinValue && (long) k <= int.MaxValue) + { + IntConstant.EmitInt (ig, (int) (long) k); + ig.Emit (OpCodes.Conv_I8); + } + else + LongConstant.EmitLong (ig, (long) k); + } + else if (k is ulong) + { + if ((ulong) k < (1L<<32)) + { + IntConstant.EmitInt (ig, (int) (long) k); + ig.Emit (OpCodes.Conv_U8); + } + else + { + LongConstant.EmitLong (ig, unchecked ((long) (ulong) k)); + } + } + else if (k is char) + IntConstant.EmitInt (ig, (int) ((char) k)); + else if (k is sbyte) + IntConstant.EmitInt (ig, (int) ((sbyte) k)); + else if (k is byte) + IntConstant.EmitInt (ig, (int) ((byte) k)); + else if (k is short) + IntConstant.EmitInt (ig, (int) ((short) k)); + else if (k is ushort) + IntConstant.EmitInt (ig, (int) ((ushort) k)); + else if (k is bool) + IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0); + else + throw new Exception ("Unhandled case"); + } + + // structure used to hold blocks of keys while calculating table switch + class KeyBlock : IComparable + { + public KeyBlock (long _nFirst) + { + nFirst = nLast = _nFirst; + } + public long nFirst; + public long nLast; + public ArrayList rgKeys = null; + public int Length + { + get { return (int) (nLast - nFirst + 1); } + } + public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast) + { + return kbLast.nLast - kbFirst.nFirst + 1; + } + public int CompareTo (object obj) + { + KeyBlock kb = (KeyBlock) obj; + int nLength = Length; + int nLengthOther = kb.Length; + if (nLengthOther == nLength) + return (int) (kb.nFirst - nFirst); + return nLength - nLengthOther; + } + } + + /// <summary> + /// This method emits code for a lookup-based switch statement (non-string) + /// Basically it groups the cases into blocks that are at least half full, + /// and then spits out individual lookup opcodes for each block. + /// It emits the longest blocks first, and short blocks are just + /// handled with direct compares. + /// </summary> + /// <param name="ec"></param> + /// <param name="val"></param> + /// <returns></returns> + bool TableSwitchEmit (EmitContext ec, LocalBuilder val) + { + int cElements = Elements.Count; + object [] rgKeys = new object [cElements]; + Elements.Keys.CopyTo (rgKeys, 0); + Array.Sort (rgKeys); + + // initialize the block list with one element per key + ArrayList rgKeyBlocks = new ArrayList (); + foreach (object key in rgKeys) + rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key))); + + KeyBlock kbCurr; + // iteratively merge the blocks while they are at least half full + // there's probably a really cool way to do this with a tree... + while (rgKeyBlocks.Count > 1) + { + ArrayList rgKeyBlocksNew = new ArrayList (); + kbCurr = (KeyBlock) rgKeyBlocks [0]; + for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++) + { + KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb]; + if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb)) + { + // merge blocks + kbCurr.nLast = kb.nLast; + } + else + { + // start a new block + rgKeyBlocksNew.Add (kbCurr); + kbCurr = kb; + } + } + rgKeyBlocksNew.Add (kbCurr); + if (rgKeyBlocks.Count == rgKeyBlocksNew.Count) + break; + rgKeyBlocks = rgKeyBlocksNew; + } + + // initialize the key lists + foreach (KeyBlock kb in rgKeyBlocks) + kb.rgKeys = new ArrayList (); + + // fill the key lists + int iBlockCurr = 0; + if (rgKeyBlocks.Count > 0) { + kbCurr = (KeyBlock) rgKeyBlocks [0]; + foreach (object key in rgKeys) + { + bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast; + if (fNextBlock) + kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr]; + kbCurr.rgKeys.Add (key); + } + } + + // sort the blocks so we can tackle the largest ones first + rgKeyBlocks.Sort (); + + // okay now we can start... + ILGenerator ig = ec.ig; + Label lblEnd = ig.DefineLabel (); // at the end ;-) + Label lblDefault = ig.DefineLabel (); + + Type typeKeys = null; + if (rgKeys.Length > 0) + typeKeys = rgKeys [0].GetType (); // used for conversions + + for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock) + { + KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]); + lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel (); + if (kb.Length <= 2) + { + foreach (object key in kb.rgKeys) + { + ig.Emit (OpCodes.Ldloc, val); + EmitObjectInteger (ig, key); + SwitchLabel sl = (SwitchLabel) Elements [key]; + ig.Emit (OpCodes.Beq, sl.ILLabel); + } + } + else + { + // TODO: if all the keys in the block are the same and there are + // no gaps/defaults then just use a range-check. + if (SwitchType == TypeManager.int64_type || + SwitchType == TypeManager.uint64_type) + { + // TODO: optimize constant/I4 cases + + // check block range (could be > 2^31) + ig.Emit (OpCodes.Ldloc, val); + EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys)); + ig.Emit (OpCodes.Blt, lblDefault); + ig.Emit (OpCodes.Ldloc, val); + EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys)); + ig.Emit (OpCodes.Bgt, lblDefault); + + // normalize range + ig.Emit (OpCodes.Ldloc, val); + if (kb.nFirst != 0) + { + EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys)); + ig.Emit (OpCodes.Sub); + } + ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels! + } + else + { + // normalize range + ig.Emit (OpCodes.Ldloc, val); + int nFirst = (int) kb.nFirst; + if (nFirst > 0) + { + IntConstant.EmitInt (ig, nFirst); + ig.Emit (OpCodes.Sub); + } + else if (nFirst < 0) + { + IntConstant.EmitInt (ig, -nFirst); + ig.Emit (OpCodes.Add); + } + } + + // first, build the list of labels for the switch + int iKey = 0; + int cJumps = kb.Length; + Label [] rgLabels = new Label [cJumps]; + for (int iJump = 0; iJump < cJumps; iJump++) + { + object key = kb.rgKeys [iKey]; + if (Convert.ToInt64 (key) == kb.nFirst + iJump) + { + SwitchLabel sl = (SwitchLabel) Elements [key]; + rgLabels [iJump] = sl.ILLabel; + iKey++; + } + else + rgLabels [iJump] = lblDefault; + } + // emit the switch opcode + ig.Emit (OpCodes.Switch, rgLabels); + } + + // mark the default for this block + if (iBlock != 0) + ig.MarkLabel (lblDefault); + } + + // TODO: find the default case and emit it here, + // to prevent having to do the following jump. + // make sure to mark other labels in the default section + + // the last default just goes to the end + ig.Emit (OpCodes.Br, lblDefault); + + // now emit the code for the sections + bool fFoundDefault = false; + bool fAllReturn = true; + foreach (SwitchSection ss in Sections) + { + foreach (SwitchLabel sl in ss.Labels) + { + ig.MarkLabel (sl.ILLabel); + ig.MarkLabel (sl.ILLabelCode); + if (sl.Label == null) + { + ig.MarkLabel (lblDefault); + fFoundDefault = true; + } + } + bool returns = ss.Block.Emit (ec); + fAllReturn &= returns; + //ig.Emit (OpCodes.Br, lblEnd); + } + + if (!fFoundDefault) { + ig.MarkLabel (lblDefault); + fAllReturn = false; + } + ig.MarkLabel (lblEnd); + + return fAllReturn; + } + // + // This simple emit switch works, but does not take advantage of the + // `switch' opcode. + // TODO: remove non-string logic from here + // TODO: binary search strings? + // + bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val) + { + ILGenerator ig = ec.ig; + Label end_of_switch = ig.DefineLabel (); + Label next_test = ig.DefineLabel (); + Label null_target = ig.DefineLabel (); + bool default_found = false; + bool first_test = true; + bool pending_goto_end = false; + bool all_return = true; + bool is_string = false; + bool null_found; + + // + // Special processing for strings: we cant compare + // against null. + // + if (SwitchType == TypeManager.string_type){ + ig.Emit (OpCodes.Ldloc, val); + is_string = true; + + if (Elements.Contains (NullLiteral.Null)){ + ig.Emit (OpCodes.Brfalse, null_target); + } else + ig.Emit (OpCodes.Brfalse, default_target); + + ig.Emit (OpCodes.Ldloc, val); + ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string); + ig.Emit (OpCodes.Stloc, val); + } + + foreach (SwitchSection ss in Sections){ + Label sec_begin = ig.DefineLabel (); + + if (pending_goto_end) + ig.Emit (OpCodes.Br, end_of_switch); + + int label_count = ss.Labels.Count; + null_found = false; + foreach (SwitchLabel sl in ss.Labels){ + ig.MarkLabel (sl.ILLabel); + + if (!first_test){ + ig.MarkLabel (next_test); + next_test = ig.DefineLabel (); + } + // + // If we are the default target + // + if (sl.Label == null){ + ig.MarkLabel (default_target); + default_found = true; + } else { + object lit = sl.Converted; + + if (lit is NullLiteral){ + null_found = true; + if (label_count == 1) + ig.Emit (OpCodes.Br, next_test); + continue; + + } + if (is_string){ + StringConstant str = (StringConstant) lit; + + ig.Emit (OpCodes.Ldloc, val); + ig.Emit (OpCodes.Ldstr, str.Value); + if (label_count == 1) + ig.Emit (OpCodes.Bne_Un, next_test); + else + ig.Emit (OpCodes.Beq, sec_begin); + } else { + ig.Emit (OpCodes.Ldloc, val); + EmitObjectInteger (ig, lit); + ig.Emit (OpCodes.Ceq); + if (label_count == 1) + ig.Emit (OpCodes.Brfalse, next_test); + else + ig.Emit (OpCodes.Brtrue, sec_begin); + } + } + } + if (label_count != 1) + ig.Emit (OpCodes.Br, next_test); + + if (null_found) + ig.MarkLabel (null_target); + ig.MarkLabel (sec_begin); + foreach (SwitchLabel sl in ss.Labels) + ig.MarkLabel (sl.ILLabelCode); + + bool returns = ss.Block.Emit (ec); + if (returns) + pending_goto_end = false; + else { + all_return = false; + pending_goto_end = true; + } + first_test = false; + } + if (!default_found){ + ig.MarkLabel (default_target); + all_return = false; + } + ig.MarkLabel (next_test); + ig.MarkLabel (end_of_switch); + + return all_return; + } + + public override bool Resolve (EmitContext ec) + { + Expr = Expr.Resolve (ec); + if (Expr == null) + return false; + + new_expr = SwitchGoverningType (ec, Expr.Type); + if (new_expr == null){ + Report.Error (151, loc, "An integer type or string was expected for switch"); + return false; + } + + // Validate switch. + SwitchType = new_expr.Type; + + if (!CheckSwitch (ec)) + return false; + + Switch old_switch = ec.Switch; + ec.Switch = this; + ec.Switch.SwitchType = SwitchType; + + ec.StartFlowBranching (FlowBranchingType.SWITCH, loc); + + bool first = true; + foreach (SwitchSection ss in Sections){ + if (!first) + ec.CurrentBranching.CreateSibling (); + else + first = false; + + if (ss.Block.Resolve (ec) != true) + return false; + } + + + if (!got_default) + ec.CurrentBranching.CreateSibling (); + + ec.EndFlowBranching (); + ec.Switch = old_switch; + + return true; + } + + protected override bool DoEmit (EmitContext ec) + { + // Store variable for comparission purposes + LocalBuilder value = ec.ig.DeclareLocal (SwitchType); + new_expr.Emit (ec); + ec.ig.Emit (OpCodes.Stloc, value); + + ILGenerator ig = ec.ig; + + default_target = ig.DefineLabel (); + + // + // Setup the codegen context + // + Label old_end = ec.LoopEnd; + Switch old_switch = ec.Switch; + + ec.LoopEnd = ig.DefineLabel (); + ec.Switch = this; + + // Emit Code. + bool all_return; + if (SwitchType == TypeManager.string_type) + all_return = SimpleSwitchEmit (ec, value); + else + all_return = TableSwitchEmit (ec, value); + + // Restore context state. + ig.MarkLabel (ec.LoopEnd); + + // + // Restore the previous context + // + ec.LoopEnd = old_end; + ec.Switch = old_switch; + + return all_return; + } + } + + public class Lock : Statement { + Expression expr; + Statement Statement; + + public Lock (Expression expr, Statement stmt, Location l) + { + this.expr = expr; + Statement = stmt; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + expr = expr.Resolve (ec); + return Statement.Resolve (ec) && expr != null; + } + + protected override bool DoEmit (EmitContext ec) + { + Type type = expr.Type; + bool val; + + if (type.IsValueType){ + Report.Error (185, loc, "lock statement requires the expression to be " + + " a reference type (type is: `" + + TypeManager.CSharpName (type) + "'"); + return false; + } + + ILGenerator ig = ec.ig; + LocalBuilder temp = ig.DeclareLocal (type); + + expr.Emit (ec); + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Stloc, temp); + ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object); + + // try + Label end = ig.BeginExceptionBlock (); + bool old_in_try = ec.InTry; + ec.InTry = true; + Label finish = ig.DefineLabel (); + val = Statement.Emit (ec); + ec.InTry = old_in_try; + // ig.Emit (OpCodes.Leave, finish); + + ig.MarkLabel (finish); + + // finally + ig.BeginFinallyBlock (); + ig.Emit (OpCodes.Ldloc, temp); + ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object); + ig.EndExceptionBlock (); + + return val; + } + } + + public class Unchecked : Statement { + public readonly Block Block; + + public Unchecked (Block b) + { + Block = b; + } + + public override bool Resolve (EmitContext ec) + { + return Block.Resolve (ec); + } + + protected override bool DoEmit (EmitContext ec) + { + bool previous_state = ec.CheckState; + bool previous_state_const = ec.ConstantCheckState; + bool val; + + ec.CheckState = false; + ec.ConstantCheckState = false; + val = Block.Emit (ec); + ec.CheckState = previous_state; + ec.ConstantCheckState = previous_state_const; + + return val; + } + } + + public class Checked : Statement { + public readonly Block Block; + + public Checked (Block b) + { + Block = b; + } + + public override bool Resolve (EmitContext ec) + { + bool previous_state = ec.CheckState; + bool previous_state_const = ec.ConstantCheckState; + + ec.CheckState = true; + ec.ConstantCheckState = true; + bool ret = Block.Resolve (ec); + ec.CheckState = previous_state; + ec.ConstantCheckState = previous_state_const; + + return ret; + } + + protected override bool DoEmit (EmitContext ec) + { + bool previous_state = ec.CheckState; + bool previous_state_const = ec.ConstantCheckState; + bool val; + + ec.CheckState = true; + ec.ConstantCheckState = true; + val = Block.Emit (ec); + ec.CheckState = previous_state; + ec.ConstantCheckState = previous_state_const; + + return val; + } + } + + public class Unsafe : Statement { + public readonly Block Block; + + public Unsafe (Block b) + { + Block = b; + } + + public override bool Resolve (EmitContext ec) + { + bool previous_state = ec.InUnsafe; + bool val; + + ec.InUnsafe = true; + val = Block.Resolve (ec); + ec.InUnsafe = previous_state; + + return val; + } + + protected override bool DoEmit (EmitContext ec) + { + bool previous_state = ec.InUnsafe; + bool val; + + ec.InUnsafe = true; + val = Block.Emit (ec); + ec.InUnsafe = previous_state; + + return val; + } + } + + // + // Fixed statement + // + public class Fixed : Statement { + Expression type; + ArrayList declarators; + Statement statement; + Type expr_type; + FixedData[] data; + + struct FixedData { + public bool is_object; + public VariableInfo vi; + public Expression expr; + public Expression converted; + } + + public Fixed (Expression type, ArrayList decls, Statement stmt, Location l) + { + this.type = type; + declarators = decls; + statement = stmt; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + expr_type = ec.DeclSpace.ResolveType (type, false, loc); + if (expr_type == null) + return false; + + data = new FixedData [declarators.Count]; + + int i = 0; + foreach (Pair p in declarators){ + VariableInfo vi = (VariableInfo) p.First; + Expression e = (Expression) p.Second; + + vi.Number = -1; + + // + // The rules for the possible declarators are pretty wise, + // but the production on the grammar is more concise. + // + // So we have to enforce these rules here. + // + // We do not resolve before doing the case 1 test, + // because the grammar is explicit in that the token & + // is present, so we need to test for this particular case. + // + + // + // Case 1: & object. + // + if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){ + Expression child = ((Unary) e).Expr; + + vi.MakePinned (); + if (child is ParameterReference || child is LocalVariableReference){ + Report.Error ( + 213, loc, + "No need to use fixed statement for parameters or " + + "local variable declarations (address is already " + + "fixed)"); + return false; + } + + e = e.Resolve (ec); + if (e == null) + return false; + + child = ((Unary) e).Expr; + + if (!TypeManager.VerifyUnManaged (child.Type, loc)) + return false; + + data [i].is_object = true; + data [i].expr = e; + data [i].converted = null; + data [i].vi = vi; + i++; + + continue; + } + + e = e.Resolve (ec); + if (e == null) + return false; + + // + // Case 2: Array + // + if (e.Type.IsArray){ + Type array_type = e.Type.GetElementType (); + + vi.MakePinned (); + // + // Provided that array_type is unmanaged, + // + if (!TypeManager.VerifyUnManaged (array_type, loc)) + return false; + + // + // and T* is implicitly convertible to the + // pointer type given in the fixed statement. + // + ArrayPtr array_ptr = new ArrayPtr (e, loc); + + Expression converted = Expression.ConvertImplicitRequired ( + ec, array_ptr, vi.VariableType, loc); + if (converted == null) + return false; + + data [i].is_object = false; + data [i].expr = e; + data [i].converted = converted; + data [i].vi = vi; + i++; + + continue; + } + + // + // Case 3: string + // + if (e.Type == TypeManager.string_type){ + data [i].is_object = false; + data [i].expr = e; + data [i].converted = null; + data [i].vi = vi; + i++; + } + } + + return statement.Resolve (ec); + } + + protected override bool DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + bool is_ret = false; + + for (int i = 0; i < data.Length; i++) { + VariableInfo vi = data [i].vi; + + // + // Case 1: & object. + // + if (data [i].is_object) { + // + // Store pointer in pinned location + // + data [i].expr.Emit (ec); + ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + + is_ret = statement.Emit (ec); + + // Clear the pinned variable. + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Conv_U); + ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + + continue; + } + + // + // Case 2: Array + // + if (data [i].expr.Type.IsArray){ + // + // Store pointer in pinned location + // + data [i].converted.Emit (ec); + + ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + + is_ret = statement.Emit (ec); + + // Clear the pinned variable. + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Conv_U); + ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + + continue; + } + + // + // Case 3: string + // + if (data [i].expr.Type == TypeManager.string_type){ + LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type); + TypeManager.MakePinned (pinned_string); + + data [i].expr.Emit (ec); + ig.Emit (OpCodes.Stloc, pinned_string); + + Expression sptr = new StringPtr (pinned_string, loc); + Expression converted = Expression.ConvertImplicitRequired ( + ec, sptr, vi.VariableType, loc); + + if (converted == null) + continue; + + converted.Emit (ec); + ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + + is_ret = statement.Emit (ec); + + // Clear the pinned variable + ig.Emit (OpCodes.Ldnull); + ig.Emit (OpCodes.Stloc, pinned_string); + } + } + + return is_ret; + } + } + + public class Catch { + public readonly string Name; + public readonly Block Block; + public readonly Location Location; + + Expression type_expr; + Type type; + + public Catch (Expression type, string name, Block block, Location l) + { + type_expr = type; + Name = name; + Block = block; + Location = l; + } + + public Type CatchType { + get { + return type; + } + } + + public bool IsGeneral { + get { + return type_expr == null; + } + } + + public bool Resolve (EmitContext ec) + { + if (type_expr != null) { + type = ec.DeclSpace.ResolveType (type_expr, false, Location); + if (type == null) + return false; + + if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){ + Report.Error (155, Location, + "The type caught or thrown must be derived " + + "from System.Exception"); + return false; + } + } else + type = null; + + if (!Block.Resolve (ec)) + return false; + + return true; + } + } + + public class Try : Statement { + public readonly Block Fini, Block; + public readonly ArrayList Specific; + public readonly Catch General; + + // + // specific, general and fini might all be null. + // + public Try (Block block, ArrayList specific, Catch general, Block fini, Location l) + { + if (specific == null && general == null){ + Console.WriteLine ("CIR.Try: Either specific or general have to be non-null"); + } + + this.Block = block; + this.Specific = specific; + this.General = general; + this.Fini = fini; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + bool ok = true; + + ec.StartFlowBranching (FlowBranchingType.EXCEPTION, Block.StartLocation); + + Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation); + + bool old_in_try = ec.InTry; + ec.InTry = true; + + if (!Block.Resolve (ec)) + ok = false; + + ec.InTry = old_in_try; + + FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector; + + Report.Debug (1, "START OF CATCH BLOCKS", vector); + + foreach (Catch c in Specific){ + ec.CurrentBranching.CreateSibling (); + Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching); + + if (c.Name != null) { + VariableInfo vi = c.Block.GetVariableInfo (c.Name); + if (vi == null) + throw new Exception (); + + vi.Number = -1; + } + + bool old_in_catch = ec.InCatch; + ec.InCatch = true; + + if (!c.Resolve (ec)) + ok = false; + + ec.InCatch = old_in_catch; + + FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector; + + if (!current.AlwaysReturns && !current.AlwaysBreaks) + vector.AndLocals (current); + } + + Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching); + + if (General != null){ + ec.CurrentBranching.CreateSibling (); + Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching); + + bool old_in_catch = ec.InCatch; + ec.InCatch = true; + + if (!General.Resolve (ec)) + ok = false; + + ec.InCatch = old_in_catch; + + FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector; + + if (!current.AlwaysReturns && !current.AlwaysBreaks) + vector.AndLocals (current); + } + + Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching); + + if (Fini != null) { + ec.CurrentBranching.CreateSiblingForFinally (); + Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector); + + bool old_in_finally = ec.InFinally; + ec.InFinally = true; + + if (!Fini.Resolve (ec)) + ok = false; + + ec.InFinally = old_in_finally; + } + + FlowReturns returns = ec.EndFlowBranching (); + + FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector; + + Report.Debug (1, "END OF FINALLY", ec.CurrentBranching, returns, vector, f_vector); + + if ((returns == FlowReturns.SOMETIMES) || (returns == FlowReturns.ALWAYS)) { + ec.CurrentBranching.CheckOutParameters (f_vector.Parameters, loc); + } + + ec.CurrentBranching.CurrentUsageVector.Or (vector); + + Report.Debug (1, "END OF TRY", ec.CurrentBranching); + + return ok; + } + + protected override bool DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + Label end; + Label finish = ig.DefineLabel ();; + bool returns; + + ec.TryCatchLevel++; + end = ig.BeginExceptionBlock (); + bool old_in_try = ec.InTry; + ec.InTry = true; + returns = Block.Emit (ec); + ec.InTry = old_in_try; + + // + // System.Reflection.Emit provides this automatically: + // ig.Emit (OpCodes.Leave, finish); + + bool old_in_catch = ec.InCatch; + ec.InCatch = true; + DeclSpace ds = ec.DeclSpace; + + foreach (Catch c in Specific){ + VariableInfo vi; + + ig.BeginCatchBlock (c.CatchType); + + if (c.Name != null){ + vi = c.Block.GetVariableInfo (c.Name); + if (vi == null) + throw new Exception ("Variable does not exist in this block"); + + ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + } else + ig.Emit (OpCodes.Pop); + + if (!c.Block.Emit (ec)) + returns = false; + } + + if (General != null){ + ig.BeginCatchBlock (TypeManager.object_type); + ig.Emit (OpCodes.Pop); + if (!General.Block.Emit (ec)) + returns = false; + } + ec.InCatch = old_in_catch; + + ig.MarkLabel (finish); + if (Fini != null){ + ig.BeginFinallyBlock (); + bool old_in_finally = ec.InFinally; + ec.InFinally = true; + Fini.Emit (ec); + ec.InFinally = old_in_finally; + } + + ig.EndExceptionBlock (); + ec.TryCatchLevel--; + + if (!returns || ec.InTry || ec.InCatch) + return returns; + + // Unfortunately, System.Reflection.Emit automatically emits a leave + // to the end of the finally block. This is a problem if `returns' + // is true since we may jump to a point after the end of the method. + // As a workaround, emit an explicit ret here. + + if (ec.ReturnType != null) + ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ()); + ec.ig.Emit (OpCodes.Ret); + + return true; + } + } + + public class Using : Statement { + object expression_or_block; + Statement Statement; + ArrayList var_list; + Expression expr; + Type expr_type; + Expression conv; + Expression [] converted_vars; + ExpressionStatement [] assign; + + public Using (object expression_or_block, Statement stmt, Location l) + { + this.expression_or_block = expression_or_block; + Statement = stmt; + loc = l; + } + + // + // Resolves for the case of using using a local variable declaration. + // + bool ResolveLocalVariableDecls (EmitContext ec) + { + bool need_conv = false; + expr_type = ec.DeclSpace.ResolveType (expr, false, loc); + int i = 0; + + if (expr_type == null) + return false; + + // + // The type must be an IDisposable or an implicit conversion + // must exist. + // + converted_vars = new Expression [var_list.Count]; + assign = new ExpressionStatement [var_list.Count]; + if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){ + foreach (DictionaryEntry e in var_list){ + Expression var = (Expression) e.Key; + + var = var.ResolveLValue (ec, new EmptyExpression ()); + if (var == null) + return false; + + converted_vars [i] = Expression.ConvertImplicitRequired ( + ec, var, TypeManager.idisposable_type, loc); + + if (converted_vars [i] == null) + return false; + i++; + } + need_conv = true; + } + + i = 0; + foreach (DictionaryEntry e in var_list){ + LocalVariableReference var = (LocalVariableReference) e.Key; + Expression new_expr = (Expression) e.Value; + Expression a; + + a = new Assign (var, new_expr, loc); + a = a.Resolve (ec); + if (a == null) + return false; + + if (!need_conv) + converted_vars [i] = var; + assign [i] = (ExpressionStatement) a; + i++; + } + + return true; + } + + bool ResolveExpression (EmitContext ec) + { + if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){ + conv = Expression.ConvertImplicitRequired ( + ec, expr, TypeManager.idisposable_type, loc); + + if (conv == null) + return false; + } + + return true; + } + + // + // Emits the code for the case of using using a local variable declaration. + // + bool EmitLocalVariableDecls (EmitContext ec) + { + ILGenerator ig = ec.ig; + int i = 0; + + bool old_in_try = ec.InTry; + ec.InTry = true; + for (i = 0; i < assign.Length; i++) { + assign [i].EmitStatement (ec); + + ig.BeginExceptionBlock (); + } + Statement.Emit (ec); + ec.InTry = old_in_try; + + bool old_in_finally = ec.InFinally; + ec.InFinally = true; + var_list.Reverse (); + foreach (DictionaryEntry e in var_list){ + LocalVariableReference var = (LocalVariableReference) e.Key; + Label skip = ig.DefineLabel (); + i--; + + ig.BeginFinallyBlock (); + + var.Emit (ec); + ig.Emit (OpCodes.Brfalse, skip); + converted_vars [i].Emit (ec); + ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); + ig.MarkLabel (skip); + ig.EndExceptionBlock (); + } + ec.InFinally = old_in_finally; + + return false; + } + + bool EmitExpression (EmitContext ec) + { + // + // Make a copy of the expression and operate on that. + // + ILGenerator ig = ec.ig; + LocalBuilder local_copy = ig.DeclareLocal (expr_type); + if (conv != null) + conv.Emit (ec); + else + expr.Emit (ec); + ig.Emit (OpCodes.Stloc, local_copy); + + bool old_in_try = ec.InTry; + ec.InTry = true; + ig.BeginExceptionBlock (); + Statement.Emit (ec); + ec.InTry = old_in_try; + + Label skip = ig.DefineLabel (); + bool old_in_finally = ec.InFinally; + ig.BeginFinallyBlock (); + ig.Emit (OpCodes.Ldloc, local_copy); + ig.Emit (OpCodes.Brfalse, skip); + ig.Emit (OpCodes.Ldloc, local_copy); + ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); + ig.MarkLabel (skip); + ec.InFinally = old_in_finally; + ig.EndExceptionBlock (); + + return false; + } + + public override bool Resolve (EmitContext ec) + { + if (expression_or_block is DictionaryEntry){ + expr = (Expression) ((DictionaryEntry) expression_or_block).Key; + var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value; + + if (!ResolveLocalVariableDecls (ec)) + return false; + + } else if (expression_or_block is Expression){ + expr = (Expression) expression_or_block; + + expr = expr.Resolve (ec); + if (expr == null) + return false; + + expr_type = expr.Type; + + if (!ResolveExpression (ec)) + return false; + } + + return Statement.Resolve (ec); + } + + protected override bool DoEmit (EmitContext ec) + { + if (expression_or_block is DictionaryEntry) + return EmitLocalVariableDecls (ec); + else if (expression_or_block is Expression) + return EmitExpression (ec); + + return false; + } + } + + /// <summary> + /// Implementation of the foreach C# statement + /// </summary> + public class Foreach : Statement { + Expression type; + LocalVariableReference variable; + Expression expr; + Statement statement; + ForeachHelperMethods hm; + Expression empty, conv; + Type array_type, element_type; + Type var_type; + + public Foreach (Expression type, LocalVariableReference var, Expression expr, + Statement stmt, Location l) + { + this.type = type; + this.variable = var; + this.expr = expr; + statement = stmt; + loc = l; + } + + public override bool Resolve (EmitContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return false; + + var_type = ec.DeclSpace.ResolveType (type, false, loc); + if (var_type == null) + return false; + + // + // We need an instance variable. Not sure this is the best + // way of doing this. + // + // FIXME: When we implement propertyaccess, will those turn + // out to return values in ExprClass? I think they should. + // + if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value || + expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){ + error1579 (expr.Type); + return false; + } + + if (expr.Type.IsArray) { + array_type = expr.Type; + element_type = array_type.GetElementType (); + + empty = new EmptyExpression (element_type); + } else { + hm = ProbeCollectionType (ec, expr.Type); + if (hm == null){ + error1579 (expr.Type); + return false; + } + + array_type = expr.Type; + element_type = hm.element_type; + + empty = new EmptyExpression (hm.element_type); + } + + ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc); + ec.CurrentBranching.CreateSibling (); + + // + // + // FIXME: maybe we can apply the same trick we do in the + // array handling to avoid creating empty and conv in some cases. + // + // Although it is not as important in this case, as the type + // will not likely be object (what the enumerator will return). + // + conv = Expression.ConvertExplicit (ec, empty, var_type, loc); + if (conv == null) + return false; + + if (variable.ResolveLValue (ec, empty) == null) + return false; + + if (!statement.Resolve (ec)) + return false; + + FlowReturns returns = ec.EndFlowBranching (); + + return true; + } + + // + // Retrieves a `public bool MoveNext ()' method from the Type `t' + // + static MethodInfo FetchMethodMoveNext (Type t) + { + MemberList move_next_list; + + move_next_list = TypeContainer.FindMembers ( + t, MemberTypes.Method, + BindingFlags.Public | BindingFlags.Instance, + Type.FilterName, "MoveNext"); + if (move_next_list.Count == 0) + return null; + + foreach (MemberInfo m in move_next_list){ + MethodInfo mi = (MethodInfo) m; + Type [] args; + + args = TypeManager.GetArgumentTypes (mi); + if (args != null && args.Length == 0){ + if (mi.ReturnType == TypeManager.bool_type) + return mi; + } + } + return null; + } + + // + // Retrieves a `public T get_Current ()' method from the Type `t' + // + static MethodInfo FetchMethodGetCurrent (Type t) + { + MemberList move_next_list; + + move_next_list = TypeContainer.FindMembers ( + t, MemberTypes.Method, + BindingFlags.Public | BindingFlags.Instance, + Type.FilterName, "get_Current"); + if (move_next_list.Count == 0) + return null; + + foreach (MemberInfo m in move_next_list){ + MethodInfo mi = (MethodInfo) m; + Type [] args; + + args = TypeManager.GetArgumentTypes (mi); + if (args != null && args.Length == 0) + return mi; + } + return null; + } + + // + // This struct records the helper methods used by the Foreach construct + // + class ForeachHelperMethods { + public EmitContext ec; + public MethodInfo get_enumerator; + public MethodInfo move_next; + public MethodInfo get_current; + public Type element_type; + public Type enumerator_type; + public bool is_disposable; + + public ForeachHelperMethods (EmitContext ec) + { + this.ec = ec; + this.element_type = TypeManager.object_type; + this.enumerator_type = TypeManager.ienumerator_type; + this.is_disposable = true; + } + } + + static bool GetEnumeratorFilter (MemberInfo m, object criteria) + { + if (m == null) + return false; + + if (!(m is MethodInfo)) + return false; + + if (m.Name != "GetEnumerator") + return false; + + MethodInfo mi = (MethodInfo) m; + Type [] args = TypeManager.GetArgumentTypes (mi); + if (args != null){ + if (args.Length != 0) + return false; + } + ForeachHelperMethods hm = (ForeachHelperMethods) criteria; + EmitContext ec = hm.ec; + + // + // Check whether GetEnumerator is accessible to us + // + MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask; + + Type declaring = mi.DeclaringType; + if (prot == MethodAttributes.Private){ + if (declaring != ec.ContainerType) + return false; + } else if (prot == MethodAttributes.FamANDAssem){ + // If from a different assembly, false + if (!(mi is MethodBuilder)) + return false; + // + // Are we being invoked from the same class, or from a derived method? + // + if (ec.ContainerType != declaring){ + if (!ec.ContainerType.IsSubclassOf (declaring)) + return false; + } + } else if (prot == MethodAttributes.FamORAssem){ + if (!(mi is MethodBuilder || + ec.ContainerType == declaring || + ec.ContainerType.IsSubclassOf (declaring))) + return false; + } if (prot == MethodAttributes.Family){ + if (!(ec.ContainerType == declaring || + ec.ContainerType.IsSubclassOf (declaring))) + return false; + } + + // + // Ok, we can access it, now make sure that we can do something + // with this `GetEnumerator' + // + + if (mi.ReturnType == TypeManager.ienumerator_type || + TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) || + (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) { + hm.move_next = TypeManager.bool_movenext_void; + hm.get_current = TypeManager.object_getcurrent_void; + return true; + } + + // + // Ok, so they dont return an IEnumerable, we will have to + // find if they support the GetEnumerator pattern. + // + Type return_type = mi.ReturnType; + + hm.move_next = FetchMethodMoveNext (return_type); + if (hm.move_next == null) + return false; + hm.get_current = FetchMethodGetCurrent (return_type); + if (hm.get_current == null) + return false; + + hm.element_type = hm.get_current.ReturnType; + hm.enumerator_type = return_type; + hm.is_disposable = TypeManager.ImplementsInterface ( + hm.enumerator_type, TypeManager.idisposable_type); + + return true; + } + + /// <summary> + /// This filter is used to find the GetEnumerator method + /// on which IEnumerator operates + /// </summary> + static MemberFilter FilterEnumerator; + + static Foreach () + { + FilterEnumerator = new MemberFilter (GetEnumeratorFilter); + } + + void error1579 (Type t) + { + Report.Error (1579, loc, + "foreach statement cannot operate on variables of type `" + + t.FullName + "' because that class does not provide a " + + " GetEnumerator method or it is inaccessible"); + } + + static bool TryType (Type t, ForeachHelperMethods hm) + { + MemberList mi; + + mi = TypeContainer.FindMembers (t, MemberTypes.Method, + BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance, + FilterEnumerator, hm); + + if (mi.Count == 0) + return false; + + hm.get_enumerator = (MethodInfo) mi [0]; + return true; + } + + // + // Looks for a usable GetEnumerator in the Type, and if found returns + // the three methods that participate: GetEnumerator, MoveNext and get_Current + // + ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t) + { + ForeachHelperMethods hm = new ForeachHelperMethods (ec); + + if (TryType (t, hm)) + return hm; + + // + // Now try to find the method in the interfaces + // + while (t != null){ + Type [] ifaces = t.GetInterfaces (); + + foreach (Type i in ifaces){ + if (TryType (i, hm)) + return hm; + } + + // + // Since TypeBuilder.GetInterfaces only returns the interface + // types for this type, we have to keep looping, but once + // we hit a non-TypeBuilder (ie, a Type), then we know we are + // done, because it returns all the types + // + if ((t is TypeBuilder)) + t = t.BaseType; + else + break; + } + + return null; + } + + // + // FIXME: possible optimization. + // We might be able to avoid creating `empty' if the type is the sam + // + bool EmitCollectionForeach (EmitContext ec) + { + ILGenerator ig = ec.ig; + LocalBuilder enumerator, disposable; + + enumerator = ig.DeclareLocal (hm.enumerator_type); + if (hm.is_disposable) + disposable = ig.DeclareLocal (TypeManager.idisposable_type); + else + disposable = null; + + // + // Instantiate the enumerator + // + if (expr.Type.IsValueType){ + if (expr is IMemoryLocation){ + IMemoryLocation ml = (IMemoryLocation) expr; + + ml.AddressOf (ec, AddressOp.Load); + } else + throw new Exception ("Expr " + expr + " of type " + expr.Type + + " does not implement IMemoryLocation"); + ig.Emit (OpCodes.Call, hm.get_enumerator); + } else { + expr.Emit (ec); + ig.Emit (OpCodes.Callvirt, hm.get_enumerator); + } + ig.Emit (OpCodes.Stloc, enumerator); + + // + // Protect the code in a try/finalize block, so that + // if the beast implement IDisposable, we get rid of it + // + Label l; + bool old_in_try = ec.InTry; + + if (hm.is_disposable) { + l = ig.BeginExceptionBlock (); + ec.InTry = true; + } + + Label end_try = ig.DefineLabel (); + + ig.MarkLabel (ec.LoopBegin); + ig.Emit (OpCodes.Ldloc, enumerator); + ig.Emit (OpCodes.Callvirt, hm.move_next); + ig.Emit (OpCodes.Brfalse, end_try); + ig.Emit (OpCodes.Ldloc, enumerator); + ig.Emit (OpCodes.Callvirt, hm.get_current); + variable.EmitAssign (ec, conv); + statement.Emit (ec); + ig.Emit (OpCodes.Br, ec.LoopBegin); + ig.MarkLabel (end_try); + ec.InTry = old_in_try; + + // The runtime provides this for us. + // ig.Emit (OpCodes.Leave, end); + + // + // Now the finally block + // + if (hm.is_disposable) { + Label end_finally = ig.DefineLabel (); + bool old_in_finally = ec.InFinally; + ec.InFinally = true; + ig.BeginFinallyBlock (); + + ig.Emit (OpCodes.Ldloc, enumerator); + ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type); + ig.Emit (OpCodes.Stloc, disposable); + ig.Emit (OpCodes.Ldloc, disposable); + ig.Emit (OpCodes.Brfalse, end_finally); + ig.Emit (OpCodes.Ldloc, disposable); + ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); + ig.MarkLabel (end_finally); + ec.InFinally = old_in_finally; + + // The runtime generates this anyways. + // ig.Emit (OpCodes.Endfinally); + + ig.EndExceptionBlock (); + } + + ig.MarkLabel (ec.LoopEnd); + return false; + } + + // + // FIXME: possible optimization. + // We might be able to avoid creating `empty' if the type is the sam + // + bool EmitArrayForeach (EmitContext ec) + { + int rank = array_type.GetArrayRank (); + ILGenerator ig = ec.ig; + + LocalBuilder copy = ig.DeclareLocal (array_type); + + // + // Make our copy of the array + // + expr.Emit (ec); + ig.Emit (OpCodes.Stloc, copy); + + if (rank == 1){ + LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type); + + Label loop, test; + + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Stloc, counter); + test = ig.DefineLabel (); + ig.Emit (OpCodes.Br, test); + + loop = ig.DefineLabel (); + ig.MarkLabel (loop); + + ig.Emit (OpCodes.Ldloc, copy); + ig.Emit (OpCodes.Ldloc, counter); + ArrayAccess.EmitLoadOpcode (ig, var_type); + + variable.EmitAssign (ec, conv); + + statement.Emit (ec); + + ig.MarkLabel (ec.LoopBegin); + ig.Emit (OpCodes.Ldloc, counter); + ig.Emit (OpCodes.Ldc_I4_1); + ig.Emit (OpCodes.Add); + ig.Emit (OpCodes.Stloc, counter); + + ig.MarkLabel (test); + ig.Emit (OpCodes.Ldloc, counter); + ig.Emit (OpCodes.Ldloc, copy); + ig.Emit (OpCodes.Ldlen); + ig.Emit (OpCodes.Conv_I4); + ig.Emit (OpCodes.Blt, loop); + } else { + LocalBuilder [] dim_len = new LocalBuilder [rank]; + LocalBuilder [] dim_count = new LocalBuilder [rank]; + Label [] loop = new Label [rank]; + Label [] test = new Label [rank]; + int dim; + + for (dim = 0; dim < rank; dim++){ + dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type); + dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type); + test [dim] = ig.DefineLabel (); + loop [dim] = ig.DefineLabel (); + } + + for (dim = 0; dim < rank; dim++){ + ig.Emit (OpCodes.Ldloc, copy); + IntLiteral.EmitInt (ig, dim); + ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int); + ig.Emit (OpCodes.Stloc, dim_len [dim]); + } + + for (dim = 0; dim < rank; dim++){ + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Stloc, dim_count [dim]); + ig.Emit (OpCodes.Br, test [dim]); + ig.MarkLabel (loop [dim]); + } + + ig.Emit (OpCodes.Ldloc, copy); + for (dim = 0; dim < rank; dim++) + ig.Emit (OpCodes.Ldloc, dim_count [dim]); + + // + // FIXME: Maybe we can cache the computation of `get'? + // + Type [] args = new Type [rank]; + MethodInfo get; + + for (int i = 0; i < rank; i++) + args [i] = TypeManager.int32_type; + + ModuleBuilder mb = CodeGen.ModuleBuilder; + get = mb.GetArrayMethod ( + array_type, "Get", + CallingConventions.HasThis| CallingConventions.Standard, + var_type, args); + ig.Emit (OpCodes.Call, get); + variable.EmitAssign (ec, conv); + statement.Emit (ec); + ig.MarkLabel (ec.LoopBegin); + for (dim = rank - 1; dim >= 0; dim--){ + ig.Emit (OpCodes.Ldloc, dim_count [dim]); + ig.Emit (OpCodes.Ldc_I4_1); + ig.Emit (OpCodes.Add); + ig.Emit (OpCodes.Stloc, dim_count [dim]); + + ig.MarkLabel (test [dim]); + ig.Emit (OpCodes.Ldloc, dim_count [dim]); + ig.Emit (OpCodes.Ldloc, dim_len [dim]); + ig.Emit (OpCodes.Blt, loop [dim]); + } + } + ig.MarkLabel (ec.LoopEnd); + + return false; + } + + protected override bool DoEmit (EmitContext ec) + { + bool ret_val; + + ILGenerator ig = ec.ig; + + Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd; + bool old_inloop = ec.InLoop; + int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel; + ec.LoopBegin = ig.DefineLabel (); + ec.LoopEnd = ig.DefineLabel (); + ec.InLoop = true; + ec.LoopBeginTryCatchLevel = ec.TryCatchLevel; + + if (hm != null) + ret_val = EmitCollectionForeach (ec); + else + ret_val = EmitArrayForeach (ec); + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + ec.InLoop = old_inloop; + ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level; + + return ret_val; + } + } +} diff --git a/mcs/mbas/statementCollection.cs b/mcs/mbas/statementCollection.cs new file mode 100644 index 00000000000..9e5141dfdc6 --- /dev/null +++ b/mcs/mbas/statementCollection.cs @@ -0,0 +1,166 @@ +// +// System.CodeDOM CodeStatementCollection Class implementation +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. +// + +namespace Mono.CSharp { + + using System.Collections; + using System; + + public class StatementCollection : IList, ICollection, IEnumerable { + + ArrayList statements; + + // + // Constructors + // + public StatementCollection () + { + statements = new ArrayList (); + } + + // + // Properties + // + public int Count { + get { + return statements.Count; + } + } + + // + // Methods + // + public void Add (Statement value) + { + statements.Add (value); + } + + public void AddRange (Statement [] values) + { + foreach (Statement ca in values) + statements.Add (ca); + + } + + public void Clear () + { + statements.Clear (); + } + + private class Enumerator : IEnumerator { + private StatementCollection collection; + private int currentIndex = -1; + + internal Enumerator (StatementCollection collection) + { + this.collection = collection; + } + + public object Current { + get { + if (currentIndex == collection.Count) + throw new InvalidOperationException (); + return collection [currentIndex]; + } + } + + public bool MoveNext () + { + if (currentIndex > collection.Count) + throw new InvalidOperationException (); + return ++currentIndex < collection.Count; + } + + public void Reset () + { + currentIndex = -1; + } + } + + public IEnumerator GetEnumerator () + { + return new StatementCollection.Enumerator (this); + } + + // + // IList method implementations + // + public int Add (object value) + { + return statements.Add (value); + } + + public bool Contains (Object value) + { + return statements.Contains (value); + } + + public int IndexOf (Object value) + { + return statements.IndexOf (value); + } + + public void Insert (int index, Object value) + { + statements [index] = value; + } + + public object this[int index] { + get { + return statements [index]; + } + + set { + statements [index] = value; + } + } + + public void Remove (object value) + { + statements.Remove (value); + } + + public void RemoveAt (int index) + { + statements.RemoveAt (index); + } + + // + // ICollection method implementations + // + public void CopyTo (Array array, int index) + { + statements.CopyTo (array, index); + } + + public object SyncRoot { + get { + return statements.SyncRoot; + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + public bool IsSynchronized { + get { + return statements.IsSynchronized; + } + } + + public bool IsFixedSize { + get { + return false; + } + } + } +} diff --git a/mcs/mbas/support.cs b/mcs/mbas/support.cs new file mode 100644 index 00000000000..3db32ac9da6 --- /dev/null +++ b/mcs/mbas/support.cs @@ -0,0 +1,258 @@ +// +// support.cs: Support routines to work around the fact that System.Reflection.Emit +// can not introspect types that are being constructed +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// + +using System; +using System.Text; +using System.Reflection; +using System.Collections; +using System.Reflection.Emit; +using System.Globalization; + +namespace Mono.CSharp { + + public interface ParameterData { + Type ParameterType (int pos); + int Count { get; } + string ParameterName (int pos); + string ParameterDesc (int pos); + Parameter.Modifier ParameterModifier (int pos); + } + + public class ReflectionParameters : ParameterData { + ParameterInfo [] pi; + bool last_arg_is_params = false; + + public ReflectionParameters (ParameterInfo [] pi) + { + object [] attrs; + + this.pi = pi; + + int count = pi.Length-1; + + if (count >= 0) { + attrs = pi [count].GetCustomAttributes (TypeManager.param_array_type, true); + + if (attrs == null) + return; + + if (attrs.Length == 0) + return; + + last_arg_is_params = true; + } + } + + public Type ParameterType (int pos) + { + if (last_arg_is_params && pos >= pi.Length - 1) + return pi [pi.Length - 1].ParameterType; + else + return pi [pos].ParameterType; + } + + public string ParameterName (int pos) + { + if (last_arg_is_params && pos >= pi.Length - 1) + return pi [pi.Length - 1].Name; + else + return pi [pos].Name; + } + + public string ParameterDesc (int pos) + { + StringBuilder sb = new StringBuilder (); + + if (pi [pos].IsOut) + sb.Append ("out "); + + if (pi [pos].IsIn) + sb.Append ("in "); + + if (pos >= pi.Length - 1 && last_arg_is_params) + sb.Append ("params "); + + sb.Append (TypeManager.CSharpName (ParameterType (pos))); + + return sb.ToString (); + + } + + public Parameter.Modifier ParameterModifier (int pos) + { + int len = pi.Length; + + if (pos >= len - 1) + if (last_arg_is_params) + return Parameter.Modifier.PARAMS; + + Type t = pi [pos].ParameterType; + if (t.IsByRef) + return Parameter.Modifier.ISBYREF; + + return Parameter.Modifier.NONE; + } + + public int Count { + get { + return pi.Length; + } + } + + } + + public class InternalParameters : ParameterData { + Type [] param_types; + + public readonly Parameters Parameters; + + public InternalParameters (Type [] param_types, Parameters parameters) + { + this.param_types = param_types; + this.Parameters = parameters; + } + + public InternalParameters (DeclSpace ds, Parameters parameters) + : this (parameters.GetParameterInfo (ds), parameters) + { + } + + public int Count { + get { + if (param_types == null) + return 0; + + return param_types.Length; + } + } + + public Type ParameterType (int pos) + { + if (param_types == null) + return null; + + Parameter [] fixed_pars = Parameters.FixedParameters; + if (fixed_pars != null){ + int len = fixed_pars.Length; + if (pos < len) + return Parameters.FixedParameters [pos].ParameterType; + else + return Parameters.ArrayParameter.ParameterType; + } else + return Parameters.ArrayParameter.ParameterType; + } + + public string ParameterName (int pos) + { + Parameter p; + + if (pos >= Parameters.FixedParameters.Length) + p = Parameters.ArrayParameter; + else + p = Parameters.FixedParameters [pos]; + + return p.Name; + } + + public string ParameterDesc (int pos) + { + string tmp = String.Empty; + Parameter p; + + if (pos >= Parameters.FixedParameters.Length) + p = Parameters.ArrayParameter; + else + p = Parameters.FixedParameters [pos]; + + if (p.ModFlags == Parameter.Modifier.REF) + tmp = "ref "; + else if (p.ModFlags == Parameter.Modifier.OUT) + tmp = "out "; + else if (p.ModFlags == Parameter.Modifier.PARAMS) + tmp = "params "; + + Type t = ParameterType (pos); + + return tmp + TypeManager.CSharpName (t); + } + + public Parameter.Modifier ParameterModifier (int pos) + { + Parameter.Modifier mod; + + if (Parameters.FixedParameters == null) { + if (Parameters.ArrayParameter != null) + mod = Parameters.ArrayParameter.ModFlags; + else + mod = Parameter.Modifier.NONE; + } else if (pos >= Parameters.FixedParameters.Length) + mod = Parameters.ArrayParameter.ModFlags; + else + mod = Parameters.FixedParameters [pos].ModFlags; + + if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0) + mod |= Parameter.Modifier.ISBYREF; + + return mod; + } + + } + + class PtrHashtable : Hashtable { + class PtrComparer : IComparer { + public int Compare (object x, object y) + { + if (x == y) + return 0; + else + return 1; + } + } + + public PtrHashtable () + { + comparer = new PtrComparer (); + } + } + + // + // Compares member infos based on their name and + // also allows one argument to be a string + // + class MemberInfoCompare : IComparer { + + public int Compare (object a, object b) + { + if (a == null || b == null){ + Console.WriteLine ("Invalid information passed"); + throw new Exception (); + } + + if (a is string) + return String.Compare ((string) a, ((MemberInfo)b).Name); + + if (b is string) + return String.Compare (((MemberInfo)a).Name, (string) b); + + return String.Compare (((MemberInfo)a).Name, ((MemberInfo)b).Name); + } + } + + struct Pair { + public object First; + public object Second; + + public Pair (object f, object s) + { + First = f; + Second = s; + } + } +} diff --git a/mcs/mbas/testmbas/.cvsignore b/mcs/mbas/testmbas/.cvsignore new file mode 100644 index 00000000000..656158779db --- /dev/null +++ b/mcs/mbas/testmbas/.cvsignore @@ -0,0 +1,6 @@ +*.exe
+*.pdb
+*.vbproj
+*.vbproj.user
+*~ +.*~ diff --git a/mcs/mbas/testmbas/AssemblyInfo.vb b/mcs/mbas/testmbas/AssemblyInfo.vb new file mode 100644 index 00000000000..f600c94bc01 --- /dev/null +++ b/mcs/mbas/testmbas/AssemblyInfo.vb @@ -0,0 +1,31 @@ +Imports System.Reflection
+Imports System.Runtime.InteropServices
+
+' General Information about an assembly is controlled through the following
+' set of attributes. Change these attribute values to modify the information
+' associated with an assembly.
+
+' Review the values of the assembly attributes
+
+<Assembly: AssemblyTitle("")>
+<Assembly: AssemblyDescription("")>
+<Assembly: AssemblyCompany("")>
+<Assembly: AssemblyProduct("")>
+<Assembly: AssemblyCopyright("")>
+<Assembly: AssemblyTrademark("")>
+<Assembly: CLSCompliant(True)>
+
+'The following GUID is for the ID of the typelib if this project is exposed to COM
+<Assembly: Guid("EA89BB68-1010-427C-A8B5-917940328A4B")>
+
+' Version information for an assembly consists of the following four values:
+'
+' Major Version
+' Minor Version
+' Build Number
+' Revision
+'
+' You can specify all the values or you can default the Build and Revision Numbers
+' by using the '*' as shown below:
+
+<Assembly: AssemblyVersion("1.0.*")>
diff --git a/mcs/mbas/testmbas/WriteOK.vb b/mcs/mbas/testmbas/WriteOK.vb new file mode 100644 index 00000000000..ee17f11069e --- /dev/null +++ b/mcs/mbas/testmbas/WriteOK.vb @@ -0,0 +1,19 @@ +Imports System + +Module WriteOK
+
+ Sub Main()
+ REM Testing old-fashioned comments
+ Console.WriteLine("OK!") ' Simple comments
+ End Sub
+
+End Module
+
+Module WriteOK2
+
+ Sub [Sub]() ' Escaped identifier
+ Console.WriteLine("Sub:OK!")
+ End Sub
+
+End Module
+
diff --git a/mcs/mbas/testmbas/ctest.vb b/mcs/mbas/testmbas/ctest.vb new file mode 100644 index 00000000000..e3457f4cb08 --- /dev/null +++ b/mcs/mbas/testmbas/ctest.vb @@ -0,0 +1,33 @@ +Imports System.Windows.Forms
+Imports System.Drawing
+
+Delegate Sub d_b1_onClick (s, evt)
+
+Public Class TestClass
+ Inherits Form
+
+Public button1 as Button
+
+Public Sub PutButtonOnForm()
+
+End Sub
+
+Public Sub b1_onClick_handler (a,b) Handles button1.Click
+
+End Sub
+
+Public Sub New(ctest_a as string)
+ Dim b1_onClick as d_b1_onClick
+
+ b1_onClick = New d_b1_onClick(AddressOf Me.b1_onClick_handler)
+
+ Me.button1 = New Button()
+ Me.button1.Text = "Click Me"
+ Me.button1.Location = New Point(100, 10)
+ 'Me.Click = b1_onClick
+ Me.Controls.Add(Me.button1)
+
+ System.Console.WriteLine (ctest_a)
+End Sub
+
+End Class
diff --git a/mcs/mbas/testmbas/filelist b/mcs/mbas/testmbas/filelist new file mode 100644 index 00000000000..d4d01a17442 --- /dev/null +++ b/mcs/mbas/testmbas/filelist @@ -0,0 +1 @@ +--main WriteOK testmbas\WriteOk.vb
\ No newline at end of file diff --git a/mcs/mbas/testmbas/gtk.vb b/mcs/mbas/testmbas/gtk.vb new file mode 100644 index 00000000000..9196053df67 --- /dev/null +++ b/mcs/mbas/testmbas/gtk.vb @@ -0,0 +1,19 @@ +Imports System +Imports Gtk + +Module GtkTest + + Sub Main() + DIM Win as Window + DIM Btn as Button + + Application.Init () + Win = new Window ("VB Gtk+ Hello World") + Btn = new Button ("Click Me!") + Win.Add (Btn) + Win.ShowAll() + Application.Run () + End Sub + +End Module + diff --git a/mcs/mbas/testmbas/test.vb b/mcs/mbas/testmbas/test.vb new file mode 100644 index 00000000000..a2cd4e2dfdf --- /dev/null +++ b/mcs/mbas/testmbas/test.vb @@ -0,0 +1,43 @@ +Imports System.Windows.Forms + +Module Test + +Sub MySub(b) + System.Console.WriteLine (b) +End Sub + +Sub Main() + Dim a as Integer + Dim fgh as Integer + Dim btn as Button + Dim frm as TestClass + + System.Console.WriteLine ("This var ") + System.Console.WriteLine ("contains ") + a = (1 + 2) * 144 + + System.Console.WriteLine (a) + a = 1 + If (a > 2) Then + System.Console.WriteLine ("Greater than 2") + Else + System.Console.WriteLine ("Less than 2") + End If + + a = 3 + If (a > 2) Then + System.Console.WriteLine ("Greater than 2") + Else + System.Console.WriteLine ("Less than 2") + End If + + frm = new TestClass("a") + frm.Width = 300 + frm.Height = 80 + frm.PutButtonOnForm() + frm.button1.Text = "AAA" + frm.ShowDialog() + MySub("parameter!!!!!!") +End Sub + +End Module diff --git a/mcs/mbas/testmbas/test2.vb b/mcs/mbas/testmbas/test2.vb new file mode 100644 index 00000000000..ba84bbb471b --- /dev/null +++ b/mcs/mbas/testmbas/test2.vb @@ -0,0 +1,14 @@ +Module Test + +Sub Main() + Dim a, b as Object + Dim t as Boolean + + a = "abc" + b = "def" + t = (a < b) + System.Console.Write ("a < b : ") + System.Console.WriteLine (t) +End Sub + +End Module diff --git a/mcs/mbas/testmbas/test3.vb b/mcs/mbas/testmbas/test3.vb new file mode 100644 index 00000000000..a0f9e388ff5 --- /dev/null +++ b/mcs/mbas/testmbas/test3.vb @@ -0,0 +1,51 @@ +Imports System.Windows.Forms
+Imports System.Drawing
+
+Module Test
+
+Sub Main()
+ ' Create a new instance of the form.
+ Dim form1 As Form
+ Dim button1 As Button
+ Dim button2 As Button
+
+ form1 = New Form()
+ button1 = New Button()
+ button2 = New Button()
+
+ ' Set the text of button1 to "OK".
+ button1.Text = "OK"
+ ' Set the position of the button on the form.
+ button1.Location = New Point(10, 10)
+ ' Set the text of button2 to "Cancel".
+ button2.Text = "Cancel"
+ ' Set the position of the button based on the location of button1.
+ button2.Location = New Point(button1.Left, button1.Height + button1.Top + 10)
+ ' Set the caption bar text of the form.
+ form1.Text = "My Dialog Box"
+ ' Display a help button on the form.
+ form1.HelpButton = True
+
+ ' Define the border style of the form to a dialog box.
+ form1.FormBorderStyle = FormBorderStyle.FixedDialog
+ ' Set the MaximizeBox to false to remove the maximize box.
+ form1.MaximizeBox = False
+ ' Set the MinimizeBox to false to remove the minimize box.
+ form1.MinimizeBox = False
+ ' Set the accept button of the form to button1.
+ form1.AcceptButton = button1
+ ' Set the cancel button of the form to button2.
+ form1.CancelButton = button2
+ ' Set the start position of the form to the center of the screen.
+ form1.StartPosition = FormStartPosition.CenterScreen
+
+ ' Add button1 to the form.
+ form1.Controls.Add(button1)
+ ' Add button2 to the form.
+ form1.Controls.Add(button2)
+
+ ' Display the form as a modal dialog box.
+ form1.ShowDialog()
+End Sub
+
+End Module
diff --git a/mcs/mbas/testmbas/test4.vb b/mcs/mbas/testmbas/test4.vb new file mode 100644 index 00000000000..926a3d7c586 --- /dev/null +++ b/mcs/mbas/testmbas/test4.vb @@ -0,0 +1,16 @@ +Delegate Sub SubDelegate() + +Module Test + +Sub MySub() + System.Console.WriteLine ("In MySub") +End Sub + +Sub Main() + Dim dsub as SubDelegate + + dsub = New SubDelegate (AddressOf MySub) + dsub() +End Sub + +End Module diff --git a/mcs/mbas/testmbas/test5.vb b/mcs/mbas/testmbas/test5.vb new file mode 100644 index 00000000000..34f8d53bd29 --- /dev/null +++ b/mcs/mbas/testmbas/test5.vb @@ -0,0 +1,32 @@ +Imports System.Windows.Forms +Imports System.Drawing +Imports System + +Public Class Form1 + Inherits Form + +Public WithEvents b1 As Button +Public WithEvents b2 As Button + +Public Sub b1_onClick (sender As Object, e as System.EventArgs) + Console.WriteLine ("b1 clicked") +End Sub + +Public Sub b2_onClick (sender As Object, e as System.EventArgs) + Console.WriteLine ("b2 clicked") +End Sub + +Public Sub New() + b1 = New Button() + b1.Location = New Point(100, 10) + Controls.Add (b1) + AddHandler b1.Click, AddressOf b1_onClick + + b2 = New Button() + b2.Location = New Point(100, 60) + Controls.Add (b2) + AddHandler b2.Click, AddressOf b2_onClick +End Sub + +End Class + diff --git a/mcs/mbas/testmbas/testm.vb b/mcs/mbas/testmbas/testm.vb new file mode 100644 index 00000000000..99a6d8b588d --- /dev/null +++ b/mcs/mbas/testmbas/testm.vb @@ -0,0 +1,11 @@ +Module Pippo + +Public Sub Main() + Dim c as Form1 + + c = new Form1() + c.ShowDialog() + +End Sub + +End Module diff --git a/mcs/mbas/tree.cs b/mcs/mbas/tree.cs new file mode 100644 index 00000000000..2cf5be8ca5c --- /dev/null +++ b/mcs/mbas/tree.cs @@ -0,0 +1,109 @@ +// +// tree.cs: keeps a tree representation of the generated code +// +// Author: Miguel de Icaza (miguel@gnu.org) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// +// + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.IO; + +namespace Mono.CSharp +{ + + public interface ITreeDump { + int Dump (Tree tree, StreamWriter output); + void ParseOptions (string options); + } + + // <summary> + // + // We store here all the toplevel types that we have parsed, + // this is the root of all information we have parsed. + // + // </summary> + + public class Tree { + TypeContainer root_types; + + // <summary> + // Keeps track of namespaces defined in the source code + // </summary> + Hashtable namespaces; + + // <summary> + // Keeps track of all the types definied (classes, structs, ifaces, enums) + // </summary> + Hashtable decls; + + public Tree () + { + root_types = new TypeContainer (null, "", new Location (-1)); + + decls = new Hashtable (); + namespaces = new Hashtable (); + } + + public void RecordDecl (string name, DeclSpace ds) + { + if (decls.Contains (name)){ + Report.Error ( + 101, ds.Location, + "There is already a definition for `" + name + "'"); + DeclSpace other = (DeclSpace) decls [name]; + Report.Error (0, + other.Location, "(Location of symbol related to previous error)"); + return; + } + decls.Add (name, ds); + } + + public Namespace RecordNamespace (Namespace parent, string file, string name) + { + Namespace ns = new Namespace (parent, name); + + if (namespaces.Contains (file)){ + Hashtable ns_ns = (Hashtable) namespaces [file]; + + if (ns_ns.Contains (ns.Name)) + return (Namespace) ns_ns [ns.Name]; + ns_ns.Add (ns.Name, ns); + } else { + Hashtable new_table = new Hashtable (); + namespaces [file] = new_table; + + new_table.Add (ns.Name, ns); + } + + return ns; + } + + // + // FIXME: Why are we using Types? + // + public TypeContainer Types { + get { + return root_types; + } + } + + public Hashtable Decls { + get { + return decls; + } + } + + public Hashtable Namespaces { + get { + return namespaces; + } + } + } +} diff --git a/mcs/mbas/typemanager.cs b/mcs/mbas/typemanager.cs new file mode 100644 index 00000000000..b144bce45cf --- /dev/null +++ b/mcs/mbas/typemanager.cs @@ -0,0 +1,2355 @@ +// +// typemanager.cs: C# type manager +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Ravi Pratap (ravi@ximian.com) +// +// Licensed under the terms of the GNU GPL +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// +// +using System; +using System.Globalization; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Text.RegularExpressions; +using System.Runtime.CompilerServices; +using System.Diagnostics; + +namespace Mono.CSharp { + +public class TypeManager { + // + // A list of core types that the compiler requires or uses + // + static public Type object_type; + static public Type value_type; + static public Type string_type; + static public Type int32_type; + static public Type uint32_type; + static public Type int64_type; + static public Type uint64_type; + static public Type float_type; + static public Type double_type; + static public Type char_type; + static public Type char_ptr_type; + static public Type short_type; + static public Type decimal_type; + static public Type bool_type; + static public Type sbyte_type; + static public Type byte_type; + static public Type ushort_type; + static public Type enum_type; + static public Type delegate_type; + static public Type multicast_delegate_type; + static public Type void_type; + static public Type enumeration_type; + static public Type array_type; + static public Type runtime_handle_type; + static public Type icloneable_type; + static public Type type_type; + static public Type ienumerator_type; + static public Type idisposable_type; + static public Type default_member_type; + static public Type iasyncresult_type; + static public Type asynccallback_type; + static public Type intptr_type; + static public Type monitor_type; + static public Type runtime_field_handle_type; + static public Type attribute_type; + static public Type attribute_usage_type; + static public Type dllimport_type; + static public Type unverifiable_code_type; + static public Type methodimpl_attr_type; + static public Type marshal_as_attr_type; + static public Type param_array_type; + static public Type void_ptr_type; + static public Type indexer_name_type; + static public Type exception_type; + static public object obsolete_attribute_type; + static public object conditional_attribute_type; + + // + // An empty array of types + // + static public Type [] NoTypes; + + + // + // Expressions representing the internal types. Used during declaration + // definition. + // + static public Expression system_object_expr, system_string_expr; + static public Expression system_boolean_expr, system_decimal_expr; + static public Expression system_single_expr, system_double_expr; + static public Expression system_sbyte_expr, system_byte_expr; + static public Expression system_int16_expr, system_uint16_expr; + static public Expression system_int32_expr, system_uint32_expr; + static public Expression system_int64_expr, system_uint64_expr; + static public Expression system_char_expr, system_void_expr; + static public Expression system_asynccallback_expr; + static public Expression system_iasyncresult_expr; + + // + // This is only used when compiling corlib + // + static public Type system_int32_type; + static public Type system_array_type; + static public Type system_type_type; + static public Type system_assemblybuilder_type; + static public MethodInfo system_int_array_get_length; + static public MethodInfo system_int_array_get_rank; + static public MethodInfo system_object_array_clone; + static public MethodInfo system_int_array_get_length_int; + static public MethodInfo system_int_array_get_lower_bound_int; + static public MethodInfo system_int_array_get_upper_bound_int; + static public MethodInfo system_void_array_copyto_array_int; + static public MethodInfo system_void_set_corlib_type_builders; + + + // + // Internal, not really used outside + // + static Type runtime_helpers_type; + + // + // These methods are called by code generated by the compiler + // + static public MethodInfo string_concat_string_string; + static public MethodInfo string_concat_object_object; + static public MethodInfo string_isinterneted_string; + static public MethodInfo system_type_get_type_from_handle; + static public MethodInfo object_getcurrent_void; + static public MethodInfo bool_movenext_void; + static public MethodInfo void_dispose_void; + static public MethodInfo void_monitor_enter_object; + static public MethodInfo void_monitor_exit_object; + static public MethodInfo void_initializearray_array_fieldhandle; + static public MethodInfo int_getlength_int; + static public MethodInfo delegate_combine_delegate_delegate; + static public MethodInfo delegate_remove_delegate_delegate; + static public MethodInfo int_get_offset_to_string_data; + static public MethodInfo int_array_get_length; + static public MethodInfo int_array_get_rank; + static public MethodInfo object_array_clone; + static public MethodInfo int_array_get_length_int; + static public MethodInfo int_array_get_lower_bound_int; + static public MethodInfo int_array_get_upper_bound_int; + static public MethodInfo void_array_copyto_array_int; + + // + // The attribute constructors. + // + static public ConstructorInfo cons_param_array_attribute; + static public ConstructorInfo void_decimal_ctor_five_args; + static public ConstructorInfo unverifiable_code_ctor; + + // <remarks> + // Holds the Array of Assemblies that have been loaded + // (either because it is the default or the user used the + // -r command line option) + // </remarks> + static Assembly [] assemblies; + + // <remarks> + // Keeps a list of module builders. We used this to do lookups + // on the modulebuilder using GetType -- needed for arrays + // </remarks> + static ModuleBuilder [] modules; + + // <remarks> + // This is the type_cache from the assemblies to avoid + // hitting System.Reflection on every lookup. + // </summary> + static Hashtable types; + + // <remarks> + // This is used to hotld the corresponding TypeContainer objects + // since we need this in FindMembers + // </remarks> + static Hashtable typecontainers; + + // <remarks> + // Keeps track of those types that are defined by the + // user's program + // </remarks> + static ArrayList user_types; + + static PtrHashtable builder_to_declspace; + + // <remarks> + // Tracks the interfaces implemented by typebuilders. We only + // enter those who do implement or or more interfaces + // </remarks> + static PtrHashtable builder_to_ifaces; + + // <remarks> + // Maps MethodBase.RuntimeTypeHandle to a Type array that contains + // the arguments to the method + // </remarks> + static Hashtable method_arguments; + + // <remarks> + // Maps PropertyBuilder to a Type array that contains + // the arguments to the indexer + // </remarks> + static Hashtable indexer_arguments; + + // <remarks> + // Maybe `method_arguments' should be replaced and only + // method_internal_params should be kept? + // <remarks> + static Hashtable method_internal_params; + + // <remarks> + // Keeps track of attribute types + // </remarks> + + static Hashtable builder_to_attr; + + // <remarks> + // Keeps track of methods + // </remarks> + + static Hashtable builder_to_method; + + struct Signature { + public string name; + public Type [] args; + } + + /// <summary> + /// A filter for Findmembers that uses the Signature object to + /// extract objects + /// </summary> + static bool SignatureFilter (MemberInfo mi, object criteria) + { + Signature sig = (Signature) criteria; + + if (!(mi is MethodBase)) + return false; + + if (mi.Name != sig.name) + return false; + + int count = sig.args.Length; + + if (mi is MethodBuilder || mi is ConstructorBuilder){ + Type [] candidate_args = GetArgumentTypes ((MethodBase) mi); + + if (candidate_args.Length != count) + return false; + + for (int i = 0; i < count; i++) + if (candidate_args [i] != sig.args [i]) + return false; + + return true; + } else { + ParameterInfo [] pars = ((MethodBase) mi).GetParameters (); + + if (pars.Length != count) + return false; + + for (int i = 0; i < count; i++) + if (pars [i].ParameterType != sig.args [i]) + return false; + return true; + } + } + + // A delegate that points to the filter above. + static MemberFilter signature_filter; + + // + // These are expressions that represent some of the internal data types, used + // elsewhere + // + static void InitExpressionTypes () + { + system_object_expr = new TypeLookupExpression ("System.Object"); + system_string_expr = new TypeLookupExpression ("System.String"); + system_boolean_expr = new TypeLookupExpression ("System.Boolean"); + system_decimal_expr = new TypeLookupExpression ("System.Decimal"); + system_single_expr = new TypeLookupExpression ("System.Single"); + system_double_expr = new TypeLookupExpression ("System.Double"); + system_sbyte_expr = new TypeLookupExpression ("System.SByte"); + system_byte_expr = new TypeLookupExpression ("System.Byte"); + system_int16_expr = new TypeLookupExpression ("System.Int16"); + system_uint16_expr = new TypeLookupExpression ("System.UInt16"); + system_int32_expr = new TypeLookupExpression ("System.Int32"); + system_uint32_expr = new TypeLookupExpression ("System.UInt32"); + system_int64_expr = new TypeLookupExpression ("System.Int64"); + system_uint64_expr = new TypeLookupExpression ("System.UInt64"); + system_char_expr = new TypeLookupExpression ("System.Char"); + system_void_expr = new TypeLookupExpression ("System.Void"); + system_asynccallback_expr = new TypeLookupExpression ("System.AsyncCallback"); + system_iasyncresult_expr = new TypeLookupExpression ("System.IAsyncResult"); + } + + static TypeManager () + { + assemblies = new Assembly [0]; + modules = null; + user_types = new ArrayList (); + + types = new Hashtable (); + typecontainers = new Hashtable (); + + builder_to_declspace = new PtrHashtable (); + builder_to_attr = new PtrHashtable (); + builder_to_method = new PtrHashtable (); + method_arguments = new PtrHashtable (); + method_internal_params = new PtrHashtable (); + indexer_arguments = new PtrHashtable (); + builder_to_ifaces = new PtrHashtable (); + + NoTypes = new Type [0]; + + signature_filter = new MemberFilter (SignatureFilter); + InitExpressionTypes (); + } + + public static void AddUserType (string name, TypeBuilder t, Type [] ifaces) + { + try { + types.Add (name, t); + } catch { + Type prev = (Type) types [name]; + TypeContainer tc = builder_to_declspace [prev] as TypeContainer; + + if (tc != null){ + // + // This probably never happens, as we catch this before + // + Report.Error (-17, "The type `" + name + "' has already been defined."); + return; + } + + tc = builder_to_declspace [t] as TypeContainer; + + Report.Warning ( + 1595, "The type `" + name + "' is defined in an existing assembly;"+ + " Using the new definition from: " + tc.Location); + Report.Warning (1595, "Previously defined in: " + prev.Assembly.FullName); + + types.Remove (name); + types.Add (name, t); + } + user_types.Add (t); + + if (ifaces != null) + builder_to_ifaces [t] = ifaces; + } + + // + // This entry point is used by types that we define under the covers + // + public static void RegisterBuilder (TypeBuilder tb, Type [] ifaces) + { + if (ifaces != null) + builder_to_ifaces [tb] = ifaces; + } + + public static void AddUserType (string name, TypeBuilder t, TypeContainer tc, Type [] ifaces) + { + builder_to_declspace.Add (t, tc); + typecontainers.Add (name, tc); + AddUserType (name, t, ifaces); + } + + public static void AddDelegateType (string name, TypeBuilder t, Delegate del) + { + types.Add (name, t); + builder_to_declspace.Add (t, del); + } + + public static void AddEnumType (string name, TypeBuilder t, Enum en) + { + types.Add (name, t); + builder_to_declspace.Add (t, en); + } + + public static void AddUserInterface (string name, TypeBuilder t, Interface i, Type [] ifaces) + { + AddUserType (name, t, ifaces); + builder_to_declspace.Add (t, i); + } + + public static void AddMethod (MethodBuilder builder, MethodData method) + { + builder_to_method.Add (builder, method); + } + + public static void RegisterAttrType (Type t, TypeContainer tc) + { + builder_to_attr.Add (t, tc); + } + + /// <summary> + /// Returns the TypeContainer whose Type is `t' or null if there is no + /// TypeContainer for `t' (ie, the Type comes from a library) + /// </summary> + public static TypeContainer LookupTypeContainer (Type t) + { + return builder_to_declspace [t] as TypeContainer; + } + + public static IMemberContainer LookupMemberContainer (Type t) + { + if (t is TypeBuilder) { + IMemberContainer container = builder_to_declspace [t] as IMemberContainer; + if (container != null) + return container; + } + + return TypeHandle.GetTypeHandle (t); + } + + public static Interface LookupInterface (Type t) + { + return builder_to_declspace [t] as Interface; + } + + public static Delegate LookupDelegate (Type t) + { + return builder_to_declspace [t] as Delegate; + } + + public static Enum LookupEnum (Type t) + { + return builder_to_declspace [t] as Enum; + } + + public static TypeContainer LookupAttr (Type t) + { + return (TypeContainer) builder_to_attr [t]; + } + + /// <summary> + /// Registers an assembly to load types from. + /// </summary> + public static void AddAssembly (Assembly a) + { + int top = assemblies.Length; + Assembly [] n = new Assembly [top + 1]; + + assemblies.CopyTo (n, 0); + + n [top] = a; + assemblies = n; + } + + /// <summary> + /// Registers a module builder to lookup types from + /// </summary> + public static void AddModule (ModuleBuilder mb) + { + int top = modules != null ? modules.Length : 0; + ModuleBuilder [] n = new ModuleBuilder [top + 1]; + + if (modules != null) + modules.CopyTo (n, 0); + n [top] = mb; + modules = n; + } + + // + // Low-level lookup, cache-less + // + static Type LookupTypeReflection (string name) + { + Type t; + + foreach (Assembly a in assemblies){ + t = a.GetType (name); + if (t != null) + return t; + } + + foreach (ModuleBuilder mb in modules) { + t = mb.GetType (name); + if (t != null){ + return t; + } + } + return null; + } + + // + // This function is used when you want to avoid the lookups, and want to go + // directly to the source. This will use the cache. + // + // Notice that bypassing the cache is bad, because on Microsoft.NET runtime + // GetType ("DynamicType[]") != GetType ("DynamicType[]"), and there is no + // way to test things other than doing a fullname compare + // + public static Type LookupTypeDirect (string name) + { + Type t = (Type) types [name]; + if (t != null) + return t; + + t = LookupTypeReflection (name); + if (t == null) + return null; + + types [name] = t; + return t; + } + + /// <summary> + /// Returns the Type associated with @name, takes care of the fact that + /// reflection expects nested types to be separated from the main type + /// with a "+" instead of a "." + /// </summary> + public static Type LookupType (string name) + { + Type t; + + // + // First lookup in user defined and cached values + // + + t = (Type) types [name]; + if (t != null) + return t; + + // + // Optimization: ComposedCast will work with an existing type, and might already have the + // full name of the type, so the full system lookup can probably be avoided. + // + + string [] elements = name.Split ('.'); + int count = elements.Length; + + for (int n = 1; n <= count; n++){ + string top_level_type = String.Join (".", elements, 0, n); + + t = (Type) types [top_level_type]; + if (t == null){ + t = LookupTypeReflection (top_level_type); + if (t == null) + continue; + } + + if (count == n){ + types [name] = t; + return t; + } + + string newt = top_level_type + "+" + String.Join ("+", elements, n, count - n); + t = LookupTypeDirect (newt); + if (t != null) + types [newt] = t; + return t; + } + return null; + } + + // + // Returns a list of all namespaces in the assemblies and types loaded. + // + public static Hashtable GetNamespaces () + { + Hashtable namespaces = new Hashtable (); + + foreach (Assembly a in assemblies){ + foreach (Type t in a.GetTypes ()){ + string ns = t.Namespace; + + if (namespaces.Contains (ns)) + continue; + namespaces [ns] = ns; + } + } + + foreach (ModuleBuilder mb in modules){ + foreach (Type t in mb.GetTypes ()){ + string ns = t.Namespace; + + if (namespaces.Contains (ns)) + continue; + namespaces [ns] = ns; + } + } + return namespaces; + } + + /// <summary> + /// Returns the C# name of a type if possible, or the full type name otherwise + /// </summary> + static public string CSharpName (Type t) + { + return Regex.Replace (t.FullName, + @"^System\." + + @"(Int32|UInt32|Int16|Uint16|Int64|UInt64|" + + @"Single|Double|Char|Decimal|Byte|SByte|Object|" + + @"Boolean|String|Void)" + + @"(\W+|\b)", + new MatchEvaluator (CSharpNameMatch)); + } + + static String CSharpNameMatch (Match match) + { + string s = match.Groups [1].Captures [0].Value; + return s.ToLower (). + Replace ("int32", "int"). + Replace ("uint32", "uint"). + Replace ("int16", "short"). + Replace ("uint16", "ushort"). + Replace ("int64", "long"). + Replace ("uint64", "ulong"). + Replace ("single", "float"). + Replace ("boolean", "bool") + + match.Groups [2].Captures [0].Value; + } + + /// <summary> + /// Returns the signature of the method + /// </summary> + static public string CSharpSignature (MethodBase mb) + { + string sig = "("; + + // + // FIXME: We should really have a single function to do + // everything instead of the following 5 line pattern + // + ParameterData iparams = LookupParametersByBuilder (mb); + + if (iparams == null){ + ParameterInfo [] pi = mb.GetParameters (); + iparams = new ReflectionParameters (pi); + } + + for (int i = 0; i < iparams.Count; i++) { + if (i > 0) { + sig += ", "; + } + sig += iparams.ParameterDesc(i); + } + sig += ")"; + + return mb.DeclaringType.Name + "." + mb.Name + sig; + } + + /// <summary> + /// Looks up a type, and aborts if it is not found. This is used + /// by types required by the compiler + /// </summary> + static Type CoreLookupType (string name) + { + Type t = LookupType (name); + + if (t == null){ + Report.Error (518, "The predefined type `" + name + "' is not defined or imported"); + Environment.Exit (0); + } + + return t; + } + + /// <summary> + /// Returns the MethodInfo for a method named `name' defined + /// in type `t' which takes arguments of types `args' + /// </summary> + static MethodInfo GetMethod (Type t, string name, Type [] args) + { + MemberList list; + Signature sig; + + sig.name = name; + sig.args = args; + + list = FindMembers (t, MemberTypes.Method, instance_and_static | BindingFlags.Public, + signature_filter, sig); + if (list.Count == 0) { + Report.Error (-19, "Can not find the core function `" + name + "'"); + return null; + } + + MethodInfo mi = list [0] as MethodInfo; + if (mi == null) { + Report.Error (-19, "Can not find the core function `" + name + "'"); + return null; + } + + return mi; + } + + /// <summary> + /// Returns the ConstructorInfo for "args" + /// </summary> + static ConstructorInfo GetConstructor (Type t, Type [] args) + { + MemberList list; + Signature sig; + + sig.name = ".ctor"; + sig.args = args; + + list = FindMembers (t, MemberTypes.Constructor, + instance_and_static | BindingFlags.Public | BindingFlags.DeclaredOnly, + signature_filter, sig); + if (list.Count == 0){ + Report.Error (-19, "Can not find the core constructor for type `" + t.Name + "'"); + return null; + } + + ConstructorInfo ci = list [0] as ConstructorInfo; + if (ci == null){ + Report.Error (-19, "Can not find the core constructor for type `" + t.Name + "'"); + return null; + } + + return ci; + } + + public static void InitEnumUnderlyingTypes () + { + + int32_type = CoreLookupType ("System.Int32"); + int64_type = CoreLookupType ("System.Int64"); + uint32_type = CoreLookupType ("System.UInt32"); + uint64_type = CoreLookupType ("System.UInt64"); + byte_type = CoreLookupType ("System.Byte"); + sbyte_type = CoreLookupType ("System.SByte"); + short_type = CoreLookupType ("System.Int16"); + ushort_type = CoreLookupType ("System.UInt16"); + } + + /// <remarks> + /// The types have to be initialized after the initial + /// population of the type has happened (for example, to + /// bootstrap the corlib.dll + /// </remarks> + public static void InitCoreTypes () + { + object_type = CoreLookupType ("System.Object"); + value_type = CoreLookupType ("System.ValueType"); + + InitEnumUnderlyingTypes (); + + char_type = CoreLookupType ("System.Char"); + string_type = CoreLookupType ("System.String"); + float_type = CoreLookupType ("System.Single"); + double_type = CoreLookupType ("System.Double"); + char_ptr_type = CoreLookupType ("System.Char*"); + decimal_type = CoreLookupType ("System.Decimal"); + bool_type = CoreLookupType ("System.Boolean"); + enum_type = CoreLookupType ("System.Enum"); + + multicast_delegate_type = CoreLookupType ("System.MulticastDelegate"); + delegate_type = CoreLookupType ("System.Delegate"); + + array_type = CoreLookupType ("System.Array"); + void_type = CoreLookupType ("System.Void"); + type_type = CoreLookupType ("System.Type"); + + runtime_field_handle_type = CoreLookupType ("System.RuntimeFieldHandle"); + runtime_helpers_type = CoreLookupType ("System.Runtime.CompilerServices.RuntimeHelpers"); + default_member_type = CoreLookupType ("System.Reflection.DefaultMemberAttribute"); + runtime_handle_type = CoreLookupType ("System.RuntimeTypeHandle"); + asynccallback_type = CoreLookupType ("System.AsyncCallback"); + iasyncresult_type = CoreLookupType ("System.IAsyncResult"); + ienumerator_type = CoreLookupType ("System.Collections.IEnumerator"); + idisposable_type = CoreLookupType ("System.IDisposable"); + icloneable_type = CoreLookupType ("System.ICloneable"); + monitor_type = CoreLookupType ("System.Threading.Monitor"); + intptr_type = CoreLookupType ("System.IntPtr"); + + attribute_type = CoreLookupType ("System.Attribute"); + attribute_usage_type = CoreLookupType ("System.AttributeUsageAttribute"); + dllimport_type = CoreLookupType ("System.Runtime.InteropServices.DllImportAttribute"); + methodimpl_attr_type = CoreLookupType ("System.Runtime.CompilerServices.MethodImplAttribute"); + marshal_as_attr_type = CoreLookupType ("System.Runtime.InteropServices.MarshalAsAttribute"); + param_array_type = CoreLookupType ("System.ParamArrayAttribute"); + + unverifiable_code_type= CoreLookupType ("System.Security.UnverifiableCodeAttribute"); + + void_ptr_type = CoreLookupType ("System.Void*"); + + indexer_name_type = CoreLookupType ("System.Runtime.CompilerServices.IndexerNameAttribute"); + + exception_type = CoreLookupType ("System.Exception"); + + // + // Attribute types + // + obsolete_attribute_type = CoreLookupType ("System.ObsoleteAttribute"); + conditional_attribute_type = CoreLookupType ("System.Diagnostics.ConditionalAttribute"); + + // + // When compiling corlib, store the "real" types here. + // + if (!RootContext.StdLib) { + system_int32_type = typeof (System.Int32); + system_array_type = typeof (System.Array); + system_type_type = typeof (System.Type); + system_assemblybuilder_type = typeof (System.Reflection.Emit.AssemblyBuilder); + + Type [] void_arg = { }; + system_int_array_get_length = GetMethod ( + system_array_type, "get_Length", void_arg); + system_int_array_get_rank = GetMethod ( + system_array_type, "get_Rank", void_arg); + system_object_array_clone = GetMethod ( + system_array_type, "Clone", void_arg); + + Type [] system_int_arg = { system_int32_type }; + system_int_array_get_length_int = GetMethod ( + system_array_type, "GetLength", system_int_arg); + system_int_array_get_upper_bound_int = GetMethod ( + system_array_type, "GetUpperBound", system_int_arg); + system_int_array_get_lower_bound_int = GetMethod ( + system_array_type, "GetLowerBound", system_int_arg); + + Type [] system_array_int_arg = { system_array_type, system_int32_type }; + system_void_array_copyto_array_int = GetMethod ( + system_array_type, "CopyTo", system_array_int_arg); + + Type [] system_type_type_arg = { system_type_type, system_type_type, system_type_type }; + + try { + system_void_set_corlib_type_builders = GetMethod ( + system_assemblybuilder_type, "SetCorlibTypeBuilders", + system_type_type_arg); + + object[] args = new object [3]; + args [0] = object_type; + args [1] = value_type; + args [2] = enum_type; + + system_void_set_corlib_type_builders.Invoke (CodeGen.AssemblyBuilder, args); + } catch { + Console.WriteLine ("Corlib compilation is not supported in Microsoft.NET due to bugs in it"); + } + } + } + + // + // The helper methods that are used by the compiler + // + public static void InitCodeHelpers () + { + // + // Now load the default methods that we use. + // + Type [] string_string = { string_type, string_type }; + string_concat_string_string = GetMethod ( + string_type, "Concat", string_string); + + Type [] object_object = { object_type, object_type }; + string_concat_object_object = GetMethod ( + string_type, "Concat", object_object); + + Type [] string_ = { string_type }; + string_isinterneted_string = GetMethod ( + string_type, "IsInterned", string_); + + Type [] runtime_type_handle = { runtime_handle_type }; + system_type_get_type_from_handle = GetMethod ( + type_type, "GetTypeFromHandle", runtime_type_handle); + + Type [] delegate_delegate = { delegate_type, delegate_type }; + delegate_combine_delegate_delegate = GetMethod ( + delegate_type, "Combine", delegate_delegate); + + delegate_remove_delegate_delegate = GetMethod ( + delegate_type, "Remove", delegate_delegate); + + // + // Void arguments + // + Type [] void_arg = { }; + object_getcurrent_void = GetMethod ( + ienumerator_type, "get_Current", void_arg); + bool_movenext_void = GetMethod ( + ienumerator_type, "MoveNext", void_arg); + void_dispose_void = GetMethod ( + idisposable_type, "Dispose", void_arg); + int_get_offset_to_string_data = GetMethod ( + runtime_helpers_type, "get_OffsetToStringData", void_arg); + int_array_get_length = GetMethod ( + array_type, "get_Length", void_arg); + int_array_get_rank = GetMethod ( + array_type, "get_Rank", void_arg); + + // + // Int32 arguments + // + Type [] int_arg = { int32_type }; + int_array_get_length_int = GetMethod ( + array_type, "GetLength", int_arg); + int_array_get_upper_bound_int = GetMethod ( + array_type, "GetUpperBound", int_arg); + int_array_get_lower_bound_int = GetMethod ( + array_type, "GetLowerBound", int_arg); + + // + // System.Array methods + // + object_array_clone = GetMethod ( + array_type, "Clone", void_arg); + Type [] array_int_arg = { array_type, int32_type }; + void_array_copyto_array_int = GetMethod ( + array_type, "CopyTo", array_int_arg); + + // + // object arguments + // + Type [] object_arg = { object_type }; + void_monitor_enter_object = GetMethod ( + monitor_type, "Enter", object_arg); + void_monitor_exit_object = GetMethod ( + monitor_type, "Exit", object_arg); + + Type [] array_field_handle_arg = { array_type, runtime_field_handle_type }; + + void_initializearray_array_fieldhandle = GetMethod ( + runtime_helpers_type, "InitializeArray", array_field_handle_arg); + + // + // Array functions + // + int_getlength_int = GetMethod ( + array_type, "GetLength", int_arg); + + // + // Decimal constructors + // + Type [] dec_arg = { int32_type, int32_type, int32_type, bool_type, byte_type }; + void_decimal_ctor_five_args = GetConstructor ( + decimal_type, dec_arg); + + // + // Attributes + // + cons_param_array_attribute = GetConstructor ( + param_array_type, void_arg); + + unverifiable_code_ctor = GetConstructor ( + unverifiable_code_type, void_arg); + + } + + const BindingFlags instance_and_static = BindingFlags.Static | BindingFlags.Instance; + + static Hashtable type_hash = new Hashtable (); + + /// <remarks> + /// This is the "old", non-cache based FindMembers() function. We cannot use + /// the cache here because there is no member name argument. + /// </remarks> + public static MemberList FindMembers (Type t, MemberTypes mt, BindingFlags bf, + MemberFilter filter, object criteria) + { + DeclSpace decl = (DeclSpace) builder_to_declspace [t]; + + // + // `builder_to_declspace' contains all dynamic types. + // + if (decl != null) { + MemberList list; + Timer.StartTimer (TimerType.FindMembers); + list = decl.FindMembers (mt, bf, filter, criteria); + Timer.StopTimer (TimerType.FindMembers); + return list; + } + + // + // We have to take care of arrays specially, because GetType on + // a TypeBuilder array will return a Type, not a TypeBuilder, + // and we can not call FindMembers on this type. + // + if (t.IsSubclassOf (TypeManager.array_type)) + return new MemberList (TypeManager.array_type.FindMembers (mt, bf, filter, criteria)); + + // + // Since FindMembers will not lookup both static and instance + // members, we emulate this behaviour here. + // + if ((bf & instance_and_static) == instance_and_static){ + MemberInfo [] i_members = t.FindMembers ( + mt, bf & ~BindingFlags.Static, filter, criteria); + + int i_len = i_members.Length; + if (i_len == 1){ + MemberInfo one = i_members [0]; + + // + // If any of these are present, we are done! + // + if ((one is Type) || (one is EventInfo) || (one is FieldInfo)) + return new MemberList (i_members); + } + + MemberInfo [] s_members = t.FindMembers ( + mt, bf & ~BindingFlags.Instance, filter, criteria); + + int s_len = s_members.Length; + if (i_len > 0 || s_len > 0) + return new MemberList (i_members, s_members); + else { + if (i_len > 0) + return new MemberList (i_members); + else + return new MemberList (s_members); + } + } + + return new MemberList (t.FindMembers (mt, bf, filter, criteria)); + } + + + /// <summary> + /// This method is only called from within MemberLookup. It tries to use the member + /// cache if possible and falls back to the normal FindMembers if not. The `used_cache' + /// flag tells the caller whether we used the cache or not. If we used the cache, then + /// our return value will already contain all inherited members and the caller don't need + /// to check base classes and interfaces anymore. + /// </summary> + private static MemberList MemberLookup_FindMembers (Type t, MemberTypes mt, BindingFlags bf, + string name, out bool used_cache) + { + // + // We have to take care of arrays specially, because GetType on + // a TypeBuilder array will return a Type, not a TypeBuilder, + // and we can not call FindMembers on this type. + // + if (t.IsSubclassOf (TypeManager.array_type)) { + used_cache = true; + return TypeHandle.ArrayType.MemberCache.FindMembers ( + mt, bf, name, FilterWithClosure_delegate, null); + } + + // + // If this is a dynamic type, it's always in the `builder_to_declspace' hash table + // and we can ask the DeclSpace for the MemberCache. + // + if (t is TypeBuilder) { + DeclSpace decl = (DeclSpace) builder_to_declspace [t]; + MemberCache cache = decl.MemberCache; + + // + // If this DeclSpace has a MemberCache, use it. + // + + if (cache != null) { + used_cache = true; + return cache.FindMembers ( + mt, bf, name, FilterWithClosure_delegate, null); + } + + // If there is no MemberCache, we need to use the "normal" FindMembers. + + MemberList list; + Timer.StartTimer (TimerType.FindMembers); + list = decl.FindMembers (mt, bf | BindingFlags.DeclaredOnly, + FilterWithClosure_delegate, name); + Timer.StopTimer (TimerType.FindMembers); + used_cache = false; + return list; + } + + // + // This call will always succeed. There is exactly one TypeHandle instance per + // type, TypeHandle.GetTypeHandle() will either return it or create a new one + // if it didn't already exist. + // + TypeHandle handle = TypeHandle.GetTypeHandle (t); + + used_cache = true; + return handle.MemberCache.FindMembers (mt, bf, name, FilterWithClosure_delegate, null); + } + + public static bool IsBuiltinType (Type t) + { + if (t == object_type || t == string_type || t == int32_type || t == uint32_type || + t == int64_type || t == uint64_type || t == float_type || t == double_type || + t == char_type || t == short_type || t == decimal_type || t == bool_type || + t == sbyte_type || t == byte_type || t == ushort_type || t == void_type) + return true; + else + return false; + } + + public static bool IsDelegateType (Type t) + { + if (t.IsSubclassOf (TypeManager.delegate_type)) + return true; + else + return false; + } + + public static bool IsEnumType (Type t) + { + if (t.IsSubclassOf (TypeManager.enum_type)) + return true; + else + return false; + } + + // + // Whether a type is unmanaged. This is used by the unsafe code (25.2) + // + public static bool IsUnmanagedType (Type t) + { + if (IsBuiltinType (t) && t != TypeManager.string_type) + return true; + + if (IsEnumType (t)) + return true; + + if (t.IsPointer) + return true; + + if (IsValueType (t)){ + if (t is TypeBuilder){ + TypeContainer tc = LookupTypeContainer (t); + + foreach (Field f in tc.Fields){ + if (f.FieldBuilder.IsStatic) + continue; + if (!IsUnmanagedType (f.FieldBuilder.FieldType)) + return false; + } + } else { + FieldInfo [] fields = t.GetFields (); + + foreach (FieldInfo f in fields){ + if (f.IsStatic) + continue; + if (!IsUnmanagedType (f.FieldType)) + return false; + } + } + return true; + } + + return false; + } + + public static bool IsValueType (Type t) + { + if (t.IsSubclassOf (TypeManager.value_type)) + return true; + else + return false; + } + + public static bool IsInterfaceType (Type t) + { + Interface iface = builder_to_declspace [t] as Interface; + + if (iface != null) + return true; + else + return false; + } + + // + // Checks whether `type' is a subclass or nested child of `parent'. + // + public static bool IsSubclassOrNestedChildOf (Type type, Type parent) + { + do { + if ((type == parent) || type.IsSubclassOf (parent)) + return true; + + // Handle nested types. + type = type.DeclaringType; + } while (type != null); + + return false; + } + + // + // Checks whether `type' is a nested child of `parent'. + // + public static bool IsNestedChildOf (Type type, Type parent) + { + if ((type == parent) || type.IsSubclassOf (parent)) + return false; + else + return IsSubclassOrNestedChildOf (type, parent); + } + + /// <summary> + /// Returns the User Defined Types + /// </summary> + public static ArrayList UserTypes { + get { + return user_types; + } + } + + public static Hashtable TypeContainers { + get { + return typecontainers; + } + } + + static Hashtable builder_to_constant; + + public static void RegisterConstant (FieldBuilder fb, Const c) + { + if (builder_to_constant == null) + builder_to_constant = new PtrHashtable (); + + if (builder_to_constant.Contains (fb)) + return; + + builder_to_constant.Add (fb, c); + } + + public static Const LookupConstant (FieldBuilder fb) + { + if (builder_to_constant == null) + return null; + + return (Const) builder_to_constant [fb]; + } + + /// <summary> + /// Gigantic work around for missing features in System.Reflection.Emit follows. + /// </summary> + /// + /// <remarks> + /// Since System.Reflection.Emit can not return MethodBase.GetParameters + /// for anything which is dynamic, and we need this in a number of places, + /// we register this information here, and use it afterwards. + /// </remarks> + static public bool RegisterMethod (MethodBase mb, InternalParameters ip, Type [] args) + { + if (args == null) + args = NoTypes; + + method_arguments.Add (mb, args); + method_internal_params.Add (mb, ip); + + return true; + } + + static public InternalParameters LookupParametersByBuilder (MethodBase mb) + { + if (! (mb is ConstructorBuilder || mb is MethodBuilder)) + return null; + + if (method_internal_params.Contains (mb)) + return (InternalParameters) method_internal_params [mb]; + else + throw new Exception ("Argument for Method not registered" + mb); + } + + /// <summary> + /// Returns the argument types for a method based on its methodbase + /// + /// For dynamic methods, we use the compiler provided types, for + /// methods from existing assemblies we load them from GetParameters, + /// and insert them into the cache + /// </summary> + static public Type [] GetArgumentTypes (MethodBase mb) + { + if (method_arguments.Contains (mb)) + return (Type []) method_arguments [mb]; + else { + ParameterInfo [] pi = mb.GetParameters (); + int c = pi.Length; + Type [] types = new Type [c]; + + for (int i = 0; i < c; i++) + types [i] = pi [i].ParameterType; + + method_arguments.Add (mb, types); + return types; + } + } + + /// <summary> + /// Returns the argument types for an indexer based on its PropertyInfo + /// + /// For dynamic indexers, we use the compiler provided types, for + /// indexers from existing assemblies we load them from GetParameters, + /// and insert them into the cache + /// </summary> + static public Type [] GetArgumentTypes (PropertyInfo indexer) + { + if (indexer_arguments.Contains (indexer)) + return (Type []) indexer_arguments [indexer]; + else if (indexer is PropertyBuilder) + // If we're a PropertyBuilder and not in the + // `indexer_arguments' hash, then we're a property and + // not an indexer. + return NoTypes; + else { + ParameterInfo [] pi = indexer.GetIndexParameters (); + // Property, not an indexer. + if (pi == null) + return NoTypes; + int c = pi.Length; + Type [] types = new Type [c]; + + for (int i = 0; i < c; i++) + types [i] = pi [i].ParameterType; + + indexer_arguments.Add (indexer, types); + return types; + } + } + + // <remarks> + // This is a workaround the fact that GetValue is not + // supported for dynamic types + // </remarks> + static Hashtable fields = new Hashtable (); + static public bool RegisterFieldValue (FieldBuilder fb, object value) + { + if (fields.Contains (fb)) + return false; + + fields.Add (fb, value); + + return true; + } + + static public object GetValue (FieldBuilder fb) + { + return fields [fb]; + } + + static Hashtable fieldbuilders_to_fields = new Hashtable (); + static public bool RegisterFieldBase (FieldBuilder fb, FieldBase f) + { + if (fieldbuilders_to_fields.Contains (fb)) + return false; + + fieldbuilders_to_fields.Add (fb, f); + return true; + } + + static public FieldBase GetField (FieldInfo fb) + { + return (FieldBase) fieldbuilders_to_fields [fb]; + } + + static Hashtable events; + + static public bool RegisterEvent (MyEventBuilder eb, MethodBase add, MethodBase remove) + { + if (events == null) + events = new Hashtable (); + + if (events.Contains (eb)) + return false; + + events.Add (eb, new Pair (add, remove)); + + return true; + } + + static public MethodInfo GetAddMethod (EventInfo ei) + { + if (ei is MyEventBuilder) { + Pair pair = (Pair) events [ei]; + + return (MethodInfo) pair.First; + } else + return ei.GetAddMethod (); + } + + static public MethodInfo GetRemoveMethod (EventInfo ei) + { + if (ei is MyEventBuilder) { + Pair pair = (Pair) events [ei]; + + return (MethodInfo) pair.Second; + } else + return ei.GetAddMethod (); + } + + static Hashtable priv_fields_events; + + static public bool RegisterPrivateFieldOfEvent (EventInfo einfo, FieldBuilder builder) + { + if (priv_fields_events == null) + priv_fields_events = new Hashtable (); + + if (priv_fields_events.Contains (einfo)) + return false; + + priv_fields_events.Add (einfo, builder); + + return true; + } + + static public MemberInfo GetPrivateFieldOfEvent (EventInfo ei) + { + return (MemberInfo) priv_fields_events [ei]; + } + + static Hashtable properties; + + static public bool RegisterProperty (PropertyBuilder pb, MethodBase get, MethodBase set) + { + if (properties == null) + properties = new Hashtable (); + + if (properties.Contains (pb)) + return false; + + properties.Add (pb, new Pair (get, set)); + + return true; + } + + static public bool RegisterIndexer (PropertyBuilder pb, MethodBase get, MethodBase set, Type[] args) + { + if (!RegisterProperty (pb, get,set)) + return false; + + indexer_arguments.Add (pb, args); + + return true; + } + + static public MethodInfo GetPropertyGetter (PropertyInfo pi) + { + if (pi is PropertyBuilder){ + Pair de = (Pair) properties [pi]; + + return (MethodInfo) de.Second; + } else + return pi.GetSetMethod (); + } + + static public MethodInfo GetPropertySetter (PropertyInfo pi) + { + if (pi is PropertyBuilder){ + Pair de = (Pair) properties [pi]; + + return (MethodInfo) de.First; + } else + return pi.GetGetMethod (); + } + + /// <summary> + /// Given an array of interface types, expand and eliminate repeated ocurrences + /// of an interface. + /// </summary> + /// + /// <remarks> + /// This expands in context like: IA; IB : IA; IC : IA, IB; the interface "IC" to + /// be IA, IB, IC. + /// </remarks> + public static Type [] ExpandInterfaces (Type [] base_interfaces) + { + ArrayList new_ifaces = new ArrayList (); + + foreach (Type iface in base_interfaces){ + if (!new_ifaces.Contains (iface)) + new_ifaces.Add (iface); + + Type [] implementing = TypeManager.GetInterfaces (iface); + + foreach (Type imp in implementing){ + if (!new_ifaces.Contains (imp)) + new_ifaces.Add (imp); + } + } + Type [] ret = new Type [new_ifaces.Count]; + new_ifaces.CopyTo (ret, 0); + return ret; + } + + /// <summary> + /// This function returns the interfaces in the type `t'. Works with + /// both types and TypeBuilders. + /// </summary> + public static Type [] GetInterfaces (Type t) + { + // + // The reason for catching the Array case is that Reflection.Emit + // will not return a TypeBuilder for Array types of TypeBuilder types, + // but will still throw an exception if we try to call GetInterfaces + // on the type. + // + // Since the array interfaces are always constant, we return those for + // the System.Array + // + + if (t.IsArray) + t = TypeManager.array_type; + + if (t is TypeBuilder){ + Type [] parent_ifaces; + + if (t.BaseType == null) + parent_ifaces = NoTypes; + else + parent_ifaces = GetInterfaces (t.BaseType); + Type [] type_ifaces = (Type []) builder_to_ifaces [t]; + if (type_ifaces == null) + type_ifaces = NoTypes; + + int parent_count = parent_ifaces.Length; + Type [] result = new Type [parent_count + type_ifaces.Length]; + parent_ifaces.CopyTo (result, 0); + type_ifaces.CopyTo (result, parent_count); + + return result; + } else + return t.GetInterfaces (); + } + + /// <remarks> + /// The following is used to check if a given type implements an interface. + /// The cache helps us reduce the expense of hitting Type.GetInterfaces everytime. + /// </remarks> + public static bool ImplementsInterface (Type t, Type iface) + { + Type [] interfaces; + + // + // FIXME OPTIMIZATION: + // as soon as we hit a non-TypeBuiler in the interface + // chain, we could return, as the `Type.GetInterfaces' + // will return all the interfaces implement by the type + // or its parents. + // + do { + interfaces = GetInterfaces (t); + + if (interfaces != null){ + foreach (Type i in interfaces){ + if (i == iface) + return true; + } + } + + t = t.BaseType; + } while (t != null); + + return false; + } + + // This is a custom version of Convert.ChangeType() which works + // with the TypeBuilder defined types when compiling corlib. + public static object ChangeType (object value, Type conversionType) + { + if (!(value is IConvertible)) + throw new ArgumentException (); + + IConvertible convertValue = (IConvertible) value; + CultureInfo ci = CultureInfo.CurrentCulture; + NumberFormatInfo provider = ci.NumberFormat; + + // + // We must use Type.Equals() here since `conversionType' is + // the TypeBuilder created version of a system type and not + // the system type itself. You cannot use Type.GetTypeCode() + // on such a type - it'd always return TypeCode.Object. + // + if (conversionType.Equals (typeof (Boolean))) + return (object)(convertValue.ToBoolean (provider)); + else if (conversionType.Equals (typeof (Byte))) + return (object)(convertValue.ToByte (provider)); + else if (conversionType.Equals (typeof (Char))) + return (object)(convertValue.ToChar (provider)); + else if (conversionType.Equals (typeof (DateTime))) + return (object)(convertValue.ToDateTime (provider)); + else if (conversionType.Equals (typeof (Decimal))) + return (object)(convertValue.ToDecimal (provider)); + else if (conversionType.Equals (typeof (Double))) + return (object)(convertValue.ToDouble (provider)); + else if (conversionType.Equals (typeof (Int16))) + return (object)(convertValue.ToInt16 (provider)); + else if (conversionType.Equals (typeof (Int32))) + return (object)(convertValue.ToInt32 (provider)); + else if (conversionType.Equals (typeof (Int64))) + return (object)(convertValue.ToInt64 (provider)); + else if (conversionType.Equals (typeof (SByte))) + return (object)(convertValue.ToSByte (provider)); + else if (conversionType.Equals (typeof (Single))) + return (object)(convertValue.ToSingle (provider)); + else if (conversionType.Equals (typeof (String))) + return (object)(convertValue.ToString (provider)); + else if (conversionType.Equals (typeof (UInt16))) + return (object)(convertValue.ToUInt16 (provider)); + else if (conversionType.Equals (typeof (UInt32))) + return (object)(convertValue.ToUInt32 (provider)); + else if (conversionType.Equals (typeof (UInt64))) + return (object)(convertValue.ToUInt64 (provider)); + else if (conversionType.Equals (typeof (Object))) + return (object)(value); + else + throw new InvalidCastException (); + } + + // + // This is needed, because enumerations from assemblies + // do not report their underlyingtype, but they report + // themselves + // + public static Type EnumToUnderlying (Type t) + { + if (t == TypeManager.enum_type) + return t; + + t = t.UnderlyingSystemType; + if (!TypeManager.IsEnumType (t)) + return t; + + if (t is TypeBuilder) { + // slow path needed to compile corlib + if (t == TypeManager.bool_type || + t == TypeManager.byte_type || + t == TypeManager.sbyte_type || + t == TypeManager.char_type || + t == TypeManager.short_type || + t == TypeManager.ushort_type || + t == TypeManager.int32_type || + t == TypeManager.uint32_type || + t == TypeManager.int64_type || + t == TypeManager.uint64_type) + return t; + throw new Exception ("Unhandled typecode in enum " + " from " + t.AssemblyQualifiedName); + } + TypeCode tc = Type.GetTypeCode (t); + + switch (tc){ + case TypeCode.Boolean: + return TypeManager.bool_type; + case TypeCode.Byte: + return TypeManager.byte_type; + case TypeCode.SByte: + return TypeManager.sbyte_type; + case TypeCode.Char: + return TypeManager.char_type; + case TypeCode.Int16: + return TypeManager.short_type; + case TypeCode.UInt16: + return TypeManager.ushort_type; + case TypeCode.Int32: + return TypeManager.int32_type; + case TypeCode.UInt32: + return TypeManager.uint32_type; + case TypeCode.Int64: + return TypeManager.int64_type; + case TypeCode.UInt64: + return TypeManager.uint64_type; + } + throw new Exception ("Unhandled typecode in enum " + tc + " from " + t.AssemblyQualifiedName); + } + + // + // When compiling corlib and called with one of the core types, return + // the corresponding typebuilder for that type. + // + public static Type TypeToCoreType (Type t) + { + if (RootContext.StdLib || (t is TypeBuilder)) + return t; + + TypeCode tc = Type.GetTypeCode (t); + + switch (tc){ + case TypeCode.Boolean: + return TypeManager.bool_type; + case TypeCode.Byte: + return TypeManager.byte_type; + case TypeCode.SByte: + return TypeManager.sbyte_type; + case TypeCode.Char: + return TypeManager.char_type; + case TypeCode.Int16: + return TypeManager.short_type; + case TypeCode.UInt16: + return TypeManager.ushort_type; + case TypeCode.Int32: + return TypeManager.int32_type; + case TypeCode.UInt32: + return TypeManager.uint32_type; + case TypeCode.Int64: + return TypeManager.int64_type; + case TypeCode.UInt64: + return TypeManager.uint64_type; + case TypeCode.String: + return TypeManager.string_type; + default: + if (t == typeof (void)) + return TypeManager.void_type; + if (t == typeof (object)) + return TypeManager.object_type; + if (t == typeof (System.Type)) + return TypeManager.type_type; + return t; + } + } + + /// <summary> + /// Utility function that can be used to probe whether a type + /// is managed or not. + /// </summary> + public static bool VerifyUnManaged (Type t, Location loc) + { + if (t.IsValueType || t.IsPointer){ + // + // FIXME: this is more complex, we actually need to + // make sure that the type does not contain any + // classes itself + // + return true; + } + + if (!RootContext.StdLib && (t == TypeManager.decimal_type)) + // We need this explicit check here to make it work when + // compiling corlib. + return true; + + Report.Error ( + 208, loc, + "Cannot take the address or size of a variable of a managed type ('" + + CSharpName (t) + "')"); + return false; + } + + /// <summary> + /// Returns the name of the indexer in a given type. + /// </summary> + /// <remarks> + /// The default is not always `Item'. The user can change this behaviour by + /// using the DefaultMemberAttribute in the class. + /// + /// For example, the String class indexer is named `Chars' not `Item' + /// </remarks> + public static string IndexerPropertyName (Type t) + { + if (t is TypeBuilder) { + if (t.IsInterface) { + Interface i = LookupInterface (t); + + if ((i == null) || (i.IndexerName == null)) + return "Item"; + + return i.IndexerName; + } else { + TypeContainer tc = LookupTypeContainer (t); + + if ((tc == null) || (tc.IndexerName == null)) + return "Item"; + + return tc.IndexerName; + } + } + + System.Attribute attr = System.Attribute.GetCustomAttribute ( + t, TypeManager.default_member_type); + if (attr != null){ + DefaultMemberAttribute dma = (DefaultMemberAttribute) attr; + return dma.MemberName; + } + + return "Item"; + } + + public static void MakePinned (LocalBuilder builder) + { + // + // FIXME: Flag the "LocalBuilder" type as being + // pinned. Figure out API. + // + } + + + // + // Returns whether the array of memberinfos contains the given method + // + static bool ArrayContainsMethod (MemberInfo [] array, MethodBase new_method) + { + Type [] new_args = TypeManager.GetArgumentTypes (new_method); + + foreach (MethodBase method in array){ + if (method.Name != new_method.Name) + continue; + + Type [] old_args = TypeManager.GetArgumentTypes (method); + int old_count = old_args.Length; + int i; + + if (new_args.Length != old_count) + continue; + + for (i = 0; i < old_count; i++){ + if (old_args [i] != new_args [i]) + break; + } + if (i != old_count) + continue; + + return true; + } + return false; + } + + // + // We copy methods from `new_members' into `target_list' if the signature + // for the method from in the new list does not exist in the target_list + // + // The name is assumed to be the same. + // + public static ArrayList CopyNewMethods (ArrayList target_list, MemberList new_members) + { + if (target_list == null){ + target_list = new ArrayList (); + + foreach (MemberInfo mi in new_members){ + if (mi is MethodBase) + target_list.Add (mi); + } + return target_list; + } + + MemberInfo [] target_array = new MemberInfo [target_list.Count]; + target_list.CopyTo (target_array, 0); + + foreach (MemberInfo mi in new_members){ + MethodBase new_method = (MethodBase) mi; + + if (!ArrayContainsMethod (target_array, new_method)) + target_list.Add (new_method); + } + return target_list; + } + + [Flags] + public enum MethodFlags { + IsObsolete = 1, + IsObsoleteError = 2, + ShouldIgnore = 3 + } + + // + // Returns the TypeManager.MethodFlags for this method. + // This emits an error 619 / warning 618 if the method is obsolete. + // In the former case, TypeManager.MethodFlags.IsObsoleteError is returned. + // + static public MethodFlags GetMethodFlags (MethodBase mb, Location loc) + { + MethodFlags flags = 0; + + if (mb.DeclaringType is TypeBuilder){ + MethodData method = (MethodData) builder_to_method [mb]; + if (method == null) { + // FIXME: implement Obsolete attribute on Property, + // Indexer and Event. + return 0; + } + + return method.GetMethodFlags (loc); + } + + object [] attrs = mb.GetCustomAttributes (true); + foreach (object ta in attrs){ + if (!(ta is System.Attribute)){ + Console.WriteLine ("Unknown type in GetMethodFlags: " + ta); + continue; + } + System.Attribute a = (System.Attribute) ta; + if (a.TypeId == TypeManager.obsolete_attribute_type){ + ObsoleteAttribute oa = (ObsoleteAttribute) a; + + string method_desc = TypeManager.CSharpSignature (mb); + + if (oa.IsError) { + Report.Error (619, loc, "Method `" + method_desc + + "' is obsolete: `" + oa.Message + "'"); + return MethodFlags.IsObsoleteError; + } else + Report.Warning (618, loc, "Method `" + method_desc + + "' is obsolete: `" + oa.Message + "'"); + + flags |= MethodFlags.IsObsolete; + + continue; + } + + // + // Skip over conditional code. + // + if (a.TypeId == TypeManager.conditional_attribute_type){ + ConditionalAttribute ca = (ConditionalAttribute) a; + + if (RootContext.AllDefines [ca.ConditionString] == null) + flags |= MethodFlags.ShouldIgnore; + } + } + + return flags; + } + +#region MemberLookup implementation + + // + // Name of the member + // + static string closure_name; + + // + // Whether we allow private members in the result (since FindMembers + // uses NonPublic for both protected and private), we need to distinguish. + // + static bool closure_private_ok; + + // + // Who is invoking us and which type is being queried currently. + // + static Type closure_invocation_type; + static Type closure_queried_type; + static Type closure_start_type; + + // + // The assembly that defines the type is that is calling us + // + static Assembly closure_invocation_assembly; + + // + // This filter filters by name + whether it is ok to include private + // members in the search + // + static internal bool FilterWithClosure (MemberInfo m, object filter_criteria) + { + // + // Hack: we know that the filter criteria will always be in the `closure' + // fields. + // + + if ((filter_criteria != null) && (m.Name != (string) filter_criteria)) + return false; + + if ((closure_start_type == closure_invocation_type) && + (m.DeclaringType == closure_invocation_type)) + return true; + + // + // Ugly: we need to find out the type of `m', and depending + // on this, tell whether we accept or not + // + if (m is MethodBase){ + MethodBase mb = (MethodBase) m; + MethodAttributes ma = mb.Attributes & MethodAttributes.MemberAccessMask; + + if (ma == MethodAttributes.Private) + return closure_private_ok || (closure_invocation_type == m.DeclaringType); + + // + // FamAndAssem requires that we not only derivate, but we are on the + // same assembly. + // + if (ma == MethodAttributes.FamANDAssem){ + if (closure_invocation_assembly != mb.DeclaringType.Assembly) + return false; + } + + // Assembly and FamORAssem succeed if we're in the same assembly. + if ((ma == MethodAttributes.Assembly) || (ma == MethodAttributes.FamORAssem)){ + if (closure_invocation_assembly == mb.DeclaringType.Assembly) + return true; + } + + // We already know that we aren't in the same assembly. + if (ma == MethodAttributes.Assembly) + return false; + + // Family and FamANDAssem require that we derive. + if ((ma == MethodAttributes.Family) || (ma == MethodAttributes.FamANDAssem)){ + if (closure_invocation_type == null) + return false; + + if (!IsSubclassOrNestedChildOf (closure_invocation_type, mb.DeclaringType)) + return false; + + return true; + } + + // Public. + return true; + } + + if (m is FieldInfo){ + FieldInfo fi = (FieldInfo) m; + FieldAttributes fa = fi.Attributes & FieldAttributes.FieldAccessMask; + + if (fa == FieldAttributes.Private) + return closure_private_ok || (closure_invocation_type == m.DeclaringType); + + // + // FamAndAssem requires that we not only derivate, but we are on the + // same assembly. + // + if (fa == FieldAttributes.FamANDAssem){ + if (closure_invocation_assembly != fi.DeclaringType.Assembly) + return false; + } + + // Assembly and FamORAssem succeed if we're in the same assembly. + if ((fa == FieldAttributes.Assembly) || (fa == FieldAttributes.FamORAssem)){ + if (closure_invocation_assembly == fi.DeclaringType.Assembly) + return true; + } + + // We already know that we aren't in the same assembly. + if (fa == FieldAttributes.Assembly) + return false; + + // Family and FamANDAssem require that we derive. + if ((fa == FieldAttributes.Family) || (fa == FieldAttributes.FamANDAssem)){ + if (closure_invocation_type == null) + return false; + + if (!IsSubclassOrNestedChildOf (closure_invocation_type, fi.DeclaringType)) + return false; + + // Although a derived class can access protected members of its base class + // it cannot do so through an instance of the base class (CS1540). + if ((closure_invocation_type != closure_start_type) && + closure_invocation_type.IsSubclassOf (closure_start_type)) + return false; + + return true; + } + + // Public. + return true; + } + + // + // EventInfos and PropertyInfos, return true + // + return true; + } + + static MemberFilter FilterWithClosure_delegate = new MemberFilter (FilterWithClosure); + + // + // Looks up a member called `name' in the `queried_type'. This lookup + // is done by code that is contained in the definition for `invocation_type'. + // + // The binding flags are `bf' and the kind of members being looked up are `mt' + // + // Returns an array of a single element for everything but Methods/Constructors + // that might return multiple matches. + // + public static MemberInfo [] MemberLookup (Type invocation_type, Type queried_type, + MemberTypes mt, BindingFlags original_bf, string name) + { + Timer.StartTimer (TimerType.MemberLookup); + + MemberInfo[] retval = RealMemberLookup (invocation_type, queried_type, + mt, original_bf, name); + + Timer.StopTimer (TimerType.MemberLookup); + + return retval; + } + + static MemberInfo [] RealMemberLookup (Type invocation_type, Type queried_type, + MemberTypes mt, BindingFlags original_bf, string name) + { + BindingFlags bf = original_bf; + + ArrayList method_list = null; + Type current_type = queried_type; + bool searching = (original_bf & BindingFlags.DeclaredOnly) == 0; + bool private_ok; + bool always_ok_flag = false; + bool skip_iface_check = true, used_cache = false; + + closure_name = name; + closure_invocation_type = invocation_type; + closure_invocation_assembly = invocation_type != null ? invocation_type.Assembly : null; + closure_start_type = queried_type; + + // + // If we are a nested class, we always have access to our container + // type names + // + if (invocation_type != null){ + string invocation_name = invocation_type.FullName; + if (invocation_name.IndexOf ('+') != -1){ + string container = queried_type.FullName + "+"; + int container_length = container.Length; + + if (invocation_name.Length > container_length){ + string shared = invocation_name.Substring (0, container_length); + + if (shared == container) + always_ok_flag = true; + } + } + } + + do { + MemberList list; + + // + // `NonPublic' is lame, because it includes both protected and + // private methods, so we need to control this behavior by + // explicitly tracking if a private method is ok or not. + // + // The possible cases are: + // public, private and protected (internal does not come into the + // equation) + // + if (invocation_type != null){ + if (invocation_type == current_type){ + private_ok = (bf & BindingFlags.NonPublic) != 0; + } else + private_ok = always_ok_flag; + + if (invocation_type.IsSubclassOf (current_type)) + private_ok = true; + + if (private_ok) + bf = original_bf | BindingFlags.NonPublic; + } else { + private_ok = false; + bf = original_bf & ~BindingFlags.NonPublic; + } + + closure_private_ok = private_ok; + closure_queried_type = current_type; + + Timer.StopTimer (TimerType.MemberLookup); + + list = MemberLookup_FindMembers (current_type, mt, bf, name, out used_cache); + + Timer.StartTimer (TimerType.MemberLookup); + + // + // When queried for an interface type, the cache will automatically check all + // inherited members, so we don't need to do this here. However, this only + // works if we already used the cache in the first iteration of this loop. + // + // If we used the cache in any further iteration, we can still terminate the + // loop since the cache always looks in all parent classes. + // + + if (used_cache) + searching = false; + else + skip_iface_check = false; + + if (current_type == TypeManager.object_type) + searching = false; + else { + current_type = current_type.BaseType; + + // + // This happens with interfaces, they have a null + // basetype. Look members up in the Object class. + // + if (current_type == null) + current_type = TypeManager.object_type; + } + + if (list.Count == 0) + continue; + + // + // Events and types are returned by both `static' and `instance' + // searches, which means that our above FindMembers will + // return two copies of the same. + // + if (list.Count == 1 && !(list [0] is MethodBase)){ + return (MemberInfo []) list; + } + + // + // Multiple properties: we query those just to find out the indexer + // name + // + if (list [0] is PropertyInfo) + return (MemberInfo []) list; + + // + // We found methods, turn the search into "method scan" + // mode. + // + + method_list = CopyNewMethods (method_list, list); + mt &= (MemberTypes.Method | MemberTypes.Constructor); + } while (searching); + + if (method_list != null && method_list.Count > 0) + return (MemberInfo []) method_list.ToArray (typeof (MemberInfo)); + + // + // This happens if we already used the cache in the first iteration, in this case + // the cache already looked in all interfaces. + // + if (skip_iface_check) + return null; + + // + // Interfaces do not list members they inherit, so we have to + // scan those. + // + if (!queried_type.IsInterface) + return null; + + if (queried_type.IsArray) + queried_type = TypeManager.array_type; + + Type [] ifaces = GetInterfaces (queried_type); + if (ifaces == null) + return null; + + foreach (Type itype in ifaces){ + MemberInfo [] x; + + x = MemberLookup (null, itype, mt, bf, name); + if (x != null) + return x; + } + + return null; + } +#endregion + +} + +/// <summary> +/// There is exactly one instance of this class per type. +/// </summary> +public sealed class TypeHandle : IMemberContainer { + public readonly TypeHandle BaseType; + + readonly int id = ++next_id; + static int next_id = 0; + + /// <summary> + /// Lookup a TypeHandle instance for the given type. If the type doesn't have + /// a TypeHandle yet, a new instance of it is created. This static method + /// ensures that we'll only have one TypeHandle instance per type. + /// </summary> + public static TypeHandle GetTypeHandle (Type t) + { + TypeHandle handle = (TypeHandle) type_hash [t]; + if (handle != null) + return handle; + + handle = new TypeHandle (t); + type_hash.Add (t, handle); + return handle; + } + + /// <summary> + /// Returns the TypeHandle for TypeManager.object_type. + /// </summary> + public static IMemberContainer ObjectType { + get { + if (object_type != null) + return object_type; + + object_type = GetTypeHandle (TypeManager.object_type); + + return object_type; + } + } + + /// <summary> + /// Returns the TypeHandle for TypeManager.array_type. + /// </summary> + public static IMemberContainer ArrayType { + get { + if (array_type != null) + return array_type; + + array_type = GetTypeHandle (TypeManager.array_type); + + return array_type; + } + } + + private static PtrHashtable type_hash = new PtrHashtable (); + + private static TypeHandle object_type = null; + private static TypeHandle array_type = null; + + private Type type; + private bool is_interface; + private MemberCache member_cache; + + private TypeHandle (Type type) + { + this.type = type; + if (type.BaseType != null) + BaseType = GetTypeHandle (type.BaseType); + else if ((type != TypeManager.object_type) && (type != typeof (object))) + is_interface = true; + this.member_cache = new MemberCache (this); + } + + // IMemberContainer methods + + public string Name { + get { + return type.FullName; + } + } + + public Type Type { + get { + return type; + } + } + + public IMemberContainer Parent { + get { + return BaseType; + } + } + + public bool IsInterface { + get { + return is_interface; + } + } + + public MemberList GetMembers (MemberTypes mt, BindingFlags bf) + { + if (mt == MemberTypes.Event) + return new MemberList (type.GetEvents (bf | BindingFlags.DeclaredOnly)); + else + return new MemberList (type.FindMembers (mt, bf | BindingFlags.DeclaredOnly, + null, null)); + } + + // IMemberFinder methods + + public MemberList FindMembers (MemberTypes mt, BindingFlags bf, string name, + MemberFilter filter, object criteria) + { + return member_cache.FindMembers (mt, bf, name, filter, criteria); + } + + public MemberCache MemberCache { + get { + return member_cache; + } + } + + public override string ToString () + { + if (BaseType != null) + return "TypeHandle (" + id + "," + Name + " : " + BaseType + ")"; + else + return "TypeHandle (" + id + "," + Name + ")"; + } +} + +} |