[csdoc-patches] csdoc/src/mcsdoc/mcs .cvsignore,NONE,1.1 AssemblyInfo.cs,NONE,1.1 ChangeLog,NONE,1.1
Status: Planning
Brought to you by:
mastergaurav
Update of /cvsroot/csdoc/csdoc/src/mcsdoc/mcs In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23564 Added Files: .cvsignore AssemblyInfo.cs ChangeLog NOTES OTODO README TODO anonymous.cs assign.cs attribute.cs cfold.cs class.cs codegen.cs compiler.doc compiler.sln const.cs constant.cs convert.cs cs-parser.jay cs-tokenizer.cs decl.cs delegate.cs driver.cs ecore.cs enum.cs errors.cs expression.cs flowanalysis.cs gen-il.cs gen-treedump.cs generic.cs genericparser.cs interface.cs iterators.cs literal.cs location.cs makefile makefile.gnu makefile.old mcs.exe.config mcs.exe.sources modifiers.cs namespace.cs old-code.cs parameter.cs parameterCollection.cs parser.cs pending.cs report.cs rootcontext.cs statement.cs statementCollection.cs support.cs symbolwriter.cs tree.cs typemanager.cs Log Message: 2004-10-28 Gaurav Vaish * **/* : Getting the latest sync from mono repository. Will resume work pretty soon. Moving final work from csdoc to mcsdoc. --- NEW FILE: .cvsignore --- *.mdb *.pdb Web References Web References Web References bin compiler.csproj compiler.csproj.user compiler.exe compiler.pdb compiler.suo cs-parser.cs mcs obj semantic.cache y.output mcs.exe --- NEW FILE: AssemblyInfo.cs --- using System.Reflection; using System.Runtime.CompilerServices; [assembly: AssemblyVersion("1.1.1")] [assembly: AssemblyTitle ("Mono C# Compiler")] [assembly: AssemblyDescription ("Mono C# Compiler")] [assembly: AssemblyCopyright ("2001, 2002, 2003 Ximian, Inc.")] [assembly: AssemblyCompany ("Ximian, Inc.")] --- NEW FILE: ChangeLog --- 2004-10-27 Miguel de Icaza <mi...@xi...> * expression.cs (LocalVariableReference.DoResolveLValue): Check for obsolete use of a variable here. Fixes regression on errors cs0619-25 and cs0619-26. 2004-10-27 Marek Safar <mar...@se...> Fix #62358, implemented security attribute encoding. * attribute.cs (Attribute.CheckSecurityActionValididy): New method. Tests permitted SecurityAction for assembly or other types. (Assembly.ExtractSecurityPermissionSet): New method. Transforms data from SecurityPermissionAttribute to PermisionSet class. * class.cs (ApplyAttributeBuilder): Added special handling for System.Security.Permissions.SecurityAttribute based types. * codegen.cs (AssemblyClass.ApplyAttributeBuilder): Added [...17202 lines suppressed...] 2001-04-27 Miguel de Icaza <mi...@xi...> * System.CodeDOM/CodeBinaryOperatorExpression.cs: Rearrange enum to match the values in System.CodeDOM. Divid renamed to Divide. * System.CodeDOM/CodeForLoopStatement.cs: Always have valid statements. (Statements.set): remove. * System.CodeDOM/CodeCatchClause.cs: always have a valid statements. * System.CodeDOM/CodeIfStatement.cs: trueStatements and falseStatements always have valid values. * cs-parser.jay: Use System.CodeDOM now. --- NEW FILE: NOTES --- * Notes on improving error handling in MCS (from Axel Schreiner <at...@cs...>) I included the 'recover' example with C# as well. Currently the package is at <http://www.cs.rit.edu/~ats/projects/jay/>. I did change some names and the embedding that the user does somewhat, i.e., it is not directly compatible with what you did. Here is the important part about error recovery. To make the typical iterations bullet-proof, code them as follows: opt : // null | opt WORD { yyErrorFlag = 0; } | opt error seq : WORD | seq WORD { yyErrorFlag = 0; } | error | seq error list : WORD | list ',' WORD { yyErrorFlag = 0; } | error | list error | list error WORD { yyErrorFlag = 0; } | list ',' error i.e., throw in 'error' wherever a token can be. 'yyErrorFlag' need not be set to zero, but if it is done this way, second errors are caught earlier. This may introduce s/r conflicts, but they tend to be harmless. In your case -- the comment concerning error recovery at the beginning of your compiler jay file -- just adding 'error' to the different global things won't work. Your example will already have started to advance in one of the rules and 'error' is then not in the lookahead of wherever the parse then is. You need to put 'error' into the iteration above those global things. --- NEW FILE: OTODO --- ---- This is a list of old tasks, just here for historical value ---- Open question: Create a toplevel block for anonymous methods? Anonymous Methods ----------------- Plan: * Resolve anonymous methods before. * Each time a Local matches, if the mode is `InAnonymous', flag the VariableInfo for `proxying'. * During Resolve track the depth required for local variables. * Before Emit, create proxy classes with proper depth. * Emit. Notes on memory allocation -------------------------- Outdated: A run of the AllocationProfile shows that the compiler allocates roughly 30 megabytes of strings. From those, 20 megabytes come from LookupType. See the notes on current_container problems below on memory usage. LookupTypeReflection: --------------------- With something like `System.Object', LookupTypeReflection will be called twice: once to find out that `System' is not a type and once for System.Object. This is required because System.Reflection requires that the type/nested types are not separated by a dot but by a plus sign. A nested class would be My+Class (My being the toplevel, Class the nested one). It is interesting to look at the most called lookups when bootstrapping MCS: 647 LTR: ArrayList 713 LTR: System.Globalization 822 LTR: System.Object+Expression 904 LTR: Mono.CSharp.ArrayList 976 LTR: System.Runtime.CompilerServices 999 LTR: Type 1118 LTR: System.Runtime 1208 LTR: Mono.CSharp.Type 1373 LTR: Mono.Languages 1599 LTR: System.Diagnostics 2036 LTR: System.Text 2302 LTR: System.Reflection.Emit 2515 LTR: System.Collections 4527 LTR: System.Reflection 22273 LTR: Mono.CSharp 24245 LTR: System 27005 LTR: Mono Analysis: The top 9 lookups are done for things which are not types. Mono.CSharp.Type happens to be a common lookup: the class Type used heavily in the compiler in the default namespace. RED FLAG: Then `Type' is looked up alone a lot of the time, this happens in parameter declarations and am not entirely sure that this is correct (FindType will pass to LookupInterfaceOrClass a the current_type.FullName, which for some reason is null!). This seems to be a problem with a lost piece of context during FindType. System.Object is also used a lot as a toplevel class, and we assume it will have children, we should just shortcut this. A cache: Adding a cache and adding a catch for `System.Object' to flag that it wont be the root of a hierarchy reduced the MCS bootstrap time from 10.22 seconds to 8.90 seconds. This cache is currently enabled with SIMPLE_SPEEDUP in typemanager.cs. Memory consumption went down from 74 megs to 65 megs with this change. Major tasks: ------------ Pinned and volatile require type modifiers that can not be encoded with Reflection.Emit. * Revisit Primary-expression, as it has now been split into non-array-creation-expression and array-creation-expression. * Emit `pinned' for pinned local variables. Both `modreq' and pinned will require special hacks in the compiler. * Make sure that we are pinning the right variable * local_variable_declaration Not sure that this grammar is correct, we might have to resolve this during semantic analysis. * Optimizations In Indexers and Properties, probably support an EmitWithDup That emits the code to call Get and then leaves a this pointer in the stack, so that later a Store can be emitted using that this pointer (consider Property++ or Indexer++) * Use of local temporary in UnaryMutator We should get rid of the Localtemporary there for some cases This turns out to be very complex, at least for the post-version, because this case: a = i++ To produce optimal code, it is necessary for UnaryMutator to know that it is being assigned to a variable (the way the stack is laid out using dup requires the store to happen inside UnaryMutator). * Interface indexers I have not figured out why the Microsoft version puts an `instance' attribute, and I am not generating this `instance' attribute. Explanation: The reason for the `instance' attribute on indexers is that indexers only apply to instances * Check for Final when overriding, if the parent is Final, then we cant allow an override. Implement base indexer access. current_container/current_namespace and the DeclSpace ----------------------------------------------------- We are storing fully qualified names in the DeclSpace instead of the node, this is because `current_namespace' (Namepsace) is not a DeclSpace like `current_container'. The reason for storing the full names today is this: namespace X { class Y { } } namespace A { class Y { } } The problem is that we only use the namespace stack to track the "prefix" for typecontainers, but they are not typecontainers themselves, so we have to use fully qualified names, because both A.X and A.Y would be entered in the toplevel type container. If we use the short names, there would be a name clash. To fix this problem, we have to make namespaces DeclSpaces. The full size, contrasted with the size that could be stored is: corlib: Size of strings held: 368901 Size of strings short: 147863 System: Size of strings held: 212677 Size of strings short: 97521 System.XML: Size of strings held: 128055 Size of strings short: 35782 System.Data: Size of strings held: 117896 Size of strings short: 36153 System.Web: Size of strings held: 194527 Size of strings short: 58064 System.Windows.Forms: Size of strings held: 220495 Size of strings short: 64923 The use of DottedName --------------------- We could probably use a different system to represent names, like this: class Name { string simplename; Name parent; } So `System.ComponentModel' becomes: x: (System, null) y: (ComponentModel, x) The problem is that we would still need to construct the name to pass to GetType. This has been now implemented, its called "QualifiedIdentifier" TODO: 1. Create a "partial" emit context for each TypeContainer.. 2. EmitContext should be partially constructed. No IL Generator. interface_type review. parameter_array, line 952: `note: must be a single dimension array type'. Validate this Instance idea ------------- It would be nice to have things that can be "instances" to have an EmitInstance method (this would default to nothing). The idea is to be able to use efficiently the instance data on stack manipulations, as opposed to the current scheme, where we basically have a few special cases. * `yield' is no longer a keyword, it only has special meaning before a return or break keywords. * Study side effects with assign * Study TemporaryStorage/LocalStorage -> Merge/rename --- NEW FILE: README --- These are the sources to the Mono C# compiler --------------------------------------------- Read the mcs/docs/compiler.txt for an overview of the compiler. Testing the Compiler -------------------- You might want to use the `make btest' in this directory to have the compiler bootstrap itself, this is the basic regression test. Before commiting changes to MCS, make sure that all the tests in `mcs/tests' pass, and all the tests in 'mcs/errors' have the expected result, type: cd mcs # The top-level 'mcs' directory make compiler-tests If you want to test the installed compiler, you can run: cd mcs # The top-level 'mcs' directory make test-installed-compiler Full Bootstrap ============== To finally ensure the state of the compiler, it is ideal to do a full bootstrap, to do this, do: cd mcs make clean; make make install That installs the compiler and assemblies compiled by the new compiler. Then, repeat that step again: make clean make If things work, the compiler has not added a new regression while building the mscorlib and the compiler itself. Tests ===== When bugs are fixed, new tests must be added to the `mcs/tests' directory to excercise the problem and to guarantee that we keep the compiler in a good state. When an error is reported, it should be added to mcs/errors. We try to make the errors numbers be the same as the ones in Microsoft C#, if this is not possible, allocate a negative error number, and list it in mcs/errors/errors.txt --- NEW FILE: TODO --- NEW NOTES: ---------- ImplicitStandardConversionExists and ImplicitStandardConversion should always be the same, but there are a few diverging lines that must be studied: if (expr_type == target_type && !(expr is NullLiteral)) return expr; vs: if (expr_type == target_type) return true; Null Type --------- Need to introduce the NullType concept into the compiler, to address a few small buglets and remove the hardcoded values for NullLiteral. NullLiteral will be the only expression that has the NullType as its type. This is what must be used to test for Null literals, instead of `is NullLiteral', and this will introduce a couple of fixes to the rules. Revert Martin's patch to Conditional expression that worked around this bug: Reference r = xx ? null : null The right fix is to introduce NullType **************************************************************************************** * * The information on the rest of this file is mostly outdated, and its kept here for * historical reasons * **************************************************************************************** Error Reporting: ---------------- * Make yyerror show a nice syntax error, instead of the current mess. Iterators --------- * Reset should throw not implemented now. Optimization ideas ------------------ Currently when we build a type cache, it contains private members, internal members, and internal protected members; We should trim these out, as it shows up on the profile. We create too many Arraylists; When we know the size, we should create an array; During parsing we use arraylists to accumulate data, like this: thing: thing_list : thing { $$ =new ArrayList (); $$.Add ($1); } | thing_list thing { ArrayList a = $1; a.Add ($2); $$ = a; } We probably could start using "Pairs" there: thing_list : thing { $$ = new Pair ($1, null); } | thing_list thing { Pair p = $1; $$ = new Pair ($2, $1); } EmitContext.ResolveTypeTree --------------------------- We should investigate its usage. The problem is that by default this will be set when calling FindType, that triggers a more expensive lookup. I believe we should pass the current EmitContext (which has this turned off by default) to ResolveType/REsolveTypeExpr and then have the routines that need ResolveType to pass null as the emit context. DeclareLocal audit ------------------ DeclareLocal is used in various statements. The audit should be done in two steps: * Identify all the declare locals. * Identify its uses. * Find if we can make wrapper functions for all of them. Then we can move DeclareLocal into a helper class. This is required to fix foreach in iterators. Large project: -------------- Drop FindMembers as our API and instead extract all the data out of a type the first time into our own datastructures, and use that to navigate and search the type instead of the callback based FindMembers. Martin has some some of this work with his TypeHandle code that we could use for this. Ideas: ------ Instead of the hack that *knows* about System.Object not having any children classes, we should just make it simple for a probe to know that there is no need for it. Dead Code Elimination bugs: --------------------------- I should also resolve all the children expressions in Switch, Fixed, Using. Major tasks: ------------ Properties and 17.6.3: Finish it. readonly variables and ref/out BUGS ---- * Break/Continue statements A finally block should reset the InLoop/LoopBegin/LoopEnd, as they are logically outside the scope of the loop. * Break/continue part 2. They should transfer control to the finally block if inside a try/catch block. * Method Registration and error CS111 The way we use the method registration to signal 111 is wrong. Method registration should only be used to register methodbuilders, we need an alternate method of checking for duplicates. * > // CSC sets beforefieldinit > class X { > // .cctor will be generated by compiler > public static readonly object O = new System.Object (); > public static void Main () {} > } > PENDING TASKS ------------- * Merge test 89 and test-34 * Code cleanup The information when registering a method in InternalParameters is duplicated, you can always get the types from the InternalParameters * Emit modreq for volatiles Handle modreq from public apis. * Merge tree.cs, rootcontext.cs OPTIMIZATIONS ------------- * User Defined Conversions is doing way too many calls to do union sets that are not needed * Add test case for destructors * Places that use `Ldelema' are basically places where I will be initializing a value type. I could apply an optimization to disable the implicit local temporary from being created (by using the method in New). * Dropping TypeContainer as an argument to EmitContext My theory is that I can get rid of the TypeBuilder completely from the EmitContext, and have typecasts where it is used (from DeclSpace to where it matters). The only pending problem is that the code that implements Aliases is on TypeContainer, and probably should go in DeclSpace. * Tests Write tests for the various reference conversions. We have test for all the numeric conversions. * Optimizations: variable allocation. When local variables of a type are required, we should request the variable and later release it when we are done, so that the same local variable slot can be reused later on. * Add a cache for the various GetArrayMethod operations. * MakeUnionSet Callers If the types are the same, there is no need to compute the unionset, we can just use the list from one of the types. * Factor the lookup code for class declarations an interfaces (interface.cs:GetInterfaceByName) RECOMMENDATIONS --------------- * Use of lexer.Location in the parser Currently we do: TOKEN nt TERMINAL nt TERMINAL nt3 { $$ = new Blah ($2, $4, $6, lexer.Location); } This is bad, because the lexer.Location is for the last item in `nt3' We need to change that to use this pattern: TOKEN { oob_stack.Push (lexer.Location) } nt TERMINAL nt TERMINAL nt3 { $$ = new Blah ($3, $5, $7, (Location) oob_stack.Pop ()); } Notice how numbering of the arguments changes as the { oob_stack.Push (lexer.Location) } takes a "slot" in the productions. --- NEW FILE: anonymous.cs --- // // anonymous.cs: Support for anonymous methods // // Author: // Miguel de Icaza (mi...@xi...) // // (C) 2003, 2004 Novell, Inc. // // TODO: Ideally, we should have the helper classes emited as a hierarchy to map // their nesting, and have the visibility set to private, instead of NestedAssembly // // // using System; using System.Text; using System.Collections; using System.Reflection; using System.Reflection.Emit; [...1098 lines suppressed...] if (scopes [b.ID] != null){ LinkScope (scope, b.ID); break; } } if (scope.ParentScope == null && ParentCaptureContext != null){ CaptureContext pcc = ParentCaptureContext; for (Block b = Host.ContainingBlock; b != null; b = b.Parent){ if (pcc.scopes [b.ID] != null){ pcc.LinkScope (scope, b.ID); break; } } } } } } } --- NEW FILE: assign.cs --- // // assign.cs: Assignments. // // Author: // Miguel de Icaza (mi...@xi...) // Martin Baulig (ma...@gn...) // // (C) 2001, 2002, 2003 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 is an extra version of Emit. If leave_copy is `true' // A copy of the expression will be left on the stack at the // end of the code generated for EmitAssign // void Emit (EmitContext ec, bool leave_copy); // // This method does the assignment // `source' will be stored into the location specified by `this' // if `leave_copy' is true, a copy of `source' will be left on the stack // if `prepare_for_load' is true, when `source' is emitted, there will // be data on the stack that it can use to compuatate its value. This is // for expressions like a [f ()] ++, where you can't call `f ()' twice. // void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load); /* For simple assignments, this interface is very simple, EmitAssign is called with source as the source expression and leave_copy and prepare_for_load false. For compound assignments it gets complicated. EmitAssign will be called as before, however, prepare_for_load will be true. The @source expression will contain an expression which calls Emit. So, the calls look like: this.EmitAssign (ec, source, false, true) -> source.Emit (ec); -> [...] -> this.Emit (ec, false); -> end this.Emit (ec, false); -> end [...] end source.Emit (ec); end this.EmitAssign (ec, source, false, true) When prepare_for_load is true, EmitAssign emits a `token' on the stack that Emit will use for its state. Let's take FieldExpr as an example. assume we are emitting f ().y += 1; Here is the call tree again. This time, each call is annotated with the IL it produces: this.EmitAssign (ec, source, false, true) call f dup Binary.Emit () this.Emit (ec, false); ldfld y end this.Emit (ec, false); IntConstant.Emit () ldc.i4.1 end IntConstant.Emit add end Binary.Emit () stfld end this.EmitAssign (ec, source, false, true) Observe two things: 1) EmitAssign left a token on the stack. It was the result of f (). 2) This token was used by Emit leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy of the expression at that point in evaluation. This is used for pre/post inc/dec and for a = x += y. Let's do the above example with leave_copy true in EmitAssign this.EmitAssign (ec, source, true, true) call f dup Binary.Emit () this.Emit (ec, false); ldfld y end this.Emit (ec, false); IntConstant.Emit () ldc.i4.1 end IntConstant.Emit add end Binary.Emit () dup stloc temp stfld ldloc temp end this.EmitAssign (ec, source, true, true) And with it true in Emit this.EmitAssign (ec, source, false, true) call f dup Binary.Emit () this.Emit (ec, true); ldfld y dup stloc temp end this.Emit (ec, true); IntConstant.Emit () ldc.i4.1 end IntConstant.Emit add end Binary.Emit () stfld ldloc temp end this.EmitAssign (ec, source, false, true) Note that these two examples are what happens for ++x and x++, respectively. */ } /// <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. /// /// If `is_address' is true, then the value that we store is the address to the /// real value, and not the value itself. /// /// This is needed for a value type, because otherwise you just end up making a /// copy of the value on the stack and modifying it. You really need a pointer /// to the origional value so that you can modify it in that location. This /// Does not happen with a class because a class is a pointer -- so you always /// get the indirection. /// /// The `is_address' stuff is really just a hack. We need to come up with a better /// way to handle it. /// </remarks> public class LocalTemporary : Expression, IMemoryLocation { LocalBuilder builder; bool is_address; public LocalTemporary (EmitContext ec, Type t) : this (ec, t, false) {} public LocalTemporary (EmitContext ec, Type t, bool is_address) { type = t; eclass = ExprClass.Value; loc = Location.Null; builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (t): t); this.is_address = is_address; } public LocalTemporary (LocalBuilder b, Type t) { type = t; eclass = ExprClass.Value; loc = Location.Null; builder = b; } public void Release (EmitContext ec) { ec.FreeTemporaryLocal (builder, type); builder = null; } public override Expression DoResolve (EmitContext ec) { return this; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; ig.Emit (OpCodes.Ldloc, builder); // we need to copy from the pointer if (is_address) LoadFromPtr (ig, type); } // NB: if you have `is_address' on the stack there must // be a managed pointer. Otherwise, it is the type from // the ctor. public void Store (EmitContext ec) { ILGenerator ig = ec.ig; ig.Emit (OpCodes.Stloc, builder); } public void AddressOf (EmitContext ec, AddressOp mode) { // if is_address, than this is just the address anyways, // so we just return this. ILGenerator ig = ec.ig; if (is_address) ig.Emit (OpCodes.Ldloc, builder); else ig.Emit (OpCodes.Ldloca, builder); } public bool PointsToAddress { get { return is_address; } } } /// <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; } protected virtual Assign GetEmbeddedAssign (Location loc) { return new Assign (this, loc); } 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 = ((Assign) source).GetEmbeddedAssign (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) { if (this is CompoundAssign) real_temp = temp = new LocalTemporary (ec, target.Type); else 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 (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 BinaryDelegate)) { error70 (ei, loc); return null; } } } if (source is New && target_type.IsValueType && (target.eclass != ExprClass.IndexerAccess) && (target.eclass != ExprClass.PropertyAccess)){ New n = (New) source; if (n.SetValueTypeVariable (target)) return n; else return null; } if (!(target is IAssignMethod) && (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.Error_UnexpectedKind ("variable or value", loc); return null; } else if ((RootContext.Version == LanguageVersion.ISO_1) && (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){ // // 1. if the source is explicitly convertible to the // target_type // source = Convert.ExplicitConversion (ec, source, target_type, loc); if (source == null){ Convert.Error_CannotImplicitConversion (loc, source_type, target_type); return null; } // // 2. and the original right side is implicitly convertible to // the type of target // if (Convert.ImplicitStandardConversionExists (ec, a.original_source, target_type)) return this; // // In the spec 2.4 they added: or if type of the target is int // and the operator is a shift operator... // if (source_type == TypeManager.int32_type && (b.Oper == Binary.Operator.LeftShift || b.Oper == Binary.Operator.RightShift)) return this; Convert.Error_CannotImplicitConversion (loc, a.original_source.Type, target_type); return null; } } source = Convert.ImplicitConversionRequired (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, false, false); 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; } IAssignMethod am = (IAssignMethod) target; 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; am.EmitAssign (ec, temp_source, !is_statement, this is CompoundAssign); 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; } protected CompoundAssign (CompoundAssign embedded, Location l) : this (embedded.op, embedded.target, embedded.source, l) { this.is_embedded = true; } protected override Assign GetEmbeddedAssign (Location loc) { return new CompoundAssign (this, loc); } public Expression ResolveSource (EmitContext ec) { return original_source.Resolve (ec); } public override Expression DoResolve (EmitContext ec) { original_source = original_source.Resolve (ec); if (original_source == null) return null; target = target.Resolve (ec); if (target == 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); } } } --- NEW FILE: attribute.cs --- // // attribute.cs: Attribute Handler // // Author: Ravi Pratap (ra...@xi...) // Marek Safar (mar...@se...) // // 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.Collections.Specialized; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; [...1526 lines suppressed...] if (excluded != null) return excluded == TRUE ? true : false; ConditionalAttribute[] attrs = mb.GetCustomAttributes (TypeManager.conditional_attribute_type, true) as ConditionalAttribute[]; if (attrs.Length == 0) { analyzed_method_excluded.Add (mb, FALSE); return false; } foreach (ConditionalAttribute a in attrs) { if (RootContext.AllDefines.Contains (a.ConditionString)) { analyzed_method_excluded.Add (mb, FALSE); return false; } } analyzed_method_excluded.Add (mb, TRUE); return true; } } } --- NEW FILE: cfold.cs --- // // cfold.cs: Constant Folding // // Author: // Miguel de Icaza (mi...@xi...) // // (C) 2002, 2003 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'. // [...1155 lines suppressed...] ((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; } } } --- NEW FILE: class.cs --- // // class.cs: Class and Struct handlers // // Authors: Miguel de Icaza (mi...@gn...) // Martin Baulig (ma...@gn...) // Marek Safar (mar...@se...) // // Licensed under the terms of the GNU GPL // // (C) 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) // // // 2002-10-11 Miguel de Icaza <mi...@xi...> // // * 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 [...7229 lines suppressed...] 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; } } } --- NEW FILE: codegen.cs --- // // codegen.cs: The code generator // // Author: // Miguel de Icaza (mi...@xi...) // // (C) 2001, 2002, 2003 Ximian, Inc. // (C) 2004 Novell, Inc. // //#define PRODUCTION using System; using System.IO; using System.Collections; using System.Collections.Specialized; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; [...1302 lines suppressed...] ApplyAttributeBuilder (null, new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0])); } public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder) { if (a != null && a.Type == TypeManager.cls_compliant_attribute_type) { Report.Warning (3012, a.Location, "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking"); return; } Builder.SetCustomAttribute (customBuilder); } public override string[] ValidAttributeTargets { get { return attribute_targets; } } } } --- NEW FILE: compiler.doc --- Compiler operation The compiler has a number of phases: * Parsing. Initially the compiler parses all the source files and keeps a parsed representation in memory. Very syntax error checking is performed at this point. The compiler stores the information in classes whose names represent the language construct, for example, the "if" construct is stored in an `If' class. A class is stored in a `Class'. * The TypeManager The TypeManager loads all the assemblies that were referenced by the programmer. The CLR type system is used as our repository for types defined as well. So the same interface that is used to query the types, properties and flags about system types is the same interface that we use for our types. As we work our way through the code generation and semantic analysis, new types are entered into the Type system through the use of System.Reflection.Emit. The TypeManager will lookup types on both the user defined types and on the system defined ones. So special care has to be used. The order in which we proceeed from here is important. * Base class resolution and type definition. Once the parsing has happened, the compiler resolves the inheritance tree for interfaces. This is done recursively and we catch recursive interface definitions here. After this is done, we continue on with classes. Classes have can have an optional "parent" inherit from or the implicit System.Object class (for normal builds, builds with /nostdlib will allow you to compile class System.Object with no parent). At this point we do some error checking and verify that the inherits/implements section of a class is correct (since we have previously built the interface inheritance). By the time we are done, all classes, structs and interfaces have been created using System.Reflection.Emit and registered with the Type Manager. This allows us to define fields and resolve argument names for methods, properties, indexers and events. * Field generation Fields are generated next, we go through all the type containers (classes and structs) and enter the fields into their types. * Method, Properties, Indexers and events definitions Now all the methods, constructors, properties, indexers and events are entered. They are only `defined' using System.Reflection.Emit. No code generation will happen until everything has been entered into System.Reflection.Emit. This is important because to actually generate code we need to know everything about the environment in which the code is being generated. * Code Generation At this point all the definitions have been entered into the type manager through System.Reflection.Emit. We can now use System.Reflection to query all the information about the types. Your normal semantic analysis and code generation phase lives here. * Statements Most of the statements are handled in the codegen.cs file. * Expressions * Error reporting We should try to use the `Report.Error' and `Report.Warning' classes which are part of the RootContext (there is even a property to access it). Error reporting should try to use the same codes that the Microsoft compiler uses (if only so we can track which errors we handle and which ones we dont). If there is an error which is specific to MSC, use negative numbers, and register the number in mcs/errors/errors.txt Try to write a test case for any error that you run into the code of the compiler if there is none already. Put your test case in a file called csNNNN.cs in the mcs/errors directory, and have the first two lines be: // csNNNN.cs: This is the description. // Line: XXX Where `XXX' is the line where the error ocurrs. We will later use this as a regression test suite for catching errors in the compiler. --- NEW FILE: compiler.sln --- Microsoft Visual Studio Solution File, Format Version 8.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "compiler", "compiler.csproj", "{896D1461-B76B-41C0-ABE6-ACA2BB4F7B5A}" ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject Global GlobalSection(SolutionConfiguration) = preSolution Debug = Debug Release = Release EndGlobalSection GlobalSection(ProjectConfiguration) = postSolution {896D1461-B76B-41C0-ABE6-ACA2BB4F7B5A}.Debug.ActiveCfg = Debug|.NET {896D1461-B76B-41C0-ABE6-ACA2BB4F7B5A}.Debug.Build.0 = Debug|.NET {896D1461-B76B-41C0-ABE6-ACA2BB4F7B5A}.Release.ActiveCfg = Release|.NET {896D1461-B76B-41C0-ABE6-ACA2BB4F7B5A}.Release.Build.0 = Release|.NET EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection GlobalSection(ExtensibilityAddIns) = postSolution EndGlobalSection EndGlobal --- NEW FILE: const.cs --- // // const.cs: Constant declarations. // // Author: // Miguel de Icaza (mi...@xi...) // // (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 : FieldMember { public Expression Expr; EmitContext const_ec; bool resolved = false; object ConstantValue = null; bool in_transit = false; public const int AllowedModifiers = Modifiers.NEW | Modifiers.PUBLIC | Modifiers.PROTECTED | Modifiers.INTERNAL | Modifiers.PRIVATE; public Const (TypeContainer parent, Expression constant_type, string name, Expression expr, int mod_flags, Attributes attrs, Location loc) : base (parent, constant_type, mod_flags, AllowedModifiers, new MemberName (name), null, attrs, loc) { Expr = expr; ModFlags |= Modifiers.STATIC; } 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 () { if (!base.Define ()) return false; const_ec = new EmitContext (Parent, Location, null, MemberType, ModFlags); Type ttype = MemberType; while (ttype.IsArray) ttype = TypeManager.GetElementType (ttype); if (!TypeManager.IsBuiltinType (ttype) && (!ttype.IsSubclassOf (TypeManager.enum_type)) && !(Expr is NullLiteral)) { Report.Error ( -3, Location, "Constant type is not valid (only system types are allowed)"); return false; } FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, FieldAttr); TypeManager.RegisterConstant (FieldBuilder, this); return true; } // // Changes the type of the constant expression `expr' to the Type `type' // Returns null on failure. // public static Constant ChangeType (Location loc, Constant expr, Type type) { if (type == TypeManager.object_type) return expr; bool fail; // from the null type to any reference-type. if (expr is NullLiteral && !type.IsValueType && !TypeManager.IsEnumType (type)) return NullLiteral.Null; if (!Convert.ImplicitStandardConversionExists (Convert.ConstantEC, expr, type)){ Convert.Error_CannotImplicitConversion (loc, expr.Type, type); return null; } object constant_value = TypeManager.ChangeType (expr.GetValue (), type, out fail); if (fail){ Convert.Error_CannotImplicitConversion (loc, expr.Type, type); // // We should always catch the error before this is ever // reached, by calling Convert.ImplicitStandardConversionExists // throw new Exception ( String.Format ("LookupConstantValue: This should never be reached {0} {1}", expr.Type, type)); } Constant retval; if (type == TypeManager.int32_type) retval = new IntConstant ((int) constant_value); else if (type == TypeManager.uint32_type) retval = new UIntConstant ((uint) constant_value); else if (type == TypeManager.int64_type) retval = new LongConstant ((long) constant_value); else if (type == TypeManager.uint64_type) retval = new ULongConstant ((ulong) constant_value); else if (type == TypeManager.float_type) retval = new FloatConstant ((float) constant_value); else if (type == TypeManager.double_type) retval = new DoubleConstant ((double) constant_value); else if (type == TypeManager.string_type) retval = new StringConstant ((string) constant_value); else if (type == TypeManager.short_type) retval = new ShortConstant ((short) constant_value); else if (type == TypeManager.ushort_type) retval = new UShortConstant ((ushort) constant_value); else if (type == TypeManager.sbyte_type) retval = new SByteConstant ((sbyte) constant_value); else if (type == TypeManager.byte_type) retval = new ByteConstant ((byte) constant_value); else if (type == TypeManager.char_type) retval = new CharConstant ((char) constant_value); else if (type == TypeManager.bool_type) retval = new BoolConstant ((bool) constant_value); else throw new Exception ("LookupConstantValue: Unhandled constant type: " + type); return retval; } /// <summary> /// Looks up the value of a constant field. Defines it if it hasn't /// already been. Similar to LookupEnumValue in spirit. /// </summary> public bool LookupConstantValue (out object value) { if (resolved) { value = ConstantValue; return true; } if (in_transit) { Report.Error (110, Location, "The evaluation of the constant value for `" + Name + "' involves a circular definition."); value = null; return false; } in_transit = true; int errors = Report.Errors; // // We might have cleared Expr ourselves in a recursive definition // if (Expr == null){ value = null; return false; } Expr = Expr.Resolve (const_ec); in_transit = false; if (Expr == null) { if (errors == Report.Errors) Report.Error (150, Location, "A constant value is expected"); value = null; return false; } Expression real_expr = Expr; Constant ce = Expr as Constant; if (ce == null){ UnCheckedExpr un_expr = Expr as UnCheckedExpr; CheckedExpr ch_expr = Expr as CheckedExpr; EmptyCast ec_expr = Expr as EmptyCast; 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 if ((ec_expr != null) && (ec_expr.Child is Constant)) Expr = ec_expr.Child; else if (Expr is ArrayCreation){ Report.Error (133, Location, "Arrays can not be constant"); } else { if (errors == Report.Errors) Report.Error (150, Location, "A constant value is expected"); value = null; return false; } ce = Expr as Constant; } if (MemberType != real_expr.Type) { ce = ChangeType (Location, ce, MemberType); if (ce == null){ value = null; return false; } Expr = ce; } ConstantValue = ce.GetValue (); if (MemberType.IsEnum){ // // This sadly does not work for our user-defined enumerations types ;-( // try { ConstantValue = System.Enum.ToObject ( MemberType, 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)) throw new Exception ("Cannot register const value"); value = ConstantValue; resolved = true; return true; } /// <summary> /// Emits the field value by evaluating the expression /// </summary> public override void Emit () { object value; LookupConstantValue (out value); if (OptAttributes != null) { OptAttributes.Emit (const_ec, this); } base.Emit (); } } } --- NEW FILE: constant.cs --- // // constant.cs: Constants. // // Author: // Miguel de Icaza (mi...@xi...) // // (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 { [...1074 lines suppressed...] } public override void Emit (EmitContext ec) { if (Value == null) ec.ig.Emit (OpCodes.Ldnull); else ec.ig.Emit (OpCodes.Ldstr, Value); } public override bool IsNegative { get { return false; } } } } --- NEW FILE: convert.cs --- // // conversion.cs: various routines for implementing conversions. // // Authors: // Miguel de Icaza (mi...@xi...) // Ravi Pratap (ra...@xi...) // // (C) 2001, 2002, 2003 Ximian, Inc. // namespace Mono.CSharp { using System; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; // // A container class for all the conversion operations [...1789 lines suppressed...] Type target_type, Location l) { Expression ne = ImplicitConversionStandard (ec, expr, target_type, l); if (ne != null) return ne; ne = ExplicitNumericConversion (ec, expr, target_type, l); if (ne != null) return ne; ne = ExplicitReferenceConversion (expr, target_type); if (ne != null) return ne; Error_CannotConvertType (l, expr.Type, target_type); return null; } } } --- NEW FILE: cs-parser.jay --- %{ // // cs-parser.jay: The Parser for the C# compiler // // Authors: Miguel de Icaza (mi...@gn...) // Ravi Pratap (ra...@xi...) // // Licensed under the terms of the GNU GPL // // (C) 2001 Ximian, Inc (http://www.ximian.com) // // TODO: // (1) Figure out why error productions dont work. `type-declaration' is a // great spot to put an `error' because you can reproduce it with this input: // "public X { }" // // Possible optimization: // Run memory profiler with parsing only, and consider dropping // arraylists where not needed. Some pieces can use linked lists. [...4406 lines suppressed...] Console.WriteLine (e); } } void CheckToken (int error, int yyToken, string msg) { if (yyToken >= Token.FIRST_KEYWORD && yyToken <= Token.LAST_KEYWORD){ Report.Error (error, lexer.Location, String.Format ("{0}: `{1}' is a keyword", msg, yyNames [yyToken].ToLower ())); return; } Report.Error (error, lexer.Location, msg); } void CheckIdentifierToken (int yyToken) { CheckToken (1041, yyToken, "Identifier expected"); } /* end end end */ } --- NEW FILE: cs-tokenizer.cs --- // // cs-tokenizer.cs: The Tokenizer for the C# compiler // This also implements the preprocessor // // Author: Miguel de Icaza (mi...@gn...) // // Licensed under the terms of the GNU GPL // // (C) 2001, 2002 Ximian, Inc (http://www.ximian.com) // /* * TODO: * Make sure we accept the proper Unicode ranges, per the spec. * Report error 1032 */ using System; using System.Text; [...2013 lines suppressed...] return Token.ERROR; } return Token.EOF; } public void cleanup () { 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"); } } } } --- NEW FILE: decl.cs --- // // decl.cs: Declaration base class for structs, classes, enums and interfaces. // // Author: Miguel de Icaza (mi...@gn...) // Marek Safar (mar...@se...) // // 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.Globalization; using System.Reflection.Emit; using System.Reflection; [...1958 lines suppressed...] if ((entry.EntryType & tested_type) != tested_type) continue; MethodBase method_to_compare = (MethodBase)entry.Member; if (AttributeTester.AreOverloadedMethodParamsClsCompliant (method.ParameterTypes, TypeManager.GetArgumentTypes (method_to_compare))) continue; IMethodData md = TypeManager.GetMethod (method_to_compare); // TODO: now we are ignoring CLSCompliance(false) on method from other assembly which is buggy. // However it is exactly what csc does. if (md != null && !md.IsClsCompliaceRequired (method.Parent)) continue; Report.SymbolRelatedToPreviousError (entry.Member); Report.Error (3006, method.Location, "Overloaded method '{0}' differing only in ref or out, or in array rank, is not CLS-compliant", method.GetSignatureForError ()); } } } } --- NEW FILE: delegate.cs --- // // delegate.cs: Delegate Handler // // Authors: // Ravi Pratap (ra...@xi...) // Miguel de Icaza (mi...@xi...) // // 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 ConstructorBuilder ConstructorBuilder; public MethodBuilder InvokeBuilder; public MethodBuilder BeginInvokeBuilder; public MethodBuilder EndInvokeBuilder; Type [] param_types; Type ret_type; static string[] attribute_targets = new string [] { "type", "return" }; Expression instance_expr; MethodBase delegate_method; ReturnParameter return_attributes; const int AllowedModifiers = Modifiers.NEW | Modifiers.PUBLIC | Modifiers.PROTECTED | Modifiers.INTERNAL | Modifiers.UNSAFE | Modifiers.PRIVATE; public Delegate (NamespaceEntry ns, TypeContainer parent, Expression type, int mod_flags, MemberName name, Parameters param_list, Attributes attrs, Location l) : base (ns, parent, name, attrs, l) { this.ReturnType = type; ModFlags = Modifiers.Check (AllowedModifiers, mod_flags, IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE, l); Parameters = param_list; } public override void ApplyAttributeBuilder(Attribute a, CustomAttributeBuilder cb) { if (a.Target == AttributeTargets.ReturnValue) { if (return_attributes == null) return_attributes = new ReturnParameter (InvokeBuilder, Location); return_attributes.ApplyAttributeBuilder (a, cb); return; } base.ApplyAttributeBuilder (a, cb); } public override TypeBuilder DefineType () { if (TypeBuilder != null) return TypeBuilder; TypeAttributes attr = Modifiers.TypeAttr (ModFlags, IsTopLevel) | TypeAttributes.Class | TypeAttributes.Sealed; if (IsTopLevel) { if (TypeManager.NamespaceClash (Name, Location)) return null; ModuleBuilder builder = CodeGen.Module.Builder; TypeBuilder = builder.DefineType ( Name, attr, TypeManager.multicast_delegate_type); } else { TypeBuilder builder = Parent.TypeBuilder; string name = Name.Substring (1 + Name.LastIndexOf ('.')); TypeBuilder = builder.DefineNestedType ( name, attr, TypeManager.multicas... [truncated message content] |