+2004-10-17 Miguel de Icaza <miguel@ximian.com>
+
+ * expression.cs (LocalVariableReference.DoResolveBase, Emit):
+ Remove the tests for `ec.RemapToProxy' from here, and encapsulate
+ all of this information into
+ EmitContext.EmitCapturedVariableInstance.
+
+ * codegen.cs (EmitCapturedVariableInstance): move here the
+ funcionality of emitting an ldarg.0 in the presence of a
+ remapping. This centralizes the instance emit code.
+
+ (EmitContext.EmitThis): If the ScopeInfo contains a THIS field,
+ then emit a load of this: it means that we have reached the
+ topmost ScopeInfo: the one that contains the pointer to the
+ instance of the class hosting the anonymous method.
+
+ * anonymous.cs (AddField, HaveCapturedFields): Propagate field
+ captures to the topmost CaptureContext.
+
+2004-10-12 Miguel de Icaza <miguel@ximian.com>
+
+ * expression.cs (LocalVariableReference): Move the knowledge about
+ the iterators into codegen's EmitCapturedVariableInstance.
+
+2004-10-11 Miguel de Icaza <miguel@ximian.com>
+
+ * codegen.cs (EmitContext.ResolveTopBlock): Emit a 1643 when not
+ all code paths return a value from an anonymous method (it is the
+ same as the 161 error, but for anonymous methods).
+
+2004-10-08 Miguel de Icaza <miguel@ximian.com>
+
+ The introduction of anonymous methods in the compiler changed
+ various ways of doing things in the compiler. The most
+ significant one is the hard split between the resolution phase
+ and the emission phases of the compiler.
+
+ For instance, routines that referenced local variables no
+ longer can safely create temporary variables during the
+ resolution phase: they must do so from the emission phase,
+ since the variable might have been "captured", hence access to
+ it can not be done with the local-variable operations from the runtime.
+
+ * statement.cs
+
+ (Block.Flags): New flag `IsTopLevel' to indicate that this block
+ is a toplevel block.
+
+ (ToplevelBlock): A new kind of Block, these are the blocks that
+ are created by the parser for all toplevel method bodies. These
+ include methods, accessors and anonymous methods.
+
+ These contain some extra information not found in regular blocks:
+ A pointer to an optional CaptureContext (for tracking captured
+ local variables and parameters). A pointer to the parent
+ ToplevelBlock.
+
+ (Return.Resolve): Catch missmatches when returning a value from an
+ anonymous method (error 1662).
+ Invoke NeedReturnLabel from the Resolve phase instead of the emit
+ phase.
+
+ (Break.Resolve): ditto.
+
+ (SwitchLabel): instead of defining the labels during the
+ resolution phase, we now turned the public ILLabel and ILLabelCode
+ labels into methods called GetILLabelCode() and GetILLabel() that
+ only define the label during the Emit phase.
+
+ (GotoCase): Track the SwitchLabel instead of the computed label
+ (its contained therein). Emit the code by using
+ SwitchLabel.GetILLabelCode ().
+
+ (LocalInfo.Flags.Captured): A new flag has been introduce to track
+ whether the Local has been captured or not.
+
+ (LocalInfo.IsCaptured): New property, used to tell whether the
+ local has been captured.
+
+ * anonymous.cs: Vastly updated to contain the anonymous method
+ support.
+
+ The main classes here are: CaptureContext which tracks any
+ captured information for a toplevel block and ScopeInfo used to
+ track the activation frames for various local variables.
+
+ Each toplevel block has an optional capture context associated
+ with it. When a method contains an anonymous method both the
+ toplevel method and the anonymous method will create a capture
+ context. When variables or parameters are captured, they are
+ recorded on the CaptureContext that owns them, for example:
+
+ void Demo () {
+ int a;
+ MyDelegate d = delegate {
+ a = 1;
+ }
+ }
+
+ Here `a' will be recorded as captured on the toplevel
+ CapturedContext, the inner captured context will not have anything
+ (it will only have data if local variables or parameters from it
+ are captured in a nested anonymous method.
+
+ The ScopeInfo is used to track the activation frames for local
+ variables, for example:
+
+ for (int i = 0; i < 10; i++)
+ for (int j = 0; j < 10; j++){
+ MyDelegate d = delegate {
+ call (i, j);
+ }
+ }
+
+ At runtime this captures a single captured variable `i', but it
+ captures 10 different versions of the variable `j'. The variable
+ `i' will be recorded on the toplevel ScopeInfo, while `j' will be
+ recorded on a child.
+
+ The toplevel ScopeInfo will also track information like the `this'
+ pointer if instance variables were referenced (this is necessary
+ as the anonymous method lives inside a nested class in the host
+ type of the method).
+
+ (AnonymousMethod): Expanded to track the Toplevel, implement
+ `AnonymousMethod.Compatible' to tell whether an anonymous method
+ can be converted to a target delegate type.
+
+ The routine now also produces the anonymous method content
+
+ (AnonymousDelegate): A helper class that derives from
+ DelegateCreation, this is used to generate the code necessary to
+ produce the delegate for the anonymous method that was created.
+
+ * assign.cs: API adjustments for new changes in
+ Convert.ImplicitStandardConversionExists.
+
+ * class.cs: Adjustments to cope with the fact that now toplevel
+ blocks are of type `ToplevelBlock'.
+
+ * cs-parser.jay: Now we produce ToplevelBlocks for toplevel blocks
+ insteda of standard blocks.
+
+ Flag errors if params arguments are passed to anonymous methods.
+
+ * codegen.cs (EmitContext): Replace `InAnonymousMethod' with
+ `CurrentAnonymousMethod' which points to the current Anonymous
+ Method. The variable points to the AnonymousMethod class that
+ holds the code being compiled. It is set in the new EmitContext
+ created for the anonymous method.
+
+ (EmitContext.Phase): Introduce a variable and an enumeration to
+ assist in enforcing some rules about when and where we are allowed
+ to invoke certain methods (EmitContext.NeedsReturnLabel is the
+ only one that enfonces this right now).
+
+ (EmitContext.HaveCaptureInfo): new helper method that returns
+ whether we have a CapturedContext initialized.
+
+ (EmitContext.CaptureVariable): New method used to register that a
+ LocalInfo must be flagged for capturing.
+
+ (EmitContext.CapturedParameter): New method used to register that a
+ parameters must be flagged for capturing.
+
+ (EmitContext.CapturedField): New method used to register that a
+ field must be flagged for capturing.
+
+ (EmitContext.HaveCapturedVariables,
+ EmitContext.HaveCapturedFields): Return whether there are captured
+ variables or fields.
+
+ (EmitContext.EmitMethodHostInstance): This is used to emit the
+ instance for the anonymous method. The instance might be null
+ (static methods), this (for anonymous methods that capture nothing
+ and happen to live side-by-side with the current method body) or a
+ more complicated expression if the method has a CaptureContext.
+
+ (EmitContext.EmitTopBlock): Routine that drives the emission of
+ code: it will first resolve the top block, then emit any metadata
+ and then emit the code. The split is done so that we can extract
+ any anonymous methods and flag any captured variables/parameters.
+
+ (EmitContext.ResolveTopBlock): Triggers the resolution phase,
+ during this phase, the ILGenerator should not be used as labels
+ and local variables declared here might not be accessible to any
+ code that is part of an anonymous method.
+
+ Exceptions to this include the temporary variables that are
+ created by some statements internally for holding temporary
+ variables.
+
+ (EmitContext.EmitMeta): New routine, in charge of emitting all the
+ metadata for a cb
+
+ (EmitContext.TemporaryReturn): This method is typically called
+ from the Emit phase, and its the only place where we allow the
+ ReturnLabel to be defined other than the EmitMeta. The reason is
+ that otherwise we would have to duplicate a lot of logic in the
+ Resolve phases of various methods that today is on the Emit
+ phase.
+
+ (EmitContext.NeedReturnLabel): This no longer creates the label,
+ as the ILGenerator is not valid during the resolve phase.
+
+ (EmitContext.EmitThis): Extended the knowledge in this class to
+ work in anonymous methods in addition to iterators.
+
+ (EmitContext.EmitCapturedVariableInstance): This emits whatever
+ code is necessary on the stack to access the instance to a local
+ variable (the variable will be accessed as a field).
+
+ (EmitContext.EmitParameter, EmitContext.EmitAssignParameter,
+ EmitContext.EmitAddressOfParameter): Routines to support
+ parameters (not completed at this point).
+
+ Removals: Removed RemapLocal and RemapLocalLValue. We probably
+ will also remove the parameters.
+
+ * convert.cs (Convert): Define a `ConstantEC' which points to a
+ null. This is just to prefity some code that uses
+ ImplicitStandardConversion code and do not have an EmitContext
+ handy.
+
+ The idea is to flag explicitly that at that point in time, it is
+ known that the conversion will not trigger the delegate checking
+ code in implicit conversions (which requires a valid
+ EmitContext).
+
+ Everywhere: pass new EmitContext parameter since
+ ImplicitStandardConversionExists now requires it to check for
+ anonymous method conversions.
+
+ (Convert.ImplicitStandardConversionExists): If the type of an
+ expression is the anonymous_method_type, and the type is a
+ delegate, we invoke the AnonymousMethod.Compatible method to check
+ whether an implicit conversion is possible.
+
+ (Convert.ImplicitConversionStandard): Only do implicit method
+ group conversions if the language level is not ISO_1.
+
+ * delegate.cs (Delegate.GetInvokeMethod): Common method to get the
+ MethodInfo for the Invoke method. used by Delegate and
+ AnonymousDelegate.
+
+ * expression.cs (Binary.DoNumericPromotions): only allow anonymous
+ method conversions if the target type is a delegate.
+
+ Removed extra debugging nops.
+
+ (LocalVariableReference): Turn the `local_info' into a public
+ field.
+
+ Add `prepared' field, the same hack used for FieldExprs to cope
+ with composed assignments, as Local variables do not necessarily
+ operate purely on the stack as they used to: they can be captured
+ fields.
+
+ Add `temp' for a temporary result, like fields.
+
+ Refactor DoResolve and DoResolveLValue into DoResolveBase.
+
+ It now copes with Local variables that are captured and emits the
+ proper instance variable to load it from a field in the captured
+ case.
+
+ (ParameterReference.DoResolveBase): During the resolve phase,
+ capture parameters if we are in an anonymous method.
+
+ (ParameterReference.Emit, ParameterReference.AddressOf): If in an
+ anonymous method, use the EmitContext helper routines to emit the
+ parameter reference.
+
+ * iterators.cs: Set RemapToProxy to true/false during the
+ EmitDispose class.
+
+ * parameters.cs (GetParameterByName): New helper method.
+
+ * typemanager.cs (anonymous_method_type) a new type that
+ represents an anonyous method. This is always an internal type,
+ used as a fencepost to test against the anonymous-methodness of an
+ expression.
+
2004-10-24 Marek Safar <marek.safar@seznam.cz>
Fixed bugs #63705, #67130
+
NEW NOTES:
----------
Iterators
---------
-
* Reset should throw not implemented now.
Optimization ideas
// Author:
// Miguel de Icaza (miguel@ximain.com)
//
-// (C) 2003 Ximian, Inc.
+// (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;
namespace Mono.CSharp {
public class AnonymousMethod : Expression {
+ // Used to generate unique method names.
+ static int anonymous_method_count;
+
// An array list of AnonymousMethodParameter or null
- Parameters parameters;
- Block block;
+ public Parameters Parameters;
+
+ //
+ // The block that makes up the body for the anonymous mehtod
+ //
+ public ToplevelBlock Block;
+
+ //
+ // The container block for this anonymous method.
+ //
+ public Block ContainingBlock;
+
+ //
+ // The implicit method we create
+ //
+ public Method method;
+
+ MethodInfo invoke_mb;
+
+ // The emit context for the anonymous method
+ public EmitContext aec;
+ public InternalParameters amp;
+ bool unreachable;
+
+ //
+ // The modifiers applied to the method, we aggregate them
+ //
+ int method_modifiers = Modifiers.INTERNAL;
- public AnonymousMethod (Parameters parameters, Block block, Location l)
+ //
+ // During the resolve stage of the anonymous method body,
+ // we discover the actual scope where we are hosted, or
+ // null to host the method in the same class
+ //
+ public ScopeInfo Scope;
+
+ //
+ // Points to our container anonymous method if its present
+ //
+ public AnonymousMethod ContainerAnonymousMethod;
+
+ public AnonymousMethod (Parameters parameters, ToplevelBlock container, ToplevelBlock block, Location l)
{
- this.parameters = parameters;
- this.block = block;
+ Parameters = parameters;
+ Block = block;
loc = l;
+
+ //
+ // The order is important: this setups the CaptureContext tree hierarchy.
+ //
+ container.SetHaveAnonymousMethods (l, this);
+ block.SetHaveAnonymousMethods (l, this);
}
public override Expression DoResolve (EmitContext ec)
//
eclass = ExprClass.Value;
-
+
//
// This hack means `The type is not accessible
// anywhere', we depend on special conversion
// rules.
//
- type = typeof (AnonymousMethod);
+ type = TypeManager.anonymous_method_type;
+
return this;
}
{
// nothing, as we only exist to not do anything.
}
+
+ //
+ // Creates the host for the anonymous method
+ //
+ bool CreateMethodHost (EmitContext ec, Type return_type)
+ {
+ //
+ // Crude hack follows: we replace the TypeBuilder during the
+ // definition to get the method hosted in the right class
+ //
+
+ TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
+ TypeBuilder type_host = Scope == null ? current_type : Scope.ScopeTypeBuilder;
+
+ if (current_type == null)
+ throw new Exception ("The current_type is null");
+
+ if (type_host == null)
+ throw new Exception ("Type host is null");
+
+ if (current_type == type_host && ec.IsStatic){
+ if (ec.IsStatic)
+ method_modifiers |= Modifiers.STATIC;
+ current_type = null;
+ }
+
+ method = new Method (
+ (TypeContainer) ec.TypeContainer,
+ new TypeExpression (return_type, loc),
+ method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++),
+ Parameters, null, loc);
+ method.Block = Block;
+
+
+ //
+ // Swap the TypeBuilder while we define the method, then restore
+ //
+ if (current_type != null)
+ ec.TypeContainer.TypeBuilder = type_host;
+ bool res = method.Define ();
+ if (current_type != null)
+ ec.TypeContainer.TypeBuilder = current_type;
+ return res;
+ }
+
+ void Error_ParameterMismatch (Type t)
+ {
+ Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
+ "{0}' since there is a parameter mismatch", t);
+ }
+
+ //
+ // Returns true if this anonymous method can be implicitly
+ // converted to the delegate type `delegate_type'
+ //
+ public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
+ {
+ //
+ // At this point its the first time we know the return type that is
+ // needed for the anonymous method. We create the method here.
+ //
+
+ invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
+ ParameterData invoke_pd = Invocation.GetParameterData (invoke_mb);
+
+ //
+ // If implicit parameters are set, then we must check for out in the parameters
+ // and flag it accordingly.
+ //
+ bool out_invalid_check = false;
+
+ if (Parameters == null){
+ int i, j;
+ out_invalid_check = true;
+
+ //
+ // We provide a set of inaccessible parameters
+ //
+ int params_idx = -1;
+ for (i = 0; i < invoke_pd.Count; i++){
+ if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
+ params_idx = i;
+ }
+ int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
+ Parameter [] fixedpars = new Parameter [n];
+
+ for (i = j = 0; i < invoke_pd.Count; i++){
+ if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
+ continue;
+ fixedpars [j] = new Parameter (
+ new TypeExpression (invoke_pd.ParameterType (i), loc),
+ "+" + j, invoke_pd.ParameterModifier (i), null);
+ j++;
+ }
+
+ Parameter variable = null;
+ if (params_idx != -1){
+ variable = new Parameter (
+ new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
+ "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null);
+ }
+
+ Parameters = new Parameters (fixedpars, variable, loc);
+ }
+
+ //
+ // First, parameter types of `delegate_type' must be compatible
+ // with the anonymous method.
+ //
+ amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
+
+ if (amp.Count != invoke_pd.Count){
+ if (!probe){
+ Report.Error (1593, loc,
+ "Anonymous method has {0} parameters, while delegate requires {1}",
+ amp.Count, invoke_pd.Count);
+ Error_ParameterMismatch (delegate_type);
+ }
+ return null;
+ }
+
+ for (int i = 0; i < amp.Count; i++){
+ Parameter.Modifier amp_mod = amp.ParameterModifier (i);
+ if (amp_mod != invoke_pd.ParameterModifier (i)){
+ if (!probe){
+ Report.Error (1676, loc,
+ "Signature mismatch in parameter modifier for parameter #0", i + 1);
+ Error_ParameterMismatch (delegate_type);
+ }
+ return null;
+ }
+
+ if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
+ if (!probe){
+ Report.Error (1678, loc,
+ "Signature mismatch in parameter {0}: need `{1}' got `{2}'", i + 1,
+ TypeManager.CSharpName (invoke_pd.ParameterType (i)),
+ TypeManager.CSharpName (amp.ParameterType (i)));
+ Error_ParameterMismatch (delegate_type);
+ }
+ return null;
+ }
+
+ if (out_invalid_check && (invoke_pd.ParameterModifier (i) & Parameter.Modifier.OUT) != 0){
+ if (!probe){
+ Report.Error (1676, loc,"Parameter {0} must include the `out' modifier ", i+1);
+ Error_ParameterMismatch (delegate_type);
+ }
+ return null;
+ }
+ }
+
+ //
+ // If we are only probing, return ourselves
+ //
+ if (probe)
+ return this;
+
+ //
+ // Second: the return type of the delegate must be compatible with
+ // the anonymous type. Instead of doing a pass to examine the block
+ // we satisfy the rule by setting the return type on the EmitContext
+ // to be the delegate type return type.
+ //
+
+ //MethodBuilder builder = method_data.MethodBuilder;
+ //ILGenerator ig = builder.GetILGenerator ();
+
+
+ aec = new EmitContext (
+ ec.TypeContainer, ec.DeclSpace, loc, null,
+ invoke_mb.ReturnType,
+ /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
+ (ec.InUnsafe ? Modifiers.UNSAFE : 0),
+ /* No constructor */ false);
+
+ aec.CurrentAnonymousMethod = this;
+ ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
+ ContainingBlock = ec.CurrentBlock;
+
+ if (aec.ResolveTopBlock (ec, Block, amp, loc, out unreachable))
+ return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
+
+ return null;
+ }
+
+ public MethodBuilder GetMethodBuilder ()
+ {
+ return method.MethodData.MethodBuilder;
+ }
+
+ public void EmitMethod (EmitContext ec)
+ {
+ if (!CreateMethodHost (ec, invoke_mb.ReturnType))
+ return;
+
+ MethodBuilder builder = GetMethodBuilder ();
+ ILGenerator ig = builder.GetILGenerator ();
+ aec.ig = ig;
+
+ Parameters.LabelParameters (aec, builder, loc);
+
+ //
+ // Adjust based on the computed state of the
+ // method from CreateMethodHost
+
+ if ((method_modifiers & Modifiers.STATIC) != 0)
+ aec.IsStatic = true;
+
+ aec.EmitMeta (Block, amp);
+ aec.EmitResolvedTopBlock (Block, unreachable);
+ }
+ }
+
+ //
+ // This will emit the code for the delegate, as well delegate creation on the host
+ //
+ public class AnonymousDelegate : DelegateCreation {
+ AnonymousMethod am;
+
+ public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
+ {
+ type = target_type;
+ loc = l;
+ this.am = am;
+ }
+
+ public override Expression DoResolve (EmitContext ec)
+ {
+ eclass = ExprClass.Value;
+ return this;
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ am.EmitMethod (ec);
+
+ //
+ // Now emit the delegate creation.
+ //
+ if ((am.method.ModFlags & Modifiers.STATIC) == 0)
+ delegate_instance_expression = new AnonymousInstance (am);
+
+ Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
+ constructor_method = ((MethodGroupExpr) ml).Methods [0];
+ delegate_method = am.GetMethodBuilder ();
+ base.Emit (ec);
+ }
+
+ class AnonymousInstance : Expression {
+ AnonymousMethod am;
+
+ public AnonymousInstance (AnonymousMethod am)
+ {
+ this.am = am;
+ eclass = ExprClass.Value;
+ }
+
+ public override Expression DoResolve (EmitContext ec)
+ {
+ return this;
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ am.aec.EmitMethodHostInstance (ec, am);
+ }
+ }
+ }
+
+ class CapturedParameter {
+ public Type Type;
+ public FieldBuilder FieldBuilder;
+ public int Idx;
+
+ public CapturedParameter (Type type, int idx)
+ {
+ Type = type;
+ Idx = idx;
+ }
+ }
+
+ //
+ // Here we cluster all the variables captured on a given scope, we also
+ // keep some extra information that might be required on each scope.
+ //
+ public class ScopeInfo {
+ public CaptureContext CaptureContext;
+ public ScopeInfo ParentScope;
+ public Block ScopeBlock;
+ public bool NeedThis = false;
+ public bool HostsParameters = false;
+
+ // For tracking the number of scopes created.
+ public int id;
+ static int count;
+ bool inited = false;
+
+ ArrayList locals = new ArrayList ();
+ ArrayList children = new ArrayList ();
+
+ //
+ // The types and fields generated
+ //
+ public TypeBuilder ScopeTypeBuilder;
+ public ConstructorBuilder ScopeConstructor;
+ public FieldBuilder THIS;
+ public FieldBuilder ParentLink;
+
+ //
+ // Points to the object of type `ScopeTypeBuilder' that
+ // holds the data for the scope
+ //
+ public LocalBuilder ScopeInstance;
+
+
+ public ScopeInfo (CaptureContext cc, Block b)
+ {
+ CaptureContext = cc;
+ ScopeBlock = b;
+ id = count++;
+
+ cc.AddScope (this);
+ }
+
+ public void AddLocal (LocalInfo li)
+ {
+ if (locals.Contains (li))
+ return;
+
+ locals.Add (li);
+ }
+
+ public bool IsCaptured (LocalInfo li)
+ {
+ return locals.Contains (li);
+ }
+
+ public void AddChild (ScopeInfo si)
+ {
+ if (children.Contains (si))
+ return;
+ children.Add (si);
+ }
+
+ static int indent = 0;
+
+ void Pad ()
+ {
+ for (int i = 0; i < indent; i++)
+ Console.Write (" ");
+ }
+
+ void EmitDebug ()
+ {
+ //Console.WriteLine (Environment.StackTrace);
+ Pad ();
+ Console.WriteLine ("START");
+ indent++;
+ Pad ();
+ Console.WriteLine ("NeedThis=" + NeedThis);
+ foreach (LocalInfo li in locals){
+ Pad ();
+ Console.WriteLine ("var {0}", li.Name);
+ }
+
+ foreach (ScopeInfo si in children)
+ si.EmitDebug ();
+ indent--;
+ Pad ();
+ Console.WriteLine ("END");
+ }
+
+ public string MakeHelperName ()
+ {
+ return String.Format ("<>AnonHelp<{0}>", id);
+ }
+
+ public void EmitScopeConstructor ()
+ {
+ Type [] constructor_types = TypeManager.NoTypes;
+ Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
+ ScopeConstructor = ScopeTypeBuilder.DefineConstructor (
+ MethodAttributes.Public | MethodAttributes.HideBySig |
+ MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
+ CallingConventions.HasThis, constructor_types);
+ InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
+ TypeManager.RegisterMethod (ScopeConstructor, parameter_info, constructor_types);
+
+ ILGenerator cig = ScopeConstructor.GetILGenerator ();
+ cig.Emit (OpCodes.Ldarg_0);
+ cig.Emit (OpCodes.Call, TypeManager.object_ctor);
+ cig.Emit (OpCodes.Ret);
+ }
+
+ public void EmitScopeType (EmitContext ec)
+ {
+ //EmitDebug ();
+
+ if (ScopeTypeBuilder != null)
+ return;
+
+ ILGenerator ig = ec.ig;
+ TypeBuilder container = ec.TypeContainer.TypeBuilder;
+
+ ScopeTypeBuilder = container.DefineNestedType (
+ MakeHelperName (), TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.NestedAssembly,
+ TypeManager.object_type, null);
+
+ if (NeedThis)
+ THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
+
+ if (ParentScope != null){
+ if (ParentScope.ScopeTypeBuilder == null){
+ throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
+ }
+
+ ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
+ FieldAttributes.Assembly);
+ }
+
+ if (NeedThis && ParentScope != null)
+ throw new Exception ("I was not expecting THIS && having a parent");
+
+ foreach (LocalInfo info in locals){
+ info.FieldBuilder = ScopeTypeBuilder.DefineField (
+ info.Name, info.VariableType, FieldAttributes.Assembly);
+ }
+
+ if (HostsParameters){
+ Hashtable captured_parameters = CaptureContext.captured_parameters;
+
+ foreach (DictionaryEntry de in captured_parameters){
+ string name = (string) de.Key;
+ CapturedParameter cp = (CapturedParameter) de.Value;
+ FieldBuilder fb;
+
+ fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
+ cp.FieldBuilder = fb;
+ }
+ }
+
+ EmitScopeConstructor ();
+ foreach (ScopeInfo si in children){
+ si.EmitScopeType (ec);
+ }
+ }
+
+ public void CloseTypes ()
+ {
+ RootContext.RegisterHelperClass (ScopeTypeBuilder);
+ foreach (ScopeInfo si in children)
+ si.CloseTypes ();
+ }
+
+ //
+ // Emits the initialization code for the scope
+ //
+ public void EmitInitScope (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ if (inited)
+ return;
+
+ ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
+ ScopeInstance = ig.DeclareLocal (ScopeTypeBuilder);
+ ig.Emit (OpCodes.Stloc, ScopeInstance);
+
+ if (THIS != null){
+ ig.Emit (OpCodes.Ldloc, ScopeInstance);
+ ig.Emit (OpCodes.Ldarg_0);
+ ig.Emit (OpCodes.Stfld, THIS);
+ }
+
+ //
+ // Copy the parameter values, if any
+ //
+ int extra = ec.IsStatic ? 0 : 1;
+ if (HostsParameters){
+ Hashtable captured_parameters = CaptureContext.captured_parameters;
+
+ foreach (DictionaryEntry de in captured_parameters){
+ string name = (string) de.Key;
+ CapturedParameter cp = (CapturedParameter) de.Value;
+
+ ig.Emit (OpCodes.Ldloc, ScopeInstance);
+ ParameterReference.EmitLdArg (ig, cp.Idx + extra);
+ ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
+ }
+ }
+
+ if (ParentScope != null){
+ //
+ // Only emit initialization in our capturecontext world
+ //
+ if (ParentScope.CaptureContext == CaptureContext){
+ ig.Emit (OpCodes.Ldloc, ScopeInstance);
+ ig.Emit (OpCodes.Ldloc, ParentScope.ScopeInstance);
+ ig.Emit (OpCodes.Stfld, ParentLink);
+ } else {
+ ig.Emit (OpCodes.Ldloc, ScopeInstance);
+ ig.Emit (OpCodes.Ldarg_0);
+ ig.Emit (OpCodes.Stfld, ParentLink);
+ }
+ }
+ inited = true;
+ }
+
+ static void DoPath (StringBuilder sb, ScopeInfo start)
+ {
+ if (start.ParentScope != null){
+ DoPath (sb, start.ParentScope);
+ sb.Append (", ");
+ }
+ sb.Append ((start.id).ToString ());
+ }
+
+ public override string ToString ()
+ {
+ StringBuilder sb = new StringBuilder ();
+
+ sb.Append ("{");
+ if (CaptureContext != null){
+ sb.Append (CaptureContext.ToString ());
+ sb.Append (":");
+ }
+
+ DoPath (sb, this);
+ sb.Append ("}");
+
+ return sb.ToString ();
+ }
+ }
+
+ //
+ // CaptureContext objects are created on demand if a method has
+ // anonymous methods and kept on the ToplevelBlock.
+ //
+ // If they exist, all ToplevelBlocks in the containing block are
+ // linked together (children pointing to their parents).
+ //
+ public class CaptureContext {
+ public static int count;
+ public int cc_id;
+ Location loc;
+
+ //
+ // Points to the toplevel block that owns this CaptureContext
+ //
+ ToplevelBlock toplevel_owner;
+ Hashtable scopes = new Hashtable ();
+ bool have_captured_vars = false;
+ ScopeInfo topmost = null;
+
+ //
+ // Captured fields
+ //
+ Hashtable captured_fields = new Hashtable ();
+ Hashtable captured_variables = new Hashtable ();
+ public Hashtable captured_parameters = new Hashtable ();
+ public AnonymousMethod Host;
+
+ public CaptureContext (ToplevelBlock toplevel_owner, Location loc, AnonymousMethod host)
+ {
+ cc_id = count++;
+ this.toplevel_owner = toplevel_owner;
+ this.loc = loc;
+
+ if (host != null)
+ Host = host;
+ }
+
+ void DoPath (StringBuilder sb, CaptureContext cc)
+ {
+ if (cc.ParentCaptureContext != null){
+ DoPath (sb, cc.ParentCaptureContext);
+ sb.Append (".");
+ }
+ sb.Append (cc_id.ToString ());
+ }
+
+ public override string ToString ()
+ {
+ ToplevelBlock parent = ParentToplevel;
+ StringBuilder sb = new StringBuilder ();
+
+ sb.Append ("[");
+ DoPath (sb, this);
+ sb.Append ("]");
+ return sb.ToString ();
+ }
+
+ public ToplevelBlock ParentToplevel {
+ get {
+ return toplevel_owner.Container;
+ }
+ }
+
+ public CaptureContext ParentCaptureContext {
+ get {
+ ToplevelBlock parent = ParentToplevel;
+
+ return (parent == null) ? null : parent.CaptureContext;
+ }
+ }
+
+ // Returns the deepest of two scopes
+ public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
+ {
+ ScopeInfo p;
+
+ if (a == null)
+ return b;
+ if (b == null)
+ return a;
+ if (a == b)
+ return a;
+
+ //
+ // If they Scopes are on the same CaptureContext, we do the double
+ // checks just so if there is an invariant change in the future,
+ // we get the exception at the end
+ //
+ for (p = a; p != null; p = p.ParentScope)
+ if (p == b)
+ return a;
+
+ for (p = b; p != null; p = p.ParentScope)
+ if (p == a)
+ return b;
+
+ CaptureContext ca = a.CaptureContext;
+ CaptureContext cb = b.CaptureContext;
+
+ for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
+ if (c == cb)
+ return a;
+
+ for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
+ if (c == ca)
+ return b;
+ throw new Exception ("Should never be reached");
+ }
+
+ void AdjustMethodScope (AnonymousMethod am, ScopeInfo scope)
+ {
+ am.Scope = Deepest (am.Scope, scope);
+ }
+
+ void LinkScope (ScopeInfo scope, int id)
+ {
+ ScopeInfo parent = (ScopeInfo) scopes [id];
+ scope.ParentScope = parent;
+ parent.AddChild (scope);
+
+ if (scope == topmost)
+ topmost = parent;
+ }
+
+ public void AddLocal (AnonymousMethod am, LocalInfo li)
+ {
+ if (li.Block.Toplevel != toplevel_owner){
+ ParentCaptureContext.AddLocal (am, li);
+ return;
+ }
+ int block_id = li.Block.ID;
+ ScopeInfo scope;
+ if (scopes [block_id] == null){
+ scope = new ScopeInfo (this, li.Block);
+ scopes [block_id] = scope;
+ } else
+ scope = (ScopeInfo) scopes [block_id];
+
+ if (topmost == null){
+ topmost = scope;
+ } else {
+ // Link to parent
+ for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
+ if (scopes [b.ID] != null){
+ LinkScope (scope, b.ID);
+ break;
+ }
+ }
+
+ if (scope.ParentScope == null && ParentCaptureContext != null){
+ CaptureContext pcc = ParentCaptureContext;
+
+ for (Block b = am.ContainingBlock; b != null; b = b.Parent){
+ if (pcc.scopes [b.ID] != null){
+ pcc.LinkScope (scope, b.ID);
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // Adjust the owner
+ //
+ if (Host != null)
+ AdjustMethodScope (Host, topmost);
+
+ //
+ // Adjust the user
+ //
+ AdjustMethodScope (am, scope);
+
+ if (captured_variables [li] != null)
+ return;
+
+ have_captured_vars = true;
+ captured_variables [li] = li;
+ scope.AddLocal (li);
+ }
+
+ //
+ // Retursn the CaptureContext for the block that defines the parameter `name'
+ //
+ static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
+ {
+ ToplevelBlock container = current.Container;
+ if (container != null){
+ CaptureContext cc = _ContextForParameter (container, name);
+ if (cc != null)
+ return cc;
+ }
+ if (current.IsParameterReference (name))
+ return current.ToplevelBlockCaptureContext;
+ return null;
+ }
+
+ static CaptureContext ContextForParameter (ToplevelBlock current, string name)
+ {
+ CaptureContext cc = _ContextForParameter (current, name);
+ if (cc == null)
+ throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
+ return cc;
+ }
+
+ //
+ // Records the captured parameter at the appropriate CaptureContext
+ //
+ public void AddParameter (EmitContext ec, AnonymousMethod am, string name, Type t, int idx)
+ {
+ CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
+
+ cc.AddParameterToContext (am, name, t, idx);
+ }
+
+ //
+ // Records the parameters in the context
+ //
+ void AddParameterToContext (AnonymousMethod am, string name, Type t, int idx)
+ {
+ if (captured_parameters == null)
+ captured_parameters = new Hashtable ();
+ if (captured_parameters [name] != null)
+ return;
+ captured_parameters [name] = new CapturedParameter (t, idx);
+
+ if (topmost == null){
+ //
+ // Create one ScopeInfo, if there are none.
+ //
+ topmost = new ScopeInfo (this, toplevel_owner);
+ scopes [toplevel_owner.ID] = topmost;
+ } else {
+ //
+ // If the topmost ScopeInfo is not at the topblock level, insert
+ // a new ScopeInfo there.
+ //
+ if (topmost.ScopeBlock != toplevel_owner){
+ ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
+ scopes [toplevel_owner.ID] = topmost;
+ topmost.ParentScope = par_si;
+ topmost = par_si;
+ }
+ }
+
+ topmost.HostsParameters = true;
+ AdjustMethodScope (am, topmost);
+ }
+
+ //
+ // Captured fields are only recorded on the topmost CaptureContext, because that
+ // one is the one linked to the owner of instance fields
+ //
+ public void AddField (FieldExpr fe)
+ {
+ if (fe.FieldInfo.IsStatic)
+ throw new Exception ("Attempt to register a static field as a captured field");
+
+ CaptureContext parent = ParentCaptureContext;
+ if (parent != null)
+ parent.AddField (fe);
+ else
+ captured_fields [fe] = fe;
+ }
+
+
+ public bool HaveCapturedVariables {
+ get {
+ return have_captured_vars;
+ }
+ }
+
+ public bool HaveCapturedFields {
+ get {
+ CaptureContext parent = ParentCaptureContext;
+ if (parent != null)
+ return parent.HaveCapturedFields;
+ return captured_fields.Count > 0;
+ }
+ }
+
+ public bool IsCaptured (LocalInfo local)
+ {
+ foreach (ScopeInfo si in scopes.Values){
+ if (si.IsCaptured (local))
+ return true;
+ }
+ return false;
+ }
+
+ //
+ // Returns whether the parameter is captured
+ //
+ public bool IsParameterCaptured (string name)
+ {
+ if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
+ return true;
+
+ if (captured_parameters != null)
+ return captured_parameters [name] != null;
+ return false;
+ }
+
+ public void EmitHelperClasses (EmitContext ec)
+ {
+ if (topmost != null){
+ topmost.NeedThis = HaveCapturedFields;
+ topmost.EmitScopeType (ec);
+ }
+ }
+
+ public void CloseHelperClasses ()
+ {
+ if (topmost != null)
+ topmost.CloseTypes ();
+ }
+
+ ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
+ {
+ ScopeInfo si;
+
+ si = (ScopeInfo) scopes [b.ID];
+ if (si == null)
+ throw new Exception ("Si is null for block " + b.ID);
+ si.EmitInitScope (ec);
+
+ return si;
+ }
+
+ //
+ // Emits the opcodes necessary to load the instance of the captured
+ // variable in `li'
+ //
+ public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li, AnonymousMethod am)
+ {
+ ILGenerator ig = ec.ig;
+ ScopeInfo si;
+
+ if (li.Block.Toplevel == toplevel_owner){
+ si = GetScopeFromBlock (ec, li.Block);
+ ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
+ return;
+ }
+
+ si = am.Scope;
+ ig.Emit (OpCodes.Ldarg_0);
+ if (si != null){
+ while (si.ScopeBlock.ID != li.Block.ID){
+ if (si.ParentLink != null)
+ ig.Emit (OpCodes.Ldfld, si.ParentLink);
+ si = si.ParentScope;
+ if (si == null)
+ throw new Exception (
+ String.Format ("Never found block {0} starting at {1} while looking up {2}",
+ li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
+ }
+ }
+ }
+
+ //
+ // Internal routine that loads the instance to reach parameter `name'
+ //
+ void EmitParameterInstance (EmitContext ec, string name)
+ {
+ CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
+ if (cc != this){
+ cc.EmitParameterInstance (ec, name);
+ return;
+ }
+ Block invocation_block = ec.CurrentBlock;
+ CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
+ ILGenerator ig = ec.ig;
+
+ ScopeInfo si;
+ if (ec.CurrentBlock == toplevel_owner){
+ si = GetScopeFromBlock (ec, toplevel_owner);
+ ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
+ return;
+ }
+
+ si = ec.CurrentAnonymousMethod.Scope;
+ ig.Emit (OpCodes.Ldarg_0);
+ if (si != null){
+ while (si.ParentLink != null) {
+ ig.Emit (OpCodes.Ldfld, si.ParentLink);
+ si = si.ParentScope;
+ }
+ }
+ }
+
+ //
+ // Emits the code necessary to load the parameter named `name' within
+ // an anonymous method.
+ //
+ public void EmitParameter (EmitContext ec, string name)
+ {
+ CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
+ if (cc != this){
+ cc.EmitParameter (ec, name);
+ return;
+ }
+ EmitParameterInstance (ec, name);
+ CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
+ ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
+ }
+
+ //
+ // Implements the assignment of `source' to the paramenter named `name' within
+ // an anonymous method.
+ //
+ public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
+ {
+ CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
+ if (cc != this){
+ cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
+ return;
+ }
+ ILGenerator ig = ec.ig;
+ CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
+
+ EmitParameterInstance (ec, name);
+ source.Emit (ec);
+ if (leave_copy)
+ ig.Emit (OpCodes.Dup);
+ ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
+ }
+
+ //
+ // Emits the address for the parameter named `name' within
+ // an anonymous method.
+ //
+ public void EmitAddressOfParameter (EmitContext ec, string name)
+ {
+ CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
+ if (cc != this){
+ cc.EmitAddressOfParameter (ec, name);
+ return;
+ }
+ EmitParameterInstance (ec, name);
+ CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
+ ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
+ }
+
+ //
+ // The following methods are only invoked on the host for the
+ // anonymous method.
+ //
+ public void EmitMethodHostInstance (EmitContext target, AnonymousMethod am)
+ {
+ ILGenerator ig = target.ig;
+ ScopeInfo si = am.Scope;
+
+ if (si == null){
+ ig.Emit (OpCodes.Ldarg_0);
+ return;
+ }
+
+ si.EmitInitScope (target);
+ ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
+ }
+
+ ArrayList all_scopes = new ArrayList ();
+
+ public void AddScope (ScopeInfo si)
+ {
+ all_scopes.Add (si);
+ toplevel_owner.RegisterCaptureContext (this);
+ }
+
+ //
+ // Links any scopes that were not linked previously
+ //
+ public void AdjustScopes ()
+ {
+ foreach (ScopeInfo scope in all_scopes){
+ if (scope.ParentScope != null)
+ continue;
+
+ for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
+ 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;
+ }
+ }
+ }
+ }
+ }
}
}
-
type = target_type;
eclass = ExprClass.Value;
+
if (target is EventExpr) {
EventInfo ei = ((EventExpr) target).EventInfo;
// 2. and the original right side is implicitly convertible to
// the type of target
//
- if (Convert.ImplicitStandardConversionExists (a.original_source, target_type))
+ if (Convert.ImplicitStandardConversionExists (ec, a.original_source, target_type))
return this;
//
public abstract class MethodCore : MemberBase {
public readonly Parameters Parameters;
- protected Block block;
+ protected ToplevelBlock block;
//
// Parameters, cached for semantic analysis.
}
}
- public Block Block {
+ public ToplevelBlock Block {
get {
return block;
}
Type ReturnType { get; }
Attributes OptAttributes { get; }
- Block Block { get; }
+ ToplevelBlock Block { get; }
EmitContext CreateEmitContext (TypeContainer tc, ILGenerator ig);
ObsoleteAttribute GetObsoleteAttribute ();
if (member is MethodCore)
((MethodCore) member).Parameters.LabelParameters (ec, MethodBuilder, loc);
- Block block = method.Block;
+ SymbolWriter sw = CodeGen.SymbolWriter;
+ ToplevelBlock block = method.Block;
//
// abstract or extern methods have no bodies
source.CloseMethod ();
}
- void EmitDestructor (EmitContext ec, Block block)
+ void EmitDestructor (EmitContext ec, ToplevelBlock block)
{
ILGenerator ig = ec.ig;
public Status status;
[Flags]
- public enum Status : byte { ASSIGNED = 1, USED = 2 }
+ public enum Status : byte {
+ ASSIGNED = 1,
+ USED = 2
+ }
static string[] attribute_targets = new string [] { "field" };
}
protected readonly Object init;
+
// Private.
Expression init_expr;
bool init_expr_initialized = false;
//
// Null if the accessor is empty, or a Block if not
//
- public Block Block;
+ public ToplevelBlock Block;
public Attributes Attributes;
public Location Location;
- public Accessor (Block b, Attributes attrs, Location loc)
+ public Accessor (ToplevelBlock b, Attributes attrs, Location loc)
{
Block = b;
Attributes = attrs;
// When it will be possible move here a lot of child code and template method type.
public abstract class AbstractPropertyEventMethod: MemberCore, IMethodData {
protected MethodData method_data;
- protected Block block;
+ protected ToplevelBlock block;
// The accessor are created event if they are not wanted.
// But we need them because their names are reserved.
#region IMethodData Members
- public Block Block {
+ public ToplevelBlock Block {
get {
return block;
}
public Operator (TypeContainer parent, OpType type, Expression ret_type,
int mod_flags, Parameters parameters,
- Block block, Attributes attrs, Location loc)
+ ToplevelBlock block, Attributes attrs, Location loc)
: base (parent, ret_type, mod_flags, AllowedModifiers, false,
new MemberName ("op_" + type), attrs, parameters, loc)
{
// Author:
// Miguel de Icaza (miguel@ximian.com)
//
-// (C) 2001 Ximian, Inc.
+// (C) 2001, 2002, 2003 Ximian, Inc.
+// (C) 2004 Novell, Inc.
//
-
+//#define PRODUCTION
using System;
using System.IO;
using System.Collections;
ig.Emit (value_type_call ? OpCodes.Call : OpCodes.Callvirt, mi);
}
}
-
+
/// <summary>
/// An Emit Context is created for each body of code (from methods,
/// properties bodies, indexer bodies or constructor bodies)
/// <summary>
/// Whether we are inside an anonymous method.
/// </summary>
- public bool InAnonymousMethod;
+ public AnonymousMethod CurrentAnonymousMethod;
/// <summary>
/// Location for this EmitContext
/// </summary>
public bool InEnumContext;
+ /// <summary>
+ /// Anonymous methods can capture local variables and fields,
+ /// this object tracks it. It is copied from the TopLevelBlock
+ /// field.
+ /// </summary>
+ public CaptureContext capture_context;
+
/// <summary>
/// Trace when method is called and is obsolete then this member suppress message
/// when call is inside next [Obsolete] method or type.
/// </summary>
public bool TestObsoleteMethodUsage = true;
+ /// <summary>
+ /// The current iterator
+ /// </summary>
public Iterator CurrentIterator;
- FlowBranching current_flow_branching;
+ /// <summary>
+ /// Whether we are in the resolving stage or not
+ /// </summary>
+ enum Phase {
+ Created,
+ Resolving,
+ Emitting
+ }
+
+ Phase current_phase;
+ FlowBranching current_flow_branching;
+
public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
Type return_type, int code_flags, bool is_constructor)
{
DeclSpace = ds;
CheckState = RootContext.Checked;
ConstantCheckState = true;
-
+
IsStatic = (code_flags & Modifiers.STATIC) != 0;
InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
RemapToProxy = InIterator;
IsConstructor = is_constructor;
CurrentBlock = null;
CurrentFile = 0;
+ current_phase = Phase.Created;
if (parent != null){
// Can only be null for the ResolveType contexts.
InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
}
loc = l;
-
+
if (ReturnType == TypeManager.void_type)
ReturnType = null;
}
}
}
+ public bool HaveCaptureInfo {
+ get {
+ return capture_context != null;
+ }
+ }
+
// <summary>
// Starts a new code branching. This inherits the state of all local
// variables and parameters from the current branching.
current_flow_branching = current_flow_branching.Parent;
}
- public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
+ public void CaptureVariable (LocalInfo li)
{
- bool unreachable = false;
+ capture_context.AddLocal (CurrentAnonymousMethod, li);
+ li.IsCaptured = true;
+ }
+
+ public void CaptureParameter (string name, Type t, int idx)
+ {
+
+ capture_context.AddParameter (this, CurrentAnonymousMethod, name, t, idx);
+ }
+
+ //
+ // Use to register a field as captured
+ //
+ public void CaptureField (FieldExpr fe)
+ {
+ capture_context.AddField (fe);
+ }
+
+ //
+ // Whether anonymous methods have captured variables
+ //
+ public bool HaveCapturedVariables ()
+ {
+ if (capture_context != null)
+ return capture_context.HaveCapturedVariables;
+ return false;
+ }
+
+ //
+ // Whether anonymous methods have captured fields or this.
+ //
+ public bool HaveCapturedFields ()
+ {
+ if (capture_context != null)
+ return capture_context.HaveCapturedFields;
+ return false;
+ }
+
+ //
+ // Emits the instance pointer for the host method
+ //
+ public void EmitMethodHostInstance (EmitContext target, AnonymousMethod am)
+ {
+ if (capture_context != null)
+ capture_context.EmitMethodHostInstance (target, am);
+ else if (IsStatic)
+ target.ig.Emit (OpCodes.Ldnull);
+ else
+ target.ig.Emit (OpCodes.Ldarg_0);
+ }
+
+ //
+ // Returns whether the `local' variable has been captured by an anonymous
+ // method
+ //
+ public bool IsCaptured (LocalInfo local)
+ {
+ return capture_context.IsCaptured (local);
+ }
+
+ public bool IsParameterCaptured (string name)
+ {
+ if (capture_context != null)
+ return capture_context.IsParameterCaptured (name);
+ return false;
+ }
+
+ public void EmitMeta (ToplevelBlock b, InternalParameters ip)
+ {
+ if (capture_context != null)
+ capture_context.EmitHelperClasses (this);
+ b.EmitMeta (this);
+
+ if (HasReturnLabel)
+ ReturnLabel = ig.DefineLabel ();
+ }
+
+ //
+ // Here until we can fix the problem with Mono.CSharp.Switch, which
+ // currently can not cope with ig == null during resolve (which must
+ // be fixed for switch statements to work on anonymous methods).
+ //
+ public void EmitTopBlock (ToplevelBlock block, InternalParameters ip, Location loc)
+ {
+ if (block == null)
+ return;
+
+ bool unreachable;
+
+ if (ResolveTopBlock (null, block, ip, loc, out unreachable)){
+ EmitMeta (block, ip);
+ current_phase = Phase.Emitting;
+ EmitResolvedTopBlock (block, unreachable);
+ }
+ }
+
+ public bool ResolveTopBlock (EmitContext anonymous_method_host, ToplevelBlock block,
+ InternalParameters ip, Location loc, out bool unreachable)
+ {
+ current_phase = Phase.Resolving;
+
+ unreachable = false;
+
+ capture_context = block.CaptureContext;
+
if (!Location.IsNull (loc))
CurrentFile = loc.File;
- if (block != null){
- try {
+#if PRODUCTION
+ try {
+#endif
int errors = Report.Errors;
- block.EmitMeta (this, ip);
+ block.ResolveMeta (block, this, ip);
+
if (Report.Errors == errors){
bool old_do_flow_analysis = DoFlowAnalysis;
DoFlowAnalysis = true;
- current_flow_branching = FlowBranching.CreateBranching (
- null, FlowBranching.BranchingType.Block, block, loc);
+ if (anonymous_method_host != null)
+ current_flow_branching = FlowBranching.CreateBranching (
+ anonymous_method_host.CurrentBranching, FlowBranching.BranchingType.Block,
+ block, loc);
+ else
+ current_flow_branching = FlowBranching.CreateBranching (
+ null, FlowBranching.BranchingType.Block, block, loc);
if (!block.Resolve (this)) {
current_flow_branching = null;
DoFlowAnalysis = old_do_flow_analysis;
- return;
+ return false;
}
FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock ();
DoFlowAnalysis = old_do_flow_analysis;
- block.Emit (this);
-
if (reachability.AlwaysReturns ||
reachability.AlwaysThrows ||
reachability.IsUnreachable)
unreachable = true;
}
- } catch (Exception e) {
+#if PRODUCTION
+ } catch (Exception e) {
Console.WriteLine ("Exception caught by the compiler while compiling:");
Console.WriteLine (" Block that caused the problem begin at: " + loc);
CurrentBlock.StartLocation, CurrentBlock.EndLocation);
}
Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
- Console.WriteLine (Report.FriendlyStackTrace (e));
-
- Environment.Exit (1);
- }
+ throw;
}
+#endif
if (ReturnType != null && !unreachable){
if (!InIterator){
- Report.Error (161, loc, "Not all code paths return a value");
- return;
+ if (CurrentAnonymousMethod != null){
+ Report.Error (1643, loc, "Not all code paths return a value in anonymous method of type `{0}'",
+ CurrentAnonymousMethod.Type);
+ } else {
+ Report.Error (161, loc, "Not all code paths return a value");
+ }
+
+ return false;
}
}
+ block.CompleteContexts ();
+
+ return true;
+ }
+ public void EmitResolvedTopBlock (ToplevelBlock block, bool unreachable)
+ {
+ if (block != null)
+ block.Emit (this);
+
if (HasReturnLabel)
ig.MarkLabel (ReturnLabel);
+
if (return_value != null){
ig.Emit (OpCodes.Ldloc, return_value);
ig.Emit (OpCodes.Ret);
ig.Emit (OpCodes.Ret);
}
}
+
+ //
+ // Close pending helper classes if we are the toplevel
+ //
+ if (capture_context != null && capture_context.ParentToplevel == null)
+ capture_context.CloseHelperClasses ();
}
/// <summary>
/// return value from the function. By default this is not
/// used. This is only required when returns are found inside
/// Try or Catch statements.
+ ///
+ /// This method is typically invoked from the Emit phase, so
+ /// we allow the creation of a return label if it was not
+ /// requested during the resolution phase. Could be cleaned
+ /// up, but it would replicate a lot of logic in the Emit phase
+ /// of the code that uses it.
/// </summary>
public LocalBuilder TemporaryReturn ()
{
if (return_value == null){
return_value = ig.DeclareLocal (ReturnType);
- ReturnLabel = ig.DefineLabel ();
- HasReturnLabel = true;
+ if (!HasReturnLabel){
+ ReturnLabel = ig.DefineLabel ();
+ HasReturnLabel = true;
+ }
}
return return_value;
}
+ /// <summary>
+ /// This method is used during the Resolution phase to flag the
+ /// need to define the ReturnLabel
+ /// </summary>
public void NeedReturnLabel ()
{
- if (!InIterator && !HasReturnLabel) {
- ReturnLabel = ig.DefineLabel ();
- HasReturnLabel = true;
+ if (current_phase != Phase.Resolving){
+ //
+ // The reason is that the `ReturnLabel' is declared between
+ // resolution and emission
+ //
+ throw new Exception ("NeedReturnLabel called from Emit phase, should only be called during Resolve");
}
+
+ if (!InIterator && !HasReturnLabel)
+ HasReturnLabel = true;
}
//
throw new Exception ("MapVariable for an unknown state");
}
- //
- // Invoke this routine to remap a VariableInfo into the
- // proper MemberAccess expression
- //
- public Expression RemapLocal (LocalInfo local_info)
- {
- FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
- fe.InstanceExpression = new ProxyInstance ();
- return fe.DoResolve (this);
- }
-
- public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
- {
- FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
- fe.InstanceExpression = new ProxyInstance ();
- return fe.DoResolveLValue (this, right_side);
- }
-
public Expression RemapParameter (int idx)
{
FieldExpr fe = new FieldExprNoAddress (CurrentIterator.parameter_fields [idx].FieldBuilder, loc);
public void EmitThis ()
{
ig.Emit (OpCodes.Ldarg_0);
- if (InIterator && !IsStatic){
- FieldBuilder this_field = CurrentIterator.this_field.FieldBuilder;
- if (TypeManager.IsValueType (this_field.FieldType))
- ig.Emit (OpCodes.Ldflda, this_field);
- else
- ig.Emit (OpCodes.Ldfld, this_field);
+ if (InIterator){
+ if (!IsStatic){
+ FieldBuilder this_field = CurrentIterator.this_field.FieldBuilder;
+ if (TypeManager.IsValueType (this_field.FieldType))
+ ig.Emit (OpCodes.Ldflda, this_field);
+ else
+ ig.Emit (OpCodes.Ldfld, this_field);
+ }
+ } else if (capture_context != null && CurrentAnonymousMethod != null){
+ ScopeInfo si = CurrentAnonymousMethod.Scope;
+ while (si != null){
+ if (si.ParentLink != null)
+ ig.Emit (OpCodes.Ldfld, si.ParentLink);
+ if (si.THIS != null){
+ ig.Emit (OpCodes.Ldfld, si.THIS);
+ break;
+ }
+ si = si.ParentScope;
+ }
+ }
+ }
+
+ //
+ // Emits the code necessary to load the instance required
+ // to access the captured LocalInfo
+ //
+ public void EmitCapturedVariableInstance (LocalInfo li)
+ {
+ if (RemapToProxy){
+ ig.Emit (OpCodes.Ldarg_0);
+ return;
}
+
+ if (capture_context == null)
+ throw new Exception ("Calling EmitCapturedContext when there is no capture_context");
+
+ capture_context.EmitCapturedVariableInstance (this, li, CurrentAnonymousMethod);
}
+ public void EmitParameter (string name)
+ {
+ capture_context.EmitParameter (this, name);
+ }
+
+ public void EmitAssignParameter (string name, Expression source, bool leave_copy, bool prepare_for_load)
+ {
+ capture_context.EmitAssignParameter (this, name, source, leave_copy, prepare_for_load);
+ }
+
+ public void EmitAddressOfParameter (string name)
+ {
+ capture_context.EmitAddressOfParameter (this, name);
+ }
+
public Expression GetThis (Location loc)
{
This my_this;
if (expr is NullLiteral && !type.IsValueType && !TypeManager.IsEnumType (type))
return NullLiteral.Null;
- if (!Convert.ImplicitStandardConversionExists (expr, type)){
+ if (!Convert.ImplicitStandardConversionExists (Convert.ConstantEC, expr, type)){
Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
return null;
}
// A container class for all the conversion operations
//
public class Convert {
+ //
+ // This is used to prettify the code: a null argument is allowed
+ // for ImplicitStandardConversion as long as it is known that
+ // no anonymous method will play a role.
+ //
+ public const EmitContext ConstantEC = null;
+
static public void Error_CannotConvertType (Location loc, Type source, Type target)
{
Report.Error (30, loc, "Cannot convert type '" +
if (expr_type.IsValueType)
return new BoxedCast (expr);
- if (expr_type.IsClass || expr_type.IsInterface || expr_type == TypeManager.enum_type)
+ if (expr_type.IsClass || expr_type.IsInterface || expr_type == TypeManager.enum_type){
+ if (target_type == TypeManager.anonymous_method_type)
+ return null;
return new EmptyCast (expr, target_type);
+ }
return null;
} else if (target_type == TypeManager.value_type) {
Type target_element_type = TypeManager.GetElementType (target_type);
if (!expr_element_type.IsValueType && !target_element_type.IsValueType)
- if (ImplicitStandardConversionExists (MyEmptyExpr,
+ if (ImplicitStandardConversionExists (ConstantEC, MyEmptyExpr,
target_element_type))
return new EmptyCast (expr, target_type);
}
if (target_type == TypeManager.object_type) {
if (expr_type.IsClass || expr_type.IsValueType ||
expr_type.IsInterface || expr_type == TypeManager.enum_type)
- return true;
+ if (target_type != TypeManager.anonymous_method_type)
+ return true;
return false;
} else if (expr_type.IsSubclassOf (target_type))
Type target_element_type = TypeManager.GetElementType (target_type);
if (!expr_element_type.IsValueType && !target_element_type.IsValueType)
- if (ImplicitStandardConversionExists (MyEmptyExpr,
+ if (ImplicitStandardConversionExists (ConstantEC, MyEmptyExpr,
target_element_type))
return true;
}
/// </summary>
public static bool ImplicitConversionExists (EmitContext ec, Expression expr, Type target_type)
{
- if (ImplicitStandardConversionExists (expr, target_type))
+ if (ImplicitStandardConversionExists (ec, expr, target_type))
return true;
Expression dummy = ImplicitUserConversion (ec, expr, target_type, Location.Null);
/// <summary>
/// Determines if a standard implicit conversion exists from
/// expr_type to target_type
+ ///
+ /// ec should point to a real EmitContext if expr.Type is TypeManager.anonymous_method_type.
/// </summary>
- public static bool ImplicitStandardConversionExists (Expression expr, Type target_type)
+ public static bool ImplicitStandardConversionExists (EmitContext ec, Expression expr, Type target_type)
{
Type expr_type = expr.Type;
if (target_type == TypeManager.void_ptr_type && expr_type.IsPointer)
return true;
+ if (expr_type == TypeManager.anonymous_method_type){
+ if (!TypeManager.IsDelegateType (target_type))
+ return false;
+
+ AnonymousMethod am = (AnonymousMethod) expr;
+ int errors = Report.Errors;
+
+ Expression conv = am.Compatible (ec, target_type, true);
+ if (conv != null)
+ return true;
+ }
+
return false;
}
/// Finds "most encompassed type" according to the spec (13.4.2)
/// amongst the methods in the MethodGroupExpr
/// </summary>
- static Type FindMostEncompassedType (ArrayList types)
+ static Type FindMostEncompassedType (EmitContext ec, ArrayList types)
{
Type best = null;
continue;
}
- if (ImplicitStandardConversionExists (priv_fmet_param, best))
+ if (ImplicitStandardConversionExists (ec, priv_fmet_param, best))
best = t;
}
/// Finds "most encompassing type" according to the spec (13.4.2)
/// amongst the types in the given set
/// </summary>
- static Type FindMostEncompassingType (ArrayList types)
+ static Type FindMostEncompassingType (EmitContext ec, ArrayList types)
{
Type best = null;
continue;
}
- if (ImplicitStandardConversionExists (priv_fmee_ret, t))
+ if (ImplicitStandardConversionExists (ec, priv_fmee_ret, t))
best = t;
}
/// 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,
+ static public Type FindMostSpecificSource (EmitContext ec, MethodGroupExpr me,
+ Expression source, bool apply_explicit_conv_rules,
Location loc)
{
ArrayList src_types_set = new ArrayList ();
// or encompassed by S to a type encompassing or encompassed by T
//
priv_fms_expr.SetType (param_type);
- if (ImplicitStandardConversionExists (priv_fms_expr, source_type))
+ if (ImplicitStandardConversionExists (ec, priv_fms_expr, source_type))
src_types_set.Add (param_type);
else {
- if (ImplicitStandardConversionExists (source, param_type))
+ if (ImplicitStandardConversionExists (ec, source, param_type))
src_types_set.Add (param_type);
}
} else {
//
// Only if S is encompassed by param_type
//
- if (ImplicitStandardConversionExists (source, param_type))
+ if (ImplicitStandardConversionExists (ec, source, param_type))
src_types_set.Add (param_type);
}
}
ArrayList candidate_set = new ArrayList ();
foreach (Type param_type in src_types_set){
- if (ImplicitStandardConversionExists (source, param_type))
+ if (ImplicitStandardConversionExists (ec, source, param_type))
candidate_set.Add (param_type);
}
if (candidate_set.Count != 0)
- return FindMostEncompassedType (candidate_set);
+ return FindMostEncompassedType (ec, candidate_set);
}
//
// Final case
//
if (apply_explicit_conv_rules)
- return FindMostEncompassingType (src_types_set);
+ return FindMostEncompassingType (ec, src_types_set);
else
- return FindMostEncompassedType (src_types_set);
+ return FindMostEncompassedType (ec, src_types_set);
}
//
/// <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,
+ static public Type FindMostSpecificTarget (EmitContext ec, MethodGroupExpr me,
+ Type target, bool apply_explicit_conv_rules,
Location loc)
{
ArrayList tgt_types_set = new ArrayList ();
// or encompassed by S to a type encompassing or encompassed by T
//
priv_fms_expr.SetType (ret_type);
- if (ImplicitStandardConversionExists (priv_fms_expr, target))
+ if (ImplicitStandardConversionExists (ec, priv_fms_expr, target))
tgt_types_set.Add (ret_type);
else {
priv_fms_expr.SetType (target);
- if (ImplicitStandardConversionExists (priv_fms_expr, ret_type))
+ if (ImplicitStandardConversionExists (ec, 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 (ImplicitStandardConversionExists (priv_fms_expr, target))
+ if (ImplicitStandardConversionExists (ec, priv_fms_expr, target))
tgt_types_set.Add (ret_type);
}
}
foreach (Type ret_type in tgt_types_set){
priv_fmt_expr.SetType (ret_type);
- if (ImplicitStandardConversionExists (priv_fmt_expr, target))
+ if (ImplicitStandardConversionExists (ec, priv_fmt_expr, target))
candidate_set.Add (ret_type);
}
if (candidate_set.Count != 0)
- return FindMostEncompassingType (candidate_set);
+ return FindMostEncompassingType (ec, candidate_set);
}
//
// Okay, final case !
//
if (apply_explicit_conv_rules)
- return FindMostEncompassedType (tgt_types_set);
+ return FindMostEncompassedType (ec, tgt_types_set);
else
- return FindMostEncompassingType (tgt_types_set);
+ return FindMostEncompassingType (ec, tgt_types_set);
}
/// <summary>
Type most_specific_source, most_specific_target;
- most_specific_source = FindMostSpecificSource (union, source, look_for_explicit, loc);
+ most_specific_source = FindMostSpecificSource (ec, union, source, look_for_explicit, loc);
if (most_specific_source == null)
return null;
- most_specific_target = FindMostSpecificTarget (union, target, look_for_explicit, loc);
+ most_specific_target = FindMostSpecificTarget (ec, union, target, look_for_explicit, loc);
if (most_specific_target == null)
return null;
if (expr.eclass == ExprClass.MethodGroup){
if (!TypeManager.IsDelegateType (target_type)){
- Report.Error (428, loc,
- String.Format (
- "Cannot convert method group to `{0}', since it is not a delegate",
- TypeManager.CSharpName (target_type)));
return null;
}
-
- return ImplicitDelegateCreation.Create (ec, (MethodGroupExpr) expr, target_type, loc);
+
+ //
+ // Only allow anonymous method conversions on post ISO_1
+ //
+ if (RootContext.Version != LanguageVersion.ISO_1){
+ MethodGroupExpr mg = expr as MethodGroupExpr;
+ if (mg != null)
+ return ImplicitDelegateCreation.Create (ec, mg, target_type, loc);
+ }
}
-
+
if (expr_type == target_type && !(expr is NullLiteral))
return expr;
target_type.IsSubclassOf (TypeManager.enum_type)) &&
expr is IntLiteral){
IntLiteral i = (IntLiteral) expr;
-
+
if (i.Value == 0)
return new EnumConstant ((Constant) expr, target_type);
}
}
}
+ if (expr_type == TypeManager.anonymous_method_type){
+ if (!TypeManager.IsDelegateType (target_type)){
+ Report.Error (1660, loc,
+ "Cannot convert anonymous method to `{0}', since it is not a delegate",
+ TypeManager.CSharpName (target_type));
+ return null;
+ }
+
+ AnonymousMethod am = (AnonymousMethod) expr;
+ int errors = Report.Errors;
+
+ Expression conv = am.Compatible (ec, target_type, false);
+ if (conv != null)
+ return conv;
+
+ //
+ // We return something instead of null, to avoid
+ // the duplicate error, since am.Compatible would have
+ // reported that already
+ //
+ if (errors != Report.Errors)
+ return new EmptyCast (expr, target_type);
+ }
+
return null;
}
static public void Error_CannotImplicitConversion (Location loc, Type source, Type target)
{
- string msg = "Cannot convert implicitly from `"+
- TypeManager.CSharpName (source) + "' to `" +
- TypeManager.CSharpName (target) + "'";
-
- Report.Error (29, loc, msg);
+ Report.Error (29, loc, "Cannot convert implicitly from {0} to `{1}'",
+ source == TypeManager.anonymous_method_type ?
+ "anonymous method" : "`" + TypeManager.CSharpName (source) + "'",
+ TypeManager.CSharpName (target));
}
/// <summary>
if (TypeManager.IsEnumType (real_target_type))
real_target_type = TypeManager.EnumToUnderlying (real_target_type);
- if (ImplicitStandardConversionExists (expr, real_target_type)){
+ if (ImplicitStandardConversionExists (ec, expr, real_target_type)){
Expression ce = ImplicitConversionStandard (ec, expr, real_target_type, loc);
if (real_target_type != target_type)
/// Current block is used to add statements as we find
/// them.
/// </summary>
- Block current_block;
+ Block current_block, top_current_block;
- /// <summary>
- /// If true, creates a toplevel block in the block production
- /// This is flagged by the delegate creation
- /// </summary>
- bool create_toplevel_block;
-
/// <summary>
/// This is used by the unary_expression code to resolve
/// a name against a parameter.
}
}
- method.Block = (Block) $3;
+ method.Block = (ToplevelBlock) $3;
current_container.AddMethod (method);
current_local_parameters = null;
}
accessor_body
{
- $$ = new Accessor ((Block) $4, (Attributes) $1, lexer.Location);
+ $$ = new Accessor ((ToplevelBlock) $4, (Attributes) $1, lexer.Location);
current_local_parameters = null;
lexer.PropertyParsing = true;
}
}
accessor_body
{
- $$ = new Accessor ((Block) $4, (Attributes) $1, lexer.Location);
+ $$ = new Accessor ((ToplevelBlock) $4, (Attributes) $1, lexer.Location);
current_local_parameters = null;
lexer.PropertyParsing = true;
}
Operator op = new Operator (
current_class, decl.optype, decl.ret_type, (int) $2,
new Parameters (param_list, null, decl.location),
- (Block) $5, (Attributes) $1, decl.location);
+ (ToplevelBlock) $5, (Attributes) $1, decl.location);
if (SimpleIteratorContainer.Simple.Yields)
op.SetYields ();
constructor_body
{
Constructor c = (Constructor) $3;
- c.Block = (Block) $4;
+ c.Block = (ToplevelBlock) $4;
c.OptAttributes = (Attributes) $1;
c.ModFlags = (int) $2;
current_class, TypeManager.system_void_expr, m, "Finalize",
new Parameters (null, null, l), (Attributes) $1, l);
- d.Block = (Block) $7;
+ d.Block = (ToplevelBlock) $7;
current_container.AddMethod (d);
}
}
}
block
{
- $$ = new Accessor ((Block) $4, (Attributes) $1, lexer.Location);
+ $$ = new Accessor ((ToplevelBlock) $4, (Attributes) $1, lexer.Location);
lexer.EventParsing = true;
}
| opt_attributes ADD error {
}
block
{
- $$ = new Accessor ((Block) $4, (Attributes) $1, lexer.Location);
+ $$ = new Accessor ((ToplevelBlock) $4, (Attributes) $1, lexer.Location);
lexer.EventParsing = true;
}
| opt_attributes REMOVE error {
: DELEGATE opt_anonymous_method_signature {
oob_stack.Push (current_local_parameters);
current_local_parameters = (Parameters)$2;
- create_toplevel_block = true;
+
+ // Force the next block to be created as a ToplevelBlock
+ oob_stack.Push (current_block);
+ oob_stack.Push (top_current_block);
+ oob_stack.Push (lexer.Location);
+ current_block = null;
} block {
- if (true){
- Report.Error (-213, lexer.Location, "Anonymous methods are not supported in this branch");
+ Location loc = (Location) oob_stack.Pop ();
+ top_current_block = (Block) oob_stack.Pop ();
+ current_block = (Block) oob_stack.Pop ();
+ if (RootContext.Version == LanguageVersion.ISO_1){
+ Report.FeatureIsNotStandardized (lexer.Location, "anonymous methods");
$$ = null;
- } else {
- create_toplevel_block = false;
- if (RootContext.Version == LanguageVersion.ISO_1){
- Report.FeatureIsNotStandardized (lexer.Location, "anonymous methods");
- $$ = null;
- } else
- $$ = new AnonymousMethod ((Parameters) $2, (Block) $4, lexer.Location);
- current_local_parameters = (Parameters) oob_stack.Pop ();
+ } else {
+ ToplevelBlock anon_block = (ToplevelBlock) $4;
+
+ anon_block.Parent = current_block;
+ $$ = new AnonymousMethod ((Parameters) $2, (ToplevelBlock) top_current_block,
+ anon_block, loc);
}
+ current_local_parameters = (Parameters) oob_stack.Pop ();
}
;
opt_anonymous_method_signature
- : /* empty */ { $$ = Parameters.EmptyReadOnlyParameters; }
+ : /* empty */ { $$ = null; }
| anonymous_method_signature
;
;
opt_anonymous_method_parameter_list
- : /* empty */ { $$ = null; }
+ : /* empty */ { $$ = null; }
| anonymous_method_parameter_list { $$ = $1; }
;
anonymous_method_parameter
: opt_parameter_modifier type IDENTIFIER {
- $$ = new Parameter ((Expression) $2, (string) $2, (Parameter.Modifier) $1, null);
+ $$ = new Parameter ((Expression) $2, (string) $3, (Parameter.Modifier) $1, null);
+ }
+ | PARAMS type IDENTIFIER {
+ Report.Error (-221, lexer.Location, "params modifier not allowed in anonymous method declaration");
+ $$ = null;
}
;
block
: OPEN_BRACE
{
- if (current_block == null || create_toplevel_block){
- current_block = new ToplevelBlock (current_local_parameters, lexer.Location);
+ if (current_block == null){
+ current_block = new ToplevelBlock ((ToplevelBlock) top_current_block, current_local_parameters, lexer.Location);
+ top_current_block = current_block;
} else {
current_block = new Block (current_block, current_local_parameters,
lexer.Location, Location.Null);
$$ = current_block;
current_block.SetEndLocation (lexer.Location);
current_block = current_block.Parent;
+ if (current_block == null)
+ top_current_block = null;
}
;
return true;
}
+ //
+ // Returns the MethodBase for "Invoke" from a delegate type, this is used
+ // to extract the signature of a delegate.
+ //
+ public static MethodInfo GetInvokeMethod (EmitContext ec, Type delegate_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 null;
+ }
+
+ return (MethodInfo) (((MethodGroupExpr) ml).Methods [0]);
+ }
+
/// <summary>
/// Verifies whether the method in question is compatible with the delegate
/// Returns the method itself if okay and null if not.
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!");
+ MethodBase invoke_mb = GetInvokeMethod (ec, delegate_type, loc);
+ if (invoke_mb == null)
return null;
- }
-
- MethodBase invoke_mb = ((MethodGroupExpr) ml).Methods [0];
ParameterData invoke_pd = Invocation.GetParameterData (invoke_mb);
if (!params_method && pd_count != arg_count) {
Report.Error (1593, loc,
- "Delegate '" + delegate_type.ToString ()
- + "' does not take '" + arg_count + "' arguments");
+ "Delegate '{0}' does not take {1} arguments",
+ delegate_type.ToString (), arg_count);
return false;
}
return null;
}
}
-
+
//
// A delegate-creation-expression, invoked from the `New' class
//
/// </summary>
public static object ConvertIntLiteral (Constant c, Type target_type, Location loc)
{
- if (!Convert.ImplicitStandardConversionExists (c, target_type)){
+ if (!Convert.ImplicitStandardConversionExists (Convert.ConstantEC, c, target_type)){
Convert.Error_CannotImplicitConversion (loc, c.Type, target_type);
return null;
}
return var.Resolve (ec);
}
- 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, current_block, idx, Name, loc);
-
+ ParameterReference pref = current_block.GetParameterReference (Name, loc);
+ if (pref != null) {
if (right_side != null)
- return param.ResolveLValue (ec, right_side);
+ return pref.ResolveLValue (ec, right_side);
else
- return param.Resolve (ec);
+ return pref.Resolve (ec);
}
}
AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (FieldInfo), loc);
}
+ if (ec.CurrentAnonymousMethod != null){
+ if (!FieldInfo.IsStatic){
+ if (ec.TypeContainer is Struct){
+ Report.Error (1673, loc, "Can not reference instance variables in anonymous methods hosted in structs");
+ return null;
+ }
+ ec.CaptureField (this);
+ }
+ }
+
// If the instance expression is a local variable or parameter.
IVariable var = instance_expr as IVariable;
if ((var == null) || (var.VariableInfo == null))
ig.Emit (OpCodes.Ldfld, FieldInfo);
}
-
+
if (leave_copy) {
ec.ig.Emit (OpCodes.Dup);
if (!FieldInfo.IsStatic) {
//
// Also, a standard conversion must exist from either one
//
- if (!(Convert.ImplicitStandardConversionExists (left, r) ||
- Convert.ImplicitStandardConversionExists (right, l))){
+ if (!(Convert.ImplicitStandardConversionExists (ec, left, r) ||
+ Convert.ImplicitStandardConversionExists (ec, right, l))){
Error_OperatorCannotBeApplied ();
return null;
}
//
if (oper == Operator.Addition || oper == Operator.Subtraction) {
if (l.IsSubclassOf (TypeManager.delegate_type)){
- if ((right.eclass == ExprClass.MethodGroup) &&
- (RootContext.Version != LanguageVersion.ISO_1)){
- Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
- if (tmp == null)
- return null;
- right = tmp;
- r = right.Type;
+ if (((right.eclass == ExprClass.MethodGroup) ||
+ (r == TypeManager.anonymous_method_type))){
+ if ((RootContext.Version != LanguageVersion.ISO_1)){
+ Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
+ if (tmp == null)
+ return null;
+ right = tmp;
+ r = right.Type;
+ }
}
-
+
if (r.IsSubclassOf (TypeManager.delegate_type)){
MethodInfo method;
ArrayList args = new ArrayList (2);
Label false_target = ig.DefineLabel ();
Label end_target = ig.DefineLabel ();
- ig.Emit (OpCodes.Nop);
-
left.Emit (ec);
left_temp.Store (ec);
ig.MarkLabel (false_target);
op.Emit (ec);
ig.MarkLabel (end_target);
-
- ig.Emit (OpCodes.Nop);
}
}
public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
public readonly string Name;
public readonly Block Block;
- LocalInfo local_info;
+ public LocalInfo local_info;
bool is_readonly;
+ bool prepared;
+ LocalTemporary temp;
public LocalVariableReference (Block block, string name, Location 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,
LocalInfo local_info, bool is_readonly)
: this (block, name, l)
}
public VariableInfo VariableInfo {
- get { return local_info.VariableInfo; }
+ get {
+ return local_info.VariableInfo;
+ }
}
public bool IsReadOnly {
}
}
- protected void DoResolveBase (EmitContext ec)
+ protected Expression DoResolveBase (EmitContext ec, Expression lvalue_right_side)
{
if (local_info == null) {
local_info = Block.GetLocalInfo (Name);
}
type = local_info.VariableType;
-#if false
- if (ec.InAnonymousMethod)
- Block.LiftVariable (local_info);
-#endif
- }
- protected Expression DoResolve (EmitContext ec, bool is_lvalue)
- {
+ VariableInfo variable_info = local_info.VariableInfo;
+ if (lvalue_right_side != null){
+ if (is_readonly){
+ Error (1604, "cannot assign to `" + Name + "' because it is readonly");
+ return null;
+ }
+
+ if (variable_info != null)
+ variable_info.SetAssigned (ec);
+ }
+
Expression e = Block.GetConstantExpression (Name);
if (e != null) {
local_info.Used = true;
return e.Resolve (ec);
}
- VariableInfo variable_info = local_info.VariableInfo;
if ((variable_info != null) && !variable_info.IsAssigned (ec, loc))
return null;
- if (!is_lvalue)
+ if (lvalue_right_side == null)
local_info.Used = true;
- if (local_info.LocalBuilder == null)
- return ec.RemapLocal (local_info);
-
+ if (ec.CurrentAnonymousMethod != null){
+ //
+ // If we are referencing a variable from the external block
+ // flag it for capturing
+ //
+ if (local_info.Block.Toplevel != ec.CurrentBlock.Toplevel){
+ ec.CaptureVariable (local_info);
+ //Console.WriteLine ("Capturing at " + loc);
+ }
+ }
+
return this;
}
public override Expression DoResolve (EmitContext ec)
{
- DoResolveBase (ec);
-
- return DoResolve (ec, false);
+ return DoResolveBase (ec, null);
}
override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
{
- DoResolveBase (ec);
-
- VariableInfo variable_info = local_info.VariableInfo;
- if (variable_info != null)
- variable_info.SetAssigned (ec);
-
- Expression e = DoResolve (ec, right_side != EmptyExpression.Null);
-
- if (e == null)
- return null;
-
- if (is_readonly){
- Error (1604, "cannot assign to `" + Name + "' because it is readonly");
- return null;
- }
-
- CheckObsoleteAttribute (e.Type);
-
- if (local_info.LocalBuilder == null)
- return ec.RemapLocalLValue (local_info, right_side);
-
- return this;
+ return DoResolveBase (ec, right_side);
}
public bool VerifyFixed (bool is_expression)
{
ILGenerator ig = ec.ig;
- ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
+ if (local_info.FieldBuilder == null){
+ //
+ // A local variable on the local CLR stack
+ //
+ ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
+ } else {
+ //
+ // A local variable captured by anonymous methods.
+ //
+ if (!prepared)
+ ec.EmitCapturedVariableInstance (local_info);
+
+ ig.Emit (OpCodes.Ldfld, local_info.FieldBuilder);
+ }
}
public void Emit (EmitContext ec, bool leave_copy)
{
Emit (ec);
- if (leave_copy)
+ if (leave_copy){
ec.ig.Emit (OpCodes.Dup);
+ if (local_info.FieldBuilder != null){
+ temp = new LocalTemporary (ec, Type);
+ temp.Store (ec);
+ }
+ }
}
public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
{
- source.Emit (ec);
- if (leave_copy)
- ec.ig.Emit (OpCodes.Dup);
- ec.ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
+ ILGenerator ig = ec.ig;
+ prepared = prepare_for_load;
+
+ if (local_info.FieldBuilder == null){
+ //
+ // A local variable on the local CLR stack
+ //
+ if (local_info.LocalBuilder == null)
+ throw new Exception ("This should not happen: both Field and Local are null");
+
+ source.Emit (ec);
+ if (leave_copy)
+ ec.ig.Emit (OpCodes.Dup);
+ ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
+ } else {
+ //
+ // A local variable captured by anonymous methods or itereators.
+ //
+ ec.EmitCapturedVariableInstance (local_info);
+
+ if (prepare_for_load)
+ ig.Emit (OpCodes.Dup);
+ source.Emit (ec);
+ if (leave_copy){
+ ig.Emit (OpCodes.Dup);
+ temp = new LocalTemporary (ec, Type);
+ temp.Store (ec);
+ }
+ ig.Emit (OpCodes.Stfld, local_info.FieldBuilder);
+ if (temp != null)
+ temp.Emit (ec);
+ }
}
public void AddressOf (EmitContext ec, AddressOp mode)
{
ILGenerator ig = ec.ig;
-
- ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
+
+ if (local_info.FieldBuilder == null){
+ //
+ // A local variable on the local CLR stack
+ //
+ ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
+ } else {
+ //
+ // A local variable captured by anonymous methods or iterators
+ //
+ ec.EmitCapturedVariableInstance (local_info);
+ ig.Emit (OpCodes.Ldflda, local_info.FieldBuilder);
+ }
}
public override string ToString ()
VariableInfo vi;
public Parameter.Modifier mod;
public bool is_ref, is_out, prepared;
+
+ public bool IsOut {
+ get {
+ return is_out;
+ }
+ }
+
+ public bool IsRef {
+ get {
+ return is_ref;
+ }
+ }
+
LocalTemporary temp;
public ParameterReference (Parameters pars, Block block, int idx, string name, Location loc)
public bool IsAssigned (EmitContext ec, Location loc)
{
- if (!ec.DoFlowAnalysis || !is_out ||
- ec.CurrentBranching.IsAssigned (vi))
+ if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
return true;
Report.Error (165, loc,
public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
{
- if (!ec.DoFlowAnalysis || !is_out ||
- ec.CurrentBranching.IsFieldAssigned (vi, field_name))
+ if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
return true;
Report.Error (170, loc,
ec.CurrentBranching.SetAssigned (vi);
}
- public void SetFieldAssigned (EmitContext ec, string field_name)
+ public void SetFieldAssigned (EmitContext ec, string field_name)
{
if (is_out && ec.DoFlowAnalysis)
ec.CurrentBranching.SetFieldAssigned (vi, field_name);
if (is_out)
vi = block.ParameterMap [idx];
+
+ if (ec.CurrentAnonymousMethod != null){
+ if (is_ref){
+ Report.Error (1628, Location,
+ "Can not reference a ref or out parameter in an anonymous method");
+ return;
+ }
+
+ //
+ // If we are referencing the parameter from the external block
+ // flag it for capturing
+ //
+ //Console.WriteLine ("Is parameter `{0}' local? {1}", name, block.IsLocalParameter (name));
+ if (!block.IsLocalParameter (name)){
+ ec.CaptureParameter (name, type, idx);
+ }
+ }
}
//
arg_idx++;
EmitLdArg (ig, arg_idx);
+
+ //
+ // FIXME: Review for anonymous methods
+ //
}
public override void Emit (EmitContext ec)
{
+ if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
+ ec.EmitParameter (name);
+ return;
+ }
+
Emit (ec, false);
}
public void Emit (EmitContext ec, bool leave_copy)
{
ILGenerator ig = ec.ig;
-
int arg_idx = idx;
if (!ec.IsStatic)
public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
{
+ if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
+ ec.EmitAssignParameter (name, source, leave_copy, prepare_for_load);
+ return;
+ }
+
ILGenerator ig = ec.ig;
int arg_idx = idx;
public void AddressOf (EmitContext ec, AddressOp mode)
{
+ if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
+ ec.EmitAddressOfParameter (name);
+ return;
+ }
+
int arg_idx = idx;
if (!ec.IsStatic)
if (Expr is ParameterReference){
ParameterReference pr = (ParameterReference) Expr;
- if (pr.is_ref)
+ if (pr.IsRef)
pr.EmitLoad (ec);
else {
Argument a = (Argument) arguments [i];
- Parameter.Modifier a_mod = a.GetParameterModifier () &
- ~(Parameter.Modifier.OUT | Parameter.Modifier.REF);
+ Parameter.Modifier a_mod = a.GetParameterModifier () &
+ (unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF)));
Parameter.Modifier p_mod = pd.ParameterModifier (i) &
- ~(Parameter.Modifier.OUT | Parameter.Modifier.REF);
+ (unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF)));
if (a_mod == p_mod) {
Argument a = (Argument) arguments [i];
Parameter.Modifier a_mod = a.GetParameterModifier () &
- ~(Parameter.Modifier.OUT | Parameter.Modifier.REF);
+ unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
Parameter.Modifier p_mod = pd.ParameterModifier (i) &
- ~(Parameter.Modifier.OUT | Parameter.Modifier.REF);
+ unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
if (a_mod == p_mod ||
}
Parameter.Modifier a_mod = a.GetParameterModifier () &
- ~(Parameter.Modifier.OUT | Parameter.Modifier.REF);
+ unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
Parameter.Modifier p_mod = pd.ParameterModifier (j) &
- ~(Parameter.Modifier.OUT | Parameter.Modifier.REF);
+ unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
if (a_mod != p_mod &&
pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS) {
"catch clause");
return false;
}
- if (ec.InAnonymousMethod){
- Report.Error (1621, loc, "yield statement can not appear " +
- "inside an anonymoud method");
+ if (ec.CurrentAnonymousMethod != null){
+ Report.Error (1621, loc, "yield statement can not appear inside an anonymoud method");
return false;
}
}
public class Iterator : Class {
+ ToplevelBlock original_block;
+ ToplevelBlock block;
string original_name;
- Block original_block;
- Block block;
Type iterator_type;
TypeExpr iterator_type_expr;
Label dispatcher = ig.DefineLabel ();
ig.Emit (OpCodes.Br, dispatcher);
+ ec.RemapToProxy = true;
Label [] labels = new Label [resume_points.Count];
for (int i = 0; i < labels.Length; i++) {
ResumePoint point = (ResumePoint) resume_points [i];
ig.EndExceptionBlock ();
ig.Emit (OpCodes.Br, end);
}
-
+ ec.RemapToProxy = false;
+
ig.MarkLabel (dispatcher);
ig.Emit (OpCodes.Ldarg_0);
ig.Emit (OpCodes.Ldfld, pc_field.FieldBuilder);
//
public Iterator (TypeContainer container, string name, Type return_type,
Type [] param_types, InternalParameters parameters,
- int modifiers, Block block, Location loc)
+ int modifiers, ToplevelBlock block, Location loc)
: base (container.NamespaceEntry, container, MakeProxyName (name),
Modifiers.PRIVATE, null, loc)
{
this.parameters = parameters;
this.original_name = name;
this.original_block = block;
- this.block = new Block (null);
+ this.block = new ToplevelBlock (loc);
fields = new Hashtable ();
Location);
AddConstructor (ctor);
- Block block = ctor.Block = new Block (null);
+ ToplevelBlock block = ctor.Block = new ToplevelBlock (Location);
if (!is_static) {
Type t = container.TypeBuilder;
void Define_Current ()
{
+ ToplevelBlock get_block = new ToplevelBlock (Location);
MemberName left = new MemberName ("System.Collections.IEnumerator");
MemberName name = new MemberName (left, "Current");
- Block get_block = new Block (null);
-
get_block.AddStatement (new If (
new Binary (
Binary.Operator.LessThanOrEqual,
Location.Null);
AddMethod (move_next);
- Block block = move_next.Block = new Block (null);
+ ToplevelBlock block = move_next.Block = new ToplevelBlock (Location);
MoveNextMethod inline = new MoveNextMethod (this, Location);
block.AddStatement (inline);
Location.Null);
AddMethod (get_enumerator);
- get_enumerator.Block = new Block (null);
+ get_enumerator.Block = new ToplevelBlock (Location);
Expression ce = new MemberAccess (
new SimpleName ("System.Threading.Interlocked", Location),
Parameters.EmptyReadOnlyParameters, null, Location);
AddMethod (reset);
- reset.Block = new Block (null);
+ reset.Block = new ToplevelBlock (Location);
reset.Block.AddStatement (Create_ThrowNotSupported ());
}
Parameters.EmptyReadOnlyParameters, null, Location);
AddMethod (dispose);
- dispose.Block = new Block (null);
+ dispose.Block = new ToplevelBlock (Location);
dispose.Block.AddStatement (new DisposeMethod (this, Location));
}
- public Block Block {
+ public ToplevelBlock Block {
get { return block; }
}
else
typeName = TypeName.ToString ();
- switch (ModFlags & ~Modifier.ISBYREF) {
+ switch (ModFlags & unchecked (~Modifier.ISBYREF)) {
case Modifier.OUT:
return "out " + typeName;
case Modifier.PARAMS:
return null;
}
+ public Parameter GetParameterByName (string name)
+ {
+ int idx;
+
+ return GetParameterByName (name, out idx);
+ }
+
bool ComputeParameterTypes (EmitContext ec)
{
int extra = (ArrayParameter != null) ? 1 : 0;
// Martin Baulig (martin@gnome.org)
//
// (C) 2001, 2002, 2003 Ximian, Inc.
+// (C) 2003, 2004 Novell, Inc.
//
using System;
{
if (ec.ReturnType == null){
if (Expr != null){
+ if (ec.CurrentAnonymousMethod != null){
+ Report.Error (1662, loc, String.Format (
+ "Anonymous method could not be converted to delegate " +
+ "since the return value does not match the delegate value"));
+ }
Error (127, "Return with a value not allowed here");
return false;
}
} else
vector.CheckOutParameters (ec.CurrentBranching);
+ if (!in_exc)
+ ec.NeedReturnLabel ();
+
ec.CurrentBranching.CurrentUsageVector.Return ();
return true;
}
ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
}
- if (in_exc) {
- ec.NeedReturnLabel ();
+ if (in_exc)
ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
- } else {
+ else
ec.ig.Emit (OpCodes.Ret);
- }
}
}
/// </summary>
public class GotoCase : Statement {
Expression expr;
- Label label;
+ SwitchLabel sl;
public GotoCase (Expression e, Location l)
{
if (val == null)
return false;
- SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
+ sl = (SwitchLabel) ec.Switch.Elements [val];
if (sl == null){
Report.Error (
return false;
}
- label = sl.ILLabelCode;
-
ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
protected override void DoEmit (EmitContext ec)
{
- ec.ig.Emit (OpCodes.Br, label);
+ ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
}
}
crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
+ if (!crossing_exc)
+ ec.NeedReturnLabel ();
+
ec.CurrentBranching.CurrentUsageVector.Break ();
return true;
}
if (crossing_exc)
ig.Emit (OpCodes.Leave, ec.LoopEnd);
else {
- ec.NeedReturnLabel ();
ig.Emit (OpCodes.Br, ec.LoopEnd);
}
}
}
}
+ //
+ // The information about a user-perceived local variable
+ //
public class LocalInfo {
public Expression Type;
//
// Most of the time a variable will be stored in a LocalBuilder
//
- // But sometimes, it will be stored in a field. The context of the field will
+ // But sometimes, it will be stored in a field (variables that have been
+ // hoisted by iterators or by anonymous methods). The context of the field will
// be stored in the EmitContext
//
//
Used = 1,
ReadOnly = 2,
Pinned = 4,
- IsThis = 8
+ IsThis = 8,
+ Captured = 16
}
Flags flags;
}
}
+ public bool IsCaptured {
+ get {
+ return (flags & Flags.Captured) != 0;
+ }
+
+ set {
+ flags |= Flags.Captured;
+ }
+ }
+
public override string ToString ()
{
return String.Format ("LocalInfo ({0},{1},{2},{3})",
return (flags & Flags.Used) != 0;
}
set {
- flags = value ? (flags | Flags.Used) : (flags & ~Flags.Used);
+ flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used));
}
}
return (flags & Flags.ReadOnly) != 0;
}
set {
- flags = value ? (flags | Flags.ReadOnly) : (flags & ~Flags.ReadOnly);
+ flags = value ? (flags | Flags.ReadOnly) : (unchecked (flags & ~Flags.ReadOnly));
}
}
/// they contain extra information that is not necessary on normal blocks.
/// </remarks>
public class Block : Statement {
- public readonly Block Parent;
+ public Block Parent;
public readonly Location StartLocation;
- public Location EndLocation = Location.Null;
+ public Location EndLocation = Location.Null;
[Flags]
- public enum Flags : byte {
+ public enum Flags {
Implicit = 1,
Unchecked = 2,
BlockUsed = 4,
HasRet = 16,
IsDestructor = 32,
HasVarargs = 64,
- Unsafe = 128,
+ IsToplevel = 128,
+ Unsafe = 256
}
Flags flags;
// Keeps track of constants
Hashtable constants;
+ //
+ // The parameters for the block, this is only needed on the toplevel block really
+ // TODO: move `parameters' into ToplevelBlock
+ Parameters parameters;
+
//
// If this is a switch section, the enclosing switch block.
//
Block switch_block;
- static int id;
+ protected static int id;
int this_id;
return e != null;
}
-
- Parameters parameters = null;
- public Parameters Parameters {
- get {
- Block b = this;
- while (b.Parent != null)
- b = b.Parent;
- return b.parameters;
- }
+
+ //
+ // Returns a `ParameterReference' for the given name, or null if there
+ // is no such parameter
+ //
+ public ParameterReference GetParameterReference (string name, Location loc)
+ {
+ Block b = this;
+
+ do {
+ Parameters pars = b.parameters;
+
+ if (pars != null){
+ Parameter par;
+ int idx;
+
+ par = pars.GetParameterByName (name, out idx);
+ if (par != null){
+ ParameterReference pr;
+
+ pr = new ParameterReference (pars, this, idx, name, loc);
+ return pr;
+ }
+ }
+ b = b.Parent;
+ } while (b != null);
+ return null;
}
+ //
+ // Whether the parameter named `name' is local to this block,
+ // or false, if the parameter belongs to an encompassing block.
+ //
+ public bool IsLocalParameter (string name)
+ {
+ Block b = this;
+ int toplevel_count = 0;
+
+ do {
+ if (this is ToplevelBlock)
+ toplevel_count++;
+
+ Parameters pars = b.parameters;
+ if (pars != null){
+ if (pars.GetParameterByName (name) != null)
+ return true;
+ return false;
+ }
+ if (toplevel_count > 0)
+ return false;
+ b = b.Parent;
+ } while (b != null);
+ return false;
+ }
+
+ //
+ // Whether the `name' is a parameter reference
+ //
+ public bool IsParameterReference (string name)
+ {
+ Block b = this;
+
+ do {
+ Parameters pars = b.parameters;
+
+ if (pars != null)
+ if (pars.GetParameterByName (name) != null)
+ return true;
+ b = b.Parent;
+ } while (b != null);
+ return false;
+ }
+
/// <returns>
/// A list of labels that were not used within this block
/// </returns>
public VariableMap ParameterMap {
get {
if ((flags & Flags.VariablesInitialized) == 0)
- throw new Exception ();
+ throw new Exception ("Variables have not been initialized yet");
return param_map;
}
public VariableMap LocalMap {
get {
if ((flags & Flags.VariablesInitialized) == 0)
- throw new Exception ();
+ throw new Exception ("Variables have not been initialized yet");
return local_map;
}
}
- public bool LiftVariable (LocalInfo local_info)
- {
- return false;
- }
-
/// <summary>
/// Emits the variable declarations and labels.
/// </summary>
/// tc: is our typecontainer (to resolve type references)
/// ig: is the code generator:
/// </remarks>
- public void EmitMeta (EmitContext ec, InternalParameters ip)
+ public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, InternalParameters ip)
{
ILGenerator ig = ec.ig;
bool old_check_state = ec.ConstantCheckState;
ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
- bool remap_locals = ec.RemapToProxy;
-
+
//
// Process this block variables
//
continue;
}
+#if false
if (remap_locals)
vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
else if (vi.Pinned)
vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
else if (!vi.IsThis)
vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
+#endif
if (constants == null)
continue;
//
if (children != null){
foreach (Block b in children)
- b.EmitMeta (ec, ip);
+ b.ResolveMeta (toplevel, ec, ip);
}
-
ec.InUnsafe = old_unsafe;
}
+ //
+ // Emits the local variable declarations for a block
+ //
+ public void EmitMeta (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ if (variables != null){
+ bool have_captured_vars = ec.HaveCapturedVariables ();
+ bool remap_locals = ec.RemapToProxy;
+
+ foreach (DictionaryEntry de in variables){
+ LocalInfo vi = (LocalInfo) de.Value;
+
+ if (have_captured_vars && ec.IsCaptured (vi))
+ continue;
+
+ if (remap_locals){
+ vi.FieldBuilder = ec.MapVariable (vi.Name, vi.VariableType);
+ } else {
+ if (vi.Pinned)
+ //
+ // This is needed to compile on both .NET 1.x and .NET 2.x
+ // the later introduced `DeclareLocal (Type t, bool pinned)'
+ //
+ vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
+ else if (!vi.IsThis)
+ vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
+ }
+ }
+ }
+
+ if (children != null){
+ foreach (Block b in children)
+ b.EmitMeta (ec);
+ }
+ }
+
void UsageWarning (FlowBranching.UsageVector vector)
{
string name;
ec.CurrentBlock = prev_block;
}
+
+ public ToplevelBlock Toplevel {
+ get {
+ Block b = this;
+ while (b.Parent != null){
+ if ((b.flags & Flags.IsToplevel) != 0)
+ break;
+ b = b.Parent;
+ }
+
+ return (ToplevelBlock) b;
+ }
+ }
+
+ //
+ // Returns true if we ar ea child of `b'.
+ //
+ public bool IsChildOf (Block b)
+ {
+ Block current = this;
+
+ do {
+ if (current.Parent == b)
+ return true;
+ current = current.Parent;
+ } while (current != null);
+ return false;
+ }
}
//
+ // A toplevel block contains extra information, the split is done
+ // only to separate information that would otherwise bloat the more
+ // lightweight Block.
+ //
+ // In particular, this was introduced when the support for Anonymous
+ // Methods was implemented.
//
public class ToplevelBlock : Block {
+ //
+ // Pointer to the host of this anonymous method, or null
+ // if we are the topmost block
+ //
+ public ToplevelBlock Container;
+ CaptureContext capture_context;
+
+ Hashtable capture_contexts;
+
+ static int did = 0;
+
+ int my_id = did++;
+
+
+ public void RegisterCaptureContext (CaptureContext cc)
+ {
+ if (capture_contexts == null)
+ capture_contexts = new Hashtable ();
+ capture_contexts [cc] = cc;
+ }
+
+ public void CompleteContexts ()
+ {
+ if (capture_contexts == null)
+ return;
+
+ foreach (CaptureContext cc in capture_contexts.Keys){
+ cc.AdjustScopes ();
+ }
+ }
+
+ public CaptureContext ToplevelBlockCaptureContext {
+ get {
+ return capture_context;
+ }
+ }
+
+ //
+ // Parent is only used by anonymous blocks to link back to their
+ // parents
+ //
+ public ToplevelBlock (ToplevelBlock container, Parameters parameters, Location start) :
+ base (null, Flags.IsToplevel, parameters, start, Location.Null)
+ {
+ Container = container;
+ }
+
public ToplevelBlock (Parameters parameters, Location start) :
- base (null, parameters, start, Location.Null)
+ base (null, Flags.IsToplevel, parameters, start, Location.Null)
{
}
public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
- base (null, flags, parameters, start, Location.Null)
+ base (null, flags | Flags.IsToplevel, parameters, start, Location.Null)
{
}
+
+ public ToplevelBlock (Location loc) : base (null, Flags.IsToplevel, loc, loc)
+ {
+ }
+
+ public void SetHaveAnonymousMethods (Location loc, AnonymousMethod host)
+ {
+ if (capture_context == null)
+ capture_context = new CaptureContext (this, loc, host);
+ }
+
+ public CaptureContext CaptureContext {
+ get {
+ return capture_context;
+ }
+ }
}
public class SwitchLabel {
Expression label;
object converted;
public Location loc;
- public Label ILLabel;
- public Label ILLabelCode;
+
+ Label il_label;
+ bool il_label_set;
+ Label il_label_code;
+ bool il_label_code_set;
//
// if expr == null, then it is the default case.
}
}
+ public Label GetILLabel (EmitContext ec)
+ {
+ if (!il_label_set){
+ il_label = ec.ig.DefineLabel ();
+ il_label_set = true;
+ }
+ return il_label;
+ }
+
+ public Label GetILLabelCode (EmitContext ec)
+ {
+ if (!il_label_code_set){
+ il_label_code = ec.ig.DefineLabel ();
+ il_label_code_set = true;
+ }
+ return il_label_code;
+ }
+
//
// 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;
if (e is StringConstant || e is NullLiteral){
if (required_type == TypeManager.string_type){
converted = e;
- ILLabel = ec.ig.DefineLabel ();
return true;
}
}
ig.Emit (OpCodes.Ldloc, val);
EmitObjectInteger (ig, key);
SwitchLabel sl = (SwitchLabel) Elements [key];
- ig.Emit (OpCodes.Beq, sl.ILLabel);
+ ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
}
}
else
if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
{
SwitchLabel sl = (SwitchLabel) Elements [key];
- rgLabels [iJump] = sl.ILLabel;
+ rgLabels [iJump] = sl.GetILLabel (ec);
iKey++;
}
else
{
foreach (SwitchLabel sl in ss.Labels)
{
- ig.MarkLabel (sl.ILLabel);
- ig.MarkLabel (sl.ILLabelCode);
+ ig.MarkLabel (sl.GetILLabel (ec));
+ ig.MarkLabel (sl.GetILLabelCode (ec));
if (sl.Label == null)
{
ig.MarkLabel (lblDefault);
null_found = false;
for (int label = 0; label < label_count; label++){
SwitchLabel sl = (SwitchLabel) ss.Labels [label];
- ig.MarkLabel (sl.ILLabel);
+ ig.MarkLabel (sl.GetILLabel (ec));
if (!first_test){
ig.MarkLabel (next_test);
ig.MarkLabel (null_target);
ig.MarkLabel (sec_begin);
foreach (SwitchLabel sl in ss.Labels)
- ig.MarkLabel (sl.ILLabelCode);
+ ig.MarkLabel (sl.GetILLabelCode (ec));
if (mark_default)
ig.MarkLabel (default_target);
if (ec.InIterator){
conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
} else
((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
ArrayAccess.EmitLoadOpcode (ig, element_type);
if (ec.InIterator){
conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
} else
((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
ig.Emit (OpCodes.Call, get);
if (ec.InIterator){
conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
} else
((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
statement.Emit (ec);
static public Type obsolete_attribute_type;
static public Type conditional_attribute_type;
static public Type in_attribute_type;
+ static public Type anonymous_method_type;
static public Type cls_compliant_attribute_type;
static public Type typed_reference_type;
static public Type arg_iterator_type;
system_asynccallback_expr.Type = asynccallback_type;
system_iasyncresult_expr.Type = iasyncresult_type;
system_valuetype_expr.Type = value_type;
+
+ //
+ // These are only used for compare purposes
+ //
+ anonymous_method_type = typeof (AnonymousMethod);
}
//